| | |
| | function escapeHtml(str) { |
| | return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') |
| | } |
| |
|
| | function parseInline(md) { |
| | |
| | md = md.replace(/`([^`]+)`/g, '<code>$1</code>') |
| | |
| | md = md.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>') |
| | md = md.replace(/__([^_]+)__/g, '<strong>$1</strong>') |
| | |
| | md = md.replace(/\*([^*]+)\*/g, '<em>$1</em>') |
| | md = md.replace(/_([^_]+)_/g, '<em>$1</em>') |
| | |
| | md = md.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (m, text, url) => { |
| | const safeUrl = url.replace(/\"/g, '%22') |
| | return `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer" class="text-blue-600 underline dark:text-blue-400">${text}</a>` |
| | }) |
| | return md |
| | } |
| |
|
| | function parsedHeaderHtml(headerMarkdown){ |
| | const raw = headerMarkdown || '' |
| | |
| | const blocks = raw.split(/\n{2,}/g) |
| | const html = blocks.map(block => { |
| | const line = block.trim() |
| | if (!line) return '' |
| | |
| | const hMatch = line.match(/^(#{1,6})\s+(.*)$/) |
| | if (hMatch) { |
| | const level = Math.min(6, hMatch[1].length) |
| | const content = parseInline(escapeHtml(hMatch[2])) |
| | |
| | return `<h${level} class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2">${content}</h${level}>` |
| | } |
| | |
| | |
| | return `<p class="text-sm text-gray-700 dark:text-gray-200 leading-relaxed mb-2">${parseInline(escapeHtml(line))}</p>` |
| | }).join('\n') |
| | return html |
| | } |
| |
|
| | export { parsedHeaderHtml } |