Skip to content

Commit b7f5dc5

Browse files
committed
Add CSS improvements to Mermaid diagrams
1 parent 53a8de3 commit b7f5dc5

19 files changed

Lines changed: 494 additions & 44 deletions

File tree

docs/mkdocs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ theme:
4242
icon:
4343
repo: fontawesome/brands/github
4444

45+
extra_css:
46+
- stylesheets/extra.css
47+
48+
extra_javascript:
49+
- javascripts/mermaid-zoom.js
50+
4551
plugins:
4652
- search
4753
- offline

docs/src/index.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,6 @@ async def me(user: CurrentUser):
7474
return {"id": user.id, "email": user.email}
7575
```
7676

77-
### Litestar
78-
79-
```python
80-
from litestar import Litestar, get
81-
from hotosm_auth_litestar import setup_auth, AuthContext
82-
83-
deps, route_handlers = setup_auth()
84-
85-
@get("/me")
86-
async def me(auth: AuthContext) -> dict:
87-
return {"id": auth.user.id, "email": auth.user.email}
88-
89-
app = Litestar(route_handlers=[*route_handlers, me], dependencies=deps)
90-
```
91-
9277
### Django
9378

9479
```python
@@ -105,6 +90,21 @@ def my_view(request):
10590
return JsonResponse({"email": user.email})
10691
```
10792

93+
### Litestar
94+
95+
```python
96+
from litestar import Litestar, get
97+
from hotosm_auth_litestar import setup_auth, AuthContext
98+
99+
deps, route_handlers = setup_auth()
100+
101+
@get("/me")
102+
async def me(auth: AuthContext) -> dict:
103+
return {"id": auth.user.id, "email": auth.user.email}
104+
105+
app = Litestar(route_handlers=[*route_handlers, me], dependencies=deps)
106+
```
107+
108108
### Frontend
109109

110110
```html
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Zoom + pan for Mermaid diagrams.
3+
* - Scroll wheel: zoom toward cursor
4+
* - Drag: pan
5+
* - Double-click: reset
6+
*/
7+
(function () {
8+
'use strict';
9+
10+
function initZoom(container) {
11+
const svg = container.querySelector('svg');
12+
if (!svg || container.dataset.zoomReady) return;
13+
container.dataset.zoomReady = '1';
14+
15+
// Mermaid wraps everything in a root <g>
16+
const rootG = svg.querySelector(':scope > g');
17+
if (!rootG) return;
18+
19+
let scale = 1;
20+
let tx = 0;
21+
let ty = 0;
22+
let dragging = false;
23+
let startX = 0;
24+
let startY = 0;
25+
26+
function applyTransform() {
27+
rootG.style.transformOrigin = '0 0';
28+
rootG.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;
29+
}
30+
31+
// Scroll to zoom toward cursor position
32+
container.addEventListener('wheel', function (e) {
33+
e.preventDefault();
34+
const rect = container.getBoundingClientRect();
35+
const mx = e.clientX - rect.left;
36+
const my = e.clientY - rect.top;
37+
38+
const factor = e.deltaY < 0 ? 1.1 : 0.9;
39+
const newScale = Math.min(Math.max(scale * factor, 0.2), 6);
40+
41+
// Shift translate so zoom centers on cursor
42+
tx = mx - (mx - tx) * (newScale / scale);
43+
ty = my - (my - ty) * (newScale / scale);
44+
scale = newScale;
45+
46+
applyTransform();
47+
}, { passive: false });
48+
49+
// Drag to pan
50+
container.addEventListener('mousedown', function (e) {
51+
// Ignore right-click
52+
if (e.button !== 0) return;
53+
dragging = true;
54+
startX = e.clientX - tx;
55+
startY = e.clientY - ty;
56+
});
57+
58+
window.addEventListener('mousemove', function (e) {
59+
if (!dragging) return;
60+
tx = e.clientX - startX;
61+
ty = e.clientY - startY;
62+
applyTransform();
63+
});
64+
65+
window.addEventListener('mouseup', function () {
66+
dragging = false;
67+
});
68+
69+
// Double-click: reset to original position
70+
container.addEventListener('dblclick', function () {
71+
scale = 1;
72+
tx = 0;
73+
ty = 0;
74+
rootG.style.transition = 'transform 0.35s cubic-bezier(0.25, 0.8, 0.25, 1)';
75+
applyTransform();
76+
setTimeout(function () {
77+
rootG.style.transition = '';
78+
}, 400);
79+
});
80+
}
81+
82+
function scanAll() {
83+
document.querySelectorAll('.mermaid').forEach(initZoom);
84+
}
85+
86+
// MkDocs Material renders Mermaid asynchronously — watch for SVG insertion
87+
const observer = new MutationObserver(scanAll);
88+
observer.observe(document.body, { childList: true, subtree: true });
89+
90+
if (document.readyState === 'loading') {
91+
document.addEventListener('DOMContentLoaded', scanAll);
92+
} else {
93+
scanAll();
94+
}
95+
})();

