import MarkdownIt from "markdown-it"; import hljs from "highlight.js"; import mila from "markdown-it-link-attributes"; import mdKatex from "@traptitech/markdown-it-katex"; /** * Markdown */ const MarkdownUtils = { mdi: null, mds: null, /** * 解析Markdown * @param {*} text * @returns */ formatMsg: (text) => { const array = text.match(/]*?>/g); if (array) { array.some(res => { text = text.replace(res, `
${res}
`); }) } return text }, /** * 高亮代码块 * @param {*} str * @param {*} lang * @returns */ highlightBlock: (str, lang = '') => { return `
${lang}${$A.L('复制')}
${str}
` }, } const MarkdownPluginUtils = { // 配置选项 config: { maxTitleLength: 200, maxDescLength: 1000, maxItems: 200, defaultTitle: '创建任务' }, // HTML转义函数 escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }, // 验证输入 validateInput(value, maxLength) { if (!value) return ''; if (typeof value !== 'string') return ''; if (value.length > maxLength) { return value.substring(0, maxLength) + '...'; } return value; }, // 解析任务项 parseTaskItems(content) { const items = []; let currentItem = {}; let itemCount = 0; content.forEach(line => { line = line.trim(); if (!line) { if (Object.keys(currentItem).length > 0) { items.push(currentItem); currentItem = {}; itemCount++; } return; } if (itemCount >= this.config.maxItems) { return; } const [key, ...valueParts] = line.split(':'); const value = valueParts.join(':').trim(); if (key === 'title' && value) { if (Object.keys(currentItem).length > 0) { items.push(currentItem); currentItem = {}; itemCount++; } currentItem.title = this.validateInput(value, this.config.maxTitleLength); } else if (key === 'desc' && value) { currentItem.desc = this.validateInput(value, this.config.maxDescLength); } }); if (Object.keys(currentItem).length > 0 && itemCount < this.config.maxItems) { items.push(currentItem); } return items; }, // 生成HTML generateTaskHtml(items, status = null) { if (!Array.isArray(items) || items.length === 0) { return ''; } const html = [ '
', '', `
${this.escapeHtml($A.L(this.config.defaultTitle))}
`, '
' ); return html.join('\n'); }, // 修改初始化插件函数 initCreateTaskPlugin(md) { md.block.ruler.before('fence', 'create_task', (state, startLine, endLine, silent) => { try { const start = state.bMarks[startLine] + state.tShift[startLine]; const max = state.eMarks[startLine]; const match = state.src.slice(start, max).trim().match(/^```\s*CreateTask\s*(applying|applied)?$/); if (!match) { return false; } if (silent) { return true; } let nextLine = startLine + 1; let content = []; let found = false; while (nextLine < endLine) { const line = state.src.slice(state.bMarks[nextLine], state.eMarks[nextLine]).trim(); if (line === '```') { found = true; break; } content.push(line); nextLine++; } if (!found) { return false; } // 创建 token 并设置为空字符串内容 const token = state.push('html_block', '', 0); // 如果有内容,则解析并生成HTML if (content.length > 0) { const items = this.parseTaskItems(content); const html = this.generateTaskHtml(items, match[1]); token.content = html || ''; } else { token.content = ''; // 空内容直接返回空字符串 } token.map = [startLine, nextLine + 1]; state.line = nextLine + 1; return true; } catch (error) { console.error('Error in create_task parser:', error); return false; } }); } }; export function MarkdownConver(text) { if (text === '...') { return '' } if (MarkdownUtils.mdi === null) { MarkdownUtils.mdi = new MarkdownIt({ linkify: true, highlight(code, language) { const validLang = !!(language && hljs.getLanguage(language)) if (validLang) { const lang = language ?? '' return MarkdownUtils.highlightBlock(hljs.highlight(code, {language: lang}).value, lang) } return MarkdownUtils.highlightBlock(hljs.highlightAuto(code).value, '') }, }) MarkdownUtils.mdi.use(mila, {attrs: {target: '_blank', rel: 'noopener'}}) MarkdownUtils.mdi.use(mdKatex, {blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000'}) MarkdownPluginUtils.initCreateTaskPlugin(MarkdownUtils.mdi); } return MarkdownUtils.formatMsg(MarkdownUtils.mdi.render(text)) } export function MarkdownPreview(text) { if (MarkdownUtils.mds === null) { MarkdownUtils.mds = MarkdownIt() } return MarkdownUtils.mds.render(text) } export function isMarkdownFormat(html) { if (html === '') { return false } const tmp = html.replace(/

/g, '\n').replace(/(^|\s+)```([\s\S]*)```/gm, '') if (/<\/(strong|s|em|u|ol|ul|li|blockquote|pre|img|a)>/i.test(tmp)) { return false } if (/]+?class="mention"[^>]*?>/i.test(tmp)) { return false } // const el = document.createElement('div') el.style.position = 'fixed' el.style.top = '0' el.style.left = '0' el.style.width = '10px' el.style.height = '10px' el.style.overflow = 'hidden' el.style.zIndex = '-9999' el.style.opacity = '0' el.innerHTML = html document.body.appendChild(el) const text = el.innerText document.body.removeChild(el) // if ( /(^|\s+)#+\s(.*)$/m.test(text) // 标题 || /(^|\s+)\*\*(.*)\*\*/m.test(text) // 粗体 || /(^|\s+)__(.*)__/m.test(text) // 粗体 || /(^|\s+)\*(.*)\*/m.test(text) // 斜体 || /(^|\s+)_(.*)_/m.test(text) // 斜体 || /(^|\s+)~~(.*)~~/m.test(text) // 删除线 || /(^|\s+)\[(.*?)\]\((.*?)\)/m.test(text) // 链接 || /(^|\s+)!\[(.*?)\]\((.*?)\)/m.test(text) // 图片 || /(^|\s+)`(.*?)`/m.test(text) // 行内代码 || /(^|\s+)```([\s\S]*?)```/m.test(text) // 代码块 ) { return true } return false }