优化: AI模块(provider重试兼容+aichat交互+工具卡片可读化+diff高亮)
This commit is contained in:
@@ -18,6 +18,56 @@
|
||||
//! 渲染结果不变。
|
||||
|
||||
import { computed, ref, type ComputedRef } from 'vue'
|
||||
// UX-2025-05: 代码块语法高亮 —— 用 highlight.js/lib/core + 按需 registerLanguage,
|
||||
// 只打常用 ~16 门语言进包(避免全量 import ~1MB);core+已注册语言 gzip 后远小于全量。
|
||||
import hljs from 'highlight.js/lib/core'
|
||||
import javascript from 'highlight.js/lib/languages/javascript'
|
||||
import typescript from 'highlight.js/lib/languages/typescript'
|
||||
import rust from 'highlight.js/lib/languages/rust'
|
||||
import python from 'highlight.js/lib/languages/python'
|
||||
import go from 'highlight.js/lib/languages/go'
|
||||
import sql from 'highlight.js/lib/languages/sql'
|
||||
import bash from 'highlight.js/lib/languages/bash'
|
||||
import json from 'highlight.js/lib/languages/json'
|
||||
import yaml from 'highlight.js/lib/languages/yaml'
|
||||
import markdown from 'highlight.js/lib/languages/markdown'
|
||||
import xml from 'highlight.js/lib/languages/xml'
|
||||
import css from 'highlight.js/lib/languages/css'
|
||||
import shell from 'highlight.js/lib/languages/shell'
|
||||
import java from 'highlight.js/lib/languages/java'
|
||||
import csharp from 'highlight.js/lib/languages/csharp'
|
||||
import cpp from 'highlight.js/lib/languages/cpp'
|
||||
// B-260616-11(②子项): diff 语言注册,```diff 代码块 +/- 行获 hljs-addition/deletion 红绿(github-dark.css 自带)
|
||||
import diff from 'highlight.js/lib/languages/diff'
|
||||
// 主题:github-dark 与现有深色 UI(#0c0e1a)贴合;亮色主题下 code 块仍可读(深底浅字)。
|
||||
// 注意:该 CSS 在 ai-md.css 之前/之后导入均可,hljs 用 .hljs/.hljs-* token class 着色,
|
||||
// 与 .ai-md pre code 的基础色互不冲突(token 是独立 <span>)。
|
||||
import 'highlight.js/styles/github-dark.css'
|
||||
|
||||
// 按需注册常用语言(大小写不敏感的别名映射见 _LANG_ALIASES)
|
||||
hljs.registerLanguage('javascript', javascript)
|
||||
hljs.registerLanguage('typescript', typescript)
|
||||
hljs.registerLanguage('rust', rust)
|
||||
hljs.registerLanguage('python', python)
|
||||
hljs.registerLanguage('go', go)
|
||||
hljs.registerLanguage('sql', sql)
|
||||
hljs.registerLanguage('bash', bash)
|
||||
hljs.registerLanguage('json', json)
|
||||
hljs.registerLanguage('yaml', yaml)
|
||||
hljs.registerLanguage('markdown', markdown)
|
||||
hljs.registerLanguage('xml', xml)
|
||||
hljs.registerLanguage('css', css)
|
||||
hljs.registerLanguage('shell', shell)
|
||||
hljs.registerLanguage('java', java)
|
||||
hljs.registerLanguage('csharp', csharp)
|
||||
hljs.registerLanguage('cpp', cpp)
|
||||
hljs.registerLanguage('diff', diff)
|
||||
// 围栏 info 字符串常见别名 → 已注册语言(hljs.getLanguage 内置部分别名,这里补齐业务常用)
|
||||
const _LANG_ALIASES: Record<string, string> = {
|
||||
js: 'javascript', ts: 'typescript', py: 'python', rb: 'ruby',
|
||||
sh: 'bash', shell: 'shell', yml: 'yaml', md: 'markdown',
|
||||
html: 'xml', vue: 'xml', svg: 'xml', cs: 'csharp', 'c++': 'cpp', 'c#': 'csharp',
|
||||
}
|
||||
|
||||
// ═══ 模块级单例:AiChat 流式核心与历史 renderMd 共享同一份渲染器/缓存 ═══
|
||||
let _marked: typeof import('marked')['marked'] | null = null
|
||||
@@ -26,10 +76,47 @@ const mdReady = ref(false)
|
||||
// 历史消息整段缓存(文本不变,重渲染命中省去 marked+sanitize 全量解析)
|
||||
const _mdCache = new Map<string, string>()
|
||||
|
||||
/**
|
||||
* UX-2025-05: marked code renderer 注入 —— 优先按 infostring 指定语言高亮(需已注册),
|
||||
* 否则 highlightAuto 在已注册语言里猜测。输出 <pre><code class="hljs language-{lang}">,
|
||||
* 保留 marked 原 code 块外层结构(pre/code),兼容现有 .ai-md pre code 样式与 DOMPurify。
|
||||
*
|
||||
* 返回 false 让 marked 用默认 code 渲染再包 pre 不可行(marked v8 code 返回值语义),
|
||||
* 故这里直接返回完整 <pre><code>...</code></pre> HTML(marked renderer.code 的契约)。
|
||||
*/
|
||||
function highlightCode(code: string, infostring: string): string {
|
||||
// infostring 形如 "ts" / "javascript" / "rust,norun" —— 取首个 token 去空白小写
|
||||
const langRaw = (infostring || '').split(/[\s,]/)[0]?.trim().toLowerCase() ?? ''
|
||||
const lang = _LANG_ALIASES[langRaw] ?? langRaw
|
||||
let highlighted: string
|
||||
try {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
highlighted = hljs.highlight(code, { language: lang, ignoreIllegals: true }).value
|
||||
} else {
|
||||
// 无 infostring 或未注册语言:在已注册语言里自动猜测(仍有限,但覆盖常用)
|
||||
highlighted = hljs.highlightAuto(code).value
|
||||
}
|
||||
} catch {
|
||||
// 高亮异常时降级为纯 HTML 转义(marked 原行为),不破坏渲染
|
||||
return code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
}
|
||||
const cls = lang ? `hljs language-${lang}` : 'hljs'
|
||||
return `<pre><code class="${cls}">${highlighted}</code></pre>`
|
||||
}
|
||||
|
||||
/** 异步加载 marked + DOMPurify;加载完成后 mdReady 翻转触发重渲染(模块级,所有调用方共享) */
|
||||
async function loadMarkdown() {
|
||||
const [{ marked }, dp] = await Promise.all([import('marked'), import('dompurify')])
|
||||
marked.setOptions({ breaks: true, gfm: true })
|
||||
// UX-2025-05: 注入 code renderer 做语法高亮(marked.use renderer 合并,仅覆盖 code,
|
||||
// 其余 renderer 沿用 marked 默认)。
|
||||
marked.use({
|
||||
renderer: {
|
||||
code({ text, lang }: { text: string; lang?: string }) {
|
||||
return highlightCode(text, lang ?? '')
|
||||
},
|
||||
},
|
||||
})
|
||||
_marked = marked
|
||||
_purify = dp.default
|
||||
_mdCache.clear() // 渲染器就绪,清兜底缓存触发完整重渲染
|
||||
|
||||
Reference in New Issue
Block a user