\s*
\s*]*class="[^"]*(?:language-plantuml|plantuml)[^"]*"[^>]*>([\s\S]*?)<\/code>\s*<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
try {
// Unescape HTML for PlantUML server, but escape for
const rawContent = decodeHTMLEntities(content);
const encoded = plantumlEncoder.encode(rawContent);
const plantUMLUrl = `https://www.plantuml.com/plantuml/svg/${encoded}`;
return `
Show PlantUML source
${escapeHtml(rawContent)}
`;
} catch (error) {
console.warn("Failed to process PlantUML block:", error);
return match;
}
},
);
// Fallback: match blocks whose content starts with @startuml or @start (global, robust)
html = html.replace(
/\s*
\s*
([\s\S]*?)<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
const lines = content.trim().split("\n");
if (
lines[0].trim().startsWith("@startuml") ||
lines[0].trim().startsWith("@start")
) {
try {
const rawContent = decodeHTMLEntities(content);
const encoded = plantumlEncoder.encode(rawContent);
const plantUMLUrl = `https://www.plantuml.com/plantuml/svg/${encoded}`;
return `
Show PlantUML source
${escapeHtml(rawContent)}
`;
} catch (error) {
console.warn("Failed to process PlantUML fallback block:", error);
return match;
}
}
return match;
},
);
return html;
}
function decodeHTMLEntities(text: string): string {
const textarea = document.createElement("textarea");
textarea.innerHTML = text;
return textarea.value;
}
/**
* Processes BPMN blocks in HTML content
*/
function processBPMNBlocks(html: string): string {
// Only match code blocks with class 'language-bpmn' or 'bpmn'
html = html.replace(
/\s*
\s*
\s*]*class="[^"]*(?:language-bpmn|bpmn)[^\"]*"[^>]*>([\s\S]*?)<\/code>\s*<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
try {
return `
Show BPMN source
${escapeHtml(content)}
`;
} catch (error) {
console.warn("Failed to process BPMN block:", error);
return match;
}
},
);
// Fallback: match blocks whose content contains 'bpmn:' or '\s*\s*
([\s\S]*?)<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
const text = content.trim();
if (
text.includes("bpmn:") ||
(text.startsWith("
Show BPMN source
${escapeHtml(content)}
`;
} catch (error) {
console.warn("Failed to process BPMN fallback block:", error);
return match;
}
}
return match;
},
);
return html;
}
/**
* Processes TikZ blocks in HTML content
*/
function processTikZBlocks(html: string): string {
// Only match code blocks with class 'language-tikz' or 'tikz'
html = html.replace(
/\s*
\s*
\s*]*class="[^"]*(?:language-tikz|tikz)[^"]*"[^>]*>([\s\S]*?)<\/code>\s*<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
try {
return `
Show TikZ source
${escapeHtml(content)}
`;
} catch (error) {
console.warn("Failed to process TikZ block:", error);
return match;
}
},
);
// Fallback: match blocks whose content starts with \begin{tikzpicture} or contains tikz
html = html.replace(
/\s*
\s*
([\s\S]*?)<\/pre>\s*<\/div>\s*<\/div>/g,
(match, content) => {
const lines = content.trim().split("\n");
if (
lines[0].trim().startsWith("\\begin{tikzpicture}") ||
content.includes("tikz")
) {
try {
return `
Show TikZ source
${escapeHtml(content)}
`;
} catch (error) {
console.warn("Failed to process TikZ fallback block:", error);
return match;
}
}
return match;
},
);
return html;
}
/**
* Escapes HTML characters for safe display
*/
function escapeHtml(text: string): string {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}