import { formatCodeBlocks } from "./syntax_highlight.js"; import { getMermaidConfig } from "./mermaid.js"; import { renderMathInElement } from "./math.js"; import FNote from "../entities/fnote.js"; import FAttachment from "../entities/fattachment.js"; import tree from "./tree.js"; import froca from "./froca.js"; import link from "./link.js"; import { isHtmlEmpty } from "./utils.js"; import { default as content_renderer, type RenderOptions } from "./content_renderer.js"; export default async function renderText(note: FNote | FAttachment, $renderedContent: JQuery, options: RenderOptions = {}) { // entity must be FNote const blob = await note.getBlob(); if (blob && !isHtmlEmpty(blob.content)) { $renderedContent.append($('
').html(blob.content)); await renderIncludedNotes($renderedContent[0]); if ($renderedContent.find("span.math-tex").length > 0) { renderMathInElement($renderedContent[0], { trust: true }); } const getNoteIdFromLink = (el: HTMLElement) => tree.getNoteIdFromUrl($(el).attr("href") || ""); const referenceLinks = $renderedContent.find("a.reference-link"); const noteIdsToPrefetch = referenceLinks.map((i, el) => getNoteIdFromLink(el)); await froca.getNotes(noteIdsToPrefetch); for (const el of referenceLinks) { await link.loadReferenceLinkTitle($(el)); } await rewriteMermaidDiagramsInContainer($renderedContent[0] as HTMLDivElement); await formatCodeBlocks($renderedContent); } else if (note instanceof FNote && !options.noChildrenList) { await renderChildrenList($renderedContent, note); } } async function renderIncludedNotes(contentEl: HTMLElement) { // TODO: Consider duplicating with server's share/content_renderer.ts. const includeNoteEls = contentEl.querySelectorAll("section.include-note"); // Gather the list of items to load. const noteIds: string[] = []; for (const includeNoteEl of includeNoteEls) { const noteId = includeNoteEl.getAttribute("data-note-id"); if (noteId) { noteIds.push(noteId); } } // Load the required notes. await froca.getNotes(noteIds); // Render and integrate the notes. for (const includeNoteEl of includeNoteEls) { const noteId = includeNoteEl.getAttribute("data-note-id"); if (!noteId) continue; const note = froca.getNoteFromCache(noteId); if (!note) { console.warn(`Unable to include ${noteId} because it could not be found.`); continue; } const renderedContent = (await content_renderer.getRenderedContent(note)).$renderedContent; includeNoteEl.replaceChildren(...renderedContent); } } /** Rewrite the code block from
 to 
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("div.mermaid-diagram")); try { await mermaid.run({ nodes }); } catch (e) { console.log(e); } } async function renderChildrenList($renderedContent: JQuery, note: FNote) { let childNoteIds = note.getChildNoteIds(); if (!childNoteIds.length) { return; } $renderedContent.css("padding", "10px"); $renderedContent.addClass("text-with-ellipsis"); if (childNoteIds.length > 10) { childNoteIds = childNoteIds.slice(0, 10); } // just load the first 10 child notes const childNotes = await froca.getNotes(childNoteIds); for (const childNote of childNotes) { $renderedContent.append( await link.createLink(`${note.noteId}/${childNote.noteId}`, { showTooltip: false, showNoteIcon: true }) ); $renderedContent.append("
"); } }