Spaces:
Sleeping
Sleeping
| function escapeHtml(text) { | |
| return text | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/"/g, '"') | |
| .replace(/'/g, '''); | |
| } | |
| export function parseMarkdown(text) { | |
| if (!text) return ''; | |
| // Escape HTML first to prevent XSS | |
| let html = escapeHtml(text); | |
| // Code blocks (``` ... ```) | |
| html = html.replace(/```(\w*)\n([\s\S]*?)```/g, (match, lang, code) => { | |
| const cls = lang ? ` class="language-${lang}"` : ''; | |
| return `<pre><code${cls}>${code.trim()}</code></pre>`; | |
| }); | |
| // Inline code | |
| html = html.replace(/`([^`\n]+)`/g, '<code>$1</code>'); | |
| // Bold | |
| html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>'); | |
| // Italic (avoid matching bold remnants) | |
| html = html.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '<em>$1</em>'); | |
| html = html.replace(/(?<!_)_([^_\n]+)_(?!_)/g, '<em>$1</em>'); | |
| // Blockquotes (lines starting with >) | |
| html = html.replace(/^>\s?(.+)$/gm, '<blockquote>$1</blockquote>'); | |
| // @mentions | |
| html = html.replace(/@(\w+)/g, '<span class="text-[#FFD700] font-semibold">@$1</span>'); | |
| // URLs — match http/https links | |
| html = html.replace( | |
| /(?<!="|'|")(https?:\/\/[^\s<]+)/g, | |
| '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>' | |
| ); | |
| // Line breaks | |
| html = html.replace(/\n/g, '<br />'); | |
| // Merge consecutive blockquotes | |
| html = html.replace(/<\/blockquote><br \/><blockquote>/g, '<br />'); | |
| return html; | |
| } | |