目录

背景

在很久以前,我给我的Blog的主题加了 Hugo 的针对 Mermaid 和 Chart.js shortcodes 的支持。但是之支持迁入代码的方式。 如果文档很复杂,在Markdown里写大段的代码体验观感也不是很好。 而现在很多Markdown生态里是支持通过代码块的方式嵌入的。 同时现在很多插件可以直接按文件后缀编辑文件,然后直接指向文件的形式引用进来,这样可维护性会好很多。

我之前也懒得一个一个组件去研究和何种模式的测试搭配,现在AI很强了,刚好可以让它帮我把这个优化干了。 并且可以让这些插件都支持各种引入方式,确实是省事儿不少。

实测下来写代码还是Claude强,Google Gemini 3 Pro/Flash 也好,GPT 5.2/5.1-Codex-Max也好都会不同层度的瞎写。 Google Gemini 3 Pro写代码感觉比GPT 5.2强一点。但是通用总结,比如让它给我改Markdown的lingt错误Google Gemini 3 Pro比GPT 5.2差很多。

以下就是花了差不多2个小时,使用的Claude Opus 4.5模型,一口气消耗了Github copilot 75个高级Request(Copilot Pro+订阅的5%)。 最后完成了对 Mermaid、Chart.js、Excalidraw、draw.io/diagrams.net 的统一支持各种引用模式的支持。

AI自动生成的测试用例

This page demonstrates all supported methods for rendering diagrams and charts:

Each format supports 4 usage methods:

  1. Image syntax: ![](file.ext)
  2. Code block: ```language
  3. Shortcode with src: {{< type src="file" >}}
  4. Shortcode inline: {{< type >}}content{{< /type >}}

1. Draw.io/diagrams.net 测试

1.1 图片语法引用 .drawio 文件

下面是通过图片语法引用的 draw.io/diagrams.net 图表:

1.2 代码块方式(内嵌XML)

1.3 Shortcode 引用外部文件

1.4 Shortcode 内联方式


2. Mermaid 图片语法测试

2.1 图片语法引用 .mermaid 文件

下面是通过图片语法引用的 Mermaid 图表:

流程图

2.2 代码块方式

graph LR
    A[代码块方式] --> B{工作正常?}
    B -->|是| C[蓝色流程图]
    B -->|否| D[检查代码]
  

2.3 Shortcode 引用外部文件

Loading mermaid diagram...

2.4 Shortcode 内联方式

graph TD
    A[Shortcode 内联] --> B[Mermaid 图表]
    B --> C{正常显示?}
    C -->|是| D[成功]
    C -->|否| E[检查]
  

3. Excalidraw 图片语法测试

3.1 图片语法引用 .excalidraw 文件

下面是通过图片语法引用的 Excalidraw 图表:

测试图表

3.2 Shortcode 引用外部文件

Loading excalidraw...

3.3 Shortcode 内联方式

Loading excalidraw...

3.4 代码块方式

Loading excalidraw libraries...

4. Chart.js 测试

4.1 图片语法引用 .chart.json 文件

下面是通过图片语法引用的 Chart.js 图表:

销售图表

4.2 Shortcode 引用外部文件

Chart 6

4.3 Shortcode 内联方式

Chart 7

4.4 代码块方式

Chart

4.5 代码块方式 (chartjs 语法)

Chart

5. ECharts 测试

ECharts 的扩展是增量式的:默认只加载核心包(含所有 2D 图表),需要 3D 时通过 extensions="gl" 追加 echarts-gl。下面分别用 2D 数学函数图 y = sin(x) 和 3D 曲面图 z = sin(sqrt(x^2 + y^2)) 覆盖四种引入方式。

5.1 图片语法引用 .echarts.json(2D 数学函数)

最小包即可渲染:

ECharts 二维函数图

5.2 代码块方式(2D 数学函数)

ECharts

5.3 Shortcode 引用外部文件(3D 曲面,gl 扩展)

3D 曲面需要 echarts-gl,通过 extensions="gl" 声明:

ECharts 8

5.4 Shortcode 内联方式(3D 曲面,gl 扩展)

ECharts 9

5.5 代码块方式(3D 曲面,gl 扩展)

代码块里通过 info string 的属性声明扩展:```echarts {extensions="gl"}。

ECharts

6. Plotly.js 测试

Plotly.js 的 partial bundle(basic / gl3d / cartesian 等)互相覆盖全局 Plotly,无法叠加。因此加载策略是:默认只加载最小包(plotly-basic,含 scatter/bar/pie);当页面里同时出现最小特性图表和声明了扩展的图表(如 3D 的 extensions="gl3d")时,自动升级到完整包。本页同时有 2D 和 3D,所以会用完整包渲染。

6.1 图片语法引用 .plotly.json(2D 数学函数)

Plotly 二维函数图

6.2 代码块方式(2D 数学函数)

Plotly

6.3 Shortcode 引用外部文件(3D 曲面,gl3d 扩展)

Plotly 10

6.4 Shortcode 内联方式(3D 曲面,gl3d 扩展)

Plotly 11

6.5 代码块方式(3D 曲面,gl3d 扩展)

