Commit 607b73b6 authored by tyyin lan's avatar tyyin lan

feat: 代码执行块渲染

parent 54aec989
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
name="viewport" name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/> />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_6yk5447mfvj.css" /> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_f6b3gmjqw8q.css" />
<link <link
rel="preload" rel="preload"
href="https://gsst-poe-sit.gz.bcebos.com/front/SourceHanSansCN-Medium.otf" href="https://gsst-poe-sit.gz.bcebos.com/front/SourceHanSansCN-Medium.otf"
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
"dompurify": "^3.2.0", "dompurify": "^3.2.0",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"github-markdown-css": "^5.7.0",
"highlight.js": "^11.10.0", "highlight.js": "^11.10.0",
"howler": "^2.2.4", "howler": "^2.2.4",
"katex": "^0.16.21", "katex": "^0.16.21",
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ import { throttle, debounce } from 'lodash-es' ...@@ -7,7 +7,7 @@ import { throttle, debounce } from 'lodash-es'
import DOMPurify from 'dompurify' import DOMPurify from 'dompurify'
import hljs from 'highlight.js' import hljs from 'highlight.js'
import 'highlight.js/styles/panda-syntax-light.css' import 'highlight.js/styles/panda-syntax-light.css'
import 'github-markdown-css' import './style/github-markdown.css'
import markedKatex, { MarkedKatexOptions } from 'marked-katex-extension' import markedKatex, { MarkedKatexOptions } from 'marked-katex-extension'
import { copyToClip } from '@/utils/copy' import { copyToClip } from '@/utils/copy'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
...@@ -16,12 +16,14 @@ interface Props { ...@@ -16,12 +16,14 @@ interface Props {
rawTextContent: string rawTextContent: string
fontSize?: number | string fontSize?: number | string
color?: string color?: string
darkTheme?: boolean
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
rawTextContent: '', rawTextContent: '',
fontSize: '14px', fontSize: '14px',
color: '#333', color: '#333',
darkTheme: false,
}) })
const katexOptions: MarkedKatexOptions = { const katexOptions: MarkedKatexOptions = {
...@@ -39,14 +41,23 @@ const marked = new Marked() ...@@ -39,14 +41,23 @@ const marked = new Marked()
.use( .use(
markedHighlight({ markedHighlight({
emptyLangClass: 'hljs', emptyLangClass: 'hljs',
langPrefix: 'hljs language-', langPrefix: 'hljs code-container-wrapper language-',
highlight(code, lang, _info) { highlight(code, lang, _info) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext' const language = hljs.getLanguage(lang) ? lang : 'plaintext'
// return hljs.highlight(code, { language }).value // return hljs.highlight(code, { language }).value
const str = hljs.highlight(code, { language }).value const str = hljs.highlight(code, { language }).value
return `<div class="code-render-container"><div class="code-operation-bar-container"><span>${language}</span><span class="code-copy-btn iconfont icon-copy"></span></div><pre class="code-render-inner"><code>${str}</code></pre></div>` // return `<div class="code-render-container"><div class="code-operation-bar-container"><span class="language">${language}</span><span><span class="code-copy-btn iconfont icon-fuzhi"></span><span class="fold-btn iconfont icon-left"></span></span></div><pre class="code-render-inner"><code>${str}</code></pre></div>`
return (
'<div class="code-render-container">' +
'<div class="code-operation-bar-container">' +
`<span class="language">${language}</span><span><span class="code-copy-btn iconfont icon-fuzhi"></span><span class="fold-btn iconfont icon-left"></span></span>` +
'</div>' +
`<div class="code-render-wrapper"><pre class="code-render-inner"><code>${str}</code></pre></div>` +
'</div>'
)
}, },
}), }),
) )
...@@ -96,6 +107,7 @@ const codeCopyBtnEventBind = debounce( ...@@ -96,6 +107,7 @@ const codeCopyBtnEventBind = debounce(
() => { () => {
if (markdownRenderContainerRef.value) { if (markdownRenderContainerRef.value) {
const codeCopyBtnElList = markdownRenderContainerRef.value.getElementsByClassName('code-copy-btn') const codeCopyBtnElList = markdownRenderContainerRef.value.getElementsByClassName('code-copy-btn')
const foldBtnElList = markdownRenderContainerRef.value.getElementsByClassName('fold-btn')
btnEventController.abort() btnEventController.abort()
btnEventController = new AbortController() btnEventController = new AbortController()
...@@ -105,7 +117,7 @@ const codeCopyBtnEventBind = debounce( ...@@ -105,7 +117,7 @@ const codeCopyBtnEventBind = debounce(
elItem.addEventListener( elItem.addEventListener(
'click', 'click',
() => { () => {
const code = elItem.parentElement?.nextElementSibling?.firstChild?.textContent const code = elItem.parentElement?.parentElement?.nextElementSibling?.firstChild?.firstChild?.textContent
if (code) { if (code) {
copyToClip(code).then(() => { copyToClip(code).then(() => {
...@@ -116,6 +128,23 @@ const codeCopyBtnEventBind = debounce( ...@@ -116,6 +128,23 @@ const codeCopyBtnEventBind = debounce(
{ signal: btnEventController.signal }, { signal: btnEventController.signal },
) )
}) })
/* 遍历 */
;[...foldBtnElList].forEach((elItem) => {
elItem.addEventListener(
'click',
() => {
elItem && elItem.classList.toggle('folding')
const codeRenderWrapperEl = elItem.parentElement?.parentElement?.nextElementSibling
if (codeRenderWrapperEl) {
codeRenderWrapperEl?.classList.toggle('folding')
}
},
{ signal: btnEventController.signal },
)
})
} }
}, },
1000, 1000,
...@@ -143,7 +172,12 @@ defineExpose({ ...@@ -143,7 +172,12 @@ defineExpose({
<template> <template>
<div ref="markdownRenderContainerRef" class="markdown-render-container"> <div ref="markdownRenderContainerRef" class="markdown-render-container">
<article class="markdown-body markdown-render-inner" :style="articleContainerStyle" v-html="renderTextContent" /> <article
class="markdown-body markdown-render-inner"
data-theme="dark"
:style="articleContainerStyle"
v-html="renderTextContent"
/>
</div> </div>
</template> </template>
...@@ -169,11 +203,31 @@ defineExpose({ ...@@ -169,11 +203,31 @@ defineExpose({
.code-operation-bar-container { .code-operation-bar-container {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
padding: 10px 16px; padding: 8px 16px;
color: v-bind('props.darkTheme ? "#fff" : "#ccc"');
user-select: none; user-select: none;
background-color: #f1f1f1; background-color: v-bind('props.darkTheme ? "#484a4b" : "#f1f1f1"');
.code-copy-btn { .language {
display: flex;
align-items: center;
justify-content: center;
}
.fold-btn {
display: inline-block;
margin-left: 16px;
transition: transform ease-in-out 0.2s !important;
transform: rotate(270deg);
&.folding {
transform: rotate(90deg);
}
}
.code-copy-btn,
.fold-btn {
font-size: 14px;
cursor: pointer; cursor: pointer;
transition: color ease-in-out 0.3s; transition: color ease-in-out 0.3s;
...@@ -184,10 +238,32 @@ defineExpose({ ...@@ -184,10 +238,32 @@ defineExpose({
} }
.code-render-inner { .code-render-inner {
padding: 10px 16px 0; padding: 10px 16px;
margin: 0; margin: 0;
overflow-x: auto; overflow-x: auto;
} }
.code-render-wrapper {
height: fit-content;
transition: all 0.3s linear;
&.folding {
height: 0;
overflow: hidden;
}
}
}
}
:deep(.markdown-body) {
&[data-theme='dark'] {
/* stylelint-disable-next-line custom-property-pattern */
--bgColor-muted: #1d1e1f;
}
& .code-container-wrapper {
display: inline-flex;
flex-direction: column;
} }
} }
......
This diff is collapsed.
...@@ -156,7 +156,7 @@ const handleContentEdit = throttle( ...@@ -156,7 +156,7 @@ const handleContentEdit = throttle(
class="py-[10px] pr-[14px] text-end" class="py-[10px] pr-[14px] text-end"
> >
<i <i
class="iconfont icon-copy1 hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px]" class="iconfont icon-fuzhi hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px]"
@click="handleContentCopy" @click="handleContentCopy"
></i> ></i>
<i <i
......
...@@ -44,13 +44,20 @@ const isShowMessageList = ref(false) ...@@ -44,13 +44,20 @@ const isShowMessageList = ref(false)
const isAgentInitLoading = ref(true) const isAgentInitLoading = ref(true)
const currentFetchEventSourceController = ref<AbortController | null>(null) const currentFetchEventSourceController = ref<AbortController | null>(null)
// messageList.value.set('1', { setTimeout(() => {
// role: 'user', messageList.value.set('1', {
// agentId: 'b058f1baedd04af983ca00775368bb8c', role: 'user',
// content: '请推荐一些适合初学者的编程学习资源。', agentId: 'b058f1baedd04af983ca00775368bb8c',
// timestamp: 1726654820427, content: '请推荐一些适合初学者的编程学习资源。',
// isAnswerLoading: true, timestamp: 1726654820427,
// }) isAnswerLoading: false,
avatar: 'http://localhost:8848/fe/src/assets/images/home/agent-avatar.png',
name: '1234',
reasoningContent: '1324',
imageUrl: '',
pluginName: '',
})
}, 60)
// messageList.value.set('2', { // messageList.value.set('2', {
// role: 'assistant', // role: 'assistant',
// agentId: 'b058f1baedd04af983ca00775368bb8c', // agentId: 'b058f1baedd04af983ca00775368bb8c',
......
...@@ -263,7 +263,7 @@ const handleContentCopy = throttle( ...@@ -263,7 +263,7 @@ const handleContentCopy = throttle(
class="pr-[13px] text-end" class="pr-[13px] text-end"
> >
<i <i
class="iconfont icon-copy1 hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px] text-[#3d3d3d]" class="iconfont icon-fuzhi hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px] text-[#3d3d3d]"
@click="handleContentCopy" @click="handleContentCopy"
/> />
<i <i
......
...@@ -223,7 +223,7 @@ async function handleExportAPIChannelPointUsageData() { ...@@ -223,7 +223,7 @@ async function handleExportAPIChannelPointUsageData() {
<template #trigger> <template #trigger>
<i <i
v-show="apiProfileInfo.apiKey" v-show="apiProfileInfo.apiKey"
class="iconfont icon-copy text-gray-font-color hover:text-theme-color cursor-pointer leading-4" class="iconfont icon-fuzhi text-gray-font-color hover:text-theme-color cursor-pointer leading-4"
@click="handleCopyText(apiProfileInfo.apiKey)" @click="handleCopyText(apiProfileInfo.apiKey)"
/> />
</template> </template>
...@@ -245,7 +245,7 @@ async function handleExportAPIChannelPointUsageData() { ...@@ -245,7 +245,7 @@ async function handleExportAPIChannelPointUsageData() {
<template #trigger> <template #trigger>
<i <i
v-show="apiProfileInfo.apiSecret" v-show="apiProfileInfo.apiSecret"
class="iconfont icon-copy text-gray-font-color hover:text-theme-color cursor-pointer leading-4" class="iconfont icon-fuzhi text-gray-font-color hover:text-theme-color cursor-pointer leading-4"
@click="handleCopyText(apiProfileInfo.apiSecret)" @click="handleCopyText(apiProfileInfo.apiSecret)"
/> />
</template> </template>
......
...@@ -291,7 +291,7 @@ const handleContentCopy = throttle( ...@@ -291,7 +291,7 @@ const handleContentCopy = throttle(
class="py-[10px] pr-[14px] text-end" class="py-[10px] pr-[14px] text-end"
> >
<i <i
class="iconfont icon-copy1 hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px]" class="iconfont icon-fuzhi hover:text-theme-color mr-[14px] transform cursor-pointer text-[14px]"
@click="handleContentCopy" @click="handleContentCopy"
/> />
<i <i
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment