From 0ff68bea991bc976e64f95cb9de44ac0875328d1 Mon Sep 17 00:00:00 2001 From: wux_labs Date: Tue, 7 Jan 2025 22:53:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E6=A1=A3=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/UAIEditor.ts | 3 +- src/utils/MarkdownUtil.ts | 103 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 src/utils/MarkdownUtil.ts diff --git a/src/core/UAIEditor.ts b/src/core/UAIEditor.ts index a826354..8552ebb 100644 --- a/src/core/UAIEditor.ts +++ b/src/core/UAIEditor.ts @@ -24,6 +24,7 @@ import i18next from "i18next"; import { zh } from "../i18n/zh.ts"; import { Resource } from "i18next"; import { allExtensions } from "./UAIExtensions.ts"; +import { markdownToHtml } from "../utils/MarkdownUtil.ts"; import { Uploader } from "../utils/FileUploader.ts"; self.MonacoEnvironment = { @@ -307,7 +308,7 @@ export class UAIEditor { this.innerEditor = new InnerEditor(this, { element: this.editor.editorContainer, - content, + content: markdownToHtml(content ?? ""), extensions: allExtensions(this, this.options), onCreate: (props) => this.onCreate(props), onTransaction: (props) => this.onTransaction(props), diff --git a/src/utils/MarkdownUtil.ts b/src/utils/MarkdownUtil.ts new file mode 100644 index 0000000..4dcd9c1 --- /dev/null +++ b/src/utils/MarkdownUtil.ts @@ -0,0 +1,103 @@ +// Copyright (c) 2024-present AI-Labs + +import markdownItKatex from '@vscode/markdown-it-katex'; +import MarkdownIt from 'markdown-it'; +import container from 'markdown-it-container'; + +import TurndownService from 'turndown'; + +import hljs from 'highlight.js'; + +const md = MarkdownIt({ + html: true, + linkify: true, + typographer: true +}); + + +const containerClasses = ['info', 'tip', 'warning', 'danger']; +containerClasses.forEach(name => { + md.use(container, name, { + validate: function (params: any) { + params = params.trim() + if (params === '') return true; + return params.indexOf(name) >= 0; + }, + render: function (tokens: any, idx: any) { + if (tokens[idx].nesting === 1) { + return `
`; + } else { + return '
'; + } + } + }) +}) + +md.use(markdownItKatex, { + // 这里可以设置katex的选项 +}); + +// @ts-ignore +// 添加一个自定义规则来处理代码块,并使用 highlight.js 进行高亮 +md.renderer.rules.fence = (tokens, idx, options, env, self) => { + const token = tokens[idx] + const language = token.info.trim() + const content = token.content + const head = `
` + const tail = "
" + if (language && hljs.getLanguage(language)) { + return `${head}
${hljs.highlight(content, { language }).value}
${tail}` + } else { + return `
${md.utils.escapeHtml(content)}
` + } +} + +//options https://github.com/mixmark-io/turndown?tab=readme-ov-file#options +const turndownService = new TurndownService({ + headingStyle: 'atx', + hr: '---', + bulletListMarker: '-', + codeBlockStyle: 'fenced', + fence: '```', + emDelimiter: '_', + strongDelimiter: '**', + linkStyle: 'inlined', + linkReferenceStyle: 'full', + preformattedCode: false, +}); + +turndownService.keep((node: any) => { + return !(node && node.nodeName === "DIV"); +}); + +export const markdownToHtml = (markdown: string) => { + if (!markdown) return markdown; + const renderHtml = md.render(markdown).trim(); + if (!renderHtml) return markdown; + const parser = new DOMParser(); + const doc = parser.parseFromString(renderHtml, 'text/html'); + const lis = doc.querySelectorAll("li"); + + //"tiptap" does not support empty list items. Here to fill in the gaps + if (lis) lis.forEach(li => { + if (!li.innerHTML) li.innerHTML = "

" + }) + + let html = ''; + for (let i = 0; i < doc.body.children.length; i++) { + const element = doc.body.children[i]; + if (i == 0 && element.tagName === "P") { + html += element.innerHTML; + } else { + html += element.querySelector("img") + ? element.innerHTML : element.outerHTML; + } + } + return html; +} + + +export const htmlToMarkdown = (html: string) => { + if (!html) return html; + return turndownService.turndown(html) +}