refactor: v2

This commit is contained in:
2026-08-15 20:55:56 +08:00
parent 990d4b0728
commit 560f6f29ea
37 changed files with 687 additions and 1506 deletions

120
src/lib/processHtml.ts Normal file
View File

@@ -0,0 +1,120 @@
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()
}