-
🌐 async-utils.jsWeb, 3.69 kB⬇
-
🌐 auth-storage.jsWeb, 735 B⬇
-
🌐 chat-helpers.jsWeb, 8.85 kB⬇
-
🌐 chat-renderer.jsWeb, 70.00 kB⬇
-
🌐 code-overlay.jsWeb, 21.77 kB⬇
-
🌐 confirm-dialog.jsWeb, 3.17 kB⬇
-
🌐 conversation-format-utils.jsWeb, 21.51 kB⬇
-
🌐 diagram-overlay.jsWeb, 7.93 kB⬇
-
🌐 dialog.jsWeb, 2.50 kB⬇
-
🌐 dom-utils.jsWeb, 8.33 kB⬇
-
🌐 drag-reorder.jsWeb, 4.82 kB⬇
-
🌐 elements.jsWeb, 11.06 kB⬇
-
🌐 export-utils.jsWeb, 1.53 kB⬇
-
🌐 format-utils.jsWeb, 29.60 kB⬇
-
🌐 history-navigation.jsWeb, 1.95 kB⬇
-
🌐 hljs-yaml.jsWeb, 5.07 kB⬇
-
🌐 inline-format.jsWeb, 7.35 kB⬇
-
🌐 instant-answer.jsWeb, 19.82 kB⬇
-
🌐 interactive-tools.jsWeb, 38.77 kB⬇
-
🌐 mermaid-fixes.jsWeb, 4.60 kB⬇
-
🌐 mermaid-render.jsWeb, 5.10 kB⬇
-
🌐 mermaid.jsWeb, 195 B⬇
-
🌐 migrations.jsWeb, 12.11 kB⬇
-
🌐 multi-choice-dialog.jsWeb, 1.33 kB⬇
-
🌐 notification-stack.jsWeb, 2.50 kB⬇
-
🌐 options-manager.jsWeb, 45.56 kB⬇
-
🌐 overlay-utils.jsWeb, 4.14 kB⬇
-
🌐 pdf-export.jsWeb, 23.59 kB⬇
-
🌐 pinch-zoom.jsWeb, 3.21 kB⬇
-
🌐 plugin-dialog.jsWeb, 2.17 kB⬇
-
🌐 read-aloud.jsWeb, 33.84 kB⬇
-
🌐 response-pipeline.jsWeb, 55.19 kB⬇
-
🌐 scratchpad-editor.jsWeb, 8.64 kB⬇
-
🌐 scratchpad-export.jsWeb, 3.56 kB⬇
-
🌐 scrollbars.jsWeb, 3.12 kB⬇
-
🌐 selection-manager.jsWeb, 3.76 kB⬇
-
🌐 settings-utils.jsWeb, 31.08 kB⬇
-
🌐 share.jsWeb, 10.75 kB⬇
-
🌐 side-drawer.jsWeb, 9.37 kB⬇
-
🌐 speech-text.jsWeb, 6.60 kB⬇
-
🌐 swipe-navigation.jsWeb, 6.16 kB⬇
-
🌐 table-overlay.jsWeb, 8.10 kB⬇
-
🌐 theme-utils.jsWeb, 8.34 kB⬇
-
🌐 thinking-panel-base.jsWeb, 14.88 kB⬇
-
🌐 ui-sets.jsWeb, 1.00 kB⬇
-
🌐 version-overlay.jsWeb, 4.63 kB⬇
/**
* Mermaid rendering helpers shared by the chat renderer and the shared viewer
* page: converts mermaid code blocks into rendered diagrams, repairs failing
* diagrams once, and attaches the expand buttons that open the diagram overlay.
*/
import { escapeHtml } from './format-utils.js';
import { open_diagram_overlay, open_diagram_overlay_message } from './diagram-overlay.js';
import { repair_mermaid_code } from './mermaid-fixes.js';
import { mermaid } from './mermaid.js';
/**
* Renders Mermaid diagrams in the specified container element.
* @param {HTMLElement} container
*/
export async function render_mermaid_in_container(container) {
if (typeof mermaid === 'undefined' || !container) return;
// Convert any pre code.language-mermaid elements into .div-diagram-mermaid containers
const codeNodes = Array.from(container.querySelectorAll('pre code.language-mermaid:not([data-processed="true"])'));
for (const codeNode of codeNodes) {
codeNode.setAttribute('data-processed', 'true');
const pre = codeNode.parentElement;
if (!pre) continue;
const rawCode = codeNode.textContent || '';
const div = document.createElement('div');
div.className = 'div-diagram-mermaid';
div.setAttribute('data-mermaid', rawCode.trim());
div.textContent = rawCode.trim();
pre.parentNode.replaceChild(div, pre);
}
const mermaidNodes = Array.from(container.querySelectorAll('.mermaid:not([data-processed="true"]), .div-diagram-mermaid:not([data-processed="true"])'))
.filter(node => !node.querySelector('svg') && (node.getAttribute('data-mermaid') || node.textContent || '').trim().length > 0);
for (let i = 0; i < mermaidNodes.length; i++) {
const node = mermaidNodes[i];
node.setAttribute('data-processed', 'true');
const rawCode = node.getAttribute('data-mermaid') || node.textContent || '';
const code = rawCode.trim();
if (!code) continue;
try {
await render_mermaid_svg(node, code);
} catch (e) {
console.warn('Mermaid render error for diagram:', e);
node.innerHTML = `<pre class="code-block language-mermaid sb-x" data-simplebar data-simplebar-auto-hide="false"><code>${escapeHtml(code)}</code></pre>`;
attach_diagram_expand_button(node, code);
}
}
}
/**
* Renders Mermaid code into a node and inserts the resulting SVG, retrying
* once with automatic syntax repairs. Throws the render error when the
* diagram still cannot be rendered.
* @param {HTMLElement} node
* @param {string} code
*/
export async function render_mermaid_svg(node, code) {
const { svg, bind_functions, error } = await render_diagram_svg(code);
if (!svg) throw error;
node.innerHTML = '';
node.appendChild(svg);
attach_diagram_expand_button(node, code);
if (typeof bind_functions === 'function') {
bind_functions(node);
}
}
/**
* Renders Mermaid code to an SVG element, retrying once with automatic
* syntax repairs applied. Mermaid's temporary render elements are cleaned
* up between attempts.
* @param {string} code - The mermaid source code.
* @returns {Promise<{svg: SVGElement|null, bind_functions: function(Element): void|null, error: string}>}
*/
export async function render_diagram_svg(code) {
const attempts = [code];
const repaired = repair_mermaid_code(code);
if (repaired.code !== code) attempts.push(repaired.code);
let error = null;
for (const attempt of attempts) {
const id = 'mermaid_' + Date.now() + '_' + Math.random().toString(36).substring(2, 7);
try {
const result = /** @type {{svg?: string, bindFunctions?: function(Element): void}} */ (await mermaid.render(id, attempt));
const holder = document.createElement('div');
holder.innerHTML = result?.svg || '';
const svg = holder.querySelector('svg');
if (svg) {
return { svg, bind_functions: result.bindFunctions || null, error: null };
}
} catch (e) {
error = e;
} finally {
const errEl = document.getElementById(id) || document.getElementById('d' + id);
if (errEl) errEl.remove();
}
}
return { svg: null, bind_functions: null, error: error ? String(error?.message || error) : 'Diagram could not be rendered.' };
}
/**
* Adds (or refreshes) the expand button on a diagram container, placed in
* the container's top right corner. With a rendered SVG it opens the
* diagram overlay; without one (code fallback) clicking retries the
* render and shows the render error in the overlay when the diagram still
* cannot be drawn.
* @param {HTMLElement} node - The diagram container.
* @param {string} code - The mermaid source used to retry rendering.
*/
export function attach_diagram_expand_button(node, code = '') {
const svg = node.querySelector('svg');
if (!svg && !code) return;
let btn = node.querySelector('.div-diagram-expand-btn');
if (!btn) {
btn = document.createElement('button');
btn.type = 'button';
btn.className = 'div-diagram-expand-btn as-icon';
btn.title = 'Expand Diagram';
btn.textContent = '🔍';
node.appendChild(btn);
}
btn.onclick = async () => {
if (svg) {
open_diagram_overlay(svg);
return;
}
const { svg: overlay_svg, error } = await render_diagram_svg(code);
if (overlay_svg) {
open_diagram_overlay(overlay_svg);
} else {
open_diagram_overlay_message(error);
}
};
}
mermaid-render.js
×
Type: Web, text/x-java
5.10 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File
Type: Web, text/x-java
5.10 Kilobytes
Last Modified 2026-11-04 09:48:28
⬇ Download File