Skip to content

Commit eb6cc1f

Browse files
zhonghuiCopilot
andcommitted
fix: switch mermaid EPUB rendering to theme:'base' for guaranteed contrast
theme:'default' has its own CSS variable cascade that headless Chrome's dark mode can partially override even when themeVariables are set, causing low-contrast or white-on-white node text. theme:'base' inherits NO CSS — every color token is determined exclusively by themeVariables, making contrast deterministic regardless of OS settings. Changes: - scripts/build-epub.js: theme:'base', explicit high-contrast themeVariables (primaryColor:'#C8E6FA', primaryTextColor:'#111111', + secondary/tertiary/ actor/cluster/edge colors all set to '#111111' text on light fills) - agents/10-bookbinder.md + i18n/{en,ja,zh-TW}: Pitfall 1 rewritten, table entries, warning blockquotes, checklists, invocation templates all updated to reflect theme:'base' requirement Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 05a234d commit eb6cc1f

5 files changed

Lines changed: 131 additions & 85 deletions

File tree

agents/10-bookbinder.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,42 +134,48 @@ EPUB 阅读器普遍不支持 JavaScript,因此 Mermaid 图表**必须在构
134134

135135
| 情境 | 处理方式 |
136136
|------|----------|
137-
| 系统已安装 `mmdc`(Mermaid CLI) | 调用 `mmdc -i input.mmd -o output.svg --theme default --backgroundColor "#FFFFF0"` 预渲染 |
137+
| 系统已安装 `mmdc`(Mermaid CLI) | 使用 `-c config.json`(含 `theme: 'base'` + 完整 `themeVariables`预渲染(见坑 1) |
138138
| 未安装 `mmdc` | 将 Mermaid 代码块以 `<pre class="mermaid-source">` 形式保留,并添加提示注释 |
139139

140140
> 建议:如需生成 EPUB,提前全局安装 `npm install -g @mermaid-js/mermaid-cli`
141141
142-
> ⚠️ **必须显式指定 `--theme default`**mmdc 默认跟随系统深色模式。若系统处于 Dark Mode,mmdc 会自动切换到暗色主题(白色文字),嵌入浅色背景 EPUB 后文字不可见。始终指定 `--theme default`(或 `neutral`)以确保深色文字,并将 `--backgroundColor` 设为书籍背景色(如 `"#FFFFF0"`)。
142+
> ⚠️ **Mermaid config 文件中必须使用 `theme: 'base'`**`theme: 'default'` 有 CSS 层叠,深色模式下即使设置 `themeVariables` 也可能造成对比度不足。`base` 主题完全由 `themeVariables` 控制(见坑 1)。
143143
144144
## ⚠️ EPUB 构建避坑清单
145145

146146
以下为实践中踩过的坑,**构建脚本实现时必须规避**
147147

148-
### 坑 1:Mermaid 文字颜色不可见
148+
### 坑 1:Mermaid 文字颜色不可见 / 对比度极低
149149

150-
- **现象**:生成的 EPUB 中 Mermaid 图表文字为白色,在浅色背景下完全不可见;节点背景色极浅(接近白色)
151-
- **根因**`mmdc` 在 macOS/Linux 深色模式下,即使传入 `--theme default`,puppeteer 的 headless Chrome 仍可能使用暗色主题 CSS 变量(mmdc ≥ 10.x 尤为明显)
152-
- **修复****必须同时使用 `-c config.json` 传入显式 `themeVariables`**,单靠 `--theme default` 不可靠
150+
- **现象**:生成的 EPUB 中 Mermaid 图表文字为白色或与节点背景颜色过于接近,在浅色背景下完全不可见
151+
- **根因一**`theme: 'default'` 有自身 CSS 变量层叠,headless Chrome 的暗色模式仍可能部分覆盖 `themeVariables`
152+
- **根因二**`primaryColor` 等未显式设置的变量会 fallback 到 `default` 主题默认值,可能与 `primaryTextColor` 对比度极低
153+
- **修复**:使用 `theme: 'base'`(而非 `'default'`)。`base` 主题完全由 `themeVariables` 控制,不继承任何 CSS 层叠,专为程序化渲染设计
153154

154155
```js
155-
// ✓ 正确:写 config 文件 + 传入显式 themeVariables
156+
// ✓ 正确:使用 theme:'base' + 完整的高对比度 themeVariables
156157
const cfgFile = path.join(tmpDir, 'mmd-config.json');
157158
fs.writeFileSync(cfgFile, JSON.stringify({
158-
theme: 'default',
159+
theme: 'base', // 关键:base 不继承 CSS,完全由 themeVariables 控制
159160
themeVariables: {
160-
background: THEME.pageBg, // SVG 背景 = 书籍背景色
161-
primaryColor: '#E8E4FF', // 节点填充色(可见的淡紫)
162-
primaryTextColor: THEME.textColor, // 节点内文字 = 书籍正文色(关键!)
163-
primaryBorderColor: '#7C5CBF',
164-
lineColor: '#555555',
165-
edgeLabelBackground: THEME.pageBg,
166-
fontSize: '16px',
161+
background: THEME.pageBg,
162+
primaryColor: '#C8E6FA', // 浅蓝填充 — 与深色文字对比明显
163+
primaryTextColor: '#111111', // 近黑色文字 — 最大对比度
164+
primaryBorderColor: '#2B7BC2',
165+
secondaryColor: '#D4EDDA', secondaryTextColor: '#111111',
166+
tertiaryColor: '#FFF3CD', tertiaryTextColor: '#111111',
167+
lineColor: '#444444',
168+
edgeLabelBackground: THEME.pageBg,
169+
clusterBkg: THEME.pageBg,
170+
actorBkg: '#C8E6FA', actorTextColor: '#111111',
171+
titleColor: THEME.textColor,
172+
fontSize: '16px',
167173
},
168174
}), 'utf8');
169175
execSync(`mmdc -i "${inFile}" -o "${outFile}" -c "${cfgFile}" --backgroundColor "${THEME.pageBg}" --quiet`);
170176

171-
// ✗ 错误:仅用 --theme default,深色模式下仍可能产生白色文字
172-
execSync(`mmdc -i "${inFile}" -o "${outFile}" --theme default --backgroundColor "#FFFFF0"`);
177+
// ✗ 错误:theme:'default' 有 CSS 层叠,对比度无法保证
178+
fs.writeFileSync(cfgFile, JSON.stringify({ theme: 'default', themeVariables: { ... } }));
173179
```
174180

175181
### 坑 2:SVG 内 `<br />` 被破坏为 `<br / />`(无效 XML)
@@ -336,7 +342,7 @@ pre { white-space: pre-wrap; word-break: break-all; }
336342
- [ ] (EPUB模式)`content.opf``nav.xhtml``toc.ncx` 均正确生成
337343
- [ ] (EPUB模式)每章 XHTML 的 `<title>` 与 nav/ncx 条目均使用真实章节标题(非文件名)
338344
- [ ] (EPUB模式)封面 SVG(`cover.svg`)已生成,`cover.xhtml` 为书脊第一项
339-
- [ ] (EPUB模式)Mermaid 图表已预渲染为 SVG(`--theme default` 确保深色文字,非白色)或以代码形式优雅降级
345+
- [ ] (EPUB模式)Mermaid 图表已以 `-c config.json``theme: 'base'` + `themeVariables`)预渲染为 SVG,或以代码形式优雅降级
340346

