diff --git a/.vscode/settings.json b/.vscode/settings.json index f96f070..084cd45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,10 +29,14 @@ "dockerhub", "esno", "GPTAPI", + "highlightjs", "hljs", "iconify", "katex", + "katexmath", + "linkify", "logprobs", + "mdhljs", "nodata", "OPENAI", "pinia", @@ -40,6 +44,7 @@ "rushstack", "Sider", "tailwindcss", + "traptitech", "tsup", "Typecheck", "unplugin", diff --git a/package.json b/package.json index fca0189..2126443 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "dependencies": { "@traptitech/markdown-it-katex": "^3.6.0", "@vueuse/core": "^9.13.0", + "highlight.js": "^11.7.0", "katex": "^0.16.4", "markdown-it": "^13.0.1", - "markdown-it-highlightjs": "^4.0.1", "naive-ui": "^2.34.3", "pinia": "^2.0.32", "vue": "^3.2.47", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d88e78f..736334a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,12 +16,12 @@ specifiers: axios: ^1.3.4 crypto-js: ^4.1.1 eslint: ^8.35.0 + highlight.js: ^11.7.0 husky: ^8.0.3 katex: ^0.16.4 less: ^4.1.3 lint-staged: ^13.1.2 markdown-it: ^13.0.1 - markdown-it-highlightjs: ^4.0.1 naive-ui: ^2.34.3 npm-run-all: ^4.1.5 pinia: ^2.0.32 @@ -38,11 +38,11 @@ specifiers: dependencies: '@traptitech/markdown-it-katex': 3.6.0 '@vueuse/core': 9.13.0_vue@3.2.47 + highlight.js: 11.7.0 katex: 0.16.4 markdown-it: 13.0.1 - markdown-it-highlightjs: 4.0.1 naive-ui: 2.34.3_vue@3.2.47 - pinia: 2.0.32_hmuptsblhheur2tugfgucj7gc4 + pinia: 2.0.33_hmuptsblhheur2tugfgucj7gc4 vue: 3.2.47 vue-i18n: 9.2.2_vue@3.2.47 vue-router: 4.1.6_vue@3.2.47 @@ -66,7 +66,7 @@ devDependencies: lint-staged: 13.1.2 npm-run-all: 4.1.5 postcss: 8.4.21 - rimraf: 4.2.0 + rimraf: 4.3.0 tailwindcss: 3.2.7_postcss@8.4.21 typescript: 4.9.5 vite: 4.1.4_4l5pdn5ozbjpiwj3fcgseihr44 @@ -602,11 +602,11 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils/4.1.2_eslint@8.35.0: - resolution: {integrity: sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==} + /@eslint-community/eslint-utils/4.2.0_eslint@8.35.0: + resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: eslint: 8.35.0 eslint-visitor-keys: 3.3.0 @@ -1318,7 +1318,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.5 - caniuse-lite: 1.0.30001458 + caniuse-lite: 1.0.30001460 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -1379,8 +1379,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001458 - electron-to-chromium: 1.4.315 + caniuse-lite: 1.0.30001460 + electron-to-chromium: 1.4.320 node-releases: 2.0.10 update-browserslist-db: 1.0.10_browserslist@4.21.5 dev: true @@ -1427,8 +1427,8 @@ packages: engines: {node: '>=6'} dev: true - /caniuse-lite/1.0.30001458: - resolution: {integrity: sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==} + /caniuse-lite/1.0.30001460: + resolution: {integrity: sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==} dev: true /chalk/2.4.2: @@ -1847,8 +1847,8 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium/1.4.315: - resolution: {integrity: sha512-ndBQYz3Eyy3rASjjQ9poMJGoAlsZ/aZnq6GBsGL4w/4sWIAwiUHVSsMuADbxa8WJw7pZ0oxLpGbtoDt4vRTdCg==} + /electron-to-chromium/1.4.320: + resolution: {integrity: sha512-h70iRscrNluMZPVICXYl5SSB+rBKo22XfuIS1ER0OQxQZpKTnFpuS6coj7wY9M/3trv7OR88rRMOlKmRvDty7Q==} dev: true /emoji-regex/8.0.0: @@ -2185,7 +2185,7 @@ packages: eslint: '>=8.28.0' dependencies: '@babel/helper-validator-identifier': 7.19.1 - '@eslint-community/eslint-utils': 4.1.2_eslint@8.35.0 + '@eslint-community/eslint-utils': 4.2.0_eslint@8.35.0 ci-info: 3.8.0 clean-regexp: 1.0.0 eslint: 8.35.0 @@ -3167,6 +3167,11 @@ packages: engines: {node: '>=10'} dev: true + /lilconfig/2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + dev: true + /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -3312,8 +3317,8 @@ packages: yallist: 4.0.0 dev: true - /lru-cache/7.18.1: - resolution: {integrity: sha512-8/HcIENyQnfUTCDizRu9rrDyG6XG/21M4X7/YEGZeD76ZJilFPAUVb/2zysFf7VVO1LEjCDFyHp8pMMvozIrvg==} + /lru-cache/7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} dev: true @@ -3346,12 +3351,6 @@ packages: engines: {node: '>=8'} dev: true - /markdown-it-highlightjs/4.0.1: - resolution: {integrity: sha512-EPXwFEN6P5nqR3G4KjT20r20xbGYKMMA/360hhSYFmeoGXTE6hsLtJAiB/8ID8slVH4CWHHEL7GX0YenyIstVQ==} - dependencies: - highlight.js: 11.7.0 - dev: false - /markdown-it/13.0.1: resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==} hasBin: true @@ -3821,7 +3820,7 @@ packages: resolution: {integrity: sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==} engines: {node: '>=14'} dependencies: - lru-cache: 7.18.1 + lru-cache: 7.18.3 minipass: 4.2.4 dev: true @@ -3873,8 +3872,8 @@ packages: dev: true optional: true - /pinia/2.0.32_hmuptsblhheur2tugfgucj7gc4: - resolution: {integrity: sha512-8Tw4OrpCSJ028UUyp0gYPP/wyjigLoEceuO/x1G+FlHVf73337e5vLm4uDmrRIoBG1hvaed/eSHnrCFjOc4nkA==} + /pinia/2.0.33_hmuptsblhheur2tugfgucj7gc4: + resolution: {integrity: sha512-HOj1yVV2itw6rNIrR2f7+MirGNxhORjrULL8GWgRwXsGSvEqIQ+SE0MYt6cwtpegzCda3i+rVTZM+AM7CG+kRg==} peerDependencies: '@vue/composition-api': ^1.4.0 typescript: '>=4.4.4' @@ -3930,7 +3929,7 @@ packages: ts-node: optional: true dependencies: - lilconfig: 2.0.6 + lilconfig: 2.1.0 postcss: 8.4.21 yaml: 1.10.2 dev: true @@ -4147,8 +4146,8 @@ packages: glob: 7.2.3 dev: true - /rimraf/4.2.0: - resolution: {integrity: sha512-tPt+gLORNVqRCk0NwuJ5SlMEcOGvt4CCU8sUPqgCFtCbnoNCTd9Q6vq7JlBbxQlACiH14OR28y7piA2Bak9Sxw==} + /rimraf/4.3.0: + resolution: {integrity: sha512-5qVDXPbByA1qSJEWMv1qAwKsoS22vVpsL2QyxCKBw4gf6XiFo1K3uRLY6uSOOBFDwnqAZtnbILqWKKlzh8bkGg==} engines: {node: '>=14'} hasBin: true dependencies: @@ -4466,7 +4465,7 @@ packages: fast-glob: 3.2.12 glob-parent: 6.0.2 is-glob: 4.0.3 - lilconfig: 2.0.6 + lilconfig: 2.1.0 micromatch: 4.0.5 normalize-path: 3.0.0 object-hash: 3.0.0 diff --git a/src/directives/highlight.ts b/src/directives/highlight.ts deleted file mode 100644 index b77669d..0000000 --- a/src/directives/highlight.ts +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index d8fde0c..0000000 --- a/src/directives/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -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 401197f..736f778 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,5 @@ import { createApp } from 'vue' import App from './App.vue' -import { setupDirectives } from './directives' import { setupI18n } from './locales' import { setupAssets } from './plugins' import { setupStore } from './store' @@ -12,8 +11,6 @@ async function bootstrap() { setupStore(app) - setupDirectives(app) - setupI18n(app) await setupRouter(app) diff --git a/src/views/chat/components/Message/Text.vue b/src/views/chat/components/Message/Text.vue index cf78ab0..61736a1 100644 --- a/src/views/chat/components/Message/Text.vue +++ b/src/views/chat/components/Message/Text.vue @@ -2,8 +2,9 @@ import { computed, ref } from 'vue' import MarkdownIt from 'markdown-it' import mdKatex from '@traptitech/markdown-it-katex' -import mdhljs from 'markdown-it-highlightjs' +import hljs from 'highlight.js' import { useBasicLayout } from '@/hooks/useBasicLayout' +import { t } from '@/locales' interface Props { inversion?: boolean @@ -20,11 +21,17 @@ const textRef = ref() const mdi = new MarkdownIt({ linkify: true, + highlight(code, language) { + const validLang = !!(language && hljs.getLanguage(language)) + if (validLang) { + const lang = language ?? '' + return highlightBlock(hljs.highlight(lang, code, true).value, lang) + } + return highlightBlock(hljs.highlightAuto(code).value, '') + }, }) -mdi - .use(mdhljs, { auto: true, inline: true }) - .use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' }) +mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' }) const wrapClass = computed(() => { return [ @@ -45,6 +52,10 @@ const text = computed(() => { return value }) +function highlightBlock(str: string, lang?: string) { + return `
${lang}${t('chat.copyCode')}
${str}
` +} + defineExpose({ textRef }) diff --git a/src/views/chat/components/Message/style.less b/src/views/chat/components/Message/style.less index ebdbad4..ce67447 100644 --- a/src/views/chat/components/Message/style.less +++ b/src/views/chat/components/Message/style.less @@ -19,7 +19,6 @@ line-height: 1.65; } - .katexmath-block, .highlight pre, pre { background-color: #fff; @@ -61,7 +60,6 @@ html.dark { .highlight pre, - .katexmath-block, pre { background-color: #282c34; } diff --git a/src/views/chat/hooks/useCopyCode.ts b/src/views/chat/hooks/useCopyCode.ts index 0941ea9..8816b5d 100644 --- a/src/views/chat/hooks/useCopyCode.ts +++ b/src/views/chat/hooks/useCopyCode.ts @@ -1,4 +1,5 @@ import { onMounted, onUpdated } from 'vue' +import { copyText } from '@/utils/format' export function useCopyCode() { function copyCodeBlock() { @@ -8,7 +9,10 @@ export function useCopyCode() { const codeBlock = wrapper.querySelector('.code-block-body') if (copyBtn && codeBlock) { copyBtn.addEventListener('click', () => { - navigator.clipboard.writeText(codeBlock.textContent ?? '') + if (navigator.clipboard?.writeText) + navigator.clipboard.writeText(codeBlock.textContent ?? '') + else + copyText({ text: codeBlock.textContent ?? '', origin: true }) }) } }) diff --git a/src/views/chat/layout/sider/List.vue b/src/views/chat/layout/sider/List.vue index 529293a..2a282e0 100644 --- a/src/views/chat/layout/sider/List.vue +++ b/src/views/chat/layout/sider/List.vue @@ -15,8 +15,8 @@ const dataSources = computed(() => chatStore.history) async function handleSelect({ uuid }: Chat.History) { if (isActive(uuid)) return - - if(chatStore.active) + + if (chatStore.active) chatStore.updateHistory(chatStore.active, { isEdit: false }) await chatStore.setActive(uuid)