fix: Render Mermaid diagrams in HTML export, towards #6205

This commit is contained in:
Tom Moor
2023-11-23 09:05:40 -05:00
parent ea8ebc3b2a
commit 8b68ee404a
4 changed files with 57 additions and 4 deletions

View File

@@ -32,6 +32,8 @@ type HTMLOptions = {
includeTitle?: boolean;
/** Whether to include style tags in the generated HTML (defaults to true) */
includeStyles?: boolean;
/** Whether to include the Mermaid script in the generated HTML (defaults to false) */
includeMermaid?: boolean;
/** Whether to include styles to center diff (defaults to true) */
centered?: boolean;
/**
@@ -107,6 +109,7 @@ export default class DocumentHelper {
let output = ProsemirrorHelper.toHTML(node, {
title: options?.includeTitle !== false ? document.title : undefined,
includeStyles: options?.includeStyles,
includeMermaid: options?.includeMermaid,
centered: options?.centered,
});

View File

@@ -19,6 +19,8 @@ export type HTMLOptions = {
title?: string;
/** Whether to include style tags in the generated HTML (defaults to true) */
includeStyles?: boolean;
/** Whether to include mermaidjs scripts in the generated HTML (defaults to false) */
includeMermaid?: boolean;
/** Whether to include styles to center diff (defaults to true) */
centered?: boolean;
};
@@ -144,7 +146,8 @@ export default class ProsemirrorHelper {
*/
static toHTML(node: Node, options?: HTMLOptions) {
const sheet = new ServerStyleSheet();
let html, styleTags;
let html = "";
let styleTags = "";
const Centered = options?.centered
? styled.article`
@@ -160,7 +163,7 @@ export default class ProsemirrorHelper {
<>
{options?.title && <h1 dir={rtl ? "rtl" : "ltr"}>{options.title}</h1>}
{options?.includeStyles !== false ? (
<EditorContainer dir={rtl ? "rtl" : "ltr"} rtl={rtl}>
<EditorContainer dir={rtl ? "rtl" : "ltr"} rtl={rtl} staticHTML>
{content}
</EditorContainer>
) : (
@@ -214,6 +217,40 @@ export default class ProsemirrorHelper {
target
);
// Inject mermaidjs scripts if the document contains mermaid diagrams
if (options?.includeMermaid) {
const mermaidElements = dom.window.document.querySelectorAll(
`[data-language="mermaidjs"] pre code`
);
// Unwrap <pre> tags to enable Mermaid script to correctly render inner content
for (const el of mermaidElements) {
const parent = el.parentNode as HTMLElement;
if (parent) {
while (el.firstChild) {
parent.insertBefore(el.firstChild, el);
}
parent.removeChild(el);
parent.setAttribute("class", "mermaid");
}
}
// Inject Mermaid script
if (mermaidElements.length) {
const element = dom.window.document.createElement("script");
element.setAttribute("type", "module");
element.innerHTML = `
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
mermaid.initialize({
startOnLoad: true,
fontFamily: "inherit",
});
window.status = "ready";
`;
dom.window.document.body.appendChild(element);
}
}
return dom.serialize();
}
}

View File

@@ -527,6 +527,7 @@ router.post(
content = await DocumentHelper.toHTML(document, {
signedUrls: true,
centered: true,
includeMermaid: true,
});
} else if (accept?.includes("application/pdf")) {
throw IncorrectEditionError(

View File

@@ -6,6 +6,7 @@ export type Props = {
rtl: boolean;
readOnly?: boolean;
readOnlyWriteCheckboxes?: boolean;
staticHTML?: boolean;
editorStyle?: React.CSSProperties;
grow?: boolean;
theme: DefaultTheme;
@@ -260,6 +261,16 @@ const emailStyle = (props: Props) => css`
}
`;
const printStyle = (props: Props) => css`
${props.staticHTML &&
`
body {
height: auto;
min-height: 0;
}
`}
`;
const style = (props: Props) => `
flex-grow: ${props.grow ? 1 : 0};
justify-content: start;
@@ -1128,7 +1139,7 @@ mark {
/* Hide code without display none so toolbar can still be positioned against it */
&:not(.code-active) {
height: 0;
height: ${props.staticHTML ? "auto" : "0"};
margin: -0.5em 0;
overflow: hidden;
}
@@ -1136,7 +1147,7 @@ mark {
/* Hide code without display none so toolbar can still be positioned against it */
.ProseMirror[contenteditable="false"] .code-block[data-language=mermaidjs] {
height: 0;
height: ${props.staticHTML ? "auto" : "0"};
margin: -0.5em 0;
overflow: hidden;
}
@@ -1559,6 +1570,7 @@ const EditorContainer = styled.div<Props>`
${codeBlockStyle}
${findAndReplaceStyle}
${emailStyle}
${printStyle}
`;
export default EditorContainer;