341347
## 配色主题选择(Phase 5 启动前必询问)
342348

@@ -410,7 +416,7 @@ node scripts/build.js
410416
1. Markdown → HTML/XHTML转换
411417
2. **Mermaid 图表渲染**:
412418
- HTML模式:` ```mermaid ` 块通过引入 Mermaid.js(CDN)渲染为交互式图表
413-
- EPUB模式:必须通过 `-c config.json`(含显式 `themeVariables`)调用 `mmdc` 预渲染为 SVG;**`--theme default` 单独使用在深色模式机器上仍可能产生白色文字(mmdc ≥ 10.x)**,必须配合 themeVariables 写入 `primaryTextColor`;未安装 mmdc 时优雅降级为代码块
419+
- EPUB模式:必须通过 `-c config.json`(`theme: 'base'` + 完整 `themeVariables`)调用 `mmdc` 预渲染为 SVG;`base` 主题无 CSS 层叠,高对比度有保障;未安装 mmdc 时优雅降级为代码块
414420
3. ASCII图表 → SVG自动转换(兼容存量内容,支持{{SVG检测类型数}}种类型)
415421
4. 代码高亮
416422
5. 护眼配色(暖白背景、柔和文字)

i18n/en/agents/10-bookbinder.md

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -146,42 +146,48 @@ EPUB readers generally do not support JavaScript, so Mermaid diagrams **must be
146146

147147
| Scenario | Handling |
148148
|----------|----------|
149-
| `mmdc` (Mermaid CLI) is installed | Use `-c config.json` with explicit `themeVariables` (see Pitfall 1) to pre-render |
149+
| `mmdc` (Mermaid CLI) is installed | Use `-c config.json` with `theme: 'base'` + explicit `themeVariables` (see Pitfall 1) to pre-render |
150150
| `mmdc` is not installed | Preserve Mermaid code as `<pre class="mermaid-source">` with a fallback comment |
151151

152152
> Recommendation: Install `npm install -g @mermaid-js/mermaid-cli` before generating EPUB.
153153
154-
> ⚠️ **`--theme default` alone is not enough on mmdc ≥ 10.x**: even with `--theme default`, puppeteer's headless Chrome may still apply dark-mode CSS variables, producing white text. Always use a JSON config file that explicitly sets `primaryTextColor`. See Pitfall 1.
154+
> ⚠️ **Always use `theme: 'base'` in the Mermaid config file**: `--theme default` has its own CSS cascade that dark-mode headless Chrome can partially override, causing low-contrast or white text. The `base` theme is 100% controlled by your `themeVariables`. See Pitfall 1.
155155
156156
## ⚠️ EPUB Build Pitfalls
157157

158158
These are real bugs caught in practice. **The build script must avoid them**:
159159

160-
### Pitfall 1: Mermaid Text Invisible (White on Light Background)
160+
### Pitfall 1: Mermaid Text Invisible or Low-Contrast
161161

162-
- **Symptom**: Mermaid diagram text is white and invisible on light EPUB pages; node backgrounds are nearly white
163-
- **Root cause**: On mmdc ≥ 10.x, even `--theme default` may not override dark-mode CSS variables inside the headless browser. `primaryTextColor` defaults to white when dark mode is active.
164-
- **Fix**: Write an explicit Mermaid JSON config file with `themeVariables`, and pass it with `-c`. Do not rely on `--theme default` alone.
162+
- **Symptom**: Mermaid diagram text is white/invisible, or text and node background colors are so similar they're unreadable
163+
- **Root cause 1**: `theme: 'default'` has its own CSS variable cascade; headless Chrome's dark mode can still partially override `themeVariables`
164+
- **Root cause 2**: Unset variables (e.g. `secondaryColor`) fall back to `default` theme values that may have very low contrast with your `primaryTextColor`
165+
- **Fix**: Use `theme: 'base'` instead of `'default'`. The `base` theme is 100% controlled by `themeVariables` — it inherits no CSS cascade and is designed for programmatic rendering.
165166

166167
```js
167-
// ✓ Correct: explicit themeVariables in a config file
168+
// ✓ Correct: theme:'base' + explicit high-contrast themeVariables
168169
const cfgFile = path.join(tmpDir, 'mmd-config.json');
169170
fs.writeFileSync(cfgFile, JSON.stringify({
170-
theme: 'default',
171+
theme: 'base', // key: no CSS inheritance — everything from themeVariables
171172
themeVariables: {
172-
background: THEME.pageBg, // SVG canvas background
173-
primaryColor: '#E8E4FF', // node fill — visible lavender
174-
primaryTextColor: THEME.textColor, // text inside nodes (critical!)
175-
primaryBorderColor: '#7C5CBF',
176-
lineColor: '#555555',
177-
edgeLabelBackground: THEME.pageBg,
178-
fontSize: '16px',
173+
background: THEME.pageBg,
174+
primaryColor: '#C8E6FA', // light-blue fill — clearly visible
175+
primaryTextColor: '#111111', // near-black — maximum contrast
176+
primaryBorderColor: '#2B7BC2',
177+
secondaryColor: '#D4EDDA', secondaryTextColor: '#111111',
178+
tertiaryColor: '#FFF3CD', tertiaryTextColor: '#111111',
179+
lineColor: '#444444',
180+
edgeLabelBackground: THEME.pageBg,
181+
clusterBkg: THEME.pageBg,
182+
actorBkg: '#C8E6FA', actorTextColor: '#111111',
183+
titleColor: THEME.textColor,
184+
fontSize: '16px',
179185
},
180186
}), 'utf8');
181187
execSync(`mmdc -i "${inFile}" -o "${outFile}" -c "${cfgFile}" --backgroundColor "${THEME.pageBg}" --quiet`);
182188

