|
|
|
|
|
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 } |