/** * @fileoverview Markdown Rendering Wrapper for Rantii * @author retoor * @description Markdown parsing and rendering with syntax highlighting * @keywords markdown, parsing, syntax, highlight, render */ class MarkdownRenderer { constructor() { this.marked = null; this.hljs = null; this.initialized = false; } async init() { if (this.initialized) { return true; } try { if (window.marked) { this.marked = window.marked; } if (window.hljs) { this.hljs = window.hljs; } if (this.marked && this.hljs) { this.marked.setOptions({ highlight: (code, lang) => { if (lang && this.hljs.getLanguage(lang)) { try { return this.hljs.highlight(code, { language: lang }).value; } catch (e) { return code; } } return this.hljs.highlightAuto(code).value; }, breaks: true, gfm: true }); } else if (this.marked) { this.marked.setOptions({ breaks: true, gfm: true }); } this.initialized = true; return true; } catch (e) { return false; } } render(text) { if (!text) { return ''; } if (this.marked) { try { return this.marked.parse(text); } catch (e) { return this.escapeHtml(text); } } return this.simpleRender(text); } simpleRender(text) { let result = this.escapeHtml(text); result = result.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { const highlighted = this.highlightCode(code.trim(), lang); return `
${highlighted}
`; }); result = result.replace(/`([^`]+)`/g, '$1'); result = result.replace(/\*\*([^*]+)\*\*/g, '$1'); result = result.replace(/\*([^*]+)\*/g, '$1'); result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); result = result.replace(/\n/g, '
'); return result; } highlightCode(code, lang) { if (this.hljs && lang && this.hljs.getLanguage(lang)) { try { return this.hljs.highlight(code, { language: lang }).value; } catch (e) { return this.escapeHtml(code); } } if (this.hljs) { try { return this.hljs.highlightAuto(code).value; } catch (e) { return this.escapeHtml(code); } } return this.escapeHtml(code); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } renderInline(text) { if (!text) { return ''; } let result = this.escapeHtml(text); result = result.replace(/`([^`]+)`/g, '$1'); result = result.replace(/\*\*([^*]+)\*\*/g, '$1'); result = result.replace(/\*([^*]+)\*/g, '$1'); return result; } stripMarkdown(text) { if (!text) { return ''; } let result = text; result = result.replace(/```[\s\S]*?```/g, ''); result = result.replace(/`([^`]+)`/g, '$1'); result = result.replace(/\*\*([^*]+)\*\*/g, '$1'); result = result.replace(/\*([^*]+)\*/g, '$1'); result = result.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1'); return result; } } const markdownRenderer = new MarkdownRenderer(); export { MarkdownRenderer, markdownRenderer };