File size: 1,800 Bytes
309320b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 一个小型且安全的 Markdown -> HTML 解析器(支持标题、段落、链接、粗体、斜体、行内代码)
function escapeHtml(str) {
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}

function parseInline(md) {
  // code
  md = md.replace(/`([^`]+)`/g, '<code>$1</code>')
  // bold **text** or __text__
  md = md.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
  md = md.replace(/__([^_]+)__/g, '<strong>$1</strong>')
  // italic *text* or _text_
  md = md.replace(/\*([^*]+)\*/g, '<em>$1</em>')
  md = md.replace(/_([^_]+)_/g, '<em>$1</em>')
  // links [text](url)
  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 || ''
  // split into lines and paragraphs
  const blocks = raw.split(/\n{2,}/g)
  const html = blocks.map(block => {
    const line = block.trim()
    if (!line) return ''
    // heading
    const hMatch = line.match(/^(#{1,6})\s+(.*)$/)
    if (hMatch) {
      const level = Math.min(6, hMatch[1].length)
      const content = parseInline(escapeHtml(hMatch[2]))
      // 增加块间距(mb-2)以扩大行之间的视觉间隔
      return `<h${level} class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2">${content}</h${level}>`
    }
    // normal paragraph
    // 使用 leading-relaxed 增加行高,并用 mb-2 增加段落间距
    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 }