/* ELYSIA MARKDOWN STUDIO v1.0 - Preview Module Real-time Markdown preview with extensions */ import Utils from "./utils.js"; const Preview = { container: null, renderer: null, init() { this.container = document.getElementById("markdown-preview"); this.setupMarked(); this.setupMermaid(); }, setupMarked() { // Configure marked.js v15+ (async API) marked.setOptions({ breaks: true, gfm: true, // GitHub Flavored Markdown headerIds: true }); // Custom renderer for task lists (Marked.js v15+ token-based API) const renderer = { listitem(token) { // In v15+, token has 'task' and 'checked' properties if (token.task) { return `
${highlighted}\n`;
}
return `${code}\n`;
}
};
marked.use({ renderer });
},
setupMermaid() {
if (window.mermaid) {
mermaid.initialize({
startOnLoad: false,
theme: "dark",
securityLevel: "loose"
});
}
},
update() {
const content = window.app?.editor.getContent() || "";
this.render(content);
},
async render(markdown) {
if (!markdown) {
this.container.innerHTML = 'Start writing to see preview...
'; return; } try { // Convert markdown to HTML (v15+ can be async) let html = await marked.parse(markdown); // Ensure html is a string, not an object if (typeof html !== "string") { console.error("Marked.parse returned non-string:", typeof html, html); html = String(html); } // Process KaTeX math html = this.processKaTeX(html); // Update container this.container.innerHTML = html; // Render mermaid diagrams this.renderMermaid(); // Highlight code blocks this.highlightCode(); } catch (err) { console.error("Preview render failed:", err); this.container.innerHTML = `Preview error: ${err.message}
`; } }, processKaTeX(html) { if (!window.katex) return html; // Inline math: $...$ html = html.replace(/\$([^\$]+)\$/g, (match, math) => { try { return katex.renderToString(math, { throwOnError: false }); } catch { return match; } }); // Block math: $$...$$ html = html.replace(/\$\$([^\$]+)\$\$/g, (match, math) => { try { return katex.renderToString(math, { throwOnError: false, displayMode: true }); } catch { return match; } }); return html; }, renderMermaid() { if (!window.mermaid) return; const mermaidBlocks = this.container.querySelectorAll("code.language-mermaid"); mermaidBlocks.forEach((block, index) => { const code = block.textContent; const id = `mermaid-${Date.now()}-${index}`; const div = document.createElement("div"); div.id = id; div.className = "mermaid"; div.textContent = code; block.parentElement.replaceWith(div); try { mermaid.init(undefined, `#${id}`); } catch (err) { console.warn("Mermaid render failed:", err); } }); }, highlightCode() { if (!window.Prism) return; this.container.querySelectorAll("pre code").forEach(block => { if (!block.classList.contains("language-mermaid")) { Prism.highlightElement(block); } }); }, // Get current HTML (for export) getHTML() { if (!this.container) { console.warn("Preview container not initialized"); return ""; } return this.container.innerHTML; }, // Apply theme setTheme(theme) { if (!this.container) { console.warn("Preview container not initialized yet"); return; } if (theme === "github") { this.container.classList.add("theme-github"); this.container.classList.remove("theme-elysia"); } else { this.container.classList.add("theme-elysia"); this.container.classList.remove("theme-github"); } } }; export default Preview;