183-
// ✗ Wrong: --theme default alone fails on dark-mode machines (mmdc ≥ 10)
184-
execSync(`mmdc -i diagram.mmd -o diagram.svg --theme default --backgroundColor "#FFFFF0"`);
189+
// ✗ Wrong: theme:'default' has CSS cascade — contrast cannot be guaranteed
190+
fs.writeFileSync(cfgFile, JSON.stringify({ theme: 'default', themeVariables: { ... } }));
185191
```
186192

187193
### Pitfall 2: `<br />` in SVG Corrupted to `<br / />` (Invalid XML)
@@ -377,7 +383,7 @@ Convert all Markdown chapters into a beautiful e-book (HTML and/or EPUB, dependi
377383
1. Markdown → HTML/XHTML conversion
378384
2. **Mermaid diagram rendering**:
379385
- HTML mode: ` ```mermaid ` blocks rendered as interactive charts via Mermaid.js (CDN)
380-
- EPUB mode: **must use `-c config.json` with explicit `themeVariables`** (especially `primaryTextColor`) to pre-render Mermaid as SVG; `--theme default` alone is unreliable on mmdc ≥ 10.x in dark mode; gracefully degrade to code blocks if mmdc is unavailable
386+
- EPUB mode: **must use `-c config.json` with `theme: 'base'` and explicit `themeVariables`** to pre-render Mermaid as SVG; `base` theme has no CSS cascade so contrast is guaranteed; gracefully degrade to code blocks if mmdc is unavailable
381387
3. ASCII diagrams → SVG auto-conversion (for legacy content; supports {{SVG检测类型数}} types)
382388
4. Code syntax highlighting
383389
5. Eye-friendly color scheme (warm white background, soft text)

