privateboard 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1008 -284
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/public/agent-overlay.js +2 -0
- package/public/agent-profile.js +7 -3
- package/public/app.js +143 -31
- package/public/avatars/chair.svg +1 -98
- package/public/avatars/first-principles.svg +1 -122
- package/public/avatars/historian.svg +1 -0
- package/public/avatars/long-horizon.svg +1 -147
- package/public/avatars/phenomenologist.svg +1 -130
- package/public/avatars/socrates.svg +1 -187
- package/public/avatars/user-empathy.svg +1 -117
- package/public/avatars/value-investor.svg +1 -117
- package/public/home.html +3 -3
- package/public/index.html +38 -1
- package/public/new-agent.js +5 -3
- package/public/report/spines/a16z-thesis.css +10 -5
- package/public/report/spines/anthropic-essay.css +11 -4
- package/public/report/spines/boardroom-dark.css +15 -5
- package/public/report/spines/gartner-note.css +8 -3
- package/public/report/spines/mckinsey-deck.css +8 -3
- package/public/report/spines/openai-paper.css +8 -3
- package/public/report.html +570 -141
- package/public/room-settings.js +2 -0
- package/public/user-settings.css +178 -0
- package/public/user-settings.js +172 -17
package/public/report.html
CHANGED
|
@@ -54,6 +54,83 @@
|
|
|
54
54
|
checkbox and produces a PDF that's universally readable on
|
|
55
55
|
screen and on physical paper. Other spines are already light
|
|
56
56
|
and pass through unchanged. */
|
|
57
|
+
|
|
58
|
+
/* ─── CJK font dispatch · GLOBAL (screen AND print) ───────────
|
|
59
|
+
Chrome does NOT do per-glyph cascade walking reliably in the
|
|
60
|
+
print pipeline: it picks the first font that "looks right for
|
|
61
|
+
the script" and stays there. With a cascade of
|
|
62
|
+
"Helvetica Neue, Inter, Arial, PingFang SC, ..." Chrome lands
|
|
63
|
+
on Arial (advertises minimal CJK coverage), renders every
|
|
64
|
+
Chinese ideograph as `.notdef` (invisible / tofu), and the
|
|
65
|
+
PingFang SC entry NEVER gets tried. Result: empty Chinese
|
|
66
|
+
text in the saved PDF, even on a Mac with PingFang installed
|
|
67
|
+
system-wide.
|
|
68
|
+
|
|
69
|
+
Fix: register three named handles — `cjk-sans`, `cjk-serif`,
|
|
70
|
+
`cjk-mono` — each scoped to the CJK Unicode ranges via
|
|
71
|
+
`unicode-range`. When a CJK code point hits the cascade,
|
|
72
|
+
Chrome is FORCED to dispatch to the CJK handle (only that
|
|
73
|
+
handle covers the range), which uses `local()` to pick
|
|
74
|
+
PingFang / Hiragino / Songti / STSong from the OS. Latin
|
|
75
|
+
code points fall through to the next font as before, so
|
|
76
|
+
Latin display still renders in Helvetica / Charter / Menlo.
|
|
77
|
+
|
|
78
|
+
OUTSIDE @media print so it applies on both screen + print.
|
|
79
|
+
Some CJK selectors live outside @media print (e.g.
|
|
80
|
+
body.is-cjk .cover-title) and need the same dispatch. */
|
|
81
|
+
/* Why STSong leads each src list:
|
|
82
|
+
- macOS regular Chrome resolves PingFang SC fine — but headless
|
|
83
|
+
Chrome (and the same user-action Save-as-PDF on some configs)
|
|
84
|
+
walks `local()` and gives up after the first miss instead of
|
|
85
|
+
trying the rest. Putting a guaranteed-available font (STSong
|
|
86
|
+
ships with macOS since 10.0) first means we always have at
|
|
87
|
+
least ONE working dispatcher; the more aesthetic faces sit
|
|
88
|
+
behind it for environments that DO resolve them.
|
|
89
|
+
- Empirically: cjk-serif (Songti SC/STSong leading) worked while
|
|
90
|
+
cjk-sans (PingFang SC leading) didn't — same chain otherwise,
|
|
91
|
+
only the lead changed. So the FIRST local() is what matters
|
|
92
|
+
most; later entries are best-effort improvements. */
|
|
93
|
+
@font-face {
|
|
94
|
+
font-family: "cjk-sans";
|
|
95
|
+
src: local("STSong"),
|
|
96
|
+
local("PingFang SC"), local("PingFangSC-Regular"),
|
|
97
|
+
local("Hiragino Sans GB"), local("HiraginoSansGB-W3"),
|
|
98
|
+
local("Source Han Sans CN"), local("Noto Sans CJK SC"),
|
|
99
|
+
local("Songti SC"), local("STHeiti");
|
|
100
|
+
unicode-range: U+1100-11FF, U+2E80-2FFF, U+3000-303F,
|
|
101
|
+
U+3040-309F, U+30A0-30FF, U+3100-312F,
|
|
102
|
+
U+3130-318F, U+3190-319F, U+31F0-31FF,
|
|
103
|
+
U+3200-32FF, U+3300-33FF, U+3400-4DBF,
|
|
104
|
+
U+4E00-9FFF, U+A000-A4CF, U+A960-A97F,
|
|
105
|
+
U+AC00-D7AF, U+F900-FAFF, U+FE30-FE4F,
|
|
106
|
+
U+FF00-FFEF;
|
|
107
|
+
}
|
|
108
|
+
@font-face {
|
|
109
|
+
font-family: "cjk-serif";
|
|
110
|
+
src: local("STSong"), local("Songti SC"),
|
|
111
|
+
local("Source Han Serif CN"), local("Noto Serif CJK SC"),
|
|
112
|
+
local("PingFang SC"), local("Hiragino Sans GB");
|
|
113
|
+
unicode-range: U+1100-11FF, U+2E80-2FFF, U+3000-303F,
|
|
114
|
+
U+3040-309F, U+30A0-30FF, U+3100-312F,
|
|
115
|
+
U+3130-318F, U+3190-319F, U+31F0-31FF,
|
|
116
|
+
U+3200-32FF, U+3300-33FF, U+3400-4DBF,
|
|
117
|
+
U+4E00-9FFF, U+A000-A4CF, U+A960-A97F,
|
|
118
|
+
U+AC00-D7AF, U+F900-FAFF, U+FE30-FE4F,
|
|
119
|
+
U+FF00-FFEF;
|
|
120
|
+
}
|
|
121
|
+
@font-face {
|
|
122
|
+
font-family: "cjk-mono";
|
|
123
|
+
src: local("STSong"), local("PingFang SC"),
|
|
124
|
+
local("Hiragino Sans GB"), local("Songti SC");
|
|
125
|
+
unicode-range: U+1100-11FF, U+2E80-2FFF, U+3000-303F,
|
|
126
|
+
U+3040-309F, U+30A0-30FF, U+3100-312F,
|
|
127
|
+
U+3130-318F, U+3190-319F, U+31F0-31FF,
|
|
128
|
+
U+3200-32FF, U+3300-33FF, U+3400-4DBF,
|
|
129
|
+
U+4E00-9FFF, U+A000-A4CF, U+A960-A97F,
|
|
130
|
+
U+AC00-D7AF, U+F900-FAFF, U+FE30-FE4F,
|
|
131
|
+
U+FF00-FFEF;
|
|
132
|
+
}
|
|
133
|
+
|
|
57
134
|
@media print {
|
|
58
135
|
*, *::before, *::after {
|
|
59
136
|
print-color-adjust: exact !important;
|
|
@@ -179,28 +256,26 @@
|
|
|
179
256
|
border-color: #DDD5C8 !important;
|
|
180
257
|
}
|
|
181
258
|
|
|
182
|
-
/*
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
the browser falls through to it automatically when PingFang
|
|
188
|
-
can't be loaded. Latin text still renders in the spine's
|
|
189
|
-
preferred face; only CJK falls through to PingFang. */
|
|
259
|
+
/* CJK dispatch handles (cjk-sans / cjk-serif / cjk-mono) are
|
|
260
|
+
declared OUTSIDE this @media block — see the @font-face
|
|
261
|
+
trio at the top of <style>. Per-element font-family rules
|
|
262
|
+
below reference those handles so CJK glyphs route to
|
|
263
|
+
PingFang / Songti / etc. via `local()`. */
|
|
190
264
|
|
|
191
|
-
/* Sans-anchored body text · Latin in Helvetica/Inter, CJK
|
|
265
|
+
/* Sans-anchored body text · Latin in Helvetica/Inter, CJK
|
|
266
|
+
routed through cjk-sans (which `local()`s to PingFang SC). */
|
|
192
267
|
body, .body, .body p, .body li,
|
|
193
268
|
.body td, .body th,
|
|
194
269
|
.rec-rationale, .rec-risk, .rec-risk-text, .rec-meta-value, .rec-action,
|
|
195
270
|
.nq-why, .cover-deck {
|
|
196
|
-
font-family: "
|
|
197
|
-
"
|
|
198
|
-
"Source Han Sans CN", "Noto Sans CJK SC",
|
|
199
|
-
"Songti SC", "STSong",
|
|
271
|
+
font-family: "cjk-sans",
|
|
272
|
+
"Helvetica Neue", "Inter", "Arial",
|
|
200
273
|
sans-serif !important;
|
|
201
274
|
}
|
|
202
275
|
/* Serif-anchored display copy · headlines, italic claims, big-idea
|
|
203
|
-
kickers — the typographic moments worth preserving in serif.
|
|
276
|
+
kickers — the typographic moments worth preserving in serif.
|
|
277
|
+
CJK ranges route to cjk-serif (which prefers Songti for the
|
|
278
|
+
serif voice, falling back to PingFang). */
|
|
204
279
|
.body h1, .body h2, .body h3, .body h4,
|
|
205
280
|
.body section.section-bottom-line p,
|
|
206
281
|
.body section.section-thesis p,
|
|
@@ -208,15 +283,15 @@
|
|
|
208
283
|
.body section.section-headline-findings .pillar h3,
|
|
209
284
|
.body section.section-big-ideas ol > li p:first-child strong,
|
|
210
285
|
.nq-question, .cover-title {
|
|
211
|
-
font-family: "
|
|
286
|
+
font-family: "cjk-serif",
|
|
287
|
+
"Charter", "Source Serif Pro", "Iowan Old Style",
|
|
212
288
|
"Georgia",
|
|
213
|
-
"PingFang SC", "PingFang TC", "Hiragino Sans GB",
|
|
214
|
-
"Source Han Sans CN", "Noto Sans CJK SC",
|
|
215
|
-
"Songti SC", "STSong",
|
|
216
|
-
"Source Han Serif CN", "Noto Serif CJK SC",
|
|
217
289
|
serif !important;
|
|
218
290
|
}
|
|
219
|
-
/* Mono kickers / labels
|
|
291
|
+
/* Mono kickers / labels · CJK chars in mono context don't really
|
|
292
|
+
have a mono face, so cjk-mono falls back to PingFang SC. The
|
|
293
|
+
result is mono-styled Latin labels with PingFang Han for the
|
|
294
|
+
rare CJK character that lands in a kicker. */
|
|
220
295
|
.top-rule .crumb,
|
|
221
296
|
.body .chapter-num,
|
|
222
297
|
.cover-tag, .cover-tag .secondary,
|
|
@@ -231,10 +306,9 @@
|
|
|
231
306
|
.body section.section-methodology strong,
|
|
232
307
|
.body code,
|
|
233
308
|
.body table.md-table th {
|
|
234
|
-
font-family: "
|
|
309
|
+
font-family: "cjk-mono",
|
|
310
|
+
"SF Mono", "JetBrains Mono", "Menlo",
|
|
235
311
|
"Helvetica Neue", "Arial",
|
|
236
|
-
"PingFang SC", "PingFang TC", "Hiragino Sans GB",
|
|
237
|
-
"Source Han Sans CN", "Songti SC", "STSong",
|
|
238
312
|
monospace !important;
|
|
239
313
|
}
|
|
240
314
|
|
|
@@ -1450,15 +1524,16 @@
|
|
|
1450
1524
|
}
|
|
1451
1525
|
.colophon-cta-url {
|
|
1452
1526
|
display: inline-block;
|
|
1453
|
-
color: var(--text, #
|
|
1527
|
+
color: var(--text-soft, #6B6B6B);
|
|
1454
1528
|
font-family: var(--mono);
|
|
1455
|
-
font-size:
|
|
1456
|
-
font-weight:
|
|
1457
|
-
letter-spacing: 0.
|
|
1529
|
+
font-size: 11px;
|
|
1530
|
+
font-weight: 400;
|
|
1531
|
+
letter-spacing: 0.02em;
|
|
1458
1532
|
text-decoration: none;
|
|
1459
1533
|
margin-top: 2px;
|
|
1534
|
+
opacity: 0.78;
|
|
1460
1535
|
}
|
|
1461
|
-
.colophon-cta-url:hover { text-decoration: underline; }
|
|
1536
|
+
.colophon-cta-url:hover { text-decoration: underline; opacity: 1; }
|
|
1462
1537
|
/* Backwards-compat · the "·" separator from the prior horizontal
|
|
1463
1538
|
layout no longer renders meaningfully in a vertical stack. Keep
|
|
1464
1539
|
the selector defined so old markup doesn't error if rendered. */
|
|
@@ -2229,9 +2304,14 @@
|
|
|
2229
2304
|
body.is-cjk .nq-why,
|
|
2230
2305
|
body.is-cjk .cover-deck,
|
|
2231
2306
|
body.is-cjk .cover-title {
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2307
|
+
/* cjk-sans handle dispatches CJK ranges to PingFang/Songti via
|
|
2308
|
+
local(). Listing it FIRST guarantees Chrome consults it for
|
|
2309
|
+
any CJK code point — without that, Chrome lands on Arial
|
|
2310
|
+
(next in the cascade, partial CJK), renders ideographs as
|
|
2311
|
+
.notdef, and the PingFang entries below NEVER get tried.
|
|
2312
|
+
That's the empty-cover-title-in-PDF bug. */
|
|
2313
|
+
font-family: "cjk-sans",
|
|
2314
|
+
"Söhne", "Inter", "Helvetica Neue", "Arial",
|
|
2235
2315
|
sans-serif !important;
|
|
2236
2316
|
}
|
|
2237
2317
|
|
|
@@ -3172,7 +3252,8 @@
|
|
|
3172
3252
|
// ```metric-strip / ```chart-block style fences regularly.
|
|
3173
3253
|
const fence = /^```([\w-]*)\s*$/.exec(lines[i]);
|
|
3174
3254
|
if (fence) {
|
|
3175
|
-
const
|
|
3255
|
+
const langRaw = fence[1] || "";
|
|
3256
|
+
const lang = langRaw.toLowerCase();
|
|
3176
3257
|
const start = i + 1;
|
|
3177
3258
|
let end = lines.length;
|
|
3178
3259
|
for (let j = start; j < lines.length; j++) {
|
|
@@ -3180,8 +3261,48 @@
|
|
|
3180
3261
|
}
|
|
3181
3262
|
const body = lines.slice(start, end).join("\n");
|
|
3182
3263
|
const idx = placeholders.length;
|
|
3183
|
-
|
|
3184
|
-
|
|
3264
|
+
// Mermaid acceptance · the canonical fence is ```mermaid,
|
|
3265
|
+
// but writers regularly emit ```quadrantChart /
|
|
3266
|
+
// ```xychart-beta / ```flowchart / ```sequenceDiagram /
|
|
3267
|
+
// etc. directly (the chart-type IS the lang in their head).
|
|
3268
|
+
// Accept those bare type tags as mermaid too — otherwise
|
|
3269
|
+
// they fall through to a plain `<pre class="codeblock">`
|
|
3270
|
+
// and ship to the user as raw mermaid source. Catalog
|
|
3271
|
+
// matches mermaid 10's diagram registry. The fenced body's
|
|
3272
|
+
// first non-empty line is the actual mermaid type, so
|
|
3273
|
+
// sanitizeMermaid handles the rest as before.
|
|
3274
|
+
const MERMAID_TYPES = new Set([
|
|
3275
|
+
"mermaid",
|
|
3276
|
+
"quadrantchart", "xychart", "xychart-beta",
|
|
3277
|
+
"flowchart", "graph",
|
|
3278
|
+
"sequencediagram", "sequence",
|
|
3279
|
+
"classdiagram", "class",
|
|
3280
|
+
"statediagram", "statediagram-v2", "state",
|
|
3281
|
+
"erdiagram", "er",
|
|
3282
|
+
"journey", "userjourney",
|
|
3283
|
+
"gantt",
|
|
3284
|
+
"pie",
|
|
3285
|
+
"mindmap",
|
|
3286
|
+
"timeline",
|
|
3287
|
+
"gitgraph",
|
|
3288
|
+
"requirementdiagram", "requirement",
|
|
3289
|
+
"c4context", "c4container", "c4component", "c4dynamic",
|
|
3290
|
+
// newer / -beta types in mermaid 10+
|
|
3291
|
+
"sankey-beta", "sankey",
|
|
3292
|
+
"block-beta", "block",
|
|
3293
|
+
"info",
|
|
3294
|
+
"packet-beta", "packet",
|
|
3295
|
+
"architecture-beta", "architecture",
|
|
3296
|
+
]);
|
|
3297
|
+
if (MERMAID_TYPES.has(lang)) {
|
|
3298
|
+
// For non-"mermaid" lang tags, the fence didn't include
|
|
3299
|
+
// the type-as-first-line — prepend the ORIGINAL-cased
|
|
3300
|
+
// lang token so mermaid's case-sensitive parser accepts
|
|
3301
|
+
// it (e.g. "quadrantChart", not "quadrantchart").
|
|
3302
|
+
const mermaidBody = (lang === "mermaid")
|
|
3303
|
+
? body
|
|
3304
|
+
: `${langRaw}\n${body}`;
|
|
3305
|
+
placeholders.push(`<pre class="mermaid">${escape(sanitizeMermaid(mermaidBody))}</pre>`);
|
|
3185
3306
|
} else if (lang === "metric-strip") {
|
|
3186
3307
|
placeholders.push(renderMetricStrip(body));
|
|
3187
3308
|
} else if (lang === "path-comparison") {
|
|
@@ -4511,117 +4632,174 @@
|
|
|
4511
4632
|
// backgrounds) so flowchart / mindmap / gantt / sequence /
|
|
4512
4633
|
// state diagrams pick up spine-coherent colours instead of
|
|
4513
4634
|
// mermaid's rainbow defaults.
|
|
4635
|
+
// Per-spine mermaid theme · every value here is taken from
|
|
4636
|
+
// the spine's :root tokens (boardroom-dark.css / a16z-thesis.css /
|
|
4637
|
+
// etc.) so charts feel like a native exhibit of the spine
|
|
4638
|
+
// rather than a foreign visualization with its own palette.
|
|
4639
|
+
// Audit cadence: when a spine adds / renames a token, mirror
|
|
4640
|
+
// it here in the same change. Drift between spine tokens and
|
|
4641
|
+
// these `vars` is what makes charts read as "off-brand".
|
|
4642
|
+
//
|
|
4643
|
+
// Mapping rules (consistent across spines):
|
|
4644
|
+
// background → spine main bg (so chart canvas blends with frame)
|
|
4645
|
+
// quadrantFill→ spine paper-soft / panel / bg-soft (slight contrast for inner zones)
|
|
4646
|
+
// pointFill → primary brand/accent (data dots = load-bearing)
|
|
4647
|
+
// titleFill → --ink / --text (max-contrast text)
|
|
4648
|
+
// axisText → --ink-faint / --text-faint (subdued axis labels)
|
|
4649
|
+
// border → --rule (NOT rule-strong) (matches body hairlines)
|
|
4650
|
+
// inner → --rule-soft (lightest separator)
|
|
4651
|
+
// accent → spine accent token (general fill)
|
|
4652
|
+
// accentSoft → spine pale variant (washed accent for backgrounds)
|
|
4653
|
+
// palette → 4-step monochrome ramp from accent (cycled across pie/journey/git slices)
|
|
4514
4654
|
const themes = {
|
|
4515
4655
|
"boardroom-dark": {
|
|
4516
|
-
|
|
4656
|
+
// theme: "base" + darkMode flag, NOT "dark". Mermaid 10's
|
|
4657
|
+
// built-in "dark" preset injects a violet-tinged primary
|
|
4658
|
+
// colour that bleeds into quadrant1Fill / quadrantPointFill
|
|
4659
|
+
// and ALSO overrides our themeVariables in some code paths
|
|
4660
|
+
// (the "Platform Strategy chart on boardroom-dark renders
|
|
4661
|
+
// purple" bug). "base" uses minimal defaults so our
|
|
4662
|
+
// explicit `vars` below are the only colour source.
|
|
4663
|
+
base: "base",
|
|
4664
|
+
darkMode: true,
|
|
4665
|
+
// Frame bg: pre.mermaid uses --panel-2 (#1A1A18). Match it
|
|
4666
|
+
// for the canvas so the SVG doesn't sit "inside" a darker
|
|
4667
|
+
// tray. Quadrant inner boxes go to --bg (#0A0A0A) so the
|
|
4668
|
+
// four boxes read as cut-outs against the canvas.
|
|
4517
4669
|
vars: {
|
|
4518
|
-
background: "#
|
|
4519
|
-
quadrantFill: "#
|
|
4520
|
-
quadrantText: "#
|
|
4521
|
-
pointFill: "#
|
|
4522
|
-
pointText: "#
|
|
4523
|
-
titleFill: "#C8C5BE",
|
|
4524
|
-
axisText: "#
|
|
4525
|
-
border: "#
|
|
4526
|
-
inner: "#
|
|
4527
|
-
accent: "#C9A46B",
|
|
4528
|
-
accentSoft: "#
|
|
4529
|
-
accentText: "#
|
|
4670
|
+
background: "#1A1A18", // --panel-2 (matches pre.mermaid frame)
|
|
4671
|
+
quadrantFill: "#0A0A0A", // --bg (cut-out zones inside chart)
|
|
4672
|
+
quadrantText: "#8E8B83", // --text-soft
|
|
4673
|
+
pointFill: "#C9A46B", // --em / --lime · load-bearing gold for data
|
|
4674
|
+
pointText: "#C8C5BE", // --text
|
|
4675
|
+
titleFill: "#C8C5BE", // --text
|
|
4676
|
+
axisText: "#5C5A4D", // --text-faint (was too bright before)
|
|
4677
|
+
border: "#3A3934", // --line-bright
|
|
4678
|
+
inner: "#21211E", // --panel-3
|
|
4679
|
+
accent: "#C9A46B", // --em (load-bearing gold)
|
|
4680
|
+
accentSoft: "#5C4422", // --lime-dim
|
|
4681
|
+
accentText: "#0A0A0A", // --bg for contrast on accent fills
|
|
4530
4682
|
},
|
|
4683
|
+
// Gold for the lead, then beige-neutrals — multi-bar
|
|
4684
|
+
// charts get one bold lead + 3 supporting tones rather
|
|
4685
|
+
// than 4 competing golds.
|
|
4686
|
+
palette: ["#C9A46B", "#B6B0A2", "#8E8B83", "#5C5A52"],
|
|
4531
4687
|
fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
|
|
4532
4688
|
},
|
|
4533
4689
|
"a16z-thesis": {
|
|
4534
4690
|
base: "default",
|
|
4535
4691
|
vars: {
|
|
4536
|
-
background: "#F7F3E8",
|
|
4537
|
-
quadrantFill: "#F2EAD8",
|
|
4538
|
-
quadrantText: "#57503F",
|
|
4539
|
-
pointFill: "#
|
|
4540
|
-
pointText: "#14110C",
|
|
4541
|
-
titleFill: "#14110C",
|
|
4542
|
-
axisText: "#847B65",
|
|
4543
|
-
border: "#
|
|
4544
|
-
inner: "#
|
|
4545
|
-
accent: "#
|
|
4546
|
-
accentSoft: "#
|
|
4692
|
+
background: "#F7F3E8", // --bg / --paper
|
|
4693
|
+
quadrantFill: "#F2EAD8", // soft panel tone (between paper and rule-soft)
|
|
4694
|
+
quadrantText: "#57503F", // --ink-mid
|
|
4695
|
+
pointFill: "#8B6A2E", // --gold (precise spine value, was #876C2A)
|
|
4696
|
+
pointText: "#14110C", // --ink
|
|
4697
|
+
titleFill: "#14110C", // --ink
|
|
4698
|
+
axisText: "#847B65", // --ink-faint
|
|
4699
|
+
border: "#DCD3BD", // --rule (not --rule-strong)
|
|
4700
|
+
inner: "#E5DECC", // --rule-soft
|
|
4701
|
+
accent: "#8B6A2E", // --gold
|
|
4702
|
+
accentSoft: "#C4A865", // --gold-pale
|
|
4547
4703
|
accentText: "#FFFFFF",
|
|
4548
4704
|
},
|
|
4705
|
+
palette: ["#8B6A2E", "#A8843A", "#C4A865", "#E2CFA0"],
|
|
4549
4706
|
fontFamily: '"Inter", "Helvetica Neue", -apple-system, system-ui, sans-serif',
|
|
4550
4707
|
},
|
|
4551
4708
|
"anthropic-essay": {
|
|
4552
4709
|
base: "default",
|
|
4553
4710
|
vars: {
|
|
4554
|
-
background: "#
|
|
4555
|
-
quadrantFill: "#
|
|
4556
|
-
quadrantText: "#6B6359",
|
|
4557
|
-
pointFill: "#A85A41",
|
|
4558
|
-
pointText: "#
|
|
4559
|
-
titleFill: "#1A1814",
|
|
4560
|
-
axisText: "#978C7E",
|
|
4561
|
-
border: "#DDD5C8",
|
|
4562
|
-
inner: "#E8E1D2",
|
|
4563
|
-
accent: "#
|
|
4564
|
-
accentSoft: "#F2E0D5",
|
|
4711
|
+
background: "#F4F0E8", // --paper (was --surface, slight off)
|
|
4712
|
+
quadrantFill: "#EDE6D6", // --paper-soft
|
|
4713
|
+
quadrantText: "#6B6359", // --ink-mid
|
|
4714
|
+
pointFill: "#A85A41", // --clay-deep · stronger contrast for data
|
|
4715
|
+
pointText: "#1A1814", // --ink (was --ink-soft, low contrast)
|
|
4716
|
+
titleFill: "#1A1814", // --ink
|
|
4717
|
+
axisText: "#978C7E", // --ink-faint
|
|
4718
|
+
border: "#DDD5C8", // --rule
|
|
4719
|
+
inner: "#E8E1D2", // --rule-soft
|
|
4720
|
+
accent: "#CC785C", // --clay (spine's --accent resolves here, NOT clay-deep)
|
|
4721
|
+
accentSoft: "#F2E0D5", // --clay-pale
|
|
4565
4722
|
accentText: "#FFFFFF",
|
|
4566
4723
|
},
|
|
4724
|
+
palette: ["#CC785C", "#A85A41", "#B65A3A", "#F2E0D5"],
|
|
4567
4725
|
fontFamily: '"Charter", "Source Serif Pro", "Iowan Old Style", Georgia, serif',
|
|
4568
4726
|
},
|
|
4569
4727
|
"gartner-note": {
|
|
4570
4728
|
base: "default",
|
|
4571
4729
|
vars: {
|
|
4572
|
-
background: "#FFFFFF",
|
|
4573
|
-
quadrantFill: "#
|
|
4574
|
-
quadrantText: "#455364",
|
|
4575
|
-
pointFill: "#0A4DA1",
|
|
4576
|
-
pointText: "#1A2332",
|
|
4577
|
-
titleFill: "#1A2332",
|
|
4578
|
-
axisText: "#6B7785",
|
|
4579
|
-
border: "#
|
|
4580
|
-
inner: "#
|
|
4581
|
-
accent: "#0A4DA1",
|
|
4582
|
-
accentSoft: "#
|
|
4730
|
+
background: "#FFFFFF", // --bg
|
|
4731
|
+
quadrantFill: "#F5F7FA", // --bg-soft (was --bg-emphasis #FAFBFC)
|
|
4732
|
+
quadrantText: "#455364", // --ink-soft
|
|
4733
|
+
pointFill: "#0A4DA1", // --brand
|
|
4734
|
+
pointText: "#1A2332", // --ink
|
|
4735
|
+
titleFill: "#1A2332", // --ink
|
|
4736
|
+
axisText: "#6B7785", // --ink-faint
|
|
4737
|
+
border: "#DDE2E8", // --rule (was --rule-strong, too heavy)
|
|
4738
|
+
inner: "#E8ECF1", // --rule-soft
|
|
4739
|
+
accent: "#0A4DA1", // --brand
|
|
4740
|
+
accentSoft: "#E8F1FB", // --brand-pale
|
|
4583
4741
|
accentText: "#FFFFFF",
|
|
4584
4742
|
},
|
|
4743
|
+
palette: ["#0A4DA1", "#1A65BD", "#3F73B8", "#7AA0CF"],
|
|
4585
4744
|
fontFamily: '"Inter", "Helvetica Neue", Arial, sans-serif',
|
|
4586
4745
|
},
|
|
4587
4746
|
"mckinsey-deck": {
|
|
4588
4747
|
base: "default",
|
|
4589
4748
|
vars: {
|
|
4590
|
-
background: "#FFFFFF",
|
|
4591
|
-
quadrantFill: "#
|
|
4592
|
-
quadrantText: "#4A5870",
|
|
4593
|
-
pointFill: "#2251FF",
|
|
4594
|
-
pointText: "#051C2C",
|
|
4595
|
-
titleFill: "#051C2C",
|
|
4596
|
-
axisText: "#758296",
|
|
4597
|
-
border: "#
|
|
4598
|
-
inner: "#
|
|
4599
|
-
accent: "#2251FF",
|
|
4600
|
-
accentSoft: "#
|
|
4749
|
+
background: "#FFFFFF", // --bg
|
|
4750
|
+
quadrantFill: "#F8FAFD", // soft panel (slightly tinted)
|
|
4751
|
+
quadrantText: "#4A5870", // --ink-soft
|
|
4752
|
+
pointFill: "#2251FF", // --blue
|
|
4753
|
+
pointText: "#051C2C", // --ink / --navy
|
|
4754
|
+
titleFill: "#051C2C", // --navy
|
|
4755
|
+
axisText: "#758296", // --ink-faint
|
|
4756
|
+
border: "#D5DCE4", // --rule (was --rule-strong)
|
|
4757
|
+
inner: "#E5EAF1", // --rule-soft
|
|
4758
|
+
accent: "#2251FF", // --blue
|
|
4759
|
+
accentSoft: "#E5EDFA", // --blue-pale
|
|
4601
4760
|
accentText: "#FFFFFF",
|
|
4602
4761
|
},
|
|
4762
|
+
palette: ["#2251FF", "#1A3FBD", "#6285FF", "#B8C2D0"],
|
|
4603
4763
|
fontFamily: '"Inter", "Helvetica Neue", Arial, sans-serif',
|
|
4604
4764
|
},
|
|
4605
4765
|
"openai-paper": {
|
|
4606
4766
|
base: "default",
|
|
4607
4767
|
vars: {
|
|
4608
|
-
background: "#FFFFFF",
|
|
4609
|
-
quadrantFill: "#FAFAFA",
|
|
4610
|
-
quadrantText: "#404040",
|
|
4611
|
-
pointFill: "#10A37F",
|
|
4612
|
-
pointText: "#0D0D0D",
|
|
4613
|
-
titleFill: "#0D0D0D",
|
|
4614
|
-
axisText: "#6E6E80",
|
|
4615
|
-
border: "#E5E5E5",
|
|
4616
|
-
inner: "#EFEFEF",
|
|
4617
|
-
accent: "#10A37F",
|
|
4618
|
-
accentSoft: "#E6F6F0",
|
|
4768
|
+
background: "#FFFFFF", // --bg
|
|
4769
|
+
quadrantFill: "#FAFAFA", // --paper
|
|
4770
|
+
quadrantText: "#404040", // --ink-soft
|
|
4771
|
+
pointFill: "#10A37F", // --teal
|
|
4772
|
+
pointText: "#0D0D0D", // --ink
|
|
4773
|
+
titleFill: "#0D0D0D", // --ink
|
|
4774
|
+
axisText: "#6E6E80", // --ink-faint
|
|
4775
|
+
border: "#E5E5E5", // --rule
|
|
4776
|
+
inner: "#EFEFEF", // --rule-soft
|
|
4777
|
+
accent: "#10A37F", // --teal
|
|
4778
|
+
accentSoft: "#E6F6F0", // --teal-pale
|
|
4619
4779
|
accentText: "#FFFFFF",
|
|
4620
4780
|
},
|
|
4781
|
+
palette: ["#10A37F", "#0E8C6D", "#52B89A", "#8FCDB7"],
|
|
4621
4782
|
fontFamily: '"Söhne", "Inter", -apple-system, system-ui, sans-serif',
|
|
4622
4783
|
},
|
|
4623
4784
|
};
|
|
4624
4785
|
const t = themes[spineKey];
|
|
4786
|
+
// Build pie / journey / state / git slice-color overrides
|
|
4787
|
+
// by cycling through the spine's monochrome `palette`.
|
|
4788
|
+
// Without these, mermaid 11+ paints pie1..pie12 +
|
|
4789
|
+
// fillType0..7 + git0..7 with its built-in rainbow set
|
|
4790
|
+
// (violet / teal / orange) — that's the source of the
|
|
4791
|
+
// "white + purple" charts on the dark spine.
|
|
4792
|
+
const sliceColors = {};
|
|
4793
|
+
for (let i = 0; i < 12; i++) {
|
|
4794
|
+
const c = t.palette[i % t.palette.length];
|
|
4795
|
+
sliceColors[`pie${i + 1}`] = c;
|
|
4796
|
+
}
|
|
4797
|
+
for (let i = 0; i < 8; i++) {
|
|
4798
|
+
const c = t.palette[i % t.palette.length];
|
|
4799
|
+
sliceColors[`fillType${i}`] = c;
|
|
4800
|
+
sliceColors[`git${i}`] = c;
|
|
4801
|
+
sliceColors[`gitInv${i}`] = c;
|
|
4802
|
+
}
|
|
4625
4803
|
// themeCSS shapes the rendered SVG beyond what themeVariables
|
|
4626
4804
|
// expose. The chart title is hidden because the markdown
|
|
4627
4805
|
// already renders an H3 caption directly above each chart —
|
|
@@ -4631,6 +4809,13 @@
|
|
|
4631
4809
|
// showing it twice is mermaid's single ugliest default. Apply
|
|
4632
4810
|
// across quadrant / xychart / pie / timeline.
|
|
4633
4811
|
const themeCSS = `
|
|
4812
|
+
/* ── SVG canvas · paint the theme bg explicitly. Without
|
|
4813
|
+
this, mermaid leaves the SVG transparent and the parent
|
|
4814
|
+
<pre>'s background bleeds through inconsistently across
|
|
4815
|
+
diagram types — most visibly on dark spines where the
|
|
4816
|
+
browser default white shows through pie/journey gaps. */
|
|
4817
|
+
svg { background: ${t.vars.background}; }
|
|
4818
|
+
.pieOuterCircle { stroke: ${t.vars.border} !important; }
|
|
4634
4819
|
/* ── Quadrant chart ── */
|
|
4635
4820
|
g.quadrant-chart text { font-family: ${t.fontFamily}; }
|
|
4636
4821
|
g.quadrant-point > circle, .quadrant-point circle {
|
|
@@ -4643,8 +4828,73 @@
|
|
|
4643
4828
|
.timeline .title-text,
|
|
4644
4829
|
.timeline-title { display: none !important; }
|
|
4645
4830
|
|
|
4646
|
-
/* ── xychart-beta · bars ──
|
|
4647
|
-
|
|
4831
|
+
/* ── xychart-beta · refined bars ──
|
|
4832
|
+
Bar WIDTH is slimmed by the post-render JS mutation
|
|
4833
|
+
(see "xychart bar slimming" block after mermaid.run) --
|
|
4834
|
+
CSS scaleX on SVG rects is unreliable across browsers,
|
|
4835
|
+
so we mutate the rect attributes directly. Here we
|
|
4836
|
+
only handle the COLOUR pass: solid accent fill, no
|
|
4837
|
+
stroke, with per-position cycling through the spine's
|
|
4838
|
+
4-shade monochrome palette so multi-bar charts stagger
|
|
4839
|
+
tones rather than reading as one flat block. */
|
|
4840
|
+
.xy-chart .bar,
|
|
4841
|
+
g.bar-plot .bar,
|
|
4842
|
+
g.bar-plot rect,
|
|
4843
|
+
g.plot > rect {
|
|
4844
|
+
fill: ${t.vars.accent} !important;
|
|
4845
|
+
stroke: none !important;
|
|
4846
|
+
}
|
|
4847
|
+
.xy-chart .bar:nth-child(2n),
|
|
4848
|
+
g.bar-plot .bar:nth-child(2n),
|
|
4849
|
+
g.bar-plot rect:nth-child(2n),
|
|
4850
|
+
g.plot > rect:nth-child(2n) { fill: ${t.palette[1]} !important; }
|
|
4851
|
+
.xy-chart .bar:nth-child(3n),
|
|
4852
|
+
g.bar-plot .bar:nth-child(3n),
|
|
4853
|
+
g.bar-plot rect:nth-child(3n),
|
|
4854
|
+
g.plot > rect:nth-child(3n) { fill: ${t.palette[2]} !important; }
|
|
4855
|
+
.xy-chart .bar:nth-child(4n),
|
|
4856
|
+
g.bar-plot .bar:nth-child(4n),
|
|
4857
|
+
g.bar-plot rect:nth-child(4n),
|
|
4858
|
+
g.plot > rect:nth-child(4n) { fill: ${t.palette[3]} !important; }
|
|
4859
|
+
/* Axis / grid refinement · 0.5px-feel hairlines, label
|
|
4860
|
+
typography in the mono kicker register so the chart's
|
|
4861
|
+
typography aligns with the rest of the doc. */
|
|
4862
|
+
.xy-chart .background { fill: transparent !important; }
|
|
4863
|
+
.xy-chart .x-axis line,
|
|
4864
|
+
.xy-chart .y-axis line,
|
|
4865
|
+
.xy-chart line.tick {
|
|
4866
|
+
stroke: ${t.vars.border} !important;
|
|
4867
|
+
stroke-width: 1px !important;
|
|
4868
|
+
}
|
|
4869
|
+
.xy-chart .x-axis path,
|
|
4870
|
+
.xy-chart .y-axis path,
|
|
4871
|
+
.xy-chart path.domain {
|
|
4872
|
+
stroke: ${t.vars.titleFill} !important;
|
|
4873
|
+
stroke-width: 1px !important;
|
|
4874
|
+
fill: none !important;
|
|
4875
|
+
}
|
|
4876
|
+
.xy-chart .grid line,
|
|
4877
|
+
.xy-chart .gridline,
|
|
4878
|
+
.xy-chart line.grid {
|
|
4879
|
+
stroke: ${t.vars.inner} !important;
|
|
4880
|
+
stroke-width: 1px !important;
|
|
4881
|
+
stroke-dasharray: 2 3 !important;
|
|
4882
|
+
}
|
|
4883
|
+
.xy-chart text,
|
|
4884
|
+
.xy-chart .x-axis text,
|
|
4885
|
+
.xy-chart .y-axis text {
|
|
4886
|
+
font-family: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace !important;
|
|
4887
|
+
font-size: 10px !important;
|
|
4888
|
+
fill: ${t.vars.axisText} !important;
|
|
4889
|
+
letter-spacing: 0.06em !important;
|
|
4890
|
+
}
|
|
4891
|
+
/* Data labels (showDataLabel) in the kicker register too. */
|
|
4892
|
+
.xy-chart .data-label,
|
|
4893
|
+
.xy-chart text.data-label {
|
|
4894
|
+
font-family: "SF Mono", "JetBrains Mono", Menlo, monospace !important;
|
|
4895
|
+
font-size: 10px !important;
|
|
4896
|
+
fill: ${t.vars.titleFill} !important;
|
|
4897
|
+
}
|
|
4648
4898
|
|
|
4649
4899
|
/* ── Pie ── */
|
|
4650
4900
|
.pieCircle { stroke: ${t.vars.background}; stroke-width: 2px; }
|
|
@@ -4878,8 +5128,11 @@
|
|
|
4878
5128
|
fontFamily: t.fontFamily,
|
|
4879
5129
|
themeCSS,
|
|
4880
5130
|
quadrantChart: {
|
|
4881
|
-
|
|
4882
|
-
|
|
5131
|
+
// Refined-compact · was 640×480; the chart fits inside a
|
|
5132
|
+
// 740–880px article column, and 480px tall reads as a
|
|
5133
|
+
// presentation slide rather than an inline exhibit.
|
|
5134
|
+
chartWidth: 460,
|
|
5135
|
+
chartHeight: 320,
|
|
4883
5136
|
// Hide mermaid's own title slot — the H3 above the chart
|
|
4884
5137
|
// is the caption. Setting padding/size to 0 keeps the
|
|
4885
5138
|
// layout from leaving an empty band at the top.
|
|
@@ -4889,15 +5142,15 @@
|
|
|
4889
5142
|
quadrantPadding: 8,
|
|
4890
5143
|
quadrantInternalBorderStrokeWidth: 0.5,
|
|
4891
5144
|
quadrantExternalBorderStrokeWidth: 1,
|
|
4892
|
-
quadrantLabelFontSize:
|
|
4893
|
-
quadrantTextTopPadding:
|
|
4894
|
-
pointRadius:
|
|
4895
|
-
pointLabelFontSize:
|
|
4896
|
-
pointTextPadding:
|
|
4897
|
-
xAxisLabelFontSize:
|
|
4898
|
-
xAxisLabelPadding:
|
|
4899
|
-
yAxisLabelFontSize:
|
|
4900
|
-
yAxisLabelPadding:
|
|
5145
|
+
quadrantLabelFontSize: 11,
|
|
5146
|
+
quadrantTextTopPadding: 10,
|
|
5147
|
+
pointRadius: 5,
|
|
5148
|
+
pointLabelFontSize: 11,
|
|
5149
|
+
pointTextPadding: 6,
|
|
5150
|
+
xAxisLabelFontSize: 11,
|
|
5151
|
+
xAxisLabelPadding: 6,
|
|
5152
|
+
yAxisLabelFontSize: 11,
|
|
5153
|
+
yAxisLabelPadding: 6,
|
|
4901
5154
|
xAxisPosition: "bottom",
|
|
4902
5155
|
yAxisPosition: "left",
|
|
4903
5156
|
},
|
|
@@ -4910,13 +5163,25 @@
|
|
|
4910
5163
|
// when the CSS forced 12px after mermaid measured at
|
|
4911
5164
|
// its 16px default).
|
|
4912
5165
|
fontSize: "12.5px",
|
|
5166
|
+
// darkMode flag tells mermaid whether to invert
|
|
5167
|
+
// computed contrast pairs — required when we feed it
|
|
5168
|
+
// dark surface colors via the "base" theme. Without
|
|
5169
|
+
// this, mermaid computes text colors assuming a light
|
|
5170
|
+
// canvas and ends up with low-contrast labels.
|
|
5171
|
+
darkMode: t.darkMode === true,
|
|
4913
5172
|
background: t.vars.background,
|
|
4914
|
-
primaryColor
|
|
5173
|
+
// primaryColor MUST NOT be the quadrantFill (which is
|
|
5174
|
+
// very dark on dark spines). Mermaid uses primaryColor
|
|
5175
|
+
// as the seed for many derived colours (cScale shades,
|
|
5176
|
+
// pie defaults, accent variations) — when it's near-
|
|
5177
|
+
// black the derivations come out muddy. Use the spine's
|
|
5178
|
+
// accent so derivations land in the brand family.
|
|
5179
|
+
primaryColor: t.vars.accent,
|
|
4915
5180
|
primaryTextColor: t.vars.titleFill,
|
|
4916
5181
|
primaryBorderColor: t.vars.border,
|
|
4917
5182
|
lineColor: t.vars.border,
|
|
4918
|
-
secondaryColor: t.vars.
|
|
4919
|
-
tertiaryColor: t.vars.
|
|
5183
|
+
secondaryColor: t.vars.accentSoft,
|
|
5184
|
+
tertiaryColor: t.vars.quadrantFill,
|
|
4920
5185
|
// All 4 quadrants share the same fill — clean matrix.
|
|
4921
5186
|
quadrant1Fill: t.vars.quadrantFill,
|
|
4922
5187
|
quadrant2Fill: t.vars.quadrantFill,
|
|
@@ -4987,6 +5252,42 @@
|
|
|
4987
5252
|
altSectionBkgColor: t.vars.quadrantFill,
|
|
4988
5253
|
sectionBkgColor2: t.vars.background,
|
|
4989
5254
|
todayLineColor: t.vars.accent,
|
|
5255
|
+
// ── Pie / journey / state / git · monochrome palette
|
|
5256
|
+
// Cycles t.palette across mermaid's per-slice color
|
|
5257
|
+
// slots so these chart types stop falling back to
|
|
5258
|
+
// mermaid's built-in violet/teal rainbow defaults
|
|
5259
|
+
// (the white+purple leak on dark spines).
|
|
5260
|
+
...sliceColors,
|
|
5261
|
+
// ── xychart-beta · single-accent bars + matched chrome.
|
|
5262
|
+
// Mermaid generates xychart SVG using these tokens BEFORE
|
|
5263
|
+
// our themeCSS overrides apply, so they have to be set
|
|
5264
|
+
// here to get the right fill at render time (not just
|
|
5265
|
+
// post-paint via CSS). Palette joined as comma-separated
|
|
5266
|
+
// string per mermaid's plotColorPalette contract.
|
|
5267
|
+
xyChart: {
|
|
5268
|
+
backgroundColor: "transparent",
|
|
5269
|
+
titleColor: t.vars.titleFill,
|
|
5270
|
+
xAxisLabelColor: t.vars.axisText,
|
|
5271
|
+
xAxisTitleColor: t.vars.titleFill,
|
|
5272
|
+
xAxisTickColor: t.vars.border,
|
|
5273
|
+
xAxisLineColor: t.vars.titleFill,
|
|
5274
|
+
yAxisLabelColor: t.vars.axisText,
|
|
5275
|
+
yAxisTitleColor: t.vars.titleFill,
|
|
5276
|
+
yAxisTickColor: t.vars.border,
|
|
5277
|
+
yAxisLineColor: t.vars.titleFill,
|
|
5278
|
+
plotColorPalette: t.palette.join(","),
|
|
5279
|
+
},
|
|
5280
|
+
pieTitleTextSize: "0px",
|
|
5281
|
+
pieTitleTextColor: t.vars.titleFill,
|
|
5282
|
+
pieSectionTextColor: t.vars.titleFill,
|
|
5283
|
+
pieSectionTextSize: "11px",
|
|
5284
|
+
pieLegendTextColor: t.vars.titleFill,
|
|
5285
|
+
pieLegendTextSize: "11px",
|
|
5286
|
+
pieStrokeColor: t.vars.background,
|
|
5287
|
+
pieStrokeWidth: "1px",
|
|
5288
|
+
pieOuterStrokeColor: t.vars.border,
|
|
5289
|
+
pieOuterStrokeWidth: "1px",
|
|
5290
|
+
pieOpacity: "1",
|
|
4990
5291
|
},
|
|
4991
5292
|
flowchart: {
|
|
4992
5293
|
useMaxWidth: true,
|
|
@@ -5004,30 +5305,30 @@
|
|
|
5004
5305
|
},
|
|
5005
5306
|
sequence: {
|
|
5006
5307
|
useMaxWidth: true,
|
|
5007
|
-
diagramMarginX:
|
|
5008
|
-
diagramMarginY:
|
|
5009
|
-
actorMargin:
|
|
5010
|
-
boxMargin:
|
|
5011
|
-
boxTextMargin:
|
|
5012
|
-
noteMargin:
|
|
5013
|
-
messageMargin:
|
|
5308
|
+
diagramMarginX: 12,
|
|
5309
|
+
diagramMarginY: 6,
|
|
5310
|
+
actorMargin: 24,
|
|
5311
|
+
boxMargin: 5,
|
|
5312
|
+
boxTextMargin: 3,
|
|
5313
|
+
noteMargin: 6,
|
|
5314
|
+
messageMargin: 18,
|
|
5014
5315
|
mirrorActors: false,
|
|
5015
5316
|
bottomMarginAdj: 0,
|
|
5016
|
-
actorFontSize:
|
|
5017
|
-
messageFontSize:
|
|
5018
|
-
noteFontSize:
|
|
5317
|
+
actorFontSize: 11,
|
|
5318
|
+
messageFontSize: 10,
|
|
5319
|
+
noteFontSize: 10,
|
|
5019
5320
|
},
|
|
5020
5321
|
gantt: {
|
|
5021
5322
|
useMaxWidth: true,
|
|
5022
|
-
fontSize:
|
|
5023
|
-
sectionFontSize:
|
|
5323
|
+
fontSize: 10,
|
|
5324
|
+
sectionFontSize: 10,
|
|
5024
5325
|
numberSectionStyles: 3,
|
|
5025
|
-
leftPadding:
|
|
5026
|
-
topPadding:
|
|
5027
|
-
rightPadding:
|
|
5028
|
-
barGap:
|
|
5029
|
-
barHeight:
|
|
5030
|
-
gridLineStartPadding:
|
|
5326
|
+
leftPadding: 64,
|
|
5327
|
+
topPadding: 18,
|
|
5328
|
+
rightPadding: 18,
|
|
5329
|
+
barGap: 3,
|
|
5330
|
+
barHeight: 14,
|
|
5331
|
+
gridLineStartPadding: 28,
|
|
5031
5332
|
},
|
|
5032
5333
|
mindmap: {
|
|
5033
5334
|
useMaxWidth: true,
|
|
@@ -5058,15 +5359,28 @@
|
|
|
5058
5359
|
// `xychart` — keep BOTH so the config isn't fragile across
|
|
5059
5360
|
// CDN bumps).
|
|
5060
5361
|
xyChart: {
|
|
5061
|
-
|
|
5062
|
-
|
|
5362
|
+
// Was 720×360 · oversized for an inline exhibit. 560×260
|
|
5363
|
+
// sits comfortably inside a 740px article column without
|
|
5364
|
+
// pretending to be a presentation slide. Slightly shorter
|
|
5365
|
+
// than the 320px we tried first — squat-ish proportions
|
|
5366
|
+
// read as "data exhibit" while taller bars look like a
|
|
5367
|
+
// pitch slide.
|
|
5368
|
+
width: 560,
|
|
5369
|
+
height: 260,
|
|
5063
5370
|
titlePadding: 0,
|
|
5064
5371
|
titleFontSize: 0,
|
|
5065
5372
|
showTitle: false,
|
|
5066
5373
|
showDataLabel: true,
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5374
|
+
// Refined bars · 50 → 32 → 20. mermaid xychart's bar
|
|
5375
|
+
// width is `slotWidth × reservedPercent / 100`, where
|
|
5376
|
+
// slotWidth scales inversely with bar count — so a value
|
|
5377
|
+
// tuned for 8 bars looks chunky on 3 bars (each slot
|
|
5378
|
+
// is ~3× wider). 20 keeps 8-12-bar charts legible while
|
|
5379
|
+
// 3-bar charts come out as ~36px-wide thin bars rather
|
|
5380
|
+
// than 60-90px filler blocks.
|
|
5381
|
+
plotReservedSpacePercent: 20,
|
|
5382
|
+
xAxis: { labelFontSize: 10, titleFontSize: 0, showTitle: false },
|
|
5383
|
+
yAxis: { labelFontSize: 10, titleFontSize: 10 },
|
|
5070
5384
|
},
|
|
5071
5385
|
// Pie · legend on the right, slice labels disabled so wide
|
|
5072
5386
|
// labels don't push the chart off-screen. The legend
|
|
@@ -5082,6 +5396,121 @@
|
|
|
5082
5396
|
},
|
|
5083
5397
|
});
|
|
5084
5398
|
await mermaid.run({ querySelector: ".mermaid" });
|
|
5399
|
+
// ── Post-render colour forcing ───────────────────────────
|
|
5400
|
+
// Mermaid 10's themeVariables don't fully replace built-in
|
|
5401
|
+
// colour defaults on every chart type — quadrant fills,
|
|
5402
|
+
// pie slices, journey faces and similar can leak the
|
|
5403
|
+
// theme preset's primary colour (purple/violet on
|
|
5404
|
+
// theme:"dark"). Walk every rendered SVG and force-set
|
|
5405
|
+
// fills directly on the elements we know about. Bulletproof
|
|
5406
|
+
// because it runs LAST, after mermaid's own paint.
|
|
5407
|
+
try {
|
|
5408
|
+
document.querySelectorAll('.mermaid svg').forEach((svg) => {
|
|
5409
|
+
// ── Quadrant chart ──
|
|
5410
|
+
// The four quadrant rects + internal/external borders +
|
|
5411
|
+
// point dots. Mermaid 10 uses class `.quadrant-fill` on
|
|
5412
|
+
// each filled rect inside the chart group.
|
|
5413
|
+
svg.querySelectorAll('rect.quadrant-fill, .quadrant-fill').forEach((el) => {
|
|
5414
|
+
el.setAttribute('fill', t.vars.quadrantFill);
|
|
5415
|
+
});
|
|
5416
|
+
// Defense for any rect inside .quadrant-chart that wasn't
|
|
5417
|
+
// class-tagged (mermaid version drift). Skip the chart's
|
|
5418
|
+
// outer container rect (full-svg-width).
|
|
5419
|
+
const chartG = svg.querySelector('g.quadrant-chart, .quadrant-chart');
|
|
5420
|
+
if (chartG) {
|
|
5421
|
+
const svgWidth = parseFloat(svg.getAttribute('width') || '0');
|
|
5422
|
+
chartG.querySelectorAll(':scope > g > rect, :scope > rect').forEach((el) => {
|
|
5423
|
+
const w = parseFloat(el.getAttribute('width') || '0');
|
|
5424
|
+
if (w > 0 && (svgWidth === 0 || w < svgWidth * 0.95)) {
|
|
5425
|
+
el.setAttribute('fill', t.vars.quadrantFill);
|
|
5426
|
+
}
|
|
5427
|
+
});
|
|
5428
|
+
}
|
|
5429
|
+
// Quadrant data points · pointFill (the spine's
|
|
5430
|
+
// strongest brand colour for marker-style data) with a
|
|
5431
|
+
// frame-bg stroke for separation from the quadrant fill.
|
|
5432
|
+
svg.querySelectorAll('circle.quadrant-point, .quadrant-point circle, g.quadrant-point > circle').forEach((el) => {
|
|
5433
|
+
el.setAttribute('fill', t.vars.pointFill);
|
|
5434
|
+
el.setAttribute('stroke', t.vars.background);
|
|
5435
|
+
});
|
|
5436
|
+
// ── Pie chart slices · cycle palette, force stroke ──
|
|
5437
|
+
svg.querySelectorAll('path.pieCircle').forEach((el, i) => {
|
|
5438
|
+
el.setAttribute('fill', t.palette[i % t.palette.length]);
|
|
5439
|
+
el.setAttribute('stroke', t.vars.background);
|
|
5440
|
+
});
|
|
5441
|
+
// Pie outer ring border.
|
|
5442
|
+
svg.querySelectorAll('.pieOuterCircle, circle.pieOuterCircle').forEach((el) => {
|
|
5443
|
+
el.setAttribute('stroke', t.vars.border);
|
|
5444
|
+
el.setAttribute('fill', 'none');
|
|
5445
|
+
});
|
|
5446
|
+
// ── Journey · face circles cycle palette ──
|
|
5447
|
+
svg.querySelectorAll('circle.face-circle, .face-circle').forEach((el, i) => {
|
|
5448
|
+
el.setAttribute('fill', t.palette[i % t.palette.length]);
|
|
5449
|
+
});
|
|
5450
|
+
// ── Generic SVG canvas guard · any rect that fills the
|
|
5451
|
+
// entire SVG and was painted with mermaid's default
|
|
5452
|
+
// primaryColor (could be off-brand on dark spines). Cap
|
|
5453
|
+
// its fill to t.vars.background.
|
|
5454
|
+
const root = svg.querySelector(':scope > rect, :scope > g > rect.background');
|
|
5455
|
+
if (root) {
|
|
5456
|
+
const w = parseFloat(root.getAttribute('width') || '0');
|
|
5457
|
+
const sw = parseFloat(svg.getAttribute('width') || '0');
|
|
5458
|
+
if (w > 0 && sw > 0 && w >= sw * 0.95) {
|
|
5459
|
+
root.setAttribute('fill', t.vars.background);
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
});
|
|
5463
|
+
} catch (forceColorErr) {
|
|
5464
|
+
console.warn('[mermaid] post-render colour force failed:', forceColorErr);
|
|
5465
|
+
}
|
|
5466
|
+
// ── Post-render xychart bar slimming ─────────────────────
|
|
5467
|
+
// mermaid xychart-beta renders bars at near-full slot width
|
|
5468
|
+
// regardless of `plotReservedSpacePercent` in some 11.x
|
|
5469
|
+
// versions, and CSS transform: scaleX(...) on the bar rects
|
|
5470
|
+
// is unreliable across browsers (transform-box: fill-box
|
|
5471
|
+
// has Chrome/Safari gaps for SVG rect). Direct attribute
|
|
5472
|
+
// mutation is bulletproof: we walk every rendered xychart
|
|
5473
|
+
// SVG, identify the bar rects via class + geometric
|
|
5474
|
+
// heuristic (rect inside g.plot, height > width, not full-
|
|
5475
|
+
// plot wide), and shrink each rect's `width` to ~50% with
|
|
5476
|
+
// a matching `x` shift so it stays centered in its slot.
|
|
5477
|
+
// Result: a 3-bar chart goes from 3 chunky filler blocks
|
|
5478
|
+
// to 3 thin accents on a wide whitespace canvas — refined-
|
|
5479
|
+
// exhibit, not slide. */
|
|
5480
|
+
try {
|
|
5481
|
+
const xyHosts = document.querySelectorAll('.mermaid');
|
|
5482
|
+
xyHosts.forEach((host) => {
|
|
5483
|
+
const svg = host.querySelector('svg');
|
|
5484
|
+
if (!svg) return;
|
|
5485
|
+
// Only xychart-beta SVGs · skip quadrant / pie / etc.
|
|
5486
|
+
if (!svg.matches('[id^="xychart"], [aria-roledescription="xychart"]')
|
|
5487
|
+
&& !svg.querySelector('g.plot, g.bar-plot, g[class*="-plot"], rect.bar')) {
|
|
5488
|
+
return;
|
|
5489
|
+
}
|
|
5490
|
+
// Catch rects across mermaid 11.x naming conventions.
|
|
5491
|
+
const candidates = svg.querySelectorAll(
|
|
5492
|
+
'rect.bar, g.bar-plot rect, g.plot rect, g[class*="plot"] > rect'
|
|
5493
|
+
);
|
|
5494
|
+
candidates.forEach((rect) => {
|
|
5495
|
+
const x = parseFloat(rect.getAttribute('x'));
|
|
5496
|
+
const w = parseFloat(rect.getAttribute('width'));
|
|
5497
|
+
const h = parseFloat(rect.getAttribute('height'));
|
|
5498
|
+
if (!Number.isFinite(x) || !Number.isFinite(w) || !Number.isFinite(h)) return;
|
|
5499
|
+
// Skip the plot-background rect (full plot width).
|
|
5500
|
+
// Don't filter on h-vs-w ratio — that catches short
|
|
5501
|
+
// bars (small data values) and leaves them at full
|
|
5502
|
+
// width while their taller siblings shrink, giving
|
|
5503
|
+
// a mismatched chart.
|
|
5504
|
+
if (w >= 200) return;
|
|
5505
|
+
const scale = 0.5;
|
|
5506
|
+
const newW = w * scale;
|
|
5507
|
+
rect.setAttribute('width', newW.toFixed(2));
|
|
5508
|
+
rect.setAttribute('x', (x + (w - newW) / 2).toFixed(2));
|
|
5509
|
+
});
|
|
5510
|
+
});
|
|
5511
|
+
} catch (slimErr) {
|
|
5512
|
+
console.warn('[mermaid] xychart bar slimming failed:', slimErr);
|
|
5513
|
+
}
|
|
5085
5514
|
} catch (e) {
|
|
5086
5515
|
// Per-diagram failures already render as inline error overlays —
|
|
5087
5516
|
// log and move on.
|