docs/src/stylesheets/extra.css

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* =============================================
2+
Mermaid diagrams — futuristic interactivity
3+
============================================= */
4+
5+
/* Container */
6+
.mermaid {
7+
position: relative;
8+
border-radius: 10px;
9+
overflow: hidden;
10+
cursor: grab;
11+
user-select: none;
12+
border: 1px solid rgba(100, 180, 255, 0.12);
13+
background: rgba(0, 10, 30, 0.03);
14+
transition: border-color 0.3s ease;
15+
}
16+
17+
[data-md-color-scheme="slate"] .mermaid {
18+
background: rgba(0, 10, 30, 0.25);
19+
border-color: rgba(100, 180, 255, 0.15);
20+
}
21+
22+
.mermaid:hover {
23+
border-color: rgba(100, 180, 255, 0.3);
24+
}
25+
26+
.mermaid:active {
27+
cursor: grabbing;
28+
}
29+
30+
/* Zoom hint */
31+
.mermaid::after {
32+
content: 'scroll · drag · dbl-click to reset';
33+
position: absolute;
34+
bottom: 8px;
35+
right: 12px;
36+
font-size: 10px;
37+
font-family: var(--md-code-font-family);
38+
color: rgba(100, 180, 255, 0.4);
39+
pointer-events: none;
40+
letter-spacing: 0.05em;
41+
}
42+
43+
/* SVG root smooth transform */
44+
.mermaid svg {
45+
display: block;
46+
}
47+
48+
.mermaid svg > g {
49+
transition: transform 0.05s linear;
50+
}
51+
52+
/* ---- Node shapes ---- */
53+
.mermaid svg .node rect,
54+
.mermaid svg .node circle,
55+
.mermaid svg .node ellipse,
56+
.mermaid svg .node polygon,
57+
.mermaid svg .node path {
58+
transition: filter 0.25s ease;
59+
}
60+
61+
.mermaid svg .node:hover rect,
62+
.mermaid svg .node:hover circle,
63+
.mermaid svg .node:hover ellipse,
64+
.mermaid svg .node:hover polygon {
65+
filter:
66+
drop-shadow(0 0 5px rgba(64, 160, 255, 0.95))
67+
drop-shadow(0 0 18px rgba(64, 160, 255, 0.55))
68+
drop-shadow(0 0 40px rgba(64, 160, 255, 0.25));
69+
}
70+
71+
/* ---- Cluster / subgraph ---- */
72+
.mermaid svg .cluster rect {
73+
transition: filter 0.3s ease;
74+
}
75+
76+
.mermaid svg .cluster:hover rect {
77+
filter:
78+
drop-shadow(0 0 6px rgba(80, 200, 255, 0.45))
79+
drop-shadow(0 0 20px rgba(80, 200, 255, 0.2));
80+
}
81+
82+
/* ---- Edges ---- */
83+
.mermaid svg .edgePath path {
84+
transition: filter 0.2s ease, stroke-width 0.2s ease;
85+
}
86+
87+
.mermaid svg .edgePath:hover path {
88+
filter: drop-shadow(0 0 4px rgba(64, 160, 255, 0.7));
89+
stroke-width: 2px;
90+
}
91+
92+
/* ---- Labels ---- */
93+
.mermaid svg .node .label {
94+
transition: opacity 0.2s ease;
95+
pointer-events: none;
96+
}
97+
98+
/* Dim non-hovered nodes slightly when any node is focused */
99+
.mermaid svg:has(.node:hover) .node:not(:hover) rect,
100+
.mermaid svg:has(.node:hover) .node:not(:hover) circle,
101+
.mermaid svg:has(.node:hover) .node:not(:hover) ellipse,
102+
.mermaid svg:has(.node:hover) .node:not(:hover) polygon {
103+
opacity: 0.55;
104+
transition: opacity 0.25s ease;
105+
}

frontend/public/docs/404.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949

5050

5151

52+
<link rel="stylesheet" href="/app/docs/stylesheets/extra.css">
53+
5254
<script>__md_scope=new URL("/app/docs/",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
5355

5456

@@ -909,6 +911,8 @@ <h1>404 - Not found</h1>
909911

910912
<script src="/app/docs/assets/javascripts/bundle.79ae519e.min.js"></script>
911913

914+
<script src="/app/docs/javascripts/mermaid-zoom.js"></script>
915+
912916

913917
</body>
914918
</html>

frontend/public/docs/admin.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555

5656

5757

58+
<link rel="stylesheet" href="stylesheets/extra.css">
59+
5860
<script>__md_scope=new URL(".",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
5961

6062

@@ -1316,6 +1318,8 @@ <h2 id="proxy-endpoints-login-backend">Proxy Endpoints (Login Backend)<a class="
13161318

13171319
<script src="assets/javascripts/bundle.79ae519e.min.js"></script>
13181320

1321+
<script src="javascripts/mermaid-zoom.js"></script>
1322+
13191323

13201324
</body>
13211325
</html>

0 commit comments

Comments
 (0)