♻️ Refactor: Mermaid theme switching again (#623)

This commit is contained in:
Cell
2025-08-17 14:32:51 +08:00
committed by GitHub
parent 3c3f6084cc
commit 77fcd7fa93
6 changed files with 151 additions and 89 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ code {
}
// indented code
pre:not(.mermaid[data-processed]) {
pre:not([data-processed]) {
margin: 0;
line-height: 1.45em;
padding: 0.5rem;
+39 -2
View File
@@ -1,9 +1,12 @@
.mermaid {
.mermaid,
.mermaid-dark,
.mermaid-neutral {
position: relative;
overflow: hidden !important;
&[data-processed] {
text-align: center;
margin-block: 0.5rem;
}
&:not([data-processed])::before {
@@ -13,7 +16,7 @@
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(0.5rem);
backdrop-filter: blur(1rem);
background-position: center;
background-repeat: no-repeat;
background-image: var(#{$rootPrefix}loading-img);
@@ -25,3 +28,37 @@
height: auto;
}
}
.mermaid {
[data-theme='dark'] & {
height: 0;
padding: 0;
}
}
.mermaid-dark {
height: 0;
padding: 0 !important;
[data-theme='dark'] & {
height: auto;
padding: 0.5rem !important;
}
}
// for print view
.mermaid-neutral {
height: 0;
padding: 0 !important;
}
@media only print {
.mermaid,
.mermaid-dark {
display: none;
}
.mermaid-neutral {
height: auto;
padding: 0.5rem !important;
}
}
+101 -4
View File
@@ -1,10 +1,107 @@
{{- $mermaid := .Param "mermaid" -}}
{{- $mermaidCDN := $mermaid.cdn | default "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs" -}}
import mermaid from "{{ $mermaidCDN }}";
import mermaid from "{{ $mermaidCDN }}"
{{- with $mermaid.zenuml }}
import zenuml from "{{ . }}";
await mermaid.registerExternalDiagrams([zenuml]);
import zenuml from "{{ . }}"
await mermaid.registerExternalDiagrams([zenuml])
{{- end }}
mermaid.startOnLoad = false;
window.mermaid = mermaid;
const mermaidConfig = {{ $mermaid | jsonify }}
let processing = false
let delayTask = null
/**
* Get current/next theme
* @param {Boolean} [next] whether to get the next theme
* @returns
*/
function getTheme(next) {
const darkMode = document.documentElement.dataset.theme === 'dark'
return (next ? !darkMode : darkMode)
? mermaidConfig.themes[1]
: mermaidConfig.themes[0]
}
/**
* Initialize Mermaid by loading the specified theme
* @param {String} [theme] specific theme to load
* @param {Boolean} [darkMode] whether to use dark mode
* @param {String} [selector] specific selector for the elements to process
* @returns
*/
async function loadMermaid({ theme, darkMode, selector }) {
if (processing) {
console.warn('Mermaid is still processing, delaying the reload.')
delayTask = () => loadMermaid({ theme, darkMode, selector })
return
}
const isDarkMode = darkMode ?? (document.documentElement.dataset.theme === 'dark')
const currentTheme = theme ?? getTheme()
const querySelector = selector ?? (isDarkMode ? '.mermaid-dark' : '.mermaid')
const nodes = document.querySelectorAll(querySelector)
if (!nodes.length) return
console.time(`Loaded [${currentTheme}]`)
processing = true
nodes.forEach((node) => {
node.toggleAttribute('data-processing', true)
})
// https://mermaid.js.org/config/schema-docs/config.html
mermaid.initialize({
startOnLoad: false,
darkMode: isDarkMode,
theme: currentTheme,
securityLevel: mermaidConfig.securityLevel,
look: mermaidConfig.look,
fontFamily: mermaidConfig.fontFamily,
altFontFamily: mermaidConfig.fontFamily
})
// skip data-processed elements in Mermaid run function, so this won't re-render
await mermaid.run({
nodes,
suppressErrors: true,
postRenderCallback: (svgId) => {
document.getElementById(svgId).parentElement.toggleAttribute('data-processing', false)
},
})
processing = false
console.timeEnd(`Loaded [${currentTheme}]`)
if (delayTask && typeof delayTask === 'function') {
delayTask();
delayTask = null;
// console.log('Delayed task executed');
}
}
async function initMermaid() {
const contentEl = document.getElementById('content');
if (!contentEl || !contentEl.innerHTML) return
const currentTheme = getTheme()
const nextTheme = getTheme(true)
const darkMode = document.documentElement.dataset.theme === 'dark'
await loadMermaid({ theme: currentTheme, darkMode })
await loadMermaid({ theme: nextTheme, darkMode: !darkMode })
await loadMermaid({ theme: 'neutral', darkMode: false, selector: '.mermaid-neutral' })
}
window.FixItMermaid = {
config: mermaidConfig,
load: loadMermaid,
init: initMermaid,
}
window.mermaid = mermaid
console.log(
`%c💫 FixIt Mermaid`,
'color: #FF3670; font-weight: bold; font-size: 16px; text-shadow: 1px 1px 2px rgba(0,0,0,0.1);',
)
if (document.readyState !== 'loading') {
initMermaid()
} else {
document.addEventListener('DOMContentLoaded', initMermaid, false)
}
+6 -73
View File
@@ -523,11 +523,11 @@ class FixIt {
* init diagram copy button
*/
initDiagramCopyBtn() {
const stagingDOM = this.util.getStagingDOM()
this.util.forEach(document.querySelectorAll('.diagram-copy-btn'), ($btn) => {
$btn.addEventListener('click', () => {
const _tempEl = document.createElement('div');
_tempEl.appendChild($btn.parentElement.querySelector('template').content.cloneNode(true));
const code = _tempEl.innerText.trim();
stagingDOM.stage($btn.parentElement.querySelector('template').content.cloneNode(true))
const code = stagingDOM.contentAsText();
this.util.copyText(code).then(() => {
$btn.toggleAttribute('data-copied', true);
setTimeout(() => {
@@ -538,6 +538,7 @@ class FixIt {
});
}, false);
});
stagingDOM.destroy();
}
initTable(target = document) {
@@ -656,73 +657,6 @@ class FixIt {
});
}
}
initMermaid() {
if (!this.config.mermaid) return;
const themes = this.config.mermaid.themes ?? ['default', 'dark'];
let processing = false;
let delayTask = null;
const loadMermaid = async () => {
processing = true;
// https://mermaid.js.org/config/schema-docs/config.html
window.mermaid.initialize({
startOnLoad: false,
darkMode: this.isDark,
theme: this.isDark ? themes[1] : themes[0],
securityLevel: this.config.mermaid.securityLevel,
look: this.config.mermaid.look,
fontFamily: this.config.mermaid.fontFamily,
altFontFamily: this.config.mermaid.fontFamily
});
await window.mermaid.run({
querySelector: '.mermaid',
suppressErrors: true,
});
processing = false;
if (delayTask && typeof delayTask === 'function') {
delayTask();
delayTask = null;
// console.log('Delayed task executed');
}
};
const reloadMermaid = async () => {
await this.util.forEach(document.querySelectorAll('.mermaid[data-processed]'), (el) => {
el.removeAttribute('data-processed');
el.parentElement.replaceChild(el.nextElementSibling.content.cloneNode(true), el);
});
await loadMermaid();
};
const waitForMermaid = () => {
return new Promise((resolve) => {
const timer = setInterval(() => {
if (window.mermaid && window.mermaid.initialize) {
clearInterval(timer);
resolve();
}
}, 100);
});
};
waitForMermaid().then(() => {
loadMermaid();
this.switchThemeEventSet.add(() => {
if (processing) {
console.warn('Mermaid is still processing, delaying the reload.');
delayTask = reloadMermaid;
return;
}
// console.log('reload immediately');
reloadMermaid().catch(console.error);
});
this.beforeprintEventSet.add(() => {
// Optionally set theme to 'neutral' for printing if required
});
})
}
initEcharts() {
if (!this.config.echarts) {
@@ -1167,7 +1101,6 @@ class FixIt {
this.initCodeWrapper();
this.initDiagramCopyBtn();
this.initTable();
this.initMermaid();
this.initEcharts();
this.initTypeit();
this.initMapbox();
@@ -1176,6 +1109,7 @@ class FixIt {
this.initTocListener();
this.initPangu();
this.initMathJax();
window.FixItMermaid?.init?.();
this.util.forEach(document.querySelectorAll('.encrypted-hidden'), ($element) => {
$element.classList.replace('encrypted-hidden', 'decrypted-shown');
});
@@ -1187,12 +1121,12 @@ class FixIt {
this.initCodeWrapper();
this.initDiagramCopyBtn();
this.initTable($content);
this.initMermaid();
this.initEcharts();
this.initTypeit($content);
this.initMapbox();
this.initPangu();
this.initMathJax();
window.FixItMermaid?.init?.();
this.util.forEach($content.querySelectorAll('.encrypted-hidden'), ($element) => {
$element.classList.replace('encrypted-hidden', 'decrypted-shown');
});
@@ -1389,7 +1323,6 @@ class FixIt {
this.initCodeWrapper();
this.initDiagramCopyBtn();
this.initTable();
this.initMermaid();
this.initEcharts();
this.initTypeit();
this.initMapbox();
-7
View File
@@ -129,13 +129,6 @@
{{- /* mermaid */ -}}
{{- if .Store.Get "hasMermaid" -}}
{{- $mermaid := .Param "mermaid" -}}
{{- $config = dict
"themes" $mermaid.themes
"securityLevel" $mermaid.securityLevel
"look" $mermaid.look
"fontFamily" $mermaid.fontFamily
| dict "mermaid" | merge $config -}}
{{- $options := dict "Source" "js/lib/mermaid.js" "Template" "js/lib/mermaid.min.js" -}}
{{- $options = dict "Context" . "Minify" true "Fingerprint" $fingerprint "Attr" `type="module"` | merge $options -}}
{{- $options | dict "Page" $ "Data" | partial "store/script.html" -}}
+4 -2
View File
@@ -1,7 +1,9 @@
{{- /* Mermaid support for code fences extended and shortcodes. */ -}}
<div class="diagram-container">
{{- dict "Label" "Copy Mermaid code" | partial "plugin/diagram-copy-btn.html" -}}
<pre class="mermaid">{{ .Inner | safeHTML }}</pre>
<template><pre class="mermaid">{{ .Inner | safeHTML }}</pre></template>
<pre class="mermaid-dark">{{ .Inner | safeHTML }}</pre>
<pre class="mermaid-neutral">{{ .Inner | safeHTML }}</pre>
{{- dict "Label" "Copy Mermaid code" | partial "plugin/diagram-copy-btn.html" -}}
<template><pre>{{ .Inner | safeHTML }}</pre></template>
</div>
{{- /* EOF */ -}}