Plotly

7. D2 测试

D2 使用构建时渲染。2026-10-07 核对官方 @d2lang/d2@0.1.34 发布包时,浏览器入口 dist/browser/index.js 的未压缩大小是 11,514,165 字节(约 11.5 MB),本地以 gzip 默认级别压缩后为 8,559,566 字节(约 8.56 MB),具体数值随压缩实现略有差异;这是该入口文件的测量值,实际传输量还受 CDN 编码和缓存影响。D2.js 能在浏览器中通过 Web Worker 和 WASM 编译、渲染图表,适合需要在线编辑的页面。本站主要展示文章里的静态图,采用 CLI 输出 SVG,可以省去读者端的编译器下载和运行。D2.js 接入文档、发布包文件列表 和 SVG 导出文档 分别说明这几项依据。

四种入口都由 Hugo 模板收集内容,构建脚本调用 D2 CLI 后,Hugo 将 SVG 内联进页面。这样保留了 SVG 中的链接和 tooltip;如果使用普通 <img> 引用 SVG,链接不会生效,具体差异见 D2 导出 FAQ。同一文件的不同引用使用不同的 SVG ID,避免本页重复引用时相互干扰。窄屏下保留 SVG 的原始尺寸并允许横向滚动,避免把文字缩到难以阅读。

本地需要 Python 3.9+、Hugo 和 D2 CLI;本节使用 D2 v0.9.0。按 D2 安装文档 安装后,构建命令如下。--buildDrafts 等 Hugo 构建参数原样传给正式构建;CI 和 deploy.sh 也调用同一脚本。

python scripts/build-d2.py --buildDrafts --renderToMemory

若工具不在 PATH 中,可设置 HUGO_BINARY 和 D2_BINARY 为可执行文件路径。脚本每次重新渲染引用的图表,包含相对导入的 .d2 文件和本地图片变更;生成文件位于已忽略的 assets/d2-generated/。修改 D2 内容或依赖后,应重新执行该脚本,再用 hugo server 预览,脚本自身不提供 watch 模式。直接执行 Hugo 时,缺少所需 SVG 会报错,不会退回显示源码或占位图。

src 只接受相对文章目录的本地路径;外部文件中的相对导入和图片以该文件所在目录为基准,内联图以文章目录为基准。布局、主题和留白等参数写在 D2 的 vars.d2-config 中。当前接入只导出根画板;多画板切换动画和 PNG 导出不在本节测试范围内;单画板的虚线流动动画见下方示例。图中使用远程 icon 时,构建仍需访问该地址;本节用例没有远程图像依赖。

7.1 图片语法引用 .d2 文件

此图展示 D2 文件、构建时渲染和 SVG 输出的关系。图片标题中的尺寸参数沿用其他图表的写法。

![D2 文件的 SVG 生成流程](2602-test-diagram.d2 "D2 离线渲染 {max-width=720px}")

D2 文件同一个文件由图片语法和 Shortcode 分别引用构建时渲染SVG同一个文件由图片语法和 Shortcode 分别引用

7.2 代码块方式

这个用例检查代码块属性、中文标签,以及 D2 字符串中的 < 和 & 是否正确保留。

代码块 <D2> & SVG中文标签编译

7.3 Shortcode 引用外部文件

这里再次引用图片语法使用的文件,检查重复图表的 ID 是否独立。图中 SVG 节点链接到 D2 导出文档,D2 文件 节点包含 tooltip。

{{< d2 src="2602-test-diagram.d2" alt="D2 外部文件 Shortcode 测试" max-width="720px" >}}{{< /d2 >}}
D2 文件同一个文件由图片语法和 Shortcode 分别引用构建时渲染SVG同一个文件由图片语法和 Shortcode 分别引用

7.4 Shortcode 内联方式

内联用例使用容器和数据库形状,检查 Shortcode 内容与样式参数的组合。

服务客户端API数据库查询请求

7.5 虚线流动动画

7.1 和 7.3 复用的 .d2 文件已给连线添加以下属性。动画由 SVG 自带的 CSS 执行,不需要读者下载 D2 运行时;系统设置“减少动态效果”时,本站会停止这些动画。这里的运动用于示意数据流向,并不代表实际流量。

source -> build -> svg: {
  style.animated: true
  style.stroke-dash: 3
}

animated 和 stroke-dash 是 D2 的原生样式,具体约束见 D2 连线样式。

PlantUML 测试

PlantUML 也在构建时生成内联 SVG,沿用 python scripts/build-d2.py 入口;脚本名称为兼容原有部署命令而保留。需要 Java、PlantUML JAR 和 Graphviz,设置 PLANTUML_JAR 为本地 JAR 路径;Java 不在 PATH 中时设置 JAVA_BINARY。本节采用 PlantUML 1.2026.8,CI 固定同一版本并校验 SHA-256。

每个入口放一组 @startuml / @enduml。外部文件只接受相对文章的本地路径;本地 !include 相对该文件解析。构建器使用 ALLOWLIST 安全配置,允许仓库内的文件和内置主题,不允许远程 !includeurl。源码错误会终止构建,不把错误图片当成正常 SVG 发布。

