From d6806b2757337bbec46b728db8bbba8b66159c3c Mon Sep 17 00:00:00 2001 From: tianyaxiang Date: Sat, 1 Feb 2025 14:23:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A8=A1=E7=89=88=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/wechat-templates.ts | 2 +- src/lib/markdown.ts | 91 ++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/config/wechat-templates.ts b/src/config/wechat-templates.ts index 0e688d8..a621e5f 100644 --- a/src/config/wechat-templates.ts +++ b/src/config/wechat-templates.ts @@ -20,7 +20,7 @@ export const templates: Template[] = [ themeColor: '#166534', fontFamily: '-apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif', textAlign: 'left', - lineHeight: '1.75', + lineHeight: '2', padding: '1rem 1.5rem', maxWidth: '100%', margin: '0 auto', diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index c101a02..cb73781 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -38,23 +38,27 @@ function preprocessMarkdown(markdown: string): string { } // Initialize marked instance -const marked = new Marked() - -// 创建基础渲染器 -const baseRenderer = new marked.Renderer() - -// 重写 strong 渲染器 -baseRenderer.strong = function(text) { - return `${text}` -} - -// 应用配置和渲染器 -marked.setOptions({ +const marked = new Marked({ gfm: true, breaks: true, async: false, - pedantic: false, - renderer: baseRenderer + pedantic: false +}) + +// 应用配置 +marked.use({ + breaks: true, + gfm: true, + walkTokens(token: Tokens.Generic) { + // 确保列表项被正确处理 + if (token.type === 'list') { + (token as Tokens.List).items.forEach(item => { + if (item.task) { + item.checked = !!item.checked + } + }) + } + } }) const defaultOptions: RendererOptions = { @@ -83,9 +87,6 @@ const defaultOptions: RendererOptions = { export function convertToWechat(markdown: string, options: RendererOptions = defaultOptions): string { const renderer = new marked.Renderer() - // 继承基础渲染器 - Object.setPrototypeOf(renderer, baseRenderer) - // 合并选项 const mergedOptions = { base: { ...defaultOptions.base, ...options.base }, @@ -156,14 +157,28 @@ export function convertToWechat(markdown: string, options: RendererOptions = def } // 重写 list 方法 -renderer.list = function(token: Tokens.List): string { +renderer.list = function(token: Tokens.List) { const tag = token.ordered ? 'ol' : 'ul' try { - const style = mergedOptions.block?.[token.ordered ? 'ol' : 'ul'] + const style = { + ...(mergedOptions.block?.[token.ordered ? 'ol' : 'ul'] || {}), + listStyle: token.ordered ? 'decimal' : 'disc', + paddingLeft: '2em', + marginBottom: '16px' + } const styleStr = cssPropertiesToString(style) const startAttr = token.ordered && token.start !== 1 ? ` start="${token.start}"` : '' - return `<${tag}${startAttr}${styleStr ? ` style="${styleStr}"` : ''}>${token.items.map(item => renderer.listitem(item)).join('')}` + const items = token.items.map(item => { + let itemText = item.text + if (item.task) { + const checkbox = ` ` + itemText = checkbox + itemText + } + return renderer.listitem({ ...item, text: itemText }) + }).join('') + + return `<${tag}${startAttr}${styleStr ? ` style="${styleStr}"` : ''}>${items}` } catch (error) { console.error(`Error rendering list: ${error}`) return `<${tag}>${token.items.map(item => renderer.listitem(item)).join('')}` @@ -173,28 +188,38 @@ renderer.list = function(token: Tokens.List): string { // 重写 listitem 方法 renderer.listitem = function(item: Tokens.ListItem) { try { - const style = mergedOptions.inline?.listitem + const style = { + ...(mergedOptions.inline?.listitem || {}), + marginBottom: '8px', + display: 'list-item' + } const styleStr = cssPropertiesToString(style) - // 移除列表项开头的破折号和空格 - let itemText = item.text.replace(/^- /, '') - - // 处理任务列表项 - if (item.task) { - const checkbox = ` ` - itemText = checkbox + itemText + // 处理嵌套列表 + let content = item.text + if (item.tokens) { + content = item.tokens.map(token => { + if (token.type === 'list') { + // 递归处理嵌套列表 + return renderer.list(token as Tokens.List) + } else { + // 处理其他类型的 token + const tokens = marked.Lexer.lexInline(token.raw) + return marked.Parser.parseInline(tokens, { renderer }) + } + }).join('') + } else { + // 如果没有 tokens,则按普通文本处理 + const tokens = marked.Lexer.lexInline(content) + content = marked.Parser.parseInline(tokens, { renderer }) } - // 使用 Lexer 和 Parser 处理剩余的内联标记 - const tokens = marked.Lexer.lexInline(itemText) - const content = marked.Parser.parseInline(tokens, { renderer }) - return `${content}` } catch (error) { console.error(`Error rendering list item: ${error}`) return `
  • ${item.text}
  • ` } - } +} // Convert Markdown to HTML using the custom renderer const html = marked.parse(markdown, { renderer }) as string