i18n/ja/agents/10-bookbinder.md

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -146,42 +146,48 @@ EPUBリーダーは一般的にJavaScriptをサポートしないため、Mermai
146146

147147
| 状況 | 処理方法 |
148148
|------|----------|
149-
| `mmdc`(Mermaid CLI)がインストール済み | `-c config.json` で明示的な `themeVariables` を指定して事前レンダリング(落とし穴 1 参照) |
149+
| `mmdc`(Mermaid CLI)がインストール済み | `-c config.json``theme: 'base'` + 完全な `themeVariables`)で事前レンダリング(落とし穴 1 参照) |
150150
| `mmdc` 未インストール | Mermaidコードを `<pre class="mermaid-source">` として保持し、フォールバックコメントを追加 |
151151

152152
> 推奨:EPUB生成前に `npm install -g @mermaid-js/mermaid-cli` をインストールしてください。
153153
154-
> ⚠️ **mmdc ≥ 10.x では `--theme default` 単独では不十分**headless Chrome がダークモードCSSを適用し白文字になることがあります。`primaryTextColor` を含む `themeVariables` を JSON configファイルで明示してください(落とし穴 1 参照)。
154+
> ⚠️ **Mermaid configファイルでは必ず `theme: 'base'` を使うこと**`theme: 'default'` はCSSカスケードを持ちダークモードで部分的に上書きされ、コントラスト不足や白文字が生じます。`base` テーマは `themeVariables` で100%制御されます(落とし穴 1 参照)。
155155
156156
## ⚠️ EPUBビルドの落とし穴
157157

158158
実際に遭遇したバグです。**ビルドスクリプト実装時に必ず回避してください**
159159

160-
### 落とし穴 1:Mermaid文字が見えない(明るい背景に白文字)
160+
### 落とし穴 1:Mermaid文字が見えない、またはコントラストが極めて低い
161161

162-
- **症状**:生成されたEPUBのMermaidダイアグラムの文字が白色で見えない;ノードの背景色がほぼ白に近い
163-
- **原因**:mmdc ≥ 10.x では `--theme default` を指定しても、headless Chrome がダークモードのCSS変数を適用し `primaryTextColor` が白になることがある
164-
- **修正**`-c config.json``themeVariables` を明示したJSONファイルを渡す。`--theme default` 単独に頼らないこと
162+
- **症状**:生成されたEPUBのMermaidダイアグラムの文字が白色で見えない、またはテキストとノード背景色が近すぎて判読不能
163+
- **原因 1**`theme: 'default'` は独自のCSS変数カスケードを持ち、headless Chromeのダークモードが部分的に `themeVariables` を上書きすることがある
164+
- **原因 2**:未設定の変数(例:`secondaryColor`)が `default` テーマのデフォルト値にフォールバックし、`primaryTextColor` との対比が極めて低くなる可能性がある
165+
- **修正**`'default'` の代わりに `theme: 'base'` を使う。`base` テーマは `themeVariables` によって100%制御され、CSSカスケードを一切継承しない。プログラム的なレンダリング向けに設計されている
165166

