fix: improve Mermaid diagram implementation (#1214)
* fix: improve Mermaid diagram implementation (#1213) This commit addresses multiple issues with Mermaid diagram rendering and adds significant improvements to the user experience. - Fix `<br/>` tags rendering as literal text instead of line breaks - Fix flash of raw mermaid code before diagram renders - Fix Gantt chart rendering issues in dark mode - Fix diagrams briefly showing during theme switch re-render - Fix expand modal showing diagram left-aligned before centering - Add fullscreen expand modal with pan/zoom functionality - Add toolbar with zoom controls (zoom in/out, reset, fit to screen) - Add keyboard support (Escape to close modal) - Add per-diagram transparent background via `%%transparent%%` directive - Add comprehensive configuration options: - `look`: classic or handDrawn (sketch style) - `lightTheme`/`darkTheme`: theme selection per mode - `lightThemeVariables`/`darkThemeVariables`: custom theme colors - `securityLevel`, `htmlLabels`, `maxTextSize`, `maxEdges` - `fontSize`, `fontFamily`, `curve`, `logLevel` - Switch from ESM to UMD bundle for faster initial load (~1s vs 2s+) - Lazy-load panzoom library only when expand modal is opened - Pre-render alternate theme during idle time for instant theme switching - Cache rendered diagrams to avoid re-rendering on theme toggle - Move all inline CSS to article.scss using SCSS nesting - Extract helper functions for better maintainability - Use data attributes and event delegation for cleaner handlers - Reduce mermaid.html from 411 to 177 lines (57% reduction) - Add comprehensive mermaid-diagrams example post with all diagram types - Document all configuration options in hugo.yaml * refactor: extract mermaid inline JS to TypeScript module Move ~155 lines of inline JavaScript from mermaid.html partial into assets/ts/mermaid.ts. The partial shrinks from 179 to 36 lines and now only contains modal HTML markup and Hugo Pipes build/import logic. - Deduplicate initWithTheme() and renderOffscreen() helpers - Enable minification via Hugo Pipes js.Build in production - Remove user-configurable mermaid version from params.toml --------- Co-authored-by: delize <4028612+delize@users.noreply.github.com>
This commit is contained in:
@@ -266,3 +266,167 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mermaid:not([data-processed]) {
|
||||||
|
background: var(--card-background);
|
||||||
|
min-height: 150px;
|
||||||
|
border-radius: 8px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0 !important;
|
||||||
|
line-height: 0;
|
||||||
|
color: transparent;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
visibility: visible;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
margin: -14px 0 0 -14px;
|
||||||
|
border: 3px solid var(--card-background);
|
||||||
|
border-top-color: var(--accent-color, #3273dc);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: mermaid-spinner 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mermaid-spinner {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mermaid diagram wrapper with expand toolbar
|
||||||
|
.mermaid-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin: 1rem 0;
|
||||||
|
|
||||||
|
.mermaid {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-toolbar {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75rem;
|
||||||
|
right: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
border: 2px solid #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mermaid fullscreen modal
|
||||||
|
.mermaid-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.95);
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-modal-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-modal-controls button,
|
||||||
|
.mermaid-modal-close {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-modal-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-modal-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
bottom: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
[data-scheme="dark"] & {
|
||||||
|
background: #1e1e1e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mermaid-panzoom-container {
|
||||||
|
display: inline-block;
|
||||||
|
transform-origin: 0 0;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
&.ready {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Off-screen container for pre-rendering alternate theme
|
||||||
|
.mermaid-offscreen {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
declare const mermaid: {
|
||||||
|
initialize(config: Record<string, any>): void;
|
||||||
|
run(options: { nodes: HTMLElement[] }): Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MermaidConfig {
|
||||||
|
transparentBackground?: boolean;
|
||||||
|
lightTheme?: string;
|
||||||
|
darkTheme?: string;
|
||||||
|
lightThemeVariables?: Record<string, any>;
|
||||||
|
darkThemeVariables?: Record<string, any>;
|
||||||
|
securityLevel?: string;
|
||||||
|
look?: string;
|
||||||
|
htmlLabels?: boolean;
|
||||||
|
maxTextSize?: number;
|
||||||
|
maxEdges?: number;
|
||||||
|
fontSize?: number;
|
||||||
|
fontFamily?: string;
|
||||||
|
curve?: string;
|
||||||
|
logLevel?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scheme = 'light' | 'dark';
|
||||||
|
|
||||||
|
const PANZOOM_CDN = 'https://cdn.jsdelivr.net/npm/panzoom@9.4.3/+esm';
|
||||||
|
|
||||||
|
function getScheme(): Scheme {
|
||||||
|
return document.documentElement.dataset.scheme === 'dark' ? 'dark' : 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildThemeConfig(cfg: MermaidConfig, scheme: Scheme) {
|
||||||
|
const isLight = scheme === 'light';
|
||||||
|
const theme = isLight ? (cfg.lightTheme ?? 'default') : (cfg.darkTheme ?? 'dark');
|
||||||
|
const vars = isLight ? (cfg.lightThemeVariables ?? {}) : (cfg.darkThemeVariables ?? {});
|
||||||
|
return {
|
||||||
|
theme,
|
||||||
|
themeVariables: { ...vars, ...(cfg.transparentBackground ? { background: 'transparent' } : {}) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBaseConfig(cfg: MermaidConfig): Record<string, any> {
|
||||||
|
const base: Record<string, any> = {
|
||||||
|
startOnLoad: false,
|
||||||
|
securityLevel: cfg.securityLevel ?? 'strict',
|
||||||
|
look: cfg.look ?? 'classic',
|
||||||
|
flowchart: { htmlLabels: cfg.htmlLabels ?? true, useMaxWidth: true },
|
||||||
|
gantt: { useWidth: 800 },
|
||||||
|
};
|
||||||
|
const optional: (keyof MermaidConfig)[] = ['maxTextSize', 'maxEdges', 'fontSize', 'fontFamily', 'curve', 'logLevel'];
|
||||||
|
for (const key of optional) {
|
||||||
|
if (cfg[key] != null) base[key] = cfg[key];
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initWithTheme(
|
||||||
|
scheme: Scheme,
|
||||||
|
themes: Record<Scheme, ReturnType<typeof buildThemeConfig>>,
|
||||||
|
baseConfig: Record<string, any>,
|
||||||
|
) {
|
||||||
|
const { theme, themeVariables } = themes[scheme];
|
||||||
|
mermaid.initialize({
|
||||||
|
...baseConfig,
|
||||||
|
theme,
|
||||||
|
...(Object.keys(themeVariables).length && { themeVariables }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderOffscreen(sources: string[]): Promise<string[]> {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'mermaid-offscreen';
|
||||||
|
document.body.appendChild(container);
|
||||||
|
const nodes = sources.map(src => {
|
||||||
|
const n = document.createElement('pre');
|
||||||
|
n.innerHTML = src;
|
||||||
|
container.appendChild(n);
|
||||||
|
return n;
|
||||||
|
});
|
||||||
|
await mermaid.run({ nodes });
|
||||||
|
const results = nodes.map(n => n.innerHTML);
|
||||||
|
container.remove();
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupWrappers(elements: NodeListOf<HTMLElement>) {
|
||||||
|
elements.forEach((el, idx) => {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'mermaid-wrapper';
|
||||||
|
el.parentNode!.insertBefore(wrapper, el);
|
||||||
|
wrapper.appendChild(el);
|
||||||
|
wrapper.insertAdjacentHTML(
|
||||||
|
'beforeend',
|
||||||
|
`<div class="mermaid-toolbar"><button data-idx="${idx}" title="Open fullscreen with pan/zoom">⛶ Expand</button></div>`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupModal(elements: NodeListOf<HTMLElement>) {
|
||||||
|
const modal = document.getElementById('mermaid-modal')!;
|
||||||
|
const modalBody = document.getElementById('mermaid-modal-body')!;
|
||||||
|
const modalContent = document.getElementById('mermaid-modal-content')!;
|
||||||
|
let pzInstance: any = null;
|
||||||
|
let panzoom: any = null;
|
||||||
|
|
||||||
|
const loadPanzoom = async () => {
|
||||||
|
if (!panzoom) {
|
||||||
|
const url = PANZOOM_CDN;
|
||||||
|
panzoom = (await import(url)).default;
|
||||||
|
}
|
||||||
|
return panzoom;
|
||||||
|
};
|
||||||
|
|
||||||
|
const fitToScreen = () => {
|
||||||
|
const wrapper = modalContent.querySelector('.mermaid-panzoom-container') as HTMLElement | null;
|
||||||
|
if (!pzInstance || !wrapper) return;
|
||||||
|
const w = +(wrapper.dataset.nativeWidth ?? 0);
|
||||||
|
const h = +(wrapper.dataset.nativeHeight ?? 0);
|
||||||
|
const rect = modalContent.getBoundingClientRect();
|
||||||
|
const scale = Math.min((rect.width - 60) / w, (rect.height - 60) / h);
|
||||||
|
pzInstance.zoomAbs(0, 0, scale);
|
||||||
|
pzInstance.moveTo((rect.width - w * scale) / 2, (rect.height - h * scale) / 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
pzInstance?.dispose();
|
||||||
|
pzInstance = null;
|
||||||
|
modalContent.innerHTML = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const openModal = async (idx: number) => {
|
||||||
|
const svg = elements[idx].querySelector('svg');
|
||||||
|
if (!svg) return;
|
||||||
|
|
||||||
|
const svgClone = svg.cloneNode(true) as SVGElement;
|
||||||
|
const viewBox = svg.getAttribute('viewBox');
|
||||||
|
const [w, h] = viewBox
|
||||||
|
? viewBox.split(/[\s,]+/).slice(2).map(Number)
|
||||||
|
: [svg.getBoundingClientRect().width || 800, svg.getBoundingClientRect().height || 600];
|
||||||
|
svgClone.setAttribute('width', String(w));
|
||||||
|
svgClone.setAttribute('height', String(h));
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'mermaid-panzoom-container';
|
||||||
|
wrapper.dataset.nativeWidth = String(w);
|
||||||
|
wrapper.dataset.nativeHeight = String(h);
|
||||||
|
wrapper.appendChild(svgClone);
|
||||||
|
|
||||||
|
modalContent.innerHTML = '';
|
||||||
|
modalContent.appendChild(wrapper);
|
||||||
|
modal.classList.add('active');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
|
||||||
|
const pz = await loadPanzoom();
|
||||||
|
setTimeout(() => {
|
||||||
|
pzInstance = pz(wrapper, { maxZoom: 10, minZoom: 0.05, bounds: false });
|
||||||
|
fitToScreen();
|
||||||
|
wrapper.classList.add('ready');
|
||||||
|
}, 50);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Event delegation
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const toolbarBtn = target.closest('.mermaid-toolbar button') as HTMLElement | null;
|
||||||
|
if (toolbarBtn) return openModal(+(toolbarBtn.dataset.idx!));
|
||||||
|
|
||||||
|
const zoomBtn = target.closest('.mermaid-modal-controls button') as HTMLElement | null;
|
||||||
|
if (zoomBtn && pzInstance) {
|
||||||
|
const z = zoomBtn.dataset.zoom;
|
||||||
|
const rect = modalBody.getBoundingClientRect();
|
||||||
|
if (z === 'fit') fitToScreen();
|
||||||
|
else if (z === '0') { pzInstance.moveTo(0, 0); pzInstance.zoomAbs(0, 0, 1); }
|
||||||
|
else pzInstance.smoothZoom(rect.width / 2, rect.height / 2, z === '1' ? 1.5 : 0.67);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('mermaid-modal-close')!.addEventListener('click', closeModal);
|
||||||
|
modalBody.addEventListener('click', (e) => { if (e.target === modalBody) closeModal(); });
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape' && modal.classList.contains('active')) closeModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function initMermaidPage(config: MermaidConfig) {
|
||||||
|
const elements = document.querySelectorAll('.mermaid') as NodeListOf<HTMLElement>;
|
||||||
|
if (!elements.length) return;
|
||||||
|
|
||||||
|
const sources = Array.from(elements).map(el => el.innerHTML);
|
||||||
|
const perDiagramTransparent = sources.map(src => /%%\s*transparent\s*%%/i.test(src));
|
||||||
|
const cache: Record<Scheme, string[]> = { light: [], dark: [] };
|
||||||
|
|
||||||
|
const themes = {
|
||||||
|
light: buildThemeConfig(config, 'light'),
|
||||||
|
dark: buildThemeConfig(config, 'dark'),
|
||||||
|
};
|
||||||
|
const baseConfig = buildBaseConfig(config);
|
||||||
|
|
||||||
|
const applyTransparency = (el: HTMLElement, i: number) => {
|
||||||
|
if (perDiagramTransparent[i]) el.querySelector('svg')?.style.setProperty('background', 'transparent');
|
||||||
|
};
|
||||||
|
|
||||||
|
setupWrappers(elements);
|
||||||
|
setupModal(elements);
|
||||||
|
|
||||||
|
// Initial render
|
||||||
|
const scheme = getScheme();
|
||||||
|
initWithTheme(scheme, themes, baseConfig);
|
||||||
|
await mermaid.run({ nodes: Array.from(elements) });
|
||||||
|
elements.forEach((el, i) => {
|
||||||
|
el.style.visibility = '';
|
||||||
|
cache[scheme][i] = el.innerHTML;
|
||||||
|
applyTransparency(el, i);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pre-render alternate theme during idle time
|
||||||
|
const alt: Scheme = scheme === 'dark' ? 'light' : 'dark';
|
||||||
|
const idle = window.requestIdleCallback ?? ((fn: IdleRequestCallback) => setTimeout(fn, 1000));
|
||||||
|
idle(() => {
|
||||||
|
if (cache[alt].length) return;
|
||||||
|
initWithTheme(alt, themes, baseConfig);
|
||||||
|
renderOffscreen(sources).then(results => { cache[alt] = results; });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Swap cached diagrams on theme change
|
||||||
|
window.addEventListener('onColorSchemeChange', async () => {
|
||||||
|
const newScheme = getScheme();
|
||||||
|
if (!cache[newScheme].length) {
|
||||||
|
initWithTheme(newScheme, themes, baseConfig);
|
||||||
|
cache[newScheme] = await renderOffscreen(sources);
|
||||||
|
}
|
||||||
|
elements.forEach((el, i) => { el.innerHTML = cache[newScheme][i]; applyTransparency(el, i); });
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -29,6 +29,29 @@ SortBy = "default"
|
|||||||
enabled = false
|
enabled = false
|
||||||
default = "Licensed under CC BY-NC-SA 4.0"
|
default = "Licensed under CC BY-NC-SA 4.0"
|
||||||
|
|
||||||
|
# Mermaid diagram configuration
|
||||||
|
# Diagrams are only loaded when mermaid code blocks are present
|
||||||
|
# Theme auto-switches based on site light/dark mode
|
||||||
|
[article.mermaid]
|
||||||
|
# Visual style: classic or handDrawn (sketch style)
|
||||||
|
look = "classic"
|
||||||
|
# Theme for light mode: default, neutral, dark, forest, base, null
|
||||||
|
lightTheme = "default"
|
||||||
|
# Theme for dark mode: default, neutral, dark, forest, base, null
|
||||||
|
darkTheme = "dark"
|
||||||
|
# Custom theme variables for light mode (only works with lightTheme: base)
|
||||||
|
# lightThemeVariables = { primaryColor = "#ff0000" }
|
||||||
|
# Custom theme variables for dark mode (only works with darkTheme: base)
|
||||||
|
# darkThemeVariables = { primaryColor = "#00ff00" }
|
||||||
|
# Security level: strict (default), loose, antiscript, sandbox
|
||||||
|
# Set to "loose" to enable HTML labels like <br/>
|
||||||
|
securityLevel = "strict"
|
||||||
|
# Enable HTML labels in diagrams (requires securityLevel: loose)
|
||||||
|
htmlLabels = true
|
||||||
|
# Make diagram backgrounds transparent (default: false)
|
||||||
|
# Can also be set per-diagram with %%transparent%% directive
|
||||||
|
transparentBackground = false
|
||||||
|
|
||||||
[widgets]
|
[widgets]
|
||||||
homepage = []
|
homepage = []
|
||||||
page = []
|
page = []
|
||||||
|
|||||||
@@ -0,0 +1,603 @@
|
|||||||
|
---
|
||||||
|
author: Hugo Authors
|
||||||
|
title: Mermaid Diagrams
|
||||||
|
date: 2025-12-23
|
||||||
|
description: A comprehensive guide to creating diagrams with Mermaid in Hugo
|
||||||
|
categories:
|
||||||
|
- Themes
|
||||||
|
- Syntax
|
||||||
|
tags:
|
||||||
|
- Mermaid
|
||||||
|
- Diagrams
|
||||||
|
- Markdown
|
||||||
|
---
|
||||||
|
|
||||||
|
This theme supports [Mermaid](https://mermaid.js.org/) diagrams directly in your Markdown content. Mermaid lets you create diagrams and visualizations using text and code.
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
## About Mermaid.js
|
||||||
|
|
||||||
|
This theme integrates [Mermaid.js](https://mermaid.js.org/) (v11) to render diagrams from text definitions within Markdown code blocks. Mermaid is a JavaScript-based diagramming and charting tool that uses text-based syntax inspired by Markdown.
|
||||||
|
|
||||||
|
For complete syntax documentation, see the [Mermaid.js documentation](https://mermaid.js.org/intro/syntax-reference.html).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
To create a Mermaid diagram, simply use a fenced code block with `mermaid` as the language identifier:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Start] --> B[Process]
|
||||||
|
B --> C[End]
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
The diagram will be automatically rendered when the page loads.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Auto-detection**: Mermaid script only loads on pages that contain diagrams
|
||||||
|
- **Theme Support**: Diagrams automatically adapt to light/dark mode
|
||||||
|
- **HTML Labels**: Support for HTML content in labels (like `<br/>` for line breaks)
|
||||||
|
- **Configurable**: Customize version, security level, and more in your site config
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
You can configure Mermaid in your site config:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
version: "11" # Mermaid version from CDN
|
||||||
|
look: classic # classic or handDrawn (sketch style)
|
||||||
|
lightTheme: default # Theme for light mode
|
||||||
|
darkTheme: neutral # Theme for dark mode
|
||||||
|
securityLevel: strict # strict (default), loose, antiscript, sandbox
|
||||||
|
htmlLabels: true # Enable HTML in labels
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
version = "11" # Mermaid version from CDN
|
||||||
|
look = "classic" # classic or handDrawn (sketch style)
|
||||||
|
lightTheme = "default" # Theme for light mode
|
||||||
|
darkTheme = "neutral" # Theme for dark mode
|
||||||
|
securityLevel = "strict" # strict (default), loose, antiscript, sandbox
|
||||||
|
htmlLabels = true # Enable HTML in labels
|
||||||
|
```
|
||||||
|
|
||||||
|
### Additional Global Options
|
||||||
|
|
||||||
|
These optional settings use Mermaid's defaults when not specified:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
maxTextSize: 50000 # Maximum text size (default: 50000)
|
||||||
|
maxEdges: 500 # Maximum edges allowed (default: 500)
|
||||||
|
fontSize: 16 # Global font size in pixels (default: 16)
|
||||||
|
fontFamily: "arial" # Global font family
|
||||||
|
curve: "basis" # Line curve: basis, cardinal, linear (default: basis)
|
||||||
|
logLevel: 5 # Debug level 0-5, 0=debug, 5=fatal (default: 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
maxTextSize = 50000 # Maximum text size (default: 50000)
|
||||||
|
maxEdges = 500 # Maximum edges allowed (default: 500)
|
||||||
|
fontSize = 16 # Global font size in pixels (default: 16)
|
||||||
|
fontFamily = "arial" # Global font family
|
||||||
|
curve = "basis" # Line curve: basis, cardinal, linear (default: basis)
|
||||||
|
logLevel = 5 # Debug level 0-5, 0=debug, 5=fatal (default: 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
For diagram-specific options (like `flowchart.useMaxWidth`), use Mermaid's init directive directly in your diagram:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
%%{init: {'flowchart': {'useMaxWidth': false}}}%%
|
||||||
|
flowchart LR
|
||||||
|
A --> B
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
> **Security Note:** The default `securityLevel: strict` is recommended. Set to `loose` only if you need HTML labels like `<br/>` in your diagrams.
|
||||||
|
|
||||||
|
### Available Themes
|
||||||
|
|
||||||
|
| Theme | Description |
|
||||||
|
|-------|-------------|
|
||||||
|
| `default` | Standard colorful theme |
|
||||||
|
| `neutral` | Grayscale, great for printing and dark mode |
|
||||||
|
| `dark` | Designed for dark backgrounds |
|
||||||
|
| `forest` | Green color palette |
|
||||||
|
| `base` | Minimal theme, customizable with themeVariables |
|
||||||
|
| `null` | Disable theming entirely |
|
||||||
|
|
||||||
|
### Custom Theme Variables
|
||||||
|
|
||||||
|
For full control, use the `base` theme with custom variables:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
lightTheme: base
|
||||||
|
darkTheme: base
|
||||||
|
lightThemeVariables:
|
||||||
|
primaryColor: "#4a90d9"
|
||||||
|
primaryTextColor: "#ffffff"
|
||||||
|
lineColor: "#333333"
|
||||||
|
darkThemeVariables:
|
||||||
|
primaryColor: "#6ab0f3"
|
||||||
|
primaryTextColor: "#ffffff"
|
||||||
|
lineColor: "#cccccc"
|
||||||
|
background: "#1a1a2e"
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
lightTheme = "base"
|
||||||
|
darkTheme = "base"
|
||||||
|
|
||||||
|
[params.article.mermaid.lightThemeVariables]
|
||||||
|
primaryColor = "#4a90d9"
|
||||||
|
primaryTextColor = "#ffffff"
|
||||||
|
lineColor = "#333333"
|
||||||
|
|
||||||
|
[params.article.mermaid.darkThemeVariables]
|
||||||
|
primaryColor = "#6ab0f3"
|
||||||
|
primaryTextColor = "#ffffff"
|
||||||
|
lineColor = "#cccccc"
|
||||||
|
background = "#1a1a2e"
|
||||||
|
```
|
||||||
|
|
||||||
|
Common variables: `primaryColor`, `secondaryColor`, `tertiaryColor`, `primaryTextColor`, `lineColor`, `background`, `fontFamily`
|
||||||
|
|
||||||
|
> **Note:** Theme variables only work with the `base` theme and must use hex color values (e.g., `#ff0000`).
|
||||||
|
|
||||||
|
## Diagram Types
|
||||||
|
|
||||||
|
### Flowchart
|
||||||
|
|
||||||
|
Flowcharts are the most common diagram type. Use `graph` or `flowchart` with direction indicators:
|
||||||
|
- `TD` or `TB`: Top to bottom
|
||||||
|
- `BT`: Bottom to top
|
||||||
|
- `LR`: Left to right
|
||||||
|
- `RL`: Right to left
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Hard edge] -->|Link text| B(Round edge)
|
||||||
|
B --> C{Decision}
|
||||||
|
C -->|One| D[Result one]
|
||||||
|
C -->|Two| E[Result two]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sequence Diagram
|
||||||
|
|
||||||
|
Perfect for showing interactions between components:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Alice
|
||||||
|
participant Bob
|
||||||
|
Alice->>John: Hello John, how are you?
|
||||||
|
loop Healthcheck
|
||||||
|
John->>John: Fight against hypochondria
|
||||||
|
end
|
||||||
|
Note right of John: Rational thoughts <br/>prevail!
|
||||||
|
John-->>Alice: Great!
|
||||||
|
John->>Bob: How about you?
|
||||||
|
Bob-->>John: Jolly good!
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class Diagram
|
||||||
|
|
||||||
|
Visualize class structures and relationships:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
Animal <|-- Duck
|
||||||
|
Animal <|-- Fish
|
||||||
|
Animal <|-- Zebra
|
||||||
|
Animal : +int age
|
||||||
|
Animal : +String gender
|
||||||
|
Animal: +isMammal()
|
||||||
|
Animal: +mate()
|
||||||
|
class Duck{
|
||||||
|
+String beakColor
|
||||||
|
+swim()
|
||||||
|
+quack()
|
||||||
|
}
|
||||||
|
class Fish{
|
||||||
|
-int sizeInFeet
|
||||||
|
-canEat()
|
||||||
|
}
|
||||||
|
class Zebra{
|
||||||
|
+bool is_wild
|
||||||
|
+run()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### State Diagram
|
||||||
|
|
||||||
|
Model state machines and transitions:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
stateDiagram-v2
|
||||||
|
[*] --> Still
|
||||||
|
Still --> [*]
|
||||||
|
|
||||||
|
Still --> Moving
|
||||||
|
Moving --> Still
|
||||||
|
Moving --> Crash
|
||||||
|
Crash --> [*]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Relationship Diagram
|
||||||
|
|
||||||
|
Document database schemas:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
CUSTOMER ||--o{ ORDER : places
|
||||||
|
ORDER ||--|{ LINE-ITEM : contains
|
||||||
|
CUSTOMER }|..|{ DELIVERY-ADDRESS : uses
|
||||||
|
CUSTOMER {
|
||||||
|
string name
|
||||||
|
string custNumber
|
||||||
|
string sector
|
||||||
|
}
|
||||||
|
ORDER {
|
||||||
|
int orderNumber
|
||||||
|
string deliveryAddress
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gantt Chart
|
||||||
|
|
||||||
|
Plan and track project schedules:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title A Gantt Diagram
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
section Section
|
||||||
|
A task :a1, 2024-01-01, 30d
|
||||||
|
Another task :after a1, 20d
|
||||||
|
section Another
|
||||||
|
Task in Another :2024-01-12, 12d
|
||||||
|
another task :24d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pie Chart
|
||||||
|
|
||||||
|
Display proportional data:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
pie showData
|
||||||
|
title Key elements in Product X
|
||||||
|
"Calcium" : 42.96
|
||||||
|
"Potassium" : 50.05
|
||||||
|
"Magnesium" : 10.01
|
||||||
|
"Iron" : 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### Git Graph
|
||||||
|
|
||||||
|
Visualize Git branching strategies:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gitGraph
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
branch develop
|
||||||
|
checkout develop
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
checkout main
|
||||||
|
merge develop
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mindmap
|
||||||
|
|
||||||
|
Create hierarchical mindmaps:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
root((mindmap))
|
||||||
|
Origins
|
||||||
|
Long history
|
||||||
|
Popularisation
|
||||||
|
British popular psychology author Tony Buzan
|
||||||
|
Research
|
||||||
|
On effectiveness<br/>and features
|
||||||
|
On Automatic creation
|
||||||
|
Uses
|
||||||
|
Creative techniques
|
||||||
|
Strategic planning
|
||||||
|
Argument mapping
|
||||||
|
Tools
|
||||||
|
Pen and paper
|
||||||
|
Mermaid
|
||||||
|
```
|
||||||
|
|
||||||
|
### Timeline
|
||||||
|
|
||||||
|
Display chronological events:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
timeline
|
||||||
|
title History of Social Media Platform
|
||||||
|
2002 : LinkedIn
|
||||||
|
2004 : Facebook
|
||||||
|
: Google
|
||||||
|
2005 : YouTube
|
||||||
|
2006 : Twitter
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
### HTML in Labels
|
||||||
|
|
||||||
|
To use HTML in labels, you must set `securityLevel: loose` in your site config:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
securityLevel: loose
|
||||||
|
htmlLabels: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
securityLevel = "loose"
|
||||||
|
htmlLabels = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Then you can use HTML tags like `<br/>` for line breaks:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[Line 1<br/>Line 2] --> B[<b>Bold</b> text]
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### Per-Diagram Theming
|
||||||
|
|
||||||
|
Override the theme for a specific diagram using Mermaid's frontmatter:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
%%{init: {'theme': 'forest'}}%%
|
||||||
|
graph TD
|
||||||
|
A[Start] --> B[End]
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
%%{init: {'theme': 'forest'}}%%
|
||||||
|
graph TD
|
||||||
|
A[Christmas] -->|Get money| B(Go shopping)
|
||||||
|
B --> C{Let me think}
|
||||||
|
C -->|One| D[Laptop]
|
||||||
|
C -->|Two| E[iPhone]
|
||||||
|
C -->|Three| F[Car]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inline Styling with `style`
|
||||||
|
|
||||||
|
You can style individual nodes directly within your diagram using the `style` directive:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Start] --> B[Process] --> C[End]
|
||||||
|
style A fill:#4ade80,stroke:#166534,color:#000
|
||||||
|
style B fill:#60a5fa,stroke:#1e40af,color:#000
|
||||||
|
style C fill:#f87171,stroke:#991b1b,color:#fff
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Start] --> B[Process] --> C[End]
|
||||||
|
style A fill:#4ade80,stroke:#166534,color:#000
|
||||||
|
style B fill:#60a5fa,stroke:#1e40af,color:#000
|
||||||
|
style C fill:#f87171,stroke:#991b1b,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
Style properties include:
|
||||||
|
- `fill` - Background color
|
||||||
|
- `stroke` - Border color
|
||||||
|
- `stroke-width` - Border thickness
|
||||||
|
- `color` - Text color
|
||||||
|
- `stroke-dasharray` - Dashed border (e.g., `5 5`)
|
||||||
|
|
||||||
|
### Styling with CSS Classes
|
||||||
|
|
||||||
|
You can define reusable styles with `classDef` and apply them using `:::className`:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A:::success --> B:::info --> C:::warning
|
||||||
|
classDef success fill:#4ade80,stroke:#166534,color:#000
|
||||||
|
classDef info fill:#60a5fa,stroke:#1e40af,color:#000
|
||||||
|
classDef warning fill:#fbbf24,stroke:#92400e,color:#000
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A:::success --> B:::info --> C:::warning
|
||||||
|
classDef success fill:#4ade80,stroke:#166534,color:#000
|
||||||
|
classDef info fill:#60a5fa,stroke:#1e40af,color:#000
|
||||||
|
classDef warning fill:#fbbf24,stroke:#92400e,color:#000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subgraphs
|
||||||
|
|
||||||
|
Group related nodes together:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
subgraph one
|
||||||
|
a1-->a2
|
||||||
|
end
|
||||||
|
subgraph two
|
||||||
|
b1-->b2
|
||||||
|
end
|
||||||
|
subgraph three
|
||||||
|
c1-->c2
|
||||||
|
end
|
||||||
|
one --> two
|
||||||
|
three --> two
|
||||||
|
two --> c2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Theme Switching
|
||||||
|
|
||||||
|
This theme automatically detects your site's light/dark mode preference and adjusts the Mermaid diagram theme accordingly:
|
||||||
|
|
||||||
|
- **Light mode**: Uses the `default` Mermaid theme
|
||||||
|
- **Dark mode**: Uses the `dark` Mermaid theme (configurable)
|
||||||
|
|
||||||
|
Try toggling the theme switcher to see diagrams update in real-time!
|
||||||
|
|
||||||
|
## Complex Example
|
||||||
|
|
||||||
|
Here's an example with subgraphs, HTML labels, emojis, and custom styling:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
subgraph client["👤 Client"]
|
||||||
|
A["User Device<br/>192.168.1.10"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph cloud["☁️ Cloud Gateway"]
|
||||||
|
B["Load Balancer<br/>(SSL Termination)"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph server["🖥️ Application Server"]
|
||||||
|
C["API Gateway<br/>10.0.0.1"]
|
||||||
|
D["Auth Service<br/>10.0.0.2"]
|
||||||
|
E["Web Server<br/>10.0.0.3"]
|
||||||
|
F["Database<br/>10.0.0.4"]
|
||||||
|
end
|
||||||
|
|
||||||
|
A -- "HTTPS Request" --> B
|
||||||
|
B -- "Forward<br/>(internal)" --> C
|
||||||
|
C -- "Authenticate" --> D
|
||||||
|
D -- "Token" --> C
|
||||||
|
C -- "Route" --> E
|
||||||
|
E --> F
|
||||||
|
|
||||||
|
style client fill:#1a365d,stroke:#2c5282,color:#fff
|
||||||
|
style cloud fill:#f6ad55,stroke:#dd6b20,color:#000
|
||||||
|
style server fill:#276749,stroke:#22543d,color:#fff
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note:** This example requires `securityLevel: loose` for HTML labels and styling to work.
|
||||||
|
|
||||||
|
## Known Limitations
|
||||||
|
|
||||||
|
### Dark Mode Theming
|
||||||
|
|
||||||
|
Mermaid.js's built-in themes have some limitations:
|
||||||
|
|
||||||
|
- **`dark` theme** (default): Best text contrast, but some diagram backgrounds may appear brownish (e.g., Gantt charts)
|
||||||
|
- **`neutral` theme**: Better background colors, but some text (labels, legends) may have reduced contrast
|
||||||
|
|
||||||
|
**For full control**, use the `base` theme with custom variables:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
darkTheme: base
|
||||||
|
darkThemeVariables:
|
||||||
|
primaryColor: "#1f2937"
|
||||||
|
primaryTextColor: "#ffffff"
|
||||||
|
lineColor: "#9ca3af"
|
||||||
|
textColor: "#e5e7eb"
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
darkTheme = "base"
|
||||||
|
|
||||||
|
[params.article.mermaid.darkThemeVariables]
|
||||||
|
primaryColor = "#1f2937"
|
||||||
|
primaryTextColor = "#ffffff"
|
||||||
|
lineColor = "#9ca3af"
|
||||||
|
textColor = "#e5e7eb"
|
||||||
|
```
|
||||||
|
|
||||||
|
We plan to improve dark mode theming in future updates as Mermaid.js evolves.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Diagram not rendering?
|
||||||
|
|
||||||
|
1. Make sure you're using a fenced code block with `mermaid` as the language
|
||||||
|
2. Check your browser's console for syntax errors
|
||||||
|
3. Verify your Mermaid syntax at [Mermaid Live Editor](https://mermaid.live/)
|
||||||
|
|
||||||
|
### HTML not working in labels?
|
||||||
|
|
||||||
|
HTML in labels requires `securityLevel: loose`. Update your configuration:
|
||||||
|
|
||||||
|
**hugo.yaml:**
|
||||||
|
```yaml
|
||||||
|
params:
|
||||||
|
article:
|
||||||
|
mermaid:
|
||||||
|
securityLevel: loose
|
||||||
|
htmlLabels: true
|
||||||
|
```
|
||||||
|
|
||||||
|
**hugo.toml:**
|
||||||
|
```toml
|
||||||
|
[params.article.mermaid]
|
||||||
|
securityLevel = "loose"
|
||||||
|
htmlLabels = true
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Warning:** Using `loose` security level allows HTML in diagrams. Only use this if you trust your diagram content.
|
||||||
|
|
||||||
|
### Syntax errors?
|
||||||
|
|
||||||
|
Mermaid is strict about syntax. Common issues:
|
||||||
|
- Missing spaces around arrows
|
||||||
|
- Unclosed brackets or quotes
|
||||||
|
- Invalid node IDs (avoid special characters)
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Mermaid Documentation](https://mermaid.js.org/intro/)
|
||||||
|
- [Mermaid Live Editor](https://mermaid.live/) - Test diagrams interactively
|
||||||
|
- [Mermaid Syntax Reference](https://mermaid.js.org/intro/syntax-reference.html)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<pre class="mermaid">
|
<pre class="mermaid" style="visibility:hidden">
|
||||||
{{ .Inner | htmlEscape | safeHTML }}
|
{{- .Inner | safeHTML -}}
|
||||||
</pre>
|
</pre>
|
||||||
{{ .Page.Store.Set "hasMermaid" true }}
|
{{- .Page.Store.Set "hasMermaid" true -}}
|
||||||
|
|||||||
@@ -1,9 +1,42 @@
|
|||||||
{{ if .Store.Get "hasMermaid" }}
|
{{- if .Store.Get "hasMermaid" -}}
|
||||||
|
{{- $cfg := site.Params.article.mermaid | default dict -}}
|
||||||
|
|
||||||
|
<div class="mermaid-modal" id="mermaid-modal">
|
||||||
|
<div class="mermaid-modal-header">
|
||||||
|
<div class="mermaid-modal-controls">
|
||||||
|
<button data-zoom="-1">− Zoom Out</button>
|
||||||
|
<button data-zoom="0">Reset (100%)</button>
|
||||||
|
<button data-zoom="1">+ Zoom In</button>
|
||||||
|
<button data-zoom="fit">Fit to Screen</button>
|
||||||
|
</div>
|
||||||
|
<button class="mermaid-modal-close" id="mermaid-modal-close">✕ Close (Esc)</button>
|
||||||
|
</div>
|
||||||
|
<div class="mermaid-modal-body" id="mermaid-modal-body">
|
||||||
|
<div class="mermaid-modal-content" id="mermaid-modal-content"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
||||||
|
{{- $opts := dict "minify" hugo.IsProduction "format" "esm" -}}
|
||||||
|
{{- $script := resources.Get "ts/mermaid.ts" | js.Build $opts -}}
|
||||||
|
{{- $jsConfig := dict
|
||||||
|
"transparentBackground" ($cfg.transparentBackground | default false)
|
||||||
|
"lightTheme" ($cfg.lightTheme | default "default")
|
||||||
|
"darkTheme" ($cfg.darkTheme | default "dark")
|
||||||
|
"lightThemeVariables" ($cfg.lightThemeVariables | default dict)
|
||||||
|
"darkThemeVariables" ($cfg.darkThemeVariables | default dict)
|
||||||
|
"securityLevel" ($cfg.securityLevel | default "strict")
|
||||||
|
"look" ($cfg.look | default "classic")
|
||||||
|
"htmlLabels" (not (eq ($cfg.htmlLabels | default true) false))
|
||||||
|
-}}
|
||||||
|
{{- with $cfg.maxTextSize }}{{ $jsConfig = merge $jsConfig (dict "maxTextSize" .) }}{{ end -}}
|
||||||
|
{{- with $cfg.maxEdges }}{{ $jsConfig = merge $jsConfig (dict "maxEdges" .) }}{{ end -}}
|
||||||
|
{{- with $cfg.fontSize }}{{ $jsConfig = merge $jsConfig (dict "fontSize" .) }}{{ end -}}
|
||||||
|
{{- with $cfg.fontFamily }}{{ $jsConfig = merge $jsConfig (dict "fontFamily" .) }}{{ end -}}
|
||||||
|
{{- with $cfg.curve }}{{ $jsConfig = merge $jsConfig (dict "curve" .) }}{{ end -}}
|
||||||
|
{{- with $cfg.logLevel }}{{ $jsConfig = merge $jsConfig (dict "logLevel" .) }}{{ end -}}
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11.12.2/+esm';
|
import { initMermaidPage } from '{{ $script.RelPermalink }}';
|
||||||
mermaid.initialize({
|
initMermaidPage({{ $jsConfig | jsonify | safeJS }});
|
||||||
startOnLoad: true,
|
|
||||||
theme: 'neutral',
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{- end -}}
|
||||||
|
|||||||
Reference in New Issue
Block a user