-
Notifications
You must be signed in to change notification settings - Fork 1
docs: interactive mermaid diagrams (render by default + source toggle + copy) #49
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| /* Eureka deployment docs — diagram enhancer. | ||
| * | ||
| * Loaded as a CLASSIC script (not an ES module) on purpose: Chrome blocks | ||
| * `<script type="module">` from file:// origins (CORS, origin "null"), which | ||
| * made the mermaid diagrams show up as raw text when the docs are opened as | ||
| * local files. A classic script + the UMD mermaid build renders reliably from | ||
| * file:// and over http. | ||
| * | ||
| * For each `.diagram` panel it: | ||
| * 1. captures the raw mermaid source (before mermaid replaces it with SVG), | ||
| * 2. renders the diagram (default view), | ||
| * 3. adds a Diagram/Source toggle and a Copy-source button. | ||
| */ | ||
| (function () { | ||
| if (typeof mermaid === "undefined") { | ||
| console.error("mermaid failed to load"); | ||
| return; | ||
| } | ||
|
|
||
| mermaid.initialize({ | ||
| startOnLoad: false, | ||
| securityLevel: "loose", | ||
| theme: "base", | ||
| fontFamily: "'IBM Plex Mono', monospace", | ||
| themeVariables: { | ||
| darkMode: true, | ||
| background: "#0e131c", | ||
| fontFamily: "'IBM Plex Mono', monospace", | ||
| fontSize: "14px", | ||
| primaryColor: "#18212f", | ||
| primaryBorderColor: "#c9f24e", | ||
| primaryTextColor: "#e9eef5", | ||
| secondaryColor: "#121927", | ||
| secondaryBorderColor: "#5fd5ee", | ||
| secondaryTextColor: "#e9eef5", | ||
| tertiaryColor: "#0e131c", | ||
| tertiaryBorderColor: "#7e8da0", | ||
| tertiaryTextColor: "#b9c4d2", | ||
| lineColor: "#5fd5ee", | ||
| textColor: "#b9c4d2", | ||
| mainBkg: "#18212f", | ||
| nodeBorder: "#c9f24e", | ||
| clusterBkg: "rgba(95,213,238,0.05)", | ||
| clusterBorder: "rgba(150,180,220,0.25)", | ||
| titleColor: "#e9eef5", | ||
| edgeLabelBackground: "#0a0e14", | ||
| actorBkg: "#18212f", | ||
| actorBorder: "#b69cff", | ||
| actorTextColor: "#e9eef5", | ||
| actorLineColor: "#7e8da0", | ||
| signalColor: "#5fd5ee", | ||
| signalTextColor: "#b9c4d2", | ||
| labelBoxBkgColor: "#121927", | ||
| labelBoxBorderColor: "#5fd5ee", | ||
| labelTextColor: "#e9eef5", | ||
| loopTextColor: "#b9c4d2", | ||
| noteBkgColor: "rgba(255,180,84,0.12)", | ||
| noteBorderColor: "#ffb454", | ||
| noteTextColor: "#e9eef5", | ||
| activationBkgColor: "#18212f", | ||
| activationBorderColor: "#c9f24e", | ||
| nodeTextColor: "#e9eef5", | ||
| }, | ||
| flowchart: { curve: "basis", htmlLabels: true, padding: 14 }, | ||
| sequence: { actorMargin: 46, messageAlign: "center", mirrorActors: false, useMaxWidth: true }, | ||
| }); | ||
|
|
||
| function enhance(panel) { | ||
| var pre = panel.querySelector("pre.mermaid"); | ||
| if (!pre) return; | ||
|
|
||
| var source = pre.textContent.replace(/^\s*\n/, "").replace(/\s+$/, ""); | ||
|
|
||
| // Views: rendered graph (holds the mermaid <pre>) and a raw-source view. | ||
| var graph = document.createElement("div"); | ||
| graph.className = "dgm-graph"; | ||
| graph.appendChild(pre); | ||
|
|
||
| var srcView = document.createElement("pre"); | ||
| srcView.className = "dgm-source"; | ||
| srcView.hidden = true; | ||
| var code = document.createElement("code"); | ||
| code.textContent = source; | ||
| srcView.appendChild(code); | ||
|
|
||
| var body = document.createElement("div"); | ||
| body.className = "dgm-body"; | ||
| body.appendChild(graph); | ||
| body.appendChild(srcView); | ||
|
|
||
| // Toolbar: segmented toggle + copy. | ||
| var bar = document.createElement("div"); | ||
| bar.className = "dgm-bar"; | ||
| bar.innerHTML = | ||
| '<div class="dgm-seg" role="tablist">' + | ||
| '<button class="dgm-tab is-active" data-view="graph" aria-selected="true">Diagram</button>' + | ||
| '<button class="dgm-tab" data-view="source" aria-selected="false">Source</button>' + | ||
| "</div>" + | ||
| '<button class="dgm-copy" type="button" aria-label="Copy Mermaid source">Copy</button>'; | ||
|
|
||
| var cap = panel.querySelector(".cap"); | ||
| if (cap) cap.appendChild(bar); | ||
| else panel.insertBefore(bar, panel.firstChild); | ||
| panel.appendChild(body); | ||
|
|
||
| var tabs = bar.querySelectorAll(".dgm-tab"); | ||
| tabs.forEach(function (tab) { | ||
| tab.addEventListener("click", function () { | ||
| var view = tab.getAttribute("data-view"); | ||
| tabs.forEach(function (t) { | ||
| var active = t === tab; | ||
| t.classList.toggle("is-active", active); | ||
| t.setAttribute("aria-selected", active ? "true" : "false"); | ||
| }); | ||
| graph.hidden = view !== "graph"; | ||
| srcView.hidden = view !== "source"; | ||
| }); | ||
| }); | ||
|
|
||
| var copy = bar.querySelector(".dgm-copy"); | ||
| copy.addEventListener("click", function () { | ||
| var done = function () { | ||
| copy.textContent = "Copied"; | ||
| copy.classList.add("ok"); | ||
| setTimeout(function () { | ||
| copy.textContent = "Copy"; | ||
| copy.classList.remove("ok"); | ||
| }, 1400); | ||
| }; | ||
| if (navigator.clipboard && navigator.clipboard.writeText) { | ||
| navigator.clipboard.writeText(source).then(done, fallback); | ||
| } else { | ||
| fallback(); | ||
| } | ||
| function fallback() { | ||
| var range = document.createRange(); | ||
| range.selectNodeContents(code); | ||
| var sel = window.getSelection(); | ||
| sel.removeAllRanges(); | ||
| sel.addRange(range); | ||
| try { document.execCommand("copy"); } catch (e) { /* ignore */ } | ||
| sel.removeAllRanges(); | ||
| done(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| document.addEventListener("DOMContentLoaded", function () { | ||
| document.querySelectorAll(".diagram").forEach(enhance); | ||
| // Render every mermaid block (now nested inside .dgm-graph). Pass an | ||
| // explicit querySelector — some mermaid builds throw if run() is given an | ||
| // options object without `nodes`/`querySelector` instead of defaulting. | ||
| mermaid.run({ querySelector: ".dgm-graph pre.mermaid" }).catch(function (err) { | ||
| console.error("mermaid render error:", err); | ||
| }); | ||
| }); | ||
| })(); | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -166,6 +166,34 @@ li::marker { color: var(--signal-dim); } | |
| .diagram .cap::before { content: "◆"; color: var(--cyan); font-size: 9px; } | ||
| .mermaid { display: flex; justify-content: center; padding: 8px; } | ||
|
|
||
| /* Diagram toolbar — Diagram/Source toggle + copy ------------------------- */ | ||
| .diagram .cap { flex-wrap: wrap; } | ||
| .dgm-bar { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; } | ||
| .dgm-seg { display: inline-flex; border: 1px solid var(--line-2); border-radius: 8px; overflow: hidden; } | ||
| .dgm-tab { | ||
| font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; text-transform: none; | ||
| padding: 5px 11px; background: transparent; color: var(--muted); border: 0; cursor: pointer; | ||
| transition: background .15s, color .15s; | ||
| } | ||
| .dgm-tab + .dgm-tab { border-left: 1px solid var(--line-2); } | ||
| .dgm-tab:not(.is-active):hover { color: var(--text); background: var(--ink-3); } | ||
| .dgm-tab.is-active { background: var(--signal); color: var(--ink); font-weight: 600; } | ||
| .dgm-copy { | ||
| font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; | ||
| padding: 5px 11px; background: var(--ink-3); color: var(--text-soft); | ||
| border: 1px solid var(--line-2); border-radius: 8px; cursor: pointer; | ||
| transition: color .15s, border-color .15s; | ||
| } | ||
| .dgm-copy:hover { color: var(--text); border-color: var(--cyan); } | ||
| .dgm-copy.ok { color: var(--signal); border-color: var(--signal-dim); } | ||
| .dgm-graph { display: flex; justify-content: center; padding: 8px; } | ||
| .dgm-source { | ||
| margin: 6px 0 0; border: 0; border-left: 3px solid var(--violet); | ||
| background: var(--ink); border-radius: 0 10px 10px 0; max-height: 540px; overflow: auto; | ||
| } | ||
| .dgm-source code { color: var(--text-soft); background: none; border: 0; padding: 0; } | ||
| [hidden] { display: none !important; } | ||
|
|
||
|
Comment on lines
+169
to
+196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift Migrate new toolbar styling to Tailwind utilities. This new block introduces custom component styling (states, colors, borders, spacing) beyond animation/layout primitives. Please move these styles to Tailwind classes (or utility composition) and keep custom CSS only for parts Tailwind cannot express. As per coding guidelines, " 🤖 Prompt for AI Agents |
||
| /* Callouts ---------------------------------------------------------------- */ | ||
| .note { | ||
| border-left: 3px solid var(--accent, var(--cyan)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,6 +140,7 @@ <h4>Rotate a secret</h4> | |
| </div> | ||
| </footer> | ||
|
|
||
| <script type="module" src="assets/mermaid-init.js"></script> | ||
| <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify all deployment docs pages that load Mermaid from CDN and check for SRI/crossorigin.
rg -n 'cdn\.jsdelivr\.net/npm/mermaid@' docs/deployment/*.html
rg -n 'mermaid\.min\.js".*integrity=' docs/deployment/*.html
rg -n 'mermaid\.min\.js".*crossorigin=' docs/deployment/*.htmlRepository: riethmayer/eureka Length of output: 539 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Mermaid jsDelivr inclusions =="
rg -n 'cdn\.jsdelivr\.net/npm/mermaid@' docs/deployment/*.html
echo
echo "== SRI matches (integrity=) on mermaid.min.js tags =="
rg -n 'mermaid\.min\.js".*integrity=' docs/deployment/*.html || true
echo
echo "== crossorigin matches on mermaid.min.js tags =="
rg -n 'mermaid\.min\.js".*crossorigin=' docs/deployment/*.html || true
echo
echo "== Context around mermaid@ occurrences (check integrity/crossorigin nearby) =="
rg -n -C 2 'cdn\.jsdelivr\.net/npm/mermaid@' docs/deployment/*.htmlRepository: riethmayer/eureka Length of output: 2029 Pin Mermaid CDN and add SRI
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>🤖 Prompt for AI Agents |
||
| <script src="assets/docs.js"></script> | ||
| </body> | ||
| </html> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid showing “Copied” when fallback copy fails.
done()is called unconditionally in the fallback path, so users can see success even if copy did not happen (Line 141). Gate success on actual copy result.Proposed fix
function fallback() { var range = document.createRange(); range.selectNodeContents(code); var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - try { document.execCommand("copy"); } catch (e) { /* ignore */ } - sel.removeAllRanges(); - done(); + if (!sel) return; + sel.removeAllRanges(); + sel.addRange(range); + var copied = false; + try { copied = document.execCommand("copy"); } catch (e) { copied = false; } + sel.removeAllRanges(); + if (copied) done(); }🤖 Prompt for AI Agents