---
date: 2026-05-10
type: tooling
tags: [mermaid, markdown, gitea, rendering]
---

# Mermaid 节点标签里的 `#` 是注释符——引用 markdown header 时整段 diagram 会渲不出来

## 现象

`docs/ops/weekly-retro-architecture.md` 的决策树 diagram 在 Gitea 1.26 web UI 上**整块不渲染**，显示为原始 code block（其他 3 个 diagram 正常）。

肇事节点：

```mermaid
Q2{body 含<br/>'## 候选改进' 段?}
```

意图：表达"判断 body 是否包含名为 `## 候选改进` 的 markdown H2 段"。

## 为什么挂

mermaid 把 `#` 视为**单行注释起始符**（同 Python / shell）。在没有用 `"..."` 包住的节点标签里，从 `#` 到行尾被吞成注释，**剩余 mermaid 语法被吞掉一段，整段图解析失败**。

测试佐证（直接调 Gitea `/api/v1/markdown` 渲染）：

```bash
curl -X POST -H "Authorization: token $GITEA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"Text":"```mermaid\nflowchart TD\nQ2{body 含<br/>\\\"## 候选改进\\\" 段?}\n```","Mode":"gfm"}' \
  "http://43.130.59.228/api/v1/markdown"
```

Gitea 后端只是按 `language-mermaid` 透传给前端 JS（`<code class="chroma language-mermaid is-loading">...</code>`），**前端 mermaid.js 实际渲染时报错**，于是停留在 raw 代码状态。

## 解法：所有节点标签强制双引号

```mermaid
Q2{"body 含<br/>「候选改进」段 ?"}
```

两步：

1. 整个标签包 `"..."`——这样 mermaid 不再把内容当语法 token 解析
2. 把 `'## ...'` 这种引用 markdown header 的写法**换成中文方括号** `「...」`——彻底避开 `#`，即使脑短路忘记加引号也不踩坑

## 通用规则（防御性 mermaid 写法）

写 mermaid 节点标签时一律加 `"..."`，特别是含以下字符时**必须加**：

| 字符 | mermaid 含义 | 不加引号会怎样 |
|---|---|---|
| `#` | 单行注释起始 | 后面整行被吞 |
| `(`, `)` | 部分形状语法 | 触发括号配对解析 |
| `;` | 语句分隔 | 节点被截断 |
| `{`, `}` | diamond 形状 | 嵌套时报错 |
| `'`, `"` | 引号嵌套 | quoting 状态错乱 |
| `\|` | 边标签分隔 | 解析为分隔符 |

**懒人规则**：所有 `[]` / `{}` / `()` 节点标签 **一律包 `"..."`**——只多 4 个字节，零风险。

## 副产物：连接线 label 也建议引号

```
A -.->|"label text"| B    -- 推荐
A -.->|label text| B      -- 大多数情况 OK 但碰到特殊字符同样翻车
```

## 不影响的部分

mermaid `sequenceDiagram` 的消息文本（`A->>B: text`）解析器更宽松，`#` / `()` 一般不挂。但保险起见也别在那里写 `'...'` quoted 子串。

## Gitea 的渲染链（背景知识）

- 后端：`gfm` markdown → 透传 `language-mermaid` 代码块（不解析内容）
- 前端：JS 加载 mermaid 库 → 替换 `<code.is-loading>` 为 SVG
- 失败时：`is-loading` class 不被移除，用户看到原始 code

所以"整块没渲染"的根因永远是**客户端 mermaid.js 解析失败**，跟 Gitea 后端、跟网络、跟 markdown extensions 都无关。
