-
-
Notifications
You must be signed in to change notification settings - Fork 431
feat: add mermaid diagram rendering support #5269
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: main
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
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,24 +9,61 @@ | |
| const COPY_SVG = | ||
| '<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"/></svg>'; | ||
|
|
||
| /** | ||
| * Find the copy target <pre> for a button. | ||
| * If the button has a data-copy-container attribute, searches within that | ||
| * ancestor. Otherwise falls back to closest("pre"). | ||
| * @param {HTMLElement} button | ||
| * @returns {HTMLElement | null} | ||
| */ | ||
| function findCopyTarget(button) { | ||
| const containerSel = button.dataset.copyContainer; | ||
| if (containerSel) { | ||
| const container = button.closest(containerSel); | ||
| return /** @type {HTMLElement | null} */ ( | ||
| container?.querySelector("pre") ?? null | ||
| ); | ||
| } | ||
| return /** @type {HTMLElement | null} */ (button.closest("pre")); | ||
| } | ||
|
|
||
| /** | ||
| * Create a copy-to-clipboard button for a code block. | ||
| * The button stores the header selector in a data attribute so the | ||
| * runtime script can re-attach the handler in exported documents. | ||
| * | ||
| * @param {string} headerSelector - Selector for the header to exclude from copy | ||
| * @param {string} [title="Copy to clipboard"] - Accessible label and tooltip | ||
| * @param {string} [containerSelector] - Ancestor selector to find the <pre> within | ||
| * @returns {HTMLButtonElement} | ||
| */ | ||
| export function createCopyButton(headerSelector, title = "Copy to clipboard") { | ||
| export function createCopyButton( | ||
| headerSelector, | ||
| title = "Copy to clipboard", | ||
|
Contributor
Author
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. We should probably localize this. |
||
| containerSelector | ||
| ) { | ||
| const button = document.createElement("button"); | ||
| button.innerHTML = COPY_SVG; | ||
| button.title = title; | ||
| button.setAttribute("aria-label", title); | ||
| button.classList.add("respec-button-copy-paste", "removeOnSave"); | ||
| button.dataset.copyHeader = headerSelector; | ||
| if (containerSelector) { | ||
| button.dataset.copyContainer = containerSelector; | ||
| } | ||
| button.addEventListener("click", () => { | ||
| const pre = button.closest("pre"); | ||
| const containerSel = button.dataset.copyContainer; | ||
|
Contributor
Author
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. Nit: avoid abbreviations in variable names. |
||
| if (containerSel) { | ||
| const container = /** @type {HTMLElement | null} */ ( | ||
| button.closest(containerSel) | ||
| ); | ||
| const source = container?.dataset.diagramSource; | ||
| if (source) { | ||
| navigator.clipboard.writeText(source); | ||
| return; | ||
| } | ||
| } | ||
| const pre = findCopyTarget(button); | ||
| if (!pre) return; | ||
| const clone = /** @type {HTMLElement} */ (pre.cloneNode(true)); | ||
| clone.querySelector(headerSelector)?.remove(); | ||
|
|
@@ -46,7 +83,18 @@ export function injectCopyScript() { | |
| script.textContent = ` | ||
| document.querySelectorAll(".respec-button-copy-paste").forEach(function(btn) { | ||
| btn.addEventListener("click", function() { | ||
| var pre = this.closest("pre"); | ||
| var containerSel = this.dataset.copyContainer; | ||
|
Contributor
Author
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. nit: use let or const where possible (applies throughout this PR. |
||
| var pre = null; | ||
| if (containerSel) { | ||
| var container = this.closest(containerSel); | ||
| if (container && container.dataset.diagramSource) { | ||
| navigator.clipboard.writeText(container.dataset.diagramSource); | ||
| return; | ||
| } | ||
| pre = container ? container.querySelector("pre") : null; | ||
| } else { | ||
| pre = this.closest("pre"); | ||
| } | ||
| if (!pre) return; | ||
| var sel = this.dataset.copyHeader; | ||
| var clone = pre.cloneNode(true); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // @ts-check | ||
| /** | ||
| * Module core/diagrams-runtime | ||
| * | ||
| * Runtime script injected into published specs. | ||
| * Handles flip buttons: toggles the flipped class for 3D rotation, | ||
| * updates ARIA state, and manages back-face height. | ||
| */ | ||
|
|
||
| export const runtimeScript = ` | ||
| "use strict"; | ||
| (function() { | ||
| document.querySelectorAll(".diagram-flip-btn").forEach(function(btn) { | ||
| btn.addEventListener("click", function(e) { | ||
| e.stopPropagation(); | ||
| var container = btn.closest(".diagram-container"); | ||
|
Contributor
Author
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. nit: use let or const. Same with other vars. |
||
| if (!container) return; | ||
| var isFlipped = container.classList.toggle("diagram-container--flipped"); | ||
| btn.setAttribute("aria-expanded", String(isFlipped)); | ||
| var viewSource = btn.dataset.labelSource; | ||
| var viewDiagram = btn.dataset.labelDiagram; | ||
| if (viewSource && viewDiagram) { | ||
| btn.setAttribute("aria-label", isFlipped ? viewDiagram : viewSource); | ||
| } | ||
| var flip = container.querySelector(".diagram-flip"); | ||
| var back = container.querySelector(".diagram-face--back"); | ||
| if (flip && back) { | ||
| flip.style.minHeight = isFlipped ? back.scrollHeight + "px" : ""; | ||
| } | ||
| }); | ||
| }); | ||
| })(); | ||
| `; | ||
Uh oh!
There was an error while loading. Please reload this page.