import DOMPurify from 'dompurify' import { marked } from 'marked' import { createHighlighterCore } from 'shiki/core' import { createJavaScriptRegexEngine } from '@shikijs/engine-javascript' import css from '@shikijs/langs/css' import html from '@shikijs/langs/html' import javascript from '@shikijs/langs/javascript' import json from '@shikijs/langs/json' import markdown from '@shikijs/langs/markdown' import python from '@shikijs/langs/python' import shell from '@shikijs/langs/shellscript' import sql from '@shikijs/langs/sql' import typescript from '@shikijs/langs/typescript' import githubDark from '@shikijs/themes/github-dark' import githubLight from '@shikijs/themes/github-light' marked.setOptions({ gfm: true, breaks: true }) // Highlighter 是昂贵的单例;复用初始化 Promise,避免每个代码块重复加载语法与主题。 const highlighter = createHighlighterCore({ themes: [githubLight, githubDark], langs: [markdown, html, css, javascript, typescript, json, python, shell, sql], engine: createJavaScriptRegexEngine(), }) const languageAliases: Record = { bash: 'shell', js: 'javascript', md: 'markdown', plaintext: 'text', py: 'python', sh: 'shell', ts: 'typescript', } export async function highlightCode(source: string, requestedLanguage = 'text'): Promise { const shiki = await highlighter const language = languageAliases[requestedLanguage] ?? requestedLanguage const loadedLanguage = shiki.getLoadedLanguages().includes(language as never) ? language : 'markdown' return shiki.codeToHtml(source, { lang: loadedLanguage, themes: { light: 'github-light', dark: 'github-dark' }, defaultColor: false, }) } export async function renderMarkdown(source: string): Promise { const html = marked.parse(source, { async: false }) as string const documentNode = new DOMParser().parseFromString(`${html}`, 'text/html') for (const code of documentNode.querySelectorAll('pre > code')) { const requestedLanguage = [...code.classList].find((name) => name.startsWith('language-'))?.slice(9) || 'text' const highlighted = await highlightCode(code.textContent ?? '', requestedLanguage) const fragment = document.createRange().createContextualFragment(highlighted) code.parentElement?.replaceWith(fragment) } // Markdown 可能来自模型或外部笔记,高亮完成后仍必须在最终出口统一净化。 return DOMPurify.sanitize(documentNode.body.innerHTML, { USE_PROFILES: { html: true } }) } // TODO(performance): 编辑器首屏稳定后评估将 Shiki 延迟加载或迁移到 Web Worker。