Skip to content

Commit 892984f

Browse files
authored
Merge pull request #235 from easyops-cn/steve/elevo-sidebar
feat(): support mermaid in markdown
2 parents a2da775 + 3ce81b5 commit 892984f

File tree

8 files changed

+897
-70
lines changed

8 files changed

+897
-70
lines changed

bricks/ai-portal/src/cruise-canvas/CodeBlock/CodeBlock.module.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,8 @@
1919
.failed::part(button) {
2020
color: var(--color-error);
2121
}
22+
23+
.code-block:global(.mermaid) {
24+
display: flex;
25+
justify-content: center;
26+
}

bricks/markdown/docs/eo-markdown-display.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,15 @@ properties:
5757
alert("Hello");
5858
}
5959
```
60+
61+
```mermaid
62+
graph TD
63+
A[Enter Chart Definition] --> B(Preview)
64+
B --> C{decide}
65+
C --> D[Keep]
66+
C --> E[Edit Definition]
67+
E --> B
68+
D --> F[Save Image and Code]
69+
F --> B
70+
```
6071
````

shared/markdown/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
"build:types": "tsc --emitDeclarationOnly --declaration --declarationDir dist/types --project tsconfig.build.json"
3535
},
3636
"dependencies": {
37+
"hast-util-from-html-isomorphic": "^2.0.0",
3738
"hast-util-to-string": "^3.0.0",
39+
"mermaid": "11.9.0",
3840
"prismjs": "^1.29.0",
3941
"react": "0.0.0-experimental-ee8509801-20230117",
4042
"refractor": "^4.8.1",

shared/markdown/src/MarkdownComponent.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import remarkToRehype from "remark-rehype";
77
import rehypeReact, { Options as RehypeReactOptions } from "rehype-react";
88
import type { Components } from "hast-util-to-jsx-runtime";
99
import { rehypePrism } from "./rehypePrism.js";
10+
import { rehypeMermaid } from "./rehypeMermaid.js";
1011

1112
const production = { Fragment, jsx, jsxs };
1213

@@ -29,6 +30,7 @@ export function MarkdownComponent({
2930
.use(remarkGfm)
3031
.use(remarkToRehype)
3132
.use([rehypePrism])
33+
.use(rehypeMermaid)
3234
.use(rehypeReact, {
3335
...production,
3436
passNode: true,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { visit } from "unist-util-visit";
2+
import { toString } from "hast-util-to-string";
3+
import type { RefractorElement } from "refractor";
4+
import mermaid from "mermaid";
5+
import { fromHtmlIsomorphic } from "hast-util-from-html-isomorphic";
6+
import { getCodeLanguage } from "./utils.js";
7+
8+
let count = 0;
9+
10+
mermaid.initialize({
11+
startOnLoad: false,
12+
themeVariables: {
13+
fontSize: "14px",
14+
},
15+
});
16+
17+
// Reference https://github.com/remcohaszing/rehype-mermaid
18+
export function rehypeMermaid() {
19+
return async (tree: RefractorElement) => {
20+
const promises: Promise<void>[] = [];
21+
22+
function visitor(
23+
node: RefractorElement,
24+
index: number | undefined,
25+
parent: RefractorElement | undefined
26+
) {
27+
if (!parent || parent.tagName !== "pre" || node.tagName !== "code") {
28+
return;
29+
}
30+
31+
const lang = getCodeLanguage(node);
32+
33+
if (lang !== "mermaid") {
34+
return;
35+
}
36+
37+
promises.push(
38+
(async () => {
39+
const id = `mermaid-${count++}`;
40+
41+
const { svg } = await mermaid.render(id, toString(node));
42+
const replacements = fromHtmlIsomorphic(svg, { fragment: true })
43+
.children as RefractorElement[];
44+
parent.children.splice(index!, 1, ...replacements);
45+
parent.properties.className = (
46+
(parent.properties.className as string[]) || []
47+
).concat("mermaid");
48+
})()
49+
);
50+
}
51+
52+
visit(tree, "element", visitor);
53+
54+
await Promise.all(promises);
55+
};
56+
}

shared/markdown/src/rehypePrism.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { visit } from "unist-util-visit";
22
import { toString } from "hast-util-to-string";
33
import { refractor, type RefractorElement } from "refractor";
4+
import { getCodeLanguage } from "./utils.js";
45

56
// Reference https://github.com/mapbox/rehype-prism
67
export function rehypePrism() {
@@ -13,9 +14,9 @@ export function rehypePrism() {
1314
return;
1415
}
1516

16-
const lang = getLanguage(node);
17+
const lang = getCodeLanguage(node);
1718

18-
if (lang === null) {
19+
if (lang === null || lang === "mermaid") {
1920
return;
2021
}
2122

@@ -35,15 +36,3 @@ export function rehypePrism() {
3536
visit(tree, "element", visitor);
3637
};
3738
}
38-
39-
function getLanguage(node: RefractorElement) {
40-
const className = (node.properties.className as string[]) || [];
41-
42-
for (const classListItem of className) {
43-
if (classListItem.slice(0, 9) === "language-") {
44-
return classListItem.slice(9).toLowerCase();
45-
}
46-
}
47-
48-
return null;
49-
}

shared/markdown/src/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { RefractorElement } from "refractor";
2+
3+
export function getCodeLanguage(node: RefractorElement) {
4+
const className = (node.properties.className as string[]) || [];
5+
6+
for (const classListItem of className) {
7+
if (classListItem.slice(0, 9) === "language-") {
8+
return classListItem.slice(9).toLowerCase();
9+
}
10+
}
11+
12+
return null;
13+
}

0 commit comments

Comments
 (0)