优化页面、修复BUG

This commit is contained in:
李琦
2026-06-26 13:37:16 +08:00
parent 87fa638b24
commit 396272908b
19 changed files with 1105 additions and 410 deletions

View File

@@ -1,46 +1,102 @@
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import type { HLJSApi, LanguageFn } from 'highlight.js'
import hljsCore from 'highlight.js/lib/core'
import javascript from 'highlight.js/lib/languages/javascript'
import typescript from 'highlight.js/lib/languages/typescript'
import python from 'highlight.js/lib/languages/python'
import go from 'highlight.js/lib/languages/go'
import java from 'highlight.js/lib/languages/java'
import css from 'highlight.js/lib/languages/css'
import xml from 'highlight.js/lib/languages/xml'
import json from 'highlight.js/lib/languages/json'
import bash from 'highlight.js/lib/languages/bash'
import sql from 'highlight.js/lib/languages/sql'
import yaml from 'highlight.js/lib/languages/yaml'
import markdown from 'highlight.js/lib/languages/markdown'
import rust from 'highlight.js/lib/languages/rust'
import cpp from 'highlight.js/lib/languages/cpp'
import c from 'highlight.js/lib/languages/c'
import php from 'highlight.js/lib/languages/php'
import shell from 'highlight.js/lib/languages/shell'
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true,
})
const LANGUAGE_REGISTRY: Array<[string, LanguageFn]> = [
['javascript', javascript],
['typescript', typescript],
['python', python],
['go', go],
['java', java],
['css', css],
['xml', xml],
['html', xml],
['json', json],
['bash', bash],
['sh', bash],
['sql', sql],
['yaml', yaml],
['yml', yaml],
['markdown', markdown],
['md', markdown],
['rust', rust],
['cpp', cpp],
['c', c],
['php', php],
['shell', shell],
]
md.renderer.rules.heading_open = function (tokens, idx) {
const token = tokens[idx]
const contentToken = tokens[idx + 1]
const title = contentToken ? contentToken.content : ''
const slug = 'heading-' + title.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-') + '-' + idx
return `<${token.tag} id="${slug}">`
let hljsInstance: HLJSApi | null = null
let mdInstance: MarkdownIt | null = null
function getHljs(): HLJSApi {
if (!hljsInstance) {
hljsInstance = hljsCore
for (const [name, register] of LANGUAGE_REGISTRY) {
hljsInstance.registerLanguage(name, register)
}
}
return hljsInstance
}
md.renderer.rules.fence = function (tokens, idx) {
const token = tokens[idx]
const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
let langName = ''
let highlighted
function buildMarkdownRenderer(hljs: HLJSApi): MarkdownIt {
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true,
})
if (info) {
langName = info.split(/\s+/g)[0]
md.renderer.rules.heading_open = function (tokens, idx) {
const token = tokens[idx]
const contentToken = tokens[idx + 1]
const title = contentToken ? contentToken.content : ''
const slug = 'heading-' + title.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-') + '-' + idx
return `<${token.tag} id="${slug}">`
}
if (langName && hljs.getLanguage(langName)) {
try {
highlighted = hljs.highlight(token.content, { language: langName, ignoreIllegals: true }).value
} catch {
md.renderer.rules.fence = function (tokens, idx) {
const token = tokens[idx]
const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
let langName = ''
let highlighted
if (info) {
langName = info.split(/\s+/g)[0]
}
if (langName && hljs.getLanguage(langName)) {
try {
highlighted = hljs.highlight(token.content, { language: langName, ignoreIllegals: true }).value
} catch {
highlighted = md.utils.escapeHtml(token.content)
}
} else {
highlighted = md.utils.escapeHtml(token.content)
}
} else {
highlighted = md.utils.escapeHtml(token.content)
}
const lines = token.content.trimEnd().split('\n')
const lineNumbers = lines.map((_, i) => i + 1).join('\n')
const lines = token.content.trimEnd().split('\n')
const lineNumbers = lines.map((_, i) => i + 1).join('\n')
const copyIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`
const copyIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`
return `
return `
<div class="ios-code-container w-full max-w-full my-6 md:my-10 rounded-lg md:rounded-xl overflow-hidden shadow-2xl bg-[#1e1e1e] border border-white/5 relative group/code text-sm">
<div class="mac-window-header flex items-center justify-between px-3 md:px-4 py-2 md:py-3 bg-[#282828] border-b border-white/5">
<div class="flex items-center gap-3 md:gap-4">
@@ -66,9 +122,25 @@ md.renderer.rules.fence = function (tokens, idx) {
</div>
</div>
`
}
return md
}
export function renderMarkdown(content: string): string {
function getMarkdownRenderer(): MarkdownIt {
if (!mdInstance) {
mdInstance = buildMarkdownRenderer(getHljs())
}
return mdInstance
}
export async function renderMarkdown(content: string): Promise<string> {
if (!content) return ''
const md = getMarkdownRenderer()
return md.render(content)
}
/** 预加载 highlight.js(可在路由进入详情页时调用) */
export function preloadMarkdownRenderer(): void {
getHljs()
}