diff --git a/CHANGELOG.md b/CHANGELOG.md index 47ec4cd..f4593fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v2.8.1 + +`2023-02-27` + +### BugFix +- 修复 `API` 版本不是 `Markdown` 时,普通 `HTML` 代码会被渲染的问题 [#146] + ## v2.8.0 `2023-02-27` diff --git a/package.json b/package.json index 29d0ba4..5a21ff8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chatgpt-web", - "version": "2.8.0", + "version": "2.8.1", "private": false, "description": "ChatGPT Web", "author": "ChenZhaoYu ", diff --git a/src/directives/highlight.ts b/src/directives/highlight.ts new file mode 100644 index 0000000..b77669d --- /dev/null +++ b/src/directives/highlight.ts @@ -0,0 +1,23 @@ +import type { App, Directive } from 'vue' +import hljs from 'highlight.js' +import { includeCode } from '@/utils/format' + +hljs.configure({ ignoreUnescapedHTML: true }) + +function highlightCode(el: HTMLElement) { + if (includeCode(el.textContent)) + hljs.highlightBlock(el) +} + +export default function setupHighlightDirective(app: App) { + const highLightDirective: Directive = { + mounted(el: HTMLElement) { + highlightCode(el) + }, + updated(el: HTMLElement) { + highlightCode(el) + }, + } + + app.directive('highlight', highLightDirective) +} diff --git a/src/directives/index.ts b/src/directives/index.ts index 5cc7923..cdcaeef 100644 --- a/src/directives/index.ts +++ b/src/directives/index.ts @@ -1 +1,6 @@ -export function setupDirectives() {} +import type { App } from 'vue' +import setupHighlightDirective from './highlight' + +export function setupDirectives(app: App) { + setupHighlightDirective(app) +} diff --git a/src/main.ts b/src/main.ts index acbe522..115198d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue' import App from './App.vue' +import { setupDirectives } from './directives' import { setupAssets } from '@/plugins' import { setupStore } from '@/store' import { setupRouter } from '@/router' @@ -10,6 +11,8 @@ async function bootstrap() { setupStore(app) + setupDirectives(app) + await setupRouter(app) app.mount('#app') diff --git a/src/utils/format/index.ts b/src/utils/format/index.ts new file mode 100644 index 0000000..8383187 --- /dev/null +++ b/src/utils/format/index.ts @@ -0,0 +1,15 @@ +// 转义 HTML 字符 +export function encodeHTML(source: string) { + return source + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +// 判断是否为代码块 +export function includeCode(text: string | null | undefined) { + const regexp = /^(?:\s{4}|\t).+/gm + return !!(text?.includes(' = ') || text?.match(regexp)) +} diff --git a/src/utils/functions/includeCode.ts b/src/utils/functions/includeCode.ts deleted file mode 100644 index 6958f91..0000000 --- a/src/utils/functions/includeCode.ts +++ /dev/null @@ -1,6 +0,0 @@ -function includeCode(text: string | null | undefined) { - const regexp = /^(?:\s{4}|\t).+/gm - return !!(text?.includes(' = ') || text?.match(regexp)) -} - -export default includeCode diff --git a/src/views/chat/components/Message/Text.vue b/src/views/chat/components/Message/Text.vue index 2dfef78..0c092bf 100644 --- a/src/views/chat/components/Message/Text.vue +++ b/src/views/chat/components/Message/Text.vue @@ -3,6 +3,7 @@ import { computed } from 'vue' import { marked } from 'marked' import hljs from 'highlight.js' import { useBasicLayout } from '@/hooks/useBasicLayout' +import { encodeHTML } from '@/utils/format' interface Props { inversion?: boolean @@ -15,12 +16,19 @@ const props = defineProps() const { isMobile } = useBasicLayout() -marked.setOptions({ - renderer: new marked.Renderer(), - highlight(code) { - return hljs.highlightAuto(code).value - }, -}) +const renderer = new marked.Renderer() + +renderer.html = (html) => { + return `

${encodeHTML(html)}

` +} + +renderer.code = (code, language) => { + const validLang = !!(language && hljs.getLanguage(language)) + const highlighted = validLang ? hljs.highlight(language, code).value : code + return `
${highlighted}
` +} + +marked.setOptions({ renderer }) const wrapClass = computed(() => { return [ @@ -35,9 +43,10 @@ const wrapClass = computed(() => { }) const text = computed(() => { - if (props.text && !props.inversion) - return marked(props.text) - return props.text + const value = props.text ?? '' + if (!props.inversion) + return marked(value) + return value })