166167
```js
167-
// ✓ 正しい:config ファイルで themeVariables を明示
168+
// ✓ 正しい:theme:'base' + 完全な高コントラスト themeVariables
168169
const cfgFile = path.join(tmpDir, 'mmd-config.json');
169170
fs.writeFileSync(cfgFile, JSON.stringify({
170-
theme: 'default',
171+
theme: 'base', // 重要:CSS継承なし — すべてthemeVariablesで制御
171172
themeVariables: {
172-
background: THEME.pageBg, // SVG 背景色
173-
primaryColor: '#E8E4FF', // ノード塗りつぶし色(可視性の高い薄紫)
174-
primaryTextColor: THEME.textColor, // ノード内文字色(重要!)
175-
primaryBorderColor: '#7C5CBF',
176-
lineColor: '#555555',
177-
edgeLabelBackground: THEME.pageBg,
178-
fontSize: '16px',
173+
background: THEME.pageBg,
174+
primaryColor: '#C8E6FA', // 薄い水色の塗りつぶし — 明確に可視
175+
primaryTextColor: '#111111', // ほぼ黒 — 最大コントラスト
176+
primaryBorderColor: '#2B7BC2',
177+
secondaryColor: '#D4EDDA', secondaryTextColor: '#111111',
178+
tertiaryColor: '#FFF3CD', tertiaryTextColor: '#111111',
179+
lineColor: '#444444',
180+
edgeLabelBackground: THEME.pageBg,
181+
clusterBkg: THEME.pageBg,
182+
actorBkg: '#C8E6FA', actorTextColor: '#111111',
183+
titleColor: THEME.textColor,
184+
fontSize: '16px',
179185
},
180186
}), 'utf8');
181187
execSync(`mmdc -i "${inFile}" -o "${outFile}" -c "${cfgFile}" --backgroundColor "${THEME.pageBg}" --quiet`);
182188

183-
// ✗ 誤り:--theme default 単独ではダークモード機で白文字になる(mmdc ≥ 10)
184-
execSync(`mmdc -i diagram.mmd -o diagram.svg --theme default --backgroundColor "#FFFFF0"`);
189+
// ✗ 誤り:theme:'default' はCSSカスケードがあり、コントラストを保証できない
190+
fs.writeFileSync(cfgFile, JSON.stringify({ theme: 'default', themeVariables: { ... } }));
185191
```
186192

187193
### 落とし穴 2:SVG内の `<br />``<br / />`(無効XML)に変換される
@@ -306,7 +312,7 @@ ASCIIダイアグラムをSVGに変換する際のカード/ノードのカラ
306312
- [ ] (EPUBモード)`content.opf``nav.xhtml``toc.ncx` が正しく生成されている
307313
- [ ] (EPUBモード)各章XHTMLの `<title>` とnav/ncxの項目に実際の章節タイトルが使われている(ファイル名でない)
308314
- [ ] (EPUBモード)表紙SVG(`cover.svg`)が生成され、`cover.xhtml` がスパインの先頭になっている
309-
- [ ] (EPUBモード)Mermaidダイアグラムが `-c config.json``themeVariables` 明示)でSVGとして事前レンダリング済み、またはコードブロックとして適切にフォールバックしている
315+
- [ ] (EPUBモード)Mermaidダイアグラムが `-c config.json``theme: 'base'` + `themeVariables` 明示)でSVGとして事前レンダリング済み、またはコードブロックとして適切にフォールバックしている
310316

311317
## 完了マーカー
312318

@@ -339,7 +345,7 @@ ASCIIダイアグラムをSVGに変換する際のカード/ノードのカラ
339345
1. Markdown → HTML/XHTML変換
340346
2. **Mermaidダイアグラムのレンダリング**:
341347
- HTMLモード:` ```mermaid ` ブロックを Mermaid.js(CDN)でインタラクティブなダイアグラムとしてレンダリング
342-
- EPUBモード:`-c config.json` で `themeVariables`(特に `primaryTextColor`)を明示したJSONを渡してSVG事前レンダリング;**`--theme default` 単独は mmdc ≥ 10.x のダークモードで無効**(白文字になる);mmdc 未インストール時はコードブロックとしてフォールバック
348+
- EPUBモード:`-c config.json` で `theme: 'base'` と `themeVariables` を明示したJSONを渡してSVG事前レンダリング;`base` テーマはCSSカスケードがないためコントラストを保証できる;mmdc 未インストール時はコードブロックとしてフォールバック
343349
3. ASCIIダイアグラム → SVG自動変換(既存コンテンツ対応、{{SVG検出タイプ数}}種のタイプをサポート)
344350
4. コードハイライト
345351
5. 目に優しいカラーパレット(ウォームホワイト背景、柔らかいテキスト)

0 commit comments

Comments
 (0)