// Requires importing: // // // // // // // // // // // // // // // // // // // // // // // When served with lollms, just use // Don't forget to get the css too // Make sure there is a global variable called mr that instanciate MarkdownRenderer // mr = new MarkdownRenderer() class MarkdownRenderer { async renderMermaidDiagrams(text) { const mermaidCodeRegex = /```mermaid\n([\s\S]*?)```/g; const matches = text.match(mermaidCodeRegex); if (!matches) return text; for (const match of matches) { const mermaidCode = match.replace(/```mermaid\n/, '').replace(/```$/, ''); const uniqueId = 'mermaid-' + Math.random().toString(36).substr(2, 9); try { const result = await mermaid.render(uniqueId, mermaidCode); const htmlCode = `
${numberedLines}
$1'); } handleHorizontalRules(text) { return text.replace(/^(-{3,}|_{3,}|\*{3,})$/gm, '
$1'); // No need to handle paragraphs separately, they will be handled as the remaining content return text; } async renderMarkdown(text) { // Handle Mermaid graphs first text = await this.renderMermaidDiagrams(text); // Handle code blocks with syntax highlighting and copy button text = await this.renderCodeBlocks(text); // Handle inline code text = this.handleInlineCode(text); // Handle LaTeX-style math equations text = this.handleMathEquations(text); // Handle tables text = await this.handleTables(text); // Handle headers text = this.handleHeaders(text); // Handle bold text text = this.handleBoldText(text); // Handle italic text text = this.handleItalicText(text); // Handle links text = this.handleLinks(text); // Handle unordered lists text = this.handleUnorderedLists(text); // Handle ordered lists text = this.handleOrderedLists(text); // Handle blockquotes text = this.handleBlockquotes(text); // Handle horizontal rules text = this.handleHorizontalRules(text); // Handle paragraphs text = this.handleParagraphs(text); return text; } initMermaid() { if (typeof mermaid !== 'undefined') { mermaid.initialize({ startOnLoad: false }); } else { console.error('Mermaid library is not loaded'); } } initPrism() { if (typeof Prism !== 'undefined') { // No further initialization needed } else { console.error('Prism library is not loaded'); } } // Helper functions for Mermaid and code block handling saveMermaidAsPNG(id) { const svg = document.querySelector(`#${id} svg`); const svgData = new XMLSerializer().serializeToString(svg); // Create a canvas with a higher resolution const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); // Set a scale factor for higher resolution (e.g., 2 for double resolution) const scaleFactor = 2; const img = new Image(); img.onload = function() { canvas.width = img.width * scaleFactor; canvas.height = img.height * scaleFactor; // Scale the context to draw the image at a higher resolution ctx.scale(scaleFactor, scaleFactor); ctx.drawImage(img, 0, 0); const pngFile = canvas.toDataURL("image/png"); const downloadLink = document.createElement("a"); downloadLink.download = "mermaid_diagram.png"; downloadLink.href = pngFile; downloadLink.click(); }; img.src = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svgData))); } saveMermaidAsSVG(id) { const svg = document.querySelector(`#${id} svg`); const svgData = new XMLSerializer().serializeToString(svg); const svgBlob = new Blob([svgData], {type: "image/svg+xml;charset=utf-8"}); const svgUrl = URL.createObjectURL(svgBlob); const downloadLink = document.createElement("a"); downloadLink.href = svgUrl; downloadLink.download = "mermaid_diagram.svg"; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); } zoomMermaid(id, factor) { const svg = document.getElementById(id).firstElementChild; const currentScale = svg.style.transform ? parseFloat(svg.style.transform.replace('scale(', '').replace(')', '')) : 1; const newScale = currentScale * factor; svg.style.transform = `scale(${newScale})`; } copyCode(button) { const codeBlock = button.closest('.code-block-wrapper').querySelector('code'); const codeLines = codeBlock.querySelectorAll('.code-line'); let codeText = ''; codeLines.forEach((line) => { const lineNumber = line.querySelector('.line-number').textContent; const lineContent = line.querySelector('.line-content').textContent; codeText += `${lineNumber} ${lineContent}\n`; }); navigator.clipboard.writeText(codeText.trim()).then(() => { button.classList.add('copied'); button.querySelector('svg').style.display = 'none'; button.innerHTML = 'Copied!'; setTimeout(() => { button.classList.remove('copied'); button.innerHTML = ` Copy `; }, 2000); }).catch(err => { console.error('Failed to copy text: ', err); }); } async highlightCode(code, language) { // Make sure the language is supported by your highlighting library const supportedLanguage = Prism.languages[language] ? language : 'plaintext'; return Prism.highlight(code, Prism.languages[supportedLanguage], supportedLanguage); } // Helper function to escape HTML special characters escapeHtml(unsafe) { if (typeof unsafe !== 'string') { console.log("Found unsafe string:", text) return ''; } return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } }