privateboard 0.1.6 → 0.1.8
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 +700 -17
- package/dist/cli.js.map +1 -1
- package/package.json +4 -2
- package/public/adjourn-overlay.css +194 -0
- package/public/agent-profile.js +14 -0
- package/public/app.js +862 -741
- package/public/bento.html +1396 -0
- package/public/home.html +389 -17
- package/public/index.html +211 -152
- package/public/magazine.html +1266 -0
- package/public/newspaper.html +1770 -0
- package/public/report.html +666 -55
- package/public/typing-sfx.js +158 -0
- package/public/user-settings.css +156 -19
- package/public/user-settings.js +109 -4
package/public/report.html
CHANGED
|
@@ -588,6 +588,168 @@
|
|
|
588
588
|
border-top-width: 0;
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
+
/* ─── Hide mermaid's internal render scratchpad ──────────────
|
|
592
|
+
During `mermaid.render(id, src)`, mermaid 10 briefly appends a
|
|
593
|
+
`<div id="dmermaid-...">` to document.body to measure layout +
|
|
594
|
+
produce the SVG. The div is removed once render() resolves,
|
|
595
|
+
but in the meantime it can flash visibly. Force it offscreen
|
|
596
|
+
so the user never sees mermaid's pre-takeover render at all.
|
|
597
|
+
The element still has natural dimensions for mermaid's layout
|
|
598
|
+
measurement — only its position + visibility is constrained. */
|
|
599
|
+
body > [id^="dmermaid-"] {
|
|
600
|
+
position: absolute !important;
|
|
601
|
+
left: -99999px !important;
|
|
602
|
+
top: 0 !important;
|
|
603
|
+
visibility: hidden !important;
|
|
604
|
+
pointer-events: none !important;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/* ─── Hide mermaid SVG until spine palette is applied ─────────
|
|
608
|
+
Pattern from user reports: "first time wrong, refresh OK" /
|
|
609
|
+
strict alternation (1st wrong, 2nd right, 3rd wrong, 4th
|
|
610
|
+
right). Cause: even with our continuous post-render takeover
|
|
611
|
+
(see report.html post-mermaid.run block), mermaid paints at
|
|
612
|
+
least once with its default palette BEFORE our takeover lands
|
|
613
|
+
— and the user sees that first paint. The takeover then
|
|
614
|
+
corrects it, but the user's already perceived "wrong colors."
|
|
615
|
+
On refresh, cached rendering may finish faster and the first
|
|
616
|
+
paint happens AFTER our takeover, which they perceive as
|
|
617
|
+
correct.
|
|
618
|
+
Defense: hide the SVG inside each pre.mermaid until we add
|
|
619
|
+
the `is-painted` class. The post-render takeover adds the
|
|
620
|
+
class as its last step. Until then, the user sees the frame
|
|
621
|
+
(matching the spine) with no SVG inside — no off-brand
|
|
622
|
+
colors flash at any point. */
|
|
623
|
+
.body pre.mermaid svg {
|
|
624
|
+
visibility: hidden;
|
|
625
|
+
}
|
|
626
|
+
.body pre.mermaid.is-painted svg {
|
|
627
|
+
visibility: visible;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* ─── CSS-variable mermaid palette · timing-race-immune ──────
|
|
631
|
+
Final defense layer for the recurring "first time wrong,
|
|
632
|
+
refresh OK" bug. JS takeover fights timing with mermaid's
|
|
633
|
+
deferred render work; CSS doesn't have that problem — as
|
|
634
|
+
soon as the SVG is in the DOM, these rules apply. The
|
|
635
|
+
spine-specific colours are injected as CSS variables on
|
|
636
|
+
`:root` from JS in the swapSpine flow (see swapSpine /
|
|
637
|
+
mermaid.initialize block). Variables update when the spine
|
|
638
|
+
changes; CSS rules are static.
|
|
639
|
+
|
|
640
|
+
Specificity: each rule uses a 3-class+ chain plus
|
|
641
|
+
!important, beating mermaid's `.quadrant-fill { fill: ... }`
|
|
642
|
+
and any `fill="..."` presentation attribute. The element
|
|
643
|
+
selectors target every mermaid 10 chart-type's known
|
|
644
|
+
visual elements. */
|
|
645
|
+
|
|
646
|
+
/* Quadrant chart · 4 quadrant rects + data points */
|
|
647
|
+
.body pre.mermaid svg .quadrants .quadrant rect,
|
|
648
|
+
.body pre.mermaid svg g.quadrant > rect,
|
|
649
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] g.quadrant rect {
|
|
650
|
+
fill: var(--mm-q-fill, #1A1A18) !important;
|
|
651
|
+
}
|
|
652
|
+
.body pre.mermaid svg .data-points circle,
|
|
653
|
+
.body pre.mermaid svg circle.data-point,
|
|
654
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .data-points circle {
|
|
655
|
+
fill: var(--mm-q-point, #C9A46B) !important;
|
|
656
|
+
stroke: var(--mm-q-bg, #1A1A18) !important;
|
|
657
|
+
}
|
|
658
|
+
.body pre.mermaid svg .quadrants line,
|
|
659
|
+
.body pre.mermaid svg .axis-line,
|
|
660
|
+
.body pre.mermaid svg .axisl-line,
|
|
661
|
+
.body pre.mermaid svg g.border line,
|
|
662
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] line,
|
|
663
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] g.border line {
|
|
664
|
+
stroke: var(--mm-q-border, #3A3934) !important;
|
|
665
|
+
}
|
|
666
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] text {
|
|
667
|
+
fill: var(--mm-q-text, #C8C5BE) !important;
|
|
668
|
+
}
|
|
669
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .bottom-axis text,
|
|
670
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .left-axis text,
|
|
671
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .top-axis text,
|
|
672
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .right-axis text {
|
|
673
|
+
fill: var(--mm-q-axis-text, #5C5A4D) !important;
|
|
674
|
+
}
|
|
675
|
+
.body pre.mermaid svg [aria-roledescription="quadrantChart"] .quadrant text {
|
|
676
|
+
fill: var(--mm-q-quad-text, #8E8B83) !important;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* xychart-beta · bars cycle through palette via :nth-child */
|
|
680
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g[class*="bar-plot"] rect,
|
|
681
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g.plot rect {
|
|
682
|
+
fill: var(--mm-pal-0, #C9A46B) !important;
|
|
683
|
+
stroke: none !important;
|
|
684
|
+
}
|
|
685
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g[class*="bar-plot"] rect:nth-child(2),
|
|
686
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g.plot rect:nth-child(2) {
|
|
687
|
+
fill: var(--mm-pal-1, #B6B0A2) !important;
|
|
688
|
+
}
|
|
689
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g[class*="bar-plot"] rect:nth-child(3),
|
|
690
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g.plot rect:nth-child(3) {
|
|
691
|
+
fill: var(--mm-pal-2, #8E8B83) !important;
|
|
692
|
+
}
|
|
693
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g[class*="bar-plot"] rect:nth-child(n+4),
|
|
694
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] g.plot rect:nth-child(n+4) {
|
|
695
|
+
fill: var(--mm-pal-3, #5C5A52) !important;
|
|
696
|
+
}
|
|
697
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] text {
|
|
698
|
+
fill: var(--mm-axis-text, #5C5A4D) !important;
|
|
699
|
+
}
|
|
700
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] .background,
|
|
701
|
+
.body pre.mermaid svg [aria-roledescription="xychart"] rect.background {
|
|
702
|
+
fill: transparent !important;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/* Pie · slice palette */
|
|
706
|
+
.body pre.mermaid svg path.pieCircle:nth-of-type(1) { fill: var(--mm-pal-0, #C9A46B) !important; stroke: var(--mm-q-bg, #1A1A18) !important; }
|
|
707
|
+
.body pre.mermaid svg path.pieCircle:nth-of-type(2) { fill: var(--mm-pal-1, #B6B0A2) !important; }
|
|
708
|
+
.body pre.mermaid svg path.pieCircle:nth-of-type(3) { fill: var(--mm-pal-2, #8E8B83) !important; }
|
|
709
|
+
.body pre.mermaid svg path.pieCircle:nth-of-type(n+4) { fill: var(--mm-pal-3, #5C5A52) !important; }
|
|
710
|
+
.body pre.mermaid svg circle.pieOuterCircle {
|
|
711
|
+
stroke: var(--mm-q-border, #3A3934) !important;
|
|
712
|
+
fill: none !important;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/* Universal text safety net inside any mermaid SVG · titleFill
|
|
716
|
+
for anything that escaped the per-type rules above. Lower
|
|
717
|
+
specificity than the type-specific rules so they still win. */
|
|
718
|
+
.body pre.mermaid svg text {
|
|
719
|
+
fill: var(--mm-text, #C8C5BE); /* no !important · type-specific rules above WIN */
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* ─── Chart display sizes · CSS-enforced ─────────────────────
|
|
723
|
+
Mermaid 10.9.5's chartWidth/chartHeight config keys for
|
|
724
|
+
quadrantChart and xychart are silently ignored by the
|
|
725
|
+
bundled CDN build (verified via DOM dump showing default
|
|
726
|
+
500×500 viewBox even when chartWidth: 460 is configured).
|
|
727
|
+
CSS-enforced sizes via `max-width` on the rendered SVG let
|
|
728
|
+
us get refined-compact dimensions regardless of what
|
|
729
|
+
mermaid's config validator does. The viewBox stays at
|
|
730
|
+
mermaid's internal coordinate space (500×500 etc.) but the
|
|
731
|
+
displayed SVG scales down to our target size. Centered with
|
|
732
|
+
margin: 0 auto so the chart sits flush in the article column
|
|
733
|
+
instead of stretching to full width. */
|
|
734
|
+
.body pre.mermaid svg[aria-roledescription="quadrantChart"] {
|
|
735
|
+
max-width: 460px !important;
|
|
736
|
+
height: auto !important;
|
|
737
|
+
margin: 0 auto !important;
|
|
738
|
+
display: block !important;
|
|
739
|
+
}
|
|
740
|
+
.body pre.mermaid svg[aria-roledescription="xychart"] {
|
|
741
|
+
max-width: 560px !important;
|
|
742
|
+
height: auto !important;
|
|
743
|
+
margin: 0 auto !important;
|
|
744
|
+
display: block !important;
|
|
745
|
+
}
|
|
746
|
+
.body pre.mermaid svg[aria-roledescription="pie"] {
|
|
747
|
+
max-width: 420px !important;
|
|
748
|
+
height: auto !important;
|
|
749
|
+
margin: 0 auto !important;
|
|
750
|
+
display: block !important;
|
|
751
|
+
}
|
|
752
|
+
|
|
591
753
|
/* ─── Metric-strip · spine-agnostic baseline ─────────────────────
|
|
592
754
|
Editorial / Swiss register: oversized thin numerals top-left, mute
|
|
593
755
|
mono label bottom-left, hairline grid (no card backgrounds, no
|
|
@@ -4783,6 +4945,31 @@
|
|
|
4783
4945
|
},
|
|
4784
4946
|
};
|
|
4785
4947
|
const t = themes[spineKey];
|
|
4948
|
+
|
|
4949
|
+
// Inject spine palette as CSS variables on :root so the
|
|
4950
|
+
// CSS rules in the global <style> block (mermaid spine
|
|
4951
|
+
// CSS) resolve to the right colours. This is the
|
|
4952
|
+
// PRIMARY mechanism — CSS applies on every paint, no JS
|
|
4953
|
+
// timing race possible. The JS takeover below stays as
|
|
4954
|
+
// belt-and-suspenders for chart elements not covered by
|
|
4955
|
+
// the CSS rules.
|
|
4956
|
+
try {
|
|
4957
|
+
const root = document.documentElement.style;
|
|
4958
|
+
root.setProperty('--mm-q-fill', t.vars.quadrantFill);
|
|
4959
|
+
root.setProperty('--mm-q-bg', t.vars.background);
|
|
4960
|
+
root.setProperty('--mm-q-point', t.vars.pointFill);
|
|
4961
|
+
root.setProperty('--mm-q-border', t.vars.border);
|
|
4962
|
+
root.setProperty('--mm-q-text', t.vars.titleFill);
|
|
4963
|
+
root.setProperty('--mm-q-axis-text', t.vars.axisText);
|
|
4964
|
+
root.setProperty('--mm-q-quad-text', t.vars.quadrantText);
|
|
4965
|
+
root.setProperty('--mm-text', t.vars.titleFill);
|
|
4966
|
+
root.setProperty('--mm-axis-text', t.vars.axisText);
|
|
4967
|
+
root.setProperty('--mm-pal-0', t.palette[0]);
|
|
4968
|
+
root.setProperty('--mm-pal-1', t.palette[1]);
|
|
4969
|
+
root.setProperty('--mm-pal-2', t.palette[2]);
|
|
4970
|
+
root.setProperty('--mm-pal-3', t.palette[3]);
|
|
4971
|
+
} catch (_) {}
|
|
4972
|
+
|
|
4786
4973
|
// Build pie / journey / state / git slice-color overrides
|
|
4787
4974
|
// by cycling through the spine's monochrome `palette`.
|
|
4788
4975
|
// Without these, mermaid 11+ paints pie1..pie12 +
|
|
@@ -5395,74 +5582,498 @@
|
|
|
5395
5582
|
useMaxWidth: true,
|
|
5396
5583
|
},
|
|
5397
5584
|
});
|
|
5398
|
-
|
|
5399
|
-
//
|
|
5400
|
-
//
|
|
5401
|
-
//
|
|
5402
|
-
//
|
|
5403
|
-
//
|
|
5404
|
-
//
|
|
5405
|
-
//
|
|
5406
|
-
//
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5585
|
+
// ── Off-screen render + paint-then-insert ────────────────
|
|
5586
|
+
// DEFINITIVE fix for "first time wrong, refresh OK" /
|
|
5587
|
+
// strict alternation pattern. Earlier strategies all let
|
|
5588
|
+
// mermaid render in-place, then patched colors after —
|
|
5589
|
+
// which gave the user a visible window of mermaid-default
|
|
5590
|
+
// colors before our takeover landed.
|
|
5591
|
+
//
|
|
5592
|
+
// The only race-free approach: render OFFSCREEN, paint our
|
|
5593
|
+
// spine palette onto the SVG while it's still detached,
|
|
5594
|
+
// THEN insert into the visible DOM. The user's first paint
|
|
5595
|
+
// sees the spine-colored SVG; there is no intermediate
|
|
5596
|
+
// mermaid-default state to perceive.
|
|
5597
|
+
//
|
|
5598
|
+
// mermaid 10's `render(id, src)` returns the SVG as a
|
|
5599
|
+
// string (not in-DOM) — perfect for this flow. We loop
|
|
5600
|
+
// through every `pre.mermaid`, render its source via
|
|
5601
|
+
// `mermaid.render`, parse the resulting string, apply the
|
|
5602
|
+
// takeover to the parsed (still-detached) SVG, then swap
|
|
5603
|
+
// the corrected SVG into the visible container.
|
|
5604
|
+
// Belt-and-suspenders fill setter · sets BOTH the inline
|
|
5605
|
+
// `style="fill: X !important"` AND the SVG `fill="X"`
|
|
5606
|
+
// presentation attribute. With mermaid's embedded <style>
|
|
5607
|
+
// already wiped, either layer is sufficient — but setting
|
|
5608
|
+
// both means even if one is somehow stripped/overridden
|
|
5609
|
+
// (browser quirk, late mermaid post-processing, CSS
|
|
5610
|
+
// cascade edge case), the chart still renders in the
|
|
5611
|
+
// spine palette.
|
|
5612
|
+
const setFill = (el, c) => {
|
|
5613
|
+
if (!c) return;
|
|
5614
|
+
try { el.style.setProperty('fill', c, 'important'); } catch (_) {}
|
|
5615
|
+
try { if (el.setAttribute) el.setAttribute('fill', c); } catch (_) {}
|
|
5616
|
+
};
|
|
5617
|
+
const setStroke = (el, c) => {
|
|
5618
|
+
if (c === undefined || c === null) return;
|
|
5619
|
+
try { el.style.setProperty('stroke', c, 'important'); } catch (_) {}
|
|
5620
|
+
try { if (el.setAttribute) el.setAttribute('stroke', c); } catch (_) {}
|
|
5621
|
+
};
|
|
5622
|
+
const setColor = (el, c) => { if (c) el.style.setProperty('color', c, 'important'); };
|
|
5623
|
+
const cyclePalette = (i) => t.palette[i % t.palette.length];
|
|
5624
|
+
|
|
5625
|
+
// Apply the spine palette to ONE SVG element (offscreen or
|
|
5626
|
+
// onscreen — works either way since every fill/stroke is
|
|
5627
|
+
// forced via inline `!important`). Pure function: no
|
|
5628
|
+
// global queries, only operates on the passed element.
|
|
5629
|
+
const applySpineToSvg = (svg) => {
|
|
5630
|
+
try {
|
|
5631
|
+
// ── 0. Pin the SVG max-width per chart type ──
|
|
5632
|
+
// Mermaid 10's chartWidth/chartHeight config is silently
|
|
5633
|
+
// ignored by the bundled CDN build, so the SVG ships at
|
|
5634
|
+
// its default size with `style="max-width: 500px"`
|
|
5635
|
+
// inline. CSS in <style> overrides it at paint time, but
|
|
5636
|
+
// there's a one-frame race on cold loads where the
|
|
5637
|
+
// user can perceive the unconstrained size before CSS
|
|
5638
|
+
// applies. Pin via inline style with !important here
|
|
5639
|
+
// for refined-compact dimensions on every first paint.
|
|
5640
|
+
const role = svg.getAttribute('aria-roledescription') || '';
|
|
5641
|
+
const chartMaxWidth = (
|
|
5642
|
+
role === 'quadrantChart' ? '460px' :
|
|
5643
|
+
role === 'xychart' ? '560px' :
|
|
5644
|
+
role === 'pie' ? '420px' :
|
|
5645
|
+
''
|
|
5646
|
+
);
|
|
5647
|
+
if (chartMaxWidth) {
|
|
5648
|
+
svg.style.setProperty('max-width', chartMaxWidth, 'important');
|
|
5649
|
+
svg.style.setProperty('height', 'auto', 'important');
|
|
5650
|
+
svg.style.setProperty('margin', '0 auto', 'important');
|
|
5651
|
+
svg.style.setProperty('display', 'block', 'important');
|
|
5652
|
+
}
|
|
5653
|
+
// ── 1. Wipe mermaid's embedded <style> block ──
|
|
5654
|
+
// Mermaid generates `<style>.quadrant-chart > .quadrant-
|
|
5655
|
+
// point > circle { fill: #...; }` etc. inside each SVG;
|
|
5656
|
+
// those CSS rules beat our setAttribute-set fills. After
|
|
5657
|
+
// removal, our inline-style fills below have no rivals.
|
|
5658
|
+
svg.querySelectorAll(':scope > style, defs > style').forEach((s) => s.remove());
|
|
5659
|
+
|
|
5660
|
+
// ── 2. SVG canvas / background ──
|
|
5661
|
+
// Any rect that fills the whole SVG is the chart canvas;
|
|
5662
|
+
// pin to t.vars.background so the chart blends with its
|
|
5663
|
+
// spine frame instead of showing mermaid's default white.
|
|
5664
|
+
const sw = parseFloat(svg.getAttribute('width') || '0');
|
|
5665
|
+
svg.querySelectorAll(':scope > rect, :scope > g > rect.background, rect.main-bkg').forEach((el) => {
|
|
5666
|
+
const w = parseFloat(el.getAttribute('width') || '0');
|
|
5667
|
+
if (w > 0 && sw > 0 && w >= sw * 0.95) setFill(el, t.vars.background);
|
|
5415
5668
|
});
|
|
5416
|
-
|
|
5417
|
-
//
|
|
5418
|
-
//
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5669
|
+
|
|
5670
|
+
// ── 3. Quadrant chart ──
|
|
5671
|
+
// Mermaid 10's actual class names (verified against rendered DOM):
|
|
5672
|
+
// `<g class="quadrant">` — group; each contains a child <rect>
|
|
5673
|
+
// `.quadrants` — outer parent group
|
|
5674
|
+
// `<circle class="data-point">` — each data circle
|
|
5675
|
+
// `.data-points` — parent group
|
|
5676
|
+
// `.axis-line` / `.axisl-line` — axis lines
|
|
5677
|
+
// `.bottom-axis`, `.left-axis` — axis text containers
|
|
5678
|
+
// CRITICAL: the `.quadrant` class is on a <g>, but the
|
|
5679
|
+
// actual fillable rect is INSIDE the group. Selecting
|
|
5680
|
+
// `.quadrants > .quadrant` matches the <g> only;
|
|
5681
|
+
// setting style.fill on a <g> does NOT override the
|
|
5682
|
+
// child <rect>'s `fill="..."` attribute in SVG. We
|
|
5683
|
+
// must select the inner rect directly.
|
|
5684
|
+
//
|
|
5685
|
+
// Belt-and-suspenders: also match ALL <rect> inside
|
|
5686
|
+
// any quadrantChart SVG (excluding the canvas-bg rect
|
|
5687
|
+
// we already handled via dimensions). Catches any
|
|
5688
|
+
// mermaid version variation in class naming.
|
|
5689
|
+
svg.querySelectorAll('.quadrants .quadrant rect, g.quadrant > rect, rect.quadrant, rect.quadrant-fill, .quadrant-fill').forEach((el) => setFill(el, t.vars.quadrantFill));
|
|
5690
|
+
if (svg.getAttribute('aria-roledescription') === 'quadrantChart') {
|
|
5691
|
+
const qsw = parseFloat(svg.getAttribute('width') || '0');
|
|
5692
|
+
svg.querySelectorAll('rect').forEach((el) => {
|
|
5693
|
+
// Skip the canvas-bg rect (full-width).
|
|
5423
5694
|
const w = parseFloat(el.getAttribute('width') || '0');
|
|
5424
|
-
if (
|
|
5425
|
-
|
|
5426
|
-
}
|
|
5695
|
+
if (qsw > 0 && w >= qsw * 0.95) return;
|
|
5696
|
+
setFill(el, t.vars.quadrantFill);
|
|
5427
5697
|
});
|
|
5428
5698
|
}
|
|
5429
|
-
// Quadrant
|
|
5430
|
-
//
|
|
5431
|
-
//
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5699
|
+
// Quadrant border lines · mermaid 10 puts the outer
|
|
5700
|
+
// rectangle + cross-divider lines inside `<g class="border">`,
|
|
5701
|
+
// a sibling of `.quadrants` (NOT a descendant). Catch
|
|
5702
|
+
// both the inside-quadrants axis-line case AND the
|
|
5703
|
+
// border group case.
|
|
5704
|
+
svg.querySelectorAll('.axis-line, .axisl-line, .quadrants line, g.border line, .border line').forEach((el) => setStroke(el, t.vars.border));
|
|
5705
|
+
svg.querySelectorAll('.quadrant-internal-border, line.quadrant-internal-border').forEach((el) => setStroke(el, t.vars.inner));
|
|
5706
|
+
svg.querySelectorAll('.quadrant-external-border, rect.quadrant-external-border').forEach((el) => {
|
|
5707
|
+
setStroke(el, t.vars.border);
|
|
5708
|
+
setFill(el, 'none');
|
|
5435
5709
|
});
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5710
|
+
svg.querySelectorAll('text.quadrant-title, .quadrant-title, .chart-title').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5711
|
+
// Quadrant labels (the 4 corner labels). Mermaid 10
|
|
5712
|
+
// doesn't put them in a class; they're plain `<text>` inside
|
|
5713
|
+
// each `.quadrant` group. Walk children of `.quadrant` text.
|
|
5714
|
+
svg.querySelectorAll('.quadrant text, .quadrants text:not(.chart-title)').forEach((el) => setFill(el, t.vars.quadrantText));
|
|
5715
|
+
svg.querySelectorAll('text.quadrant-text, text.quadrant-quadrant-1-text-fill, text.quadrant-quadrant-2-text-fill, text.quadrant-quadrant-3-text-fill, text.quadrant-quadrant-4-text-fill').forEach((el) => setFill(el, t.vars.quadrantText));
|
|
5716
|
+
// Axis labels · "Low likelihood / High likelihood" etc.
|
|
5717
|
+
svg.querySelectorAll('.bottom-axis text, .left-axis text, .top-axis text, .right-axis text, text.x-axis-text-label, text.y-axis-text-label, .quadrant-x-axis-label, .quadrant-y-axis-label').forEach((el) => setFill(el, t.vars.axisText));
|
|
5718
|
+
// Data points · the load-bearing accent.
|
|
5719
|
+
svg.querySelectorAll('.data-point, .data-points circle, circle.data-point, .quadrant-point > circle, g.quadrant-point > circle, circle.quadrant-point').forEach((el) => {
|
|
5720
|
+
setFill(el, t.vars.pointFill);
|
|
5721
|
+
setStroke(el, t.vars.background);
|
|
5722
|
+
});
|
|
5723
|
+
svg.querySelectorAll('.data-point text, .data-points text, text.point-text, .quadrant-point text').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5724
|
+
|
|
5725
|
+
// ── 4. Pie chart ──
|
|
5726
|
+
svg.querySelectorAll('path.pieCircle, g.pieCircle path, .slice').forEach((el, i) => {
|
|
5727
|
+
setFill(el, cyclePalette(i));
|
|
5728
|
+
setStroke(el, t.vars.background);
|
|
5440
5729
|
});
|
|
5441
|
-
// Pie outer ring border.
|
|
5442
5730
|
svg.querySelectorAll('.pieOuterCircle, circle.pieOuterCircle').forEach((el) => {
|
|
5443
|
-
el
|
|
5444
|
-
el
|
|
5731
|
+
setStroke(el, t.vars.border);
|
|
5732
|
+
setFill(el, 'none');
|
|
5733
|
+
});
|
|
5734
|
+
svg.querySelectorAll('text.slice, text.pieLegendText, .legend text, g.legend text').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5735
|
+
svg.querySelectorAll('.pieTitleText').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5736
|
+
|
|
5737
|
+
// ── 5. xychart-beta ──
|
|
5738
|
+
// Bars cycle through palette; axes + grid + labels match
|
|
5739
|
+
// the spine's neutrals.
|
|
5740
|
+
svg.querySelectorAll('g.plot rect, g.bar-plot rect, g[class*="-plot"] rect, rect.bar').forEach((el, i) => {
|
|
5741
|
+
// Skip the plot-background rect (full plot width).
|
|
5742
|
+
const w = parseFloat(el.getAttribute('width') || '0');
|
|
5743
|
+
if (w >= 200) return;
|
|
5744
|
+
setFill(el, cyclePalette(i));
|
|
5745
|
+
setStroke(el, 'none');
|
|
5746
|
+
});
|
|
5747
|
+
svg.querySelectorAll('.xy-chart .background, rect.background').forEach((el) => setFill(el, 'transparent'));
|
|
5748
|
+
svg.querySelectorAll('.xy-chart text, g.xy-chart text, .xy-chart .x-axis text, .xy-chart .y-axis text').forEach((el) => setFill(el, t.vars.axisText));
|
|
5749
|
+
svg.querySelectorAll('.xy-chart .x-axis path.domain, .xy-chart .y-axis path.domain, .xy-chart .x-axis line, .xy-chart .y-axis line').forEach((el) => setStroke(el, t.vars.titleFill));
|
|
5750
|
+
svg.querySelectorAll('.xy-chart .grid line, .xy-chart line.grid').forEach((el) => setStroke(el, t.vars.inner));
|
|
5751
|
+
|
|
5752
|
+
// ── 6. Flowchart ──
|
|
5753
|
+
svg.querySelectorAll('.flowchart .node rect, .flowchart .node circle, .flowchart .node ellipse, .flowchart .node polygon, .node rect, .node circle, .node polygon, .node ellipse').forEach((el) => {
|
|
5754
|
+
setFill(el, t.vars.quadrantFill);
|
|
5755
|
+
setStroke(el, t.vars.border);
|
|
5756
|
+
});
|
|
5757
|
+
svg.querySelectorAll('.flowchart-link, .edgePath path, .edgePath .path, path.flowchart-link').forEach((el) => {
|
|
5758
|
+
setStroke(el, t.vars.border);
|
|
5759
|
+
setFill(el, 'none');
|
|
5760
|
+
});
|
|
5761
|
+
svg.querySelectorAll('marker path, #arrowhead path, marker#arrowhead path').forEach((el) => {
|
|
5762
|
+
setFill(el, t.vars.border);
|
|
5763
|
+
setStroke(el, t.vars.border);
|
|
5764
|
+
});
|
|
5765
|
+
svg.querySelectorAll('.cluster rect').forEach((el) => {
|
|
5766
|
+
setFill(el, t.vars.background);
|
|
5767
|
+
setStroke(el, t.vars.border);
|
|
5768
|
+
});
|
|
5769
|
+
svg.querySelectorAll('.cluster text, .nodeLabel, .label foreignObject *').forEach((el) => {
|
|
5770
|
+
setFill(el, t.vars.titleFill);
|
|
5771
|
+
setColor(el, t.vars.titleFill);
|
|
5772
|
+
});
|
|
5773
|
+
svg.querySelectorAll('.edgeLabel, .edgeLabel foreignObject *').forEach((el) => {
|
|
5774
|
+
setFill(el, t.vars.axisText);
|
|
5775
|
+
setColor(el, t.vars.axisText);
|
|
5776
|
+
el.style.setProperty('background-color', t.vars.background, 'important');
|
|
5777
|
+
});
|
|
5778
|
+
|
|
5779
|
+
// ── 7. Mindmap ──
|
|
5780
|
+
svg.querySelectorAll('.mindmap g.section circle, .mindmap-node circle, g.mindmap-node circle').forEach((el, i) => {
|
|
5781
|
+
setFill(el, cyclePalette(i));
|
|
5782
|
+
setStroke(el, t.vars.border);
|
|
5445
5783
|
});
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
el
|
|
5784
|
+
svg.querySelectorAll('.mindmap g.section rect, .mindmap-node rect, g.mindmap-node rect').forEach((el, i) => {
|
|
5785
|
+
setFill(el, cyclePalette(i));
|
|
5786
|
+
setStroke(el, t.vars.border);
|
|
5449
5787
|
});
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5788
|
+
svg.querySelectorAll('.mindmap g.section polygon, .mindmap-node polygon, g.mindmap-node polygon').forEach((el, i) => {
|
|
5789
|
+
setFill(el, cyclePalette(i));
|
|
5790
|
+
setStroke(el, t.vars.border);
|
|
5791
|
+
});
|
|
5792
|
+
svg.querySelectorAll('.mindmap-edges path, .mindmap path.edge').forEach((el) => {
|
|
5793
|
+
setStroke(el, t.vars.border);
|
|
5794
|
+
setFill(el, 'none');
|
|
5795
|
+
});
|
|
5796
|
+
svg.querySelectorAll('.mindmap text, .mindmap-node text, g.mindmap-node text, .mindmap foreignObject *').forEach((el) => {
|
|
5797
|
+
setFill(el, t.vars.titleFill);
|
|
5798
|
+
setColor(el, t.vars.titleFill);
|
|
5799
|
+
});
|
|
5800
|
+
|
|
5801
|
+
// ── 8. Sequence diagram ──
|
|
5802
|
+
svg.querySelectorAll('rect.actor, .actor').forEach((el) => {
|
|
5803
|
+
setFill(el, t.vars.quadrantFill);
|
|
5804
|
+
setStroke(el, t.vars.border);
|
|
5805
|
+
});
|
|
5806
|
+
svg.querySelectorAll('line.actor-line, .actor-line').forEach((el) => setStroke(el, t.vars.border));
|
|
5807
|
+
svg.querySelectorAll('text.actor, text.actor tspan, .actor-box-text, .messageText, text.messageText, .noteText, text.noteText, .note text').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5808
|
+
svg.querySelectorAll('.messageLine0, .messageLine1, line.messageLine0, line.messageLine1').forEach((el) => setStroke(el, t.vars.titleFill));
|
|
5809
|
+
svg.querySelectorAll('rect.note, .note').forEach((el) => {
|
|
5810
|
+
setFill(el, t.vars.accentSoft);
|
|
5811
|
+
setStroke(el, t.vars.border);
|
|
5812
|
+
});
|
|
5813
|
+
svg.querySelectorAll('.activation0, .activation1, .activation2, rect.activation0, rect.activation1, rect.activation2').forEach((el) => {
|
|
5814
|
+
setFill(el, t.vars.inner);
|
|
5815
|
+
setStroke(el, t.vars.border);
|
|
5816
|
+
});
|
|
5817
|
+
svg.querySelectorAll('.labelBox, rect.labelBox').forEach((el) => {
|
|
5818
|
+
setFill(el, t.vars.quadrantFill);
|
|
5819
|
+
setStroke(el, t.vars.border);
|
|
5820
|
+
});
|
|
5821
|
+
|
|
5822
|
+
// ── 9. State diagram ──
|
|
5823
|
+
svg.querySelectorAll('.stateGroup rect, g.stateGroup rect, rect.state').forEach((el) => {
|
|
5824
|
+
setFill(el, t.vars.quadrantFill);
|
|
5825
|
+
setStroke(el, t.vars.border);
|
|
5826
|
+
});
|
|
5827
|
+
svg.querySelectorAll('.stateGroup circle, g.stateGroup circle').forEach((el) => {
|
|
5828
|
+
setFill(el, t.vars.titleFill);
|
|
5829
|
+
setStroke(el, t.vars.titleFill);
|
|
5830
|
+
});
|
|
5831
|
+
svg.querySelectorAll('.stateGroup line, line.transition, path.transition').forEach((el) => {
|
|
5832
|
+
setStroke(el, t.vars.border);
|
|
5833
|
+
setFill(el, 'none');
|
|
5834
|
+
});
|
|
5835
|
+
svg.querySelectorAll('.stateLabel, .state-description, .stateGroup text').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5836
|
+
svg.querySelectorAll('.compositeBackground, rect.compositeBackground').forEach((el) => {
|
|
5837
|
+
setFill(el, t.vars.background);
|
|
5838
|
+
setStroke(el, t.vars.border);
|
|
5839
|
+
});
|
|
5840
|
+
|
|
5841
|
+
// ── 10. Journey ──
|
|
5842
|
+
svg.querySelectorAll('.face-circle, circle.face-circle, .journey-section .face').forEach((el, i) => {
|
|
5843
|
+
setFill(el, cyclePalette(i));
|
|
5844
|
+
setStroke(el, t.vars.background);
|
|
5845
|
+
});
|
|
5846
|
+
svg.querySelectorAll('.journey-section text, .section text, .journey-section foreignObject *').forEach((el) => {
|
|
5847
|
+
setFill(el, t.vars.titleFill);
|
|
5848
|
+
setColor(el, t.vars.titleFill);
|
|
5849
|
+
});
|
|
5850
|
+
svg.querySelectorAll('rect.journey, .journey-section rect').forEach((el) => {
|
|
5851
|
+
setFill(el, t.vars.quadrantFill);
|
|
5852
|
+
setStroke(el, t.vars.border);
|
|
5853
|
+
});
|
|
5854
|
+
|
|
5855
|
+
// ── 11. Gantt ──
|
|
5856
|
+
svg.querySelectorAll('rect.task, rect.task0, rect.task1, rect.task2, rect.task3, rect.activeTask0, rect.activeTask1, rect.activeTask2, rect.activeTask3').forEach((el, i) => {
|
|
5857
|
+
setFill(el, cyclePalette(i));
|
|
5858
|
+
setStroke(el, 'none');
|
|
5859
|
+
});
|
|
5860
|
+
svg.querySelectorAll('rect.done, rect.done0, rect.done1, rect.done2, rect.done3, rect.doneTask').forEach((el) => {
|
|
5861
|
+
setFill(el, t.vars.inner);
|
|
5862
|
+
setStroke(el, t.vars.border);
|
|
5863
|
+
});
|
|
5864
|
+
svg.querySelectorAll('rect.crit, rect.crit0, rect.crit1, rect.crit2, rect.crit3').forEach((el) => {
|
|
5865
|
+
setFill(el, t.vars.accent);
|
|
5866
|
+
setStroke(el, t.vars.accent);
|
|
5867
|
+
});
|
|
5868
|
+
svg.querySelectorAll('.taskText, .taskTextOutsideRight, .taskTextOutsideLeft, text.taskText').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5869
|
+
svg.querySelectorAll('.tick text, g.tick text').forEach((el) => setFill(el, t.vars.axisText));
|
|
5870
|
+
svg.querySelectorAll('.tick line, .grid line, g.tick line, g.grid line').forEach((el) => setStroke(el, t.vars.inner));
|
|
5871
|
+
svg.querySelectorAll('.section0 text, .section1 text, .section2 text, .section3 text, .sectionTitle0, .sectionTitle1, .sectionTitle2, .sectionTitle3').forEach((el) => setFill(el, t.vars.axisText));
|
|
5872
|
+
svg.querySelectorAll('line.today, .today').forEach((el) => setStroke(el, t.vars.accent));
|
|
5873
|
+
|
|
5874
|
+
// ── 12. Timeline ──
|
|
5875
|
+
svg.querySelectorAll('.timeline rect, .timeline-section rect').forEach((el, i) => {
|
|
5876
|
+
setFill(el, cyclePalette(i));
|
|
5877
|
+
setStroke(el, t.vars.border);
|
|
5878
|
+
});
|
|
5879
|
+
svg.querySelectorAll('.timeline text, .timeline foreignObject *').forEach((el) => {
|
|
5880
|
+
setFill(el, t.vars.titleFill);
|
|
5881
|
+
setColor(el, t.vars.titleFill);
|
|
5882
|
+
});
|
|
5883
|
+
svg.querySelectorAll('.timeline path, .timeline-line').forEach((el) => {
|
|
5884
|
+
setStroke(el, t.vars.border);
|
|
5885
|
+
setFill(el, 'none');
|
|
5886
|
+
});
|
|
5887
|
+
|
|
5888
|
+
// ── 13. Git graph ──
|
|
5889
|
+
svg.querySelectorAll('.commit-bullets circle, circle.commit, .commit').forEach((el, i) => {
|
|
5890
|
+
setFill(el, cyclePalette(i));
|
|
5891
|
+
setStroke(el, t.vars.border);
|
|
5892
|
+
});
|
|
5893
|
+
svg.querySelectorAll('.commit-arrows path, path.commit-arrow, .commit-arrow').forEach((el) => {
|
|
5894
|
+
setStroke(el, t.vars.border);
|
|
5895
|
+
setFill(el, 'none');
|
|
5896
|
+
});
|
|
5897
|
+
svg.querySelectorAll('text.commit-label, .commit-label, .branch-label').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5898
|
+
|
|
5899
|
+
// ── 14. Generic title (every chart type) ──
|
|
5900
|
+
svg.querySelectorAll('text.titleText, .titleText, text.chart-title').forEach((el) => setFill(el, t.vars.titleFill));
|
|
5901
|
+
|
|
5902
|
+
// ── 15. Universal text safety net ──
|
|
5903
|
+
// Any text element that survived the per-type passes
|
|
5904
|
+
// without an inline style fill — pin to titleFill so no
|
|
5905
|
+
// text leaks the mermaid-default purple/blue.
|
|
5906
|
+
svg.querySelectorAll('text').forEach((el) => {
|
|
5907
|
+
if (!el.style.fill) setFill(el, t.vars.titleFill);
|
|
5908
|
+
});
|
|
5909
|
+
|
|
5910
|
+
// ── 16. Mark the parent pre.mermaid as painted ──
|
|
5911
|
+
// CSS hides `.body pre.mermaid svg` until `.is-painted`
|
|
5912
|
+
// is added on the parent. We add it here so that even
|
|
5913
|
+
// when the SVG is later inserted into the live DOM, the
|
|
5914
|
+
// CSS-based hide is already lifted (no flash).
|
|
5915
|
+
const host = svg.closest('pre.mermaid');
|
|
5916
|
+
if (host && !host.classList.contains('is-painted')) {
|
|
5917
|
+
host.classList.add('is-painted');
|
|
5918
|
+
}
|
|
5919
|
+
} catch (forceColorErr) {
|
|
5920
|
+
console.warn('[mermaid] palette takeover failed for svg:', forceColorErr);
|
|
5921
|
+
}
|
|
5922
|
+
}; // applySpineToSvg
|
|
5923
|
+
|
|
5924
|
+
// Wrapper · walks every onscreen SVG and applies the
|
|
5925
|
+
// takeover. Used by the safety polling + MutationObserver
|
|
5926
|
+
// (defense-in-depth) for any SVGs that were already
|
|
5927
|
+
// inserted before the offscreen-render loop took over.
|
|
5928
|
+
const applySpinePaletteOnce = () => {
|
|
5929
|
+
document.querySelectorAll('.mermaid svg').forEach(applySpineToSvg);
|
|
5930
|
+
};
|
|
5931
|
+
|
|
5932
|
+
// ── Offscreen render via mermaid.run + DOM relocation ─
|
|
5933
|
+
// DEFINITIVE fix for "first load wrong / strict
|
|
5934
|
+
// alternation." Keep mermaid.run (it handles lazy module
|
|
5935
|
+
// loading for diagram types correctly — mermaid.render's
|
|
5936
|
+
// first call races with that loading on cold cache),
|
|
5937
|
+
// but move every pre.mermaid INTO AN OFFSCREEN CONTAINER
|
|
5938
|
+
// before running. mermaid renders inside the offscreen
|
|
5939
|
+
// container (user can't see it), we apply the spine
|
|
5940
|
+
// takeover to every resulting SVG, then move each
|
|
5941
|
+
// pre.mermaid back to its original DOM position. The
|
|
5942
|
+
// user's first paint of each chart shows the
|
|
5943
|
+
// spine-coloured SVG; no race possible.
|
|
5944
|
+
//
|
|
5945
|
+
// Diagnostic logs (console.info) so the user can verify
|
|
5946
|
+
// which path was taken and at what timing — open DevTools
|
|
5947
|
+
// and refresh; the log narrates the flow.
|
|
5948
|
+
const _mLog = (msg, ...rest) => {
|
|
5949
|
+
try { console.info(`[mermaid-spine] ${msg}`, ...rest); } catch (_) {}
|
|
5950
|
+
};
|
|
5951
|
+
_mLog('flow start · spine =', spineKey);
|
|
5952
|
+
const charts = Array.from(document.querySelectorAll('pre.mermaid'));
|
|
5953
|
+
_mLog('charts found:', charts.length);
|
|
5954
|
+
if (charts.length > 0) {
|
|
5955
|
+
// Capture original DOM positions so we can restore each
|
|
5956
|
+
// pre.mermaid exactly where it was.
|
|
5957
|
+
const anchors = charts.map((el) => ({
|
|
5958
|
+
el,
|
|
5959
|
+
parent: el.parentNode,
|
|
5960
|
+
nextSibling: el.nextSibling,
|
|
5961
|
+
}));
|
|
5962
|
+
// Build the offscreen host. Width matches a typical
|
|
5963
|
+
// article column so mermaid sizes the SVG correctly
|
|
5964
|
+
// (useMaxWidth: true uses the offscreen container's
|
|
5965
|
+
// dimensions for layout). Height: 0 + overflow: visible
|
|
5966
|
+
// so mermaid can compute proper SVG sizes.
|
|
5967
|
+
const offscreen = document.createElement('div');
|
|
5968
|
+
offscreen.id = 'mermaid-offscreen-host';
|
|
5969
|
+
offscreen.style.cssText = [
|
|
5970
|
+
'position: absolute',
|
|
5971
|
+
'left: -99999px',
|
|
5972
|
+
'top: 0',
|
|
5973
|
+
'width: 880px',
|
|
5974
|
+
'visibility: hidden',
|
|
5975
|
+
'pointer-events: none',
|
|
5976
|
+
'z-index: -1',
|
|
5977
|
+
].join('; ');
|
|
5978
|
+
document.body.appendChild(offscreen);
|
|
5979
|
+
// Move each pre.mermaid into the offscreen host.
|
|
5980
|
+
anchors.forEach((a) => offscreen.appendChild(a.el));
|
|
5981
|
+
_mLog('moved offscreen, calling mermaid.run');
|
|
5982
|
+
// Run mermaid · scoped to the offscreen host so it only
|
|
5983
|
+
// touches elements we just moved. mermaid handles lazy
|
|
5984
|
+
// module registration internally.
|
|
5985
|
+
try {
|
|
5986
|
+
await mermaid.run({ querySelector: '#mermaid-offscreen-host pre.mermaid' });
|
|
5987
|
+
_mLog('mermaid.run resolved');
|
|
5988
|
+
} catch (runErr) {
|
|
5989
|
+
console.warn('[mermaid-spine] offscreen run failed:', runErr);
|
|
5990
|
+
}
|
|
5991
|
+
// Wait for mermaid's deferred post-render work to land.
|
|
5992
|
+
// Quadrant chart (specifically) does d3-based label
|
|
5993
|
+
// positioning in a setTimeout(0) AFTER mermaid.run
|
|
5994
|
+
// resolves — if we apply takeover before that fires,
|
|
5995
|
+
// mermaid overwrites our fills. Two rAF + setTimeout(0)
|
|
5996
|
+
// cycles guarantee deferred work has settled. Cheap
|
|
5997
|
+
// (~32ms) and only runs once per page load.
|
|
5998
|
+
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => setTimeout(r, 0))));
|
|
5999
|
+
// Apply takeover to all SVGs while still offscreen.
|
|
6000
|
+
const svgsOffscreen = offscreen.querySelectorAll('svg');
|
|
6001
|
+
_mLog('SVGs rendered offscreen:', svgsOffscreen.length);
|
|
6002
|
+
svgsOffscreen.forEach(applySpineToSvg);
|
|
6003
|
+
_mLog('takeover applied offscreen');
|
|
6004
|
+
// Move each pre.mermaid back to its original position
|
|
6005
|
+
// and mark as painted (CSS reveal rule). Single DOM
|
|
6006
|
+
// mutation per chart → single paint frame with the
|
|
6007
|
+
// corrected SVG already in place.
|
|
6008
|
+
anchors.forEach((a) => {
|
|
6009
|
+
a.el.classList.add('is-painted');
|
|
6010
|
+
if (a.parent) {
|
|
6011
|
+
if (a.nextSibling && a.nextSibling.parentNode === a.parent) {
|
|
6012
|
+
a.parent.insertBefore(a.el, a.nextSibling);
|
|
6013
|
+
} else {
|
|
6014
|
+
a.parent.appendChild(a.el);
|
|
5460
6015
|
}
|
|
5461
6016
|
}
|
|
6017
|
+
// Re-apply takeover immediately after move · catches
|
|
6018
|
+
// any post-attach mermaid behaviour (ResizeObserver
|
|
6019
|
+
// re-renders, deferred rAF) that might restyle.
|
|
6020
|
+
a.el.querySelectorAll('svg').forEach(applySpineToSvg);
|
|
5462
6021
|
});
|
|
5463
|
-
|
|
5464
|
-
|
|
6022
|
+
_mLog('moved back onscreen + reapplied takeover');
|
|
6023
|
+
// Cleanup offscreen host.
|
|
6024
|
+
offscreen.remove();
|
|
5465
6025
|
}
|
|
6026
|
+
|
|
6027
|
+
// Safety net · if the takeover errors out for some reason
|
|
6028
|
+
// (script bug, exotic chart type), make sure every chart
|
|
6029
|
+
// becomes visible within 6s so the user at least sees the
|
|
6030
|
+
// chart (even if colours leak) instead of an empty frame.
|
|
6031
|
+
// Normal flow: each successful takeover pass marks its
|
|
6032
|
+
// pre.mermaid as `.is-painted` immediately, so the chart
|
|
6033
|
+
// appears as soon as the first pass succeeds (~16ms).
|
|
6034
|
+
setTimeout(() => {
|
|
6035
|
+
document.querySelectorAll('pre.mermaid:not(.is-painted)').forEach((el) => {
|
|
6036
|
+
el.classList.add('is-painted');
|
|
6037
|
+
});
|
|
6038
|
+
}, 6000);
|
|
6039
|
+
|
|
6040
|
+
// ── Continuous re-apply for the first 5 seconds ──
|
|
6041
|
+
// The user reported strict alternation ("1st wrong, 2nd
|
|
6042
|
+
// right, 3rd wrong, 4th right…") which means a single
|
|
6043
|
+
// setTimeout chain wasn't enough — mermaid's deferred
|
|
6044
|
+
// work was landing in different timing windows on
|
|
6045
|
+
// alternate loads, and my chain was missing odd-numbered
|
|
6046
|
+
// ones.
|
|
6047
|
+
//
|
|
6048
|
+
// Defense in depth: re-apply EVERY 60ms for the first 5
|
|
6049
|
+
// seconds. By the time the user perceives the chart, our
|
|
6050
|
+
// styles have been pinned 80+ times across every possible
|
|
6051
|
+
// mermaid post-render window. After 5s, switch to a pure
|
|
6052
|
+
// MutationObserver (only fires on real DOM changes) so we
|
|
6053
|
+
// keep up with future mutations without the polling cost.
|
|
6054
|
+
//
|
|
6055
|
+
// Each call is idempotent — same selectors set the same
|
|
6056
|
+
// inline-style values, so re-running is a cheap no-op
|
|
6057
|
+
// when nothing changed.
|
|
6058
|
+
applySpinePaletteOnce(); // sync, before next paint
|
|
6059
|
+
let pollCount = 0;
|
|
6060
|
+
const pollInterval = setInterval(() => {
|
|
6061
|
+
applySpinePaletteOnce();
|
|
6062
|
+
if (++pollCount > 80) clearInterval(pollInterval); // 80 × 60ms ≈ 4.8s
|
|
6063
|
+
}, 60);
|
|
6064
|
+
|
|
6065
|
+
// Persistent MutationObserver · catches any post-poll
|
|
6066
|
+
// mutations (container resize, font swap, late SSE
|
|
6067
|
+
// re-render). Watches structural changes only — our own
|
|
6068
|
+
// setProperty calls modify the `style` attribute, which
|
|
6069
|
+
// is excluded by `attributes: false` (default), so this
|
|
6070
|
+
// can't loop on itself.
|
|
6071
|
+
document.querySelectorAll('.mermaid svg').forEach((svg) => {
|
|
6072
|
+
const obs = new MutationObserver(() => {
|
|
6073
|
+
requestAnimationFrame(applySpinePaletteOnce);
|
|
6074
|
+
});
|
|
6075
|
+
obs.observe(svg, { childList: true, subtree: true });
|
|
6076
|
+
});
|
|
5466
6077
|
// ── Post-render xychart bar slimming ─────────────────────
|
|
5467
6078
|
// mermaid xychart-beta renders bars at near-full slot width
|
|
5468
6079
|
// regardless of `plotReservedSpacePercent` in some 11.x
|