图片语法引用 .puml 文件

此图说明文章、构建脚本和 PlantUML 渲染器之间的数据传递。.plantuml 后缀也由同一入口识别。

构建时生成 PlantUML 图表Markdown ..Hugo . PythonPlantUMLMarkdown 文章Hugo / PythonPlantUMLMarkdown 文章Hugo / PythonPlantUML收集本地源码渲染 SVG返回图表页面内联 SVG

代码块方式

这个组件图检查中文、Graphviz 布局以及代码块尺寸属性。

文章页面业务服务数据库请求查询

Shortcode 引用外部文件

重复引用同一张时序图,检查各次输出的 SVG ID 是否独立。

构建时生成 PlantUML 图表Markdown ..Hugo . PythonPlantUMLMarkdown 文章Hugo / PythonPlantUMLMarkdown 文章Hugo / PythonPlantUML收集本地源码渲染 SVG返回图表页面内联 SVG

Shortcode 内联方式

这个状态图使用 !theme cerulean-outline 覆盖全站默认的 plain,检查文章级主题是否保留。

待处理已完成渲染成功

图表主题

页面外框使用深蓝灰背景,正文和图表仍在浅色阅读区内。选主题时比较了深色、灰度和浅色方案,最终采用浅色画布,流程图和数值图以蓝青色为主,PlantUML 使用深色线条;数值图保留多个可辨认的系列颜色,文章显式指定的颜色仍然优先。运行时默认值集中在主题的 partials/diagram-theme.html,没有额外加载主题包。

工具采用的主题或默认值取舍与覆盖方式
Mermaidbase + 蓝青色 themeVariables相比打印取向的 neutral 增加浅色分组;可通过图内配置覆盖。主题文档
D2本节示例使用 Cool Classics,theme-id: 4使用内置冷色主题,参数留在 .d2 源码中,其他文章不被强制改色。主题列表
PlantUMLplain白底、深色文字与线条、无阴影;优先保证时序图标签的对比度。图内 !theme 可覆盖。内置主题
ECharts本地蓝青色调色板、浅灰网格使用原生主题对象,不下载额外主题脚本;option 中的显式样式优先。样式机制
Plotly.js本地浅色 layout.template默认设置调色板、字体和坐标网格;图内 layout 或 template 可覆盖。模板机制
Chart.js系统字体、深灰文字、浅灰网格保留内置 Colors 插件和数据集的显式配色,避免改掉数据的颜色含义。颜色机制
Excalidraw / Draw.io保留文件内的主题和颜色草图、架构图的局部颜色常有含义,网站不对导出的画布做滤镜或强制反色。

8. 测试结果

类型图片语法代码块方式Shortcode (内联)Shortcode (src)
Draw.io/diagrams.net✓ .drawio✓ drawio✓✓
Mermaid✓ .mermaid✓ mermaid✓✓
Excalidraw✓ .excalidraw✓ excalidraw✓✓
Chart.js✓ .chart.json✓ chart / chartjs✓✓
ECharts✓ .echarts.json✓ echarts✓✓
Plotly.js✓ .plotly.json✓ plotly✓✓
D2✓ .d2✓ d2✓✓
PlantUML✓ .puml / .plantuml✓ plantuml✓✓

D2 在 Windows 上使用 Hugo v0.167.0 和 D2 v0.9.0 验证:四种入口均生成 SVG;Edge 禁用 JavaScript 和外部请求后仍能显示图表,1280px 与 375px 视口下分别检查了完整显示和横向滚动。相对导入、本地图片、依赖变更重渲染、重复 ID,以及缺文件和语法错误阻止正式构建的检查也已通过。CI 已接入相同构建脚本和集成测试;远程 GitHub Actions 尚未运行。

本次新增检查使用 PlantUML 1.2026.8:四种入口、Graphviz 组件图、中文、本地 !include 重渲染、主题覆盖、重复 SVG ID、清理过期图表,以及缺文件、语法错误和远程 include 阻止发布的测试通过。D2 的虚线偏移随时间变化,并在“减少动态效果”模式下停止。

页面布局使用 Playwright 的 Chromium、Firefox 和 WebKit,在 320–2560px 的 13 组视口下检查首页、本文和 About 页,共 117 组页面/视口组合。模拟广告填充、未填充和列表整行插入均未造成页面横向溢出或卡片重叠;同时启用所有旧手动广告配置项时,首页最多两个、文章末尾一个,noad 页面不输出广告位。这里验证的是模拟注入,不代表真实 AdSense 投放或后台自动广告配置已经验证。

图表主题使用 Mermaid 12.1.0、Chart.js 4.5.1、ECharts 6.1.0 和 Plotly.js 4.1.2 的实际库在 Chromium 中验证,检查了 320px、390px、768px 和 1440px 宽度。测试库来自本地 npm 包,没有将 CDN 网络可用性或真实移动设备纳入结论。截图检查中修复了 Plotly 占位文字挤占绘图区的问题,并按 Plotly v3 迁移文档 将本节的图表及坐标轴标题改成 title.text。