From eec8b477befb1b737bdb54ef41c765eb2dcddff5 Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Wed, 22 Apr 2026 12:06:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AF=A6=E6=83=85=E5=BC=B9=E6=A1=86?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E6=94=AF=E6=8C=81Markdown=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xian_favor/api.py | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/xian_favor/api.py b/xian_favor/api.py index 362af27..fef0c2b 100644 --- a/xian_favor/api.py +++ b/xian_favor/api.py @@ -1074,6 +1074,16 @@ INDEX_TEMPLATE = ''' .type-todo { border-left: 4px solid #ffc107; } .is-starred { border-left: 4px solid #ffc107; background: #fffbe6; } .is-starred:hover { background: #fff9e0; } + /* Markdown内容样式 */ + .markdown-content { line-height: 1.6; } + .markdown-content h3 { font-size: 1.2em; margin-bottom: 0.5em; } + .markdown-content h4 { font-size: 1.1em; margin-bottom: 0.4em; } + .markdown-content h5 { font-size: 1.0em; margin-bottom: 0.3em; } + .markdown-content h6 { font-size: 0.9em; margin-bottom: 0.2em; } + .markdown-content code { font-size: 0.9em; } + .markdown-content pre { font-size: 0.85em; overflow-x: auto; } + .markdown-content ul, .markdown-content ol { margin-bottom: 0.5em; } + .markdown-content a { color: #0d6efd; } .star-btn { font-size: 11px; } .status-pending { color: #ffc107; } .status-in_progress { color: #17a2b8; } @@ -2681,7 +2691,7 @@ async function showDetail(id) { } if (item.content) { - html += `
内容:
${escapeHtml(item.content)}
`; + html += `
内容:
${renderMarkdown(item.content)}
`; } if (item.source) { @@ -2701,7 +2711,7 @@ async function showDetail(id) { } if (item.note) { - html += `
详情/备注:
${escapeHtml(item.note)}
`; + html += `
详情/备注:
${renderMarkdown(item.note)}
`; } html += `
创建时间: ${formatDate(item.created_at)}
更新时间: ${formatDate(item.updated_at)}
`; @@ -3032,6 +3042,55 @@ function escapeHtml(str) { return str.replace(/&/g, '&').replace(//g, '>'); } +// 简单Markdown渲染(支持标题、粗体、斜体、代码、链接、列表) +function renderMarkdown(text) { + if (!text) return ''; + + // 先转义HTML + let html = escapeHtml(text); + + // 代码块 ```code``` + html = html.replace(/```(\w*)\n([\s\S]*?)```/g, '
$2
'); + + // 行内代码 `code` + html = html.replace(/`([^`]+)`/g, '$1'); + + // 标题 # ## ### #### + html = html.replace(/^#### (.+)$/gm, '
$1
'); + html = html.replace(/^### (.+)$/gm, '
$1
'); + html = html.replace(/^## (.+)$/gm, '

$1

'); + html = html.replace(/^# (.+)$/gm, '

$1

'); + + // 粗体 **text** + html = html.replace(/\*\*([^*]+)\*\*/g, '$1'); + + // 斜体 *text* + html = html.replace(/\*([^*]+)\*/g, '$1'); + + // 链接 [text](url) + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); + + // 无序列表 - item + html = html.replace(/^- (.+)$/gm, '
  • $1
  • '); + html = html.replace(/(\n?)+/g, ''); + + // 有序列表 1. item + html = html.replace(/^\d+\. (.+)$/gm, '
  • $1
  • '); + + // 段落分隔(两个换行) + html = html.replace(/\n\n/g, '

    '); + + // 单换行 + html = html.replace(/\n/g, '
    '); + + // 包装在段落中 + if (!html.startsWith('<')) { + html = '

    ' + html + '

    '; + } + + return html; +} + // 标签管理 let allTags = [];