|
1 | 1 | // Mermaid diagram expand button |
2 | | -// Polls for rendered SVGs inside .mermaid elements, then adds an Expand button. |
| 2 | +// Polls DOM for SVGs rendered by Mermaid and adds an Expand button. |
3 | 3 |
|
4 | 4 | (function () { |
5 | 5 | function addButtons() { |
6 | | - document.querySelectorAll("pre.mermaid svg, .mermaid svg").forEach(function (svg) { |
| 6 | + // Find all SVGs on the page that look like Mermaid diagrams |
| 7 | + document.querySelectorAll("svg").forEach(function (svg) { |
| 8 | + // Skip if already processed |
| 9 | + if (svg.getAttribute("data-zoom-done")) return; |
| 10 | + // Only target Mermaid diagrams (they have aria-roledescription or class containing mermaid-related ids) |
| 11 | + var isMermaid = svg.getAttribute("aria-roledescription") === "classDiagram" || |
| 12 | + svg.getAttribute("aria-roledescription") === "flowchart-v2" || |
| 13 | + svg.getAttribute("aria-roledescription") === "flowchart" || |
| 14 | + svg.getAttribute("aria-roledescription") === "sequence" || |
| 15 | + svg.getAttribute("aria-roledescription") === "stateDiagram" || |
| 16 | + svg.getAttribute("aria-roledescription") || |
| 17 | + (svg.id && svg.id.startsWith("mermaid-")) || |
| 18 | + (svg.parentElement && svg.parentElement.classList.contains("mermaid")); |
| 19 | + if (!isMermaid) return; |
| 20 | + |
| 21 | + svg.setAttribute("data-zoom-done", "true"); |
| 22 | + |
7 | 23 | var container = svg.parentElement; |
8 | | - if (!container || container.getAttribute("data-zoom-done")) return; |
9 | | - container.setAttribute("data-zoom-done", "true"); |
| 24 | + if (!container) return; |
10 | 25 | container.style.position = "relative"; |
11 | 26 |
|
12 | 27 | // Expand button |
13 | 28 | var btn = document.createElement("button"); |
14 | 29 | btn.textContent = "Expand"; |
15 | 30 | btn.title = "View diagram fullscreen"; |
16 | | - btn.style.cssText = "position:absolute;top:4px;right:4px;z-index:10;" + |
17 | | - "background:#4051b5;color:#fff;border:none;border-radius:4px;" + |
18 | | - "padding:4px 10px;font-size:12px;cursor:pointer;opacity:0.8;"; |
| 31 | + Object.assign(btn.style, { |
| 32 | + position: "absolute", top: "4px", right: "4px", zIndex: "10", |
| 33 | + background: "#4051b5", color: "#fff", border: "none", borderRadius: "4px", |
| 34 | + padding: "4px 10px", fontSize: "12px", cursor: "pointer", opacity: "0.8" |
| 35 | + }); |
19 | 36 | btn.onmouseenter = function () { btn.style.opacity = "1"; }; |
20 | 37 | btn.onmouseleave = function () { btn.style.opacity = "0.8"; }; |
21 | 38 | container.appendChild(btn); |
22 | 39 |
|
23 | 40 | // Fullscreen overlay |
24 | 41 | var overlay = document.createElement("div"); |
25 | | - overlay.style.cssText = "display:none;position:fixed;top:0;left:0;width:100vw;" + |
26 | | - "height:100vh;z-index:9999;background:rgba(255,255,255,0.97);" + |
27 | | - "align-items:center;justify-content:center;padding:2rem;" + |
28 | | - "box-sizing:border-box;overflow:auto;cursor:zoom-out;flex-direction:column;"; |
| 42 | + Object.assign(overlay.style, { |
| 43 | + display: "none", position: "fixed", top: "0", left: "0", |
| 44 | + width: "100vw", height: "100vh", zIndex: "9999", |
| 45 | + background: "rgba(255,255,255,0.97)", alignItems: "center", |
| 46 | + justifyContent: "center", padding: "2rem", boxSizing: "border-box", |
| 47 | + overflow: "auto", cursor: "zoom-out", flexDirection: "column" |
| 48 | + }); |
29 | 49 | var hint = document.createElement("div"); |
30 | 50 | hint.textContent = "Click anywhere or press Escape to close"; |
31 | | - hint.style.cssText = "position:fixed;bottom:1rem;left:50%;transform:translateX(-50%);" + |
32 | | - "font-size:14px;color:#666;"; |
| 51 | + Object.assign(hint.style, { |
| 52 | + position: "fixed", bottom: "1rem", left: "50%", |
| 53 | + transform: "translateX(-50%)", fontSize: "14px", color: "#666" |
| 54 | + }); |
33 | 55 | overlay.appendChild(hint); |
34 | 56 | document.body.appendChild(overlay); |
35 | 57 |
|
36 | | - // Open |
37 | 58 | btn.addEventListener("click", function (e) { |
38 | 59 | e.preventDefault(); |
39 | 60 | e.stopPropagation(); |
40 | 61 | overlay.querySelectorAll("svg").forEach(function (s) { s.remove(); }); |
41 | 62 | var clone = svg.cloneNode(true); |
42 | | - clone.style.cssText = "max-width:95vw;max-height:85vh;width:auto;height:auto;"; |
43 | 63 | clone.removeAttribute("width"); |
44 | 64 | clone.removeAttribute("height"); |
| 65 | + clone.removeAttribute("style"); |
| 66 | + Object.assign(clone.style, { |
| 67 | + maxWidth: "95vw", maxHeight: "85vh", width: "auto", height: "auto" |
| 68 | + }); |
45 | 69 | overlay.insertBefore(clone, hint); |
46 | 70 | overlay.style.display = "flex"; |
47 | 71 | document.body.style.overflow = "hidden"; |
48 | 72 | }); |
49 | 73 |
|
50 | | - // Close |
51 | 74 | overlay.addEventListener("click", function () { |
52 | 75 | overlay.style.display = "none"; |
53 | 76 | document.body.style.overflow = ""; |
54 | 77 | }); |
55 | 78 | }); |
56 | 79 | } |
57 | 80 |
|
58 | | - // Poll for Mermaid SVGs (rendered asynchronously) |
| 81 | + // Poll every 500ms for up to 20 seconds (Mermaid renders async) |
59 | 82 | var tries = 0; |
60 | 83 | var timer = setInterval(function () { |
61 | 84 | addButtons(); |
62 | | - if (++tries > 30) clearInterval(timer); |
| 85 | + if (++tries > 40) clearInterval(timer); |
63 | 86 | }, 500); |
64 | 87 |
|
65 | | - // Escape to close all overlays |
| 88 | + // Escape to close |
66 | 89 | document.addEventListener("keydown", function (e) { |
67 | 90 | if (e.key === "Escape") { |
68 | 91 | document.querySelectorAll("div").forEach(function (el) { |
|
0 commit comments