feat(print): render inline mermaid

This commit is contained in:
Elian Doran 2025-11-20 14:26:04 +02:00
parent 991d61600d
commit abfc2fea3e
No known key found for this signature in database
3 changed files with 38 additions and 23 deletions

View File

@ -2,7 +2,7 @@ import FNote from "./entities/fnote";
import { render } from "preact"; import { render } from "preact";
import { CustomNoteList, useNoteViewType } from "./widgets/collections/NoteList"; import { CustomNoteList, useNoteViewType } from "./widgets/collections/NoteList";
import { useCallback, useLayoutEffect, useRef } from "preact/hooks"; import { useCallback, useLayoutEffect, useRef } from "preact/hooks";
import content_renderer from "./services/content_renderer"; import content_renderer, { applyInlineMermaid } from "./services/content_renderer";
interface RendererProps { interface RendererProps {
note: FNote; note: FNote;
@ -71,6 +71,11 @@ function SingleNoteRenderer({ note, onReady }: RendererProps) {
}) })
); );
// Initialize mermaid.
if (note.type === "text") {
await applyInlineMermaid(container);
}
// Check custom CSS. // Check custom CSS.
await loadCustomCss(note); await loadCustomCss(note);
} }

View File

@ -10,7 +10,7 @@ import FNote from "../entities/fnote.js";
import FAttachment from "../entities/fattachment.js"; import FAttachment from "../entities/fattachment.js";
import imageContextMenuService from "../menus/image_context_menu.js"; import imageContextMenuService from "../menus/image_context_menu.js";
import { applySingleBlockSyntaxHighlight, formatCodeBlocks } from "./syntax_highlight.js"; import { applySingleBlockSyntaxHighlight, formatCodeBlocks } from "./syntax_highlight.js";
import { loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js"; import { getMermaidConfig, loadElkIfNeeded, postprocessMermaidSvg } from "./mermaid.js";
import renderDoc from "./doc_renderer.js"; import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import WheelZoom from 'vanilla-js-wheel-zoom'; import WheelZoom from 'vanilla-js-wheel-zoom';
@ -136,6 +136,7 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT
await linkService.loadReferenceLinkTitle($(el)); await linkService.loadReferenceLinkTitle($(el));
} }
await rewriteMermaidDiagramsInContainer($renderedContent[0] as HTMLDivElement);
await formatCodeBlocks($renderedContent); await formatCodeBlocks($renderedContent);
} else if (note instanceof FNote && !options.noChildrenList) { } else if (note instanceof FNote && !options.noChildrenList) {
await renderChildrenList($renderedContent, note); await renderChildrenList($renderedContent, note);
@ -370,6 +371,34 @@ function getRenderingType(entity: FNote | FAttachment) {
return type; return type;
} }
/** Rewrite the code block from <pre><code> to <div> in order not to apply a codeblock style to it. */
export async function rewriteMermaidDiagramsInContainer(container: HTMLDivElement) {
const mermaidBlocks = container.querySelectorAll('pre:has(code[class="language-mermaid"])');
if (!mermaidBlocks.length) return;
const nodes: HTMLElement[] = [];
for (const mermaidBlock of mermaidBlocks) {
const div = document.createElement("div");
div.classList.add("mermaid-diagram");
div.innerHTML = mermaidBlock.querySelector("code")?.innerHTML ?? "";
mermaidBlock.replaceWith(div);
nodes.push(div);
}
}
export async function applyInlineMermaid(container: HTMLDivElement) {
// Initialize mermaid
const mermaid = (await import("mermaid")).default;
mermaid.initialize(getMermaidConfig());
const nodes = Array.from(container.querySelectorAll<HTMLElement>("div.mermaid-diagram"));
console.log("Got nodes", nodes);
try {
await mermaid.run({ nodes });
} catch (e) {
console.log(e);
}
}
export default { export default {
getRenderedContent getRenderedContent
}; };

View File

@ -10,13 +10,13 @@ import RawHtml from "../../react/RawHtml";
import "@triliumnext/ckeditor5"; import "@triliumnext/ckeditor5";
import FNote from "../../../entities/fnote"; import FNote from "../../../entities/fnote";
import { getLocaleById } from "../../../services/i18n"; import { getLocaleById } from "../../../services/i18n";
import { getMermaidConfig } from "../../../services/mermaid";
import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./utils"; import { loadIncludedNote, refreshIncludedNote, setupImageOpening } from "./utils";
import { renderMathInElement } from "../../../services/math"; import { renderMathInElement } from "../../../services/math";
import { formatCodeBlocks } from "../../../services/syntax_highlight"; import { formatCodeBlocks } from "../../../services/syntax_highlight";
import TouchBar, { TouchBarButton, TouchBarSpacer } from "../../react/TouchBar"; import TouchBar, { TouchBarButton, TouchBarSpacer } from "../../react/TouchBar";
import appContext from "../../../components/app_context"; import appContext from "../../../components/app_context";
import { applyReferenceLinks } from "./read_only_helper"; import { applyReferenceLinks } from "./read_only_helper";
import { applyInlineMermaid, rewriteMermaidDiagramsInContainer } from "../../../services/content_renderer";
export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetProps) { export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetProps) {
const blob = useNoteBlob(note); const blob = useNoteBlob(note);
@ -29,6 +29,7 @@ export default function ReadOnlyText({ note, noteContext, ntxId }: TypeWidgetPro
const container = contentRef.current; const container = contentRef.current;
if (!container) return; if (!container) return;
rewriteMermaidDiagramsInContainer(container);
applyInlineMermaid(container); applyInlineMermaid(container);
applyIncludedNotes(container); applyIncludedNotes(container);
applyMath(container); applyMath(container);
@ -87,26 +88,6 @@ function useNoteLanguage(note: FNote) {
return { isRtl }; return { isRtl };
} }
async function applyInlineMermaid(container: HTMLDivElement) {
const mermaidBlocks = container.querySelectorAll('pre:has(code[class="language-mermaid"])');
if (!mermaidBlocks.length) return;
const nodes: HTMLElement[] = [];
// Rewrite the code block from <pre><code> to <div> in order not to apply a codeblock style to it.
for (const mermaidBlock of mermaidBlocks) {
const div = document.createElement("div");
div.classList.add("mermaid-diagram");
div.innerHTML = mermaidBlock.querySelector("code")?.innerHTML ?? "";
mermaidBlock.replaceWith(div);
nodes.push(div);
}
// Initialize mermaid
const mermaid = (await import("mermaid")).default;
mermaid.initialize(getMermaidConfig());
mermaid.run({ nodes });
}
function applyIncludedNotes(container: HTMLDivElement) { function applyIncludedNotes(container: HTMLDivElement) {
const includedNotes = container.querySelectorAll<HTMLElement>("section.include-note"); const includedNotes = container.querySelectorAll<HTMLElement>("section.include-note");
for (const includedNote of includedNotes) { for (const includedNote of includedNotes) {