import {JSDOM} from 'jsdom' import {createHighlighter} from 'shiki' const DEFAULT_THEME = 'github-dark-dimmed' const highlighter = await createHighlighter({ themes: [DEFAULT_THEME], langs: [ "bash", "json", "yaml", "xml", "html", "css", "scss", "javascript", "typescript", "jsx", "tsx", "vue", "markdown", "sql", "python", "go", "dockerfile", "nginx" ] }) const resolveLang = (lang: string) => { const supported = highlighter.getLoadedLanguages?.() || [] if (!lang) return 'text' if (supported.includes(lang)) return lang return 'text' } export const processHtmlWithJsdom = async (rawHtml: string) => { const dom = new JSDOM(rawHtml) const document = dom.window.document // 高亮处理代码块 const codeBlocks = document.querySelectorAll('pre code') for (const codeBlock of codeBlocks) { const preEl = codeBlock.parentElement if (!preEl || preEl.tagName !== 'PRE') continue const codeContent = codeBlock.textContent || '' let lang = 'text' for (const cls of codeBlock.classList) { if (cls.startsWith('language-')) { lang = cls.slice('language-'.length) break } } lang = resolveLang(lang) // 高亮 const highlighted = highlighter.codeToHtml(codeContent, { lang, theme: DEFAULT_THEME, }) // 替换 DOM const wrapper = document.createElement('div') wrapper.innerHTML = highlighted.trim() const newNode = wrapper.firstElementChild if (newNode && preEl.parentNode) { const container = document.createElement('div') container.className = 'code-block' const header = document.createElement('div') header.className = 'code-header' const headerLeft = document.createElement('div') headerLeft.className = 'code-header-left' const headerRight = document.createElement('div') headerRight.className = 'code-header-right' // 装饰点 const actions = document.createElement('div') actions.className = 'code-actions' const close = document.createElement('span') close.className = 'dot close' const minimize = document.createElement('span') minimize.className = 'dot minimize' const maximize = document.createElement('span') maximize.className = 'dot maximize' actions.append(close, minimize, maximize) // 显示语言 const langDisplay = document.createElement('div') langDisplay.className = 'code-language' langDisplay.textContent = lang ? lang : 'text' const copyBtn = document.createElement('div') copyBtn.className = 'code-copy-btn' copyBtn.textContent = '复制代码' headerLeft.append(actions, langDisplay) headerRight.append(copyBtn) header.append(headerLeft, headerRight) container.append(header, newNode) preEl.parentNode.replaceChild(container, preEl) } } return dom.serialize() }