Skip to content

Commit acb312a

Browse files
authored
Merge pull request #461 from st3penta/add-mermaid-support
Add support for Mermaid diagrams rendering
2 parents 945e700 + ac57ae1 commit acb312a

File tree

8 files changed

+1669
-47
lines changed

8 files changed

+1669
-47
lines changed

antora/antora-playbook.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ antora:
5151
asciidoc:
5252
extensions:
5353
- '@asciidoctor/tabs'
54+
- './extensions/mermaid.js'

antora/extensions/mermaid.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
function createMermaidExtension() {
2+
return function () {
3+
this.block('mermaid', function () {
4+
const self = this
5+
self.named('mermaid')
6+
self.onContext('literal')
7+
self.process(function (parent, reader) {
8+
const source = reader.getLines().join('\n')
9+
const html = `<div class="mermaid">
10+
${source}
11+
</div>`
12+
return self.createBlock(parent, 'pass', html)
13+
})
14+
})
15+
}
16+
}
17+
18+
module.exports = createMermaidExtension()

antora/package-lock.json

Lines changed: 1393 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

antora/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
},
2525
"dependencies": {
2626
"@antora/lunr-extension": "^1.0.0-alpha.10",
27-
"@asciidoctor/tabs": "^1.0.0-beta.6"
27+
"@asciidoctor/tabs": "^1.0.0-beta.6",
28+
"mermaid": "^11.10.1"
2829
}
2930
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* Mermaid diagram styling */
2+
.mermaid {
3+
text-align: center;
4+
padding: 0.5rem;
5+
border: 1px solid #e1e5e9;
6+
border-radius: 6px;
7+
background-color: #ffffff;
8+
overflow-x: auto;
9+
cursor: zoom-in;
10+
transition: box-shadow 0.2s ease;
11+
}
12+
13+
.mermaid:hover {
14+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
15+
}
16+
17+
/* Make sure text in diagrams is readable */
18+
.mermaid span {
19+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
20+
font-size: 18px !important;
21+
}
22+
23+
/* Ensure diagrams don't get cut off */
24+
.mermaid svg {
25+
max-width: 100%;
26+
height: auto;
27+
display: block;
28+
}
29+
30+
/* Zoom overlay styling */
31+
.mermaid-zoom-overlay {
32+
backdrop-filter: blur(4px);
33+
}
34+
35+
.mermaid-zoom-overlay .mermaid {
36+
cursor: zoom-out;
37+
border: none;
38+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
39+
max-width: 95vw;
40+
max-height: 95vh;
41+
overflow: auto;
42+
}
43+
44+
/* Responsive adjustments */
45+
@media (max-width: 768px) {
46+
.mermaid {
47+
padding: 0.5rem;
48+
margin: 0.5rem 0;
49+
}
50+
51+
.mermaid-zoom-overlay .mermaid {
52+
max-width: 98vw;
53+
max-height: 98vh;
54+
padding: 10px;
55+
}
56+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
document.addEventListener('DOMContentLoaded', function() {
2+
// Load Mermaid from CDN
3+
const script = document.createElement('script');
4+
script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/mermaid.min.js';
5+
script.onload = function() {
6+
// Initialize Mermaid when the script loads
7+
mermaid.initialize({
8+
startOnLoad: true,
9+
theme: 'default',
10+
securityLevel: 'loose',
11+
flowchart: {
12+
useMaxWidth: false,
13+
htmlLabels: true
14+
},
15+
sequence: {
16+
useMaxWidth: false
17+
},
18+
gantt: {
19+
useMaxWidth: false
20+
}
21+
});
22+
23+
// Add zoom functionality to Mermaid diagrams
24+
mermaid.init().then(() => {
25+
document.querySelectorAll('.mermaid').forEach(function(diagram) {
26+
// Make diagrams clickable for zoom
27+
diagram.style.cursor = 'zoom-in';
28+
diagram.style.maxWidth = '100%';
29+
diagram.style.height = 'auto';
30+
31+
// Add click handler for zoom
32+
diagram.addEventListener('click', function() {
33+
// Create zoom overlay
34+
const overlay = document.createElement('div');
35+
overlay.className = 'mermaid-zoom-overlay';
36+
overlay.style.cssText = `
37+
position: fixed;
38+
top: 0;
39+
left: 0;
40+
width: 100%;
41+
height: 100%;
42+
background: rgba(0, 0, 0, 0.8);
43+
display: flex;
44+
justify-content: center;
45+
align-items: center;
46+
z-index: 9999;
47+
cursor: grab;
48+
overflow: hidden;
49+
`;
50+
51+
// Create container for panning
52+
const container = document.createElement('div');
53+
container.style.cssText = `
54+
position: relative;
55+
cursor: grab;
56+
user-select: none;
57+
transform-origin: center center;
58+
`;
59+
60+
// Clone the diagram for the overlay
61+
const clonedDiagram = diagram.cloneNode(true);
62+
clonedDiagram.style.cssText = `
63+
max-width: none;
64+
max-height: none;
65+
background: white;
66+
padding: 20px;
67+
border-radius: 8px;
68+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
69+
cursor: grab;
70+
user-select: none;
71+
transform-origin: center center;
72+
`;
73+
74+
container.appendChild(clonedDiagram);
75+
overlay.appendChild(container);
76+
document.body.appendChild(overlay);
77+
78+
// Zoom and pan functionality
79+
let scale = 1;
80+
let translateX = 0;
81+
let translateY = 0;
82+
let isDragging = false;
83+
let lastX = 0;
84+
let lastY = 0;
85+
86+
function updateTransform() {
87+
container.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
88+
}
89+
90+
// Wheel zoom
91+
overlay.addEventListener('wheel', function(e) {
92+
e.preventDefault();
93+
const delta = e.deltaY > 0 ? 0.9 : 1.1;
94+
const newScale = Math.max(0.5, Math.min(5, scale * delta));
95+
96+
if (newScale !== scale) {
97+
const rect = overlay.getBoundingClientRect();
98+
const centerX = rect.width / 2;
99+
const centerY = rect.height / 2;
100+
const mouseX = e.clientX - rect.left;
101+
const mouseY = e.clientY - rect.top;
102+
103+
const offsetX = mouseX - centerX;
104+
const offsetY = mouseY - centerY;
105+
106+
translateX = translateX - offsetX * (newScale / scale - 1);
107+
translateY = translateY - offsetY * (newScale / scale - 1);
108+
scale = newScale;
109+
110+
updateTransform();
111+
}
112+
});
113+
114+
// Mouse drag
115+
container.addEventListener('mousedown', function(e) {
116+
if (e.button === 0) { // Left mouse button
117+
isDragging = true;
118+
lastX = e.clientX;
119+
lastY = e.clientY;
120+
container.style.cursor = 'grabbing';
121+
overlay.style.cursor = 'grabbing';
122+
e.preventDefault();
123+
}
124+
});
125+
126+
overlay.addEventListener('mousemove', function(e) {
127+
if (isDragging) {
128+
const deltaX = e.clientX - lastX;
129+
const deltaY = e.clientY - lastY;
130+
translateX += deltaX;
131+
translateY += deltaY;
132+
lastX = e.clientX;
133+
lastY = e.clientY;
134+
updateTransform();
135+
}
136+
});
137+
138+
overlay.addEventListener('mouseup', function() {
139+
isDragging = false;
140+
container.style.cursor = 'grab';
141+
overlay.style.cursor = 'grab';
142+
});
143+
144+
// Double-click to reset zoom
145+
container.addEventListener('dblclick', function(e) {
146+
e.stopPropagation();
147+
scale = 1;
148+
translateX = 0;
149+
translateY = 0;
150+
updateTransform();
151+
});
152+
153+
// Close on background click (but not on diagram)
154+
overlay.addEventListener('click', function(e) {
155+
if (e.target === overlay) {
156+
document.body.removeChild(overlay);
157+
}
158+
});
159+
160+
// Close on escape key
161+
const escapeHandler = function(e) {
162+
if (e.key === 'Escape') {
163+
document.body.removeChild(overlay);
164+
document.removeEventListener('keydown', escapeHandler);
165+
}
166+
};
167+
document.addEventListener('keydown', escapeHandler);
168+
169+
// Add instructions
170+
const instructions = document.createElement('div');
171+
instructions.style.cssText = `
172+
position: absolute;
173+
top: 20px;
174+
left: 20px;
175+
background: rgba(0, 0, 0, 0.7);
176+
color: white;
177+
padding: 10px 15px;
178+
border-radius: 5px;
179+
font-size: 14px;
180+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
181+
z-index: 10000;
182+
pointer-events: none;
183+
`;
184+
instructions.innerHTML = `
185+
<div>🔍 Scroll to zoom</div>
186+
<div>🖱️ Drag to pan</div>
187+
<div>📱 Double-click to reset</div>
188+
<div>⌨️ ESC to close</div>
189+
`;
190+
overlay.appendChild(instructions);
191+
});
192+
});
193+
});
194+
};
195+
document.head.appendChild(script);
196+
});

antora/supplemental-ui/partials/footer-scripts.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script id="site-script" src="{{uiRootPath}}/js/site.js" data-ui-root-path="{{{uiRootPath}}}"></script>
22
<script src="{{uiRootPath}}/js/zoom.js"></script>
3+
<script src="{{uiRootPath}}/js/mermaid-init.js"></script>
34
{{#if env.SITE_SEARCH_PROVIDER}}
45
{{> search-scripts}}
56
{{/if}}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<link rel="stylesheet" href="/css/site.min.css">
2-
<link rel="stylesheet" href="{{{uiRootPath}}}/css/vendor/tabs.css">
2+
<link rel="stylesheet" href="{{{uiRootPath}}}/css/vendor/tabs.css">
3+
<link rel="stylesheet" href="{{{uiRootPath}}}/css/mermaid.css">

0 commit comments

Comments
 (0)