Skip to content

Commit

Permalink
[#65532] Update mermaid diagrams
Browse files Browse the repository at this point in the history
  • Loading branch information
Trzcin authored and mgielda committed Sep 11, 2024
1 parent 1e3755a commit 17b7cca
Show file tree
Hide file tree
Showing 8 changed files with 418 additions and 116 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"test": "PW_TEST_HTML_REPORT_OPEN=never playwright test -c tests/playwright.config.js --reporter html"
},
"dependencies": {
"@agoose77/markdown-it-mermaid": "^1.1.0",
"@codemirror/commands": "^6.2.4",
"@codemirror/lang-markdown": "^6.2.3",
"@codemirror/merge": "@6.0.0",
Expand All @@ -27,6 +26,7 @@
"imurmurhash": "^0.1.4",
"markdown-it": "^12.3.2",
"markdown-it-docutils": "^0.1.3",
"mermaid": "^11.2.0",
"preact": "^10.11.3",
"styled-components": "^6.1.2",
"stylis": "^4.3.0",
Expand Down
7 changes: 7 additions & 0 deletions src/components/Preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ const Preview = styled.div`
.cm-previewFocus {
display: ${(props) => (props.mode === "Both" ? "block" : "none")};
}
.mermaid {
background-color: transparent;
padding: 0;
display: flex;
justify-content: center;
}
`;
Preview.defaultProps = { className: "myst-preview" };

Expand Down
44 changes: 44 additions & 0 deletions src/hooks/markdownMermaid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import mermaid from "mermaid";
import { waitForElement } from "../utils";

const markdownItMermaid = (md, { preview }) => {
mermaid.initialize({
theme: "neutral",
suppressErrorRendering: true,
});

const original =
md.renderer.rules.fence ||
function (tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options);
};

md.renderer.rules.fence = (tokens, idx, options, env, self) => {
/** @type {import("markdown-it/index.js").Token} */
const token = tokens[idx];
if (token.info !== "mermaid") {
return original(tokens, idx, options, env, self);
}

const code = token.content.trim();
const id = Math.random().toString().replace(".", "");
token.attrSet("data-mermaid-id", id);

waitForElement(preview.current, `[data-mermaid-id="${id}"]`).then((el) => {
mermaid
.render(`mermaid-${id}`, code)
.then(({ svg }) => {
el.innerHTML = svg;
el.className = "mermaid";
})
.catch((err) => {
el.innerHTML = `<b>Mermaid error:</b>\n${err}`;
el.classList.remove("mermaid");
});
});

return `<pre ${self.renderAttrs(token)}>${code}</pre>`;
};
};

export default markdownItMermaid;
17 changes: 2 additions & 15 deletions src/hooks/markdownReplacer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import MarkdownIt from "markdown-it";
import { Role, rolePlugin } from "markdown-it-docutils";
import { waitForElement } from "../utils";
/**
* @typedef {{
* target: string | RegExp,
Expand Down Expand Up @@ -27,20 +28,6 @@ class PreviewWrapper {
this.preview = preview;
}

waitForElementWithId(id) {
return new Promise((resolve) => {
const observer = new MutationObserver(() => {
const elem = this.preview.getElementById(id);
if (elem) {
observer.disconnect();
resolve(elem);
}
});

observer.observe(this.preview, { childList: true, subtree: true });
});
}

fillPlaceholder(placeholderId, html) {
const placeholder = this.preview.getElementById(placeholderId);
if (placeholder) placeholder.outerHTML = html;
Expand All @@ -62,7 +49,7 @@ class PreviewWrapper {
const placeholderId = "placeholder-" + Math.random().toString().slice(2);

promise
.then(this.waitForElementWithId(placeholderId))
.then(waitForElement(this.preview, `#${placeholderId}`))
.then((result) => {
setCached(input, result);
this.fillPlaceholder(placeholderId, result);
Expand Down
8 changes: 2 additions & 6 deletions src/hooks/markdownSourceMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,8 @@ function wrapFencedLinesInSpan(/** @type {markdownIt} */ md) {
const defaultOutput = defaultFenceRule(tokens, idx, options, env, self);
const token = tokens[idx];
// Some markdown-it extensions use the `fence` rule for other things than code blocks (eg. mermaid graphs) so we don't want to modify those
if (!defaultOutput.startsWith("<pre")) {
const closeIdx = defaultOutput.indexOf(">");
const start = defaultOutput.slice(0, closeIdx);
const end = defaultOutput.slice(closeIdx);
// Mermaid graphs do not get the attributes from the token directly, so we need to add them manually.
return start + ` ${self.renderAttrs(token)}` + end;
if (defaultOutput.includes("mermaid")) {
return defaultOutput;
}

const sanitizedContent = escapeHtml(token.content);
Expand Down
12 changes: 9 additions & 3 deletions src/hooks/useText.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import markdownIt from "markdown-it";
import { markdownReplacer, useCustomRoles } from "./markdownReplacer";
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "preact/hooks";
import IMurMurHash from "imurmurhash";
import markdownItMermaid from "@agoose77/markdown-it-mermaid";
import { backslashLineBreakPlugin } from "./markdownLineBreak";
import markdownSourceMap from "./markdownSourceMap";
import { StateEffect } from "@codemirror/state";
import markdownMermaid from "./markdownMermaid";

const countOccurences = (str, pattern) => (str?.match(pattern) || []).length;

Expand Down Expand Up @@ -82,7 +82,7 @@ export const useText = ({ initialText, transforms, customRoles, preview, backsla
.use(markdownitDocutils)
.use(markdownReplacer(transforms, parent))
.use(useCustomRoles(customRoles, parent))
.use(markdownItMermaid)
.use(markdownMermaid, { preview })
.use(markdownSourceMap);
if (backslashLineBreak) md.use(backslashLineBreakPlugin);
return md;
Expand Down Expand Up @@ -141,7 +141,13 @@ export const useText = ({ initialText, transforms, customRoles, preview, backsla
)
.map(({ md, startLine }, id) => {
const hash = new IMurMurHash(md, 42).result();
const html = lookup[hash] || purify.sanitize(markdown.render(md, { chunkId: id, startLine, lineMap }));
const html =
lookup[hash] ||
purify.sanitize(markdown.render(md, { chunkId: id, startLine, lineMap }), {
// Taken from Mermaid JS settings: https://github.com/mermaid-js/mermaid/blob/dd0304387e85fc57a9ebb666f89ef788c012c2c5/packages/mermaid/src/mermaidAPI.ts#L50
ADD_TAGS: ["foreignobject"],
ADD_ATTR: ["dominant-baseline"],
});
return { md, hash, id, html };
}),
[markdown],
Expand Down
19 changes: 19 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export async function waitForElement(parent, selector) {
return new Promise((resolve) => {
const elem = parent.querySelector(selector);
if (elem) {
resolve(elem);
return;
}

const observer = new MutationObserver(() => {
const elem = parent.querySelector(selector);
if (elem) {
observer.disconnect();
resolve(elem);
}
});

observer.observe(parent, { childList: true, subtree: true });
});
}
Loading

0 comments on commit 17b7cca

Please sign in to comment.