Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21fb4f817c | ||
|
|
2c509c329f | ||
|
|
4769c31d09 |
16
.github/workflows/build_docker.yml
vendored
16
.github/workflows/build_docker.yml
vendored
@@ -3,6 +3,8 @@ name: build_docker
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
|
release:
|
||||||
|
types: [created] # 表示在创建新的 Release 时触发
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_docker:
|
build_docker:
|
||||||
@@ -11,11 +13,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
- run: |
|
||||||
uses: docker/metadata-action@v4
|
echo "本次构建的版本为:${GITHUB_REF_NAME} (但是这个变量目前上下文中无法获取到)"
|
||||||
with:
|
echo 本次构建的版本为:${{ github.ref_name }}
|
||||||
images: chenzhaoyu94/chatgpt-web
|
env
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
@@ -32,6 +34,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: |
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:${{ github.ref_name }}
|
||||||
|
${{ secrets.DOCKERHUB_USERNAME }}/chatgpt-web:latest
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
## v2.8.1
|
||||||
|
|
||||||
|
`2023-02-27`
|
||||||
|
|
||||||
|
### BugFix
|
||||||
|
- 修复 `API` 版本不是 `Markdown` 时,普通 `HTML` 代码会被渲染的问题 [#146]
|
||||||
|
|
||||||
## v2.8.0
|
## v2.8.0
|
||||||
|
|
||||||
`2023-02-27`
|
`2023-02-27`
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: chenzhaoyu94/chatgpt-web:main
|
image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
||||||
ports:
|
ports:
|
||||||
- 3002:3002
|
- 3002:3002
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "chatgpt-web",
|
"name": "chatgpt-web",
|
||||||
"version": "2.8.0",
|
"version": "2.8.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "ChatGPT Web",
|
"description": "ChatGPT Web",
|
||||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||||
|
|||||||
23
src/directives/highlight.ts
Normal file
23
src/directives/highlight.ts
Normal file
@@ -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<HTMLElement> = {
|
||||||
|
mounted(el: HTMLElement) {
|
||||||
|
highlightCode(el)
|
||||||
|
},
|
||||||
|
updated(el: HTMLElement) {
|
||||||
|
highlightCode(el)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app.directive('highlight', highLightDirective)
|
||||||
|
}
|
||||||
@@ -1 +1,6 @@
|
|||||||
export function setupDirectives() {}
|
import type { App } from 'vue'
|
||||||
|
import setupHighlightDirective from './highlight'
|
||||||
|
|
||||||
|
export function setupDirectives(app: App) {
|
||||||
|
setupHighlightDirective(app)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import { setupDirectives } from './directives'
|
||||||
import { setupAssets } from '@/plugins'
|
import { setupAssets } from '@/plugins'
|
||||||
import { setupStore } from '@/store'
|
import { setupStore } from '@/store'
|
||||||
import { setupRouter } from '@/router'
|
import { setupRouter } from '@/router'
|
||||||
@@ -10,6 +11,8 @@ async function bootstrap() {
|
|||||||
|
|
||||||
setupStore(app)
|
setupStore(app)
|
||||||
|
|
||||||
|
setupDirectives(app)
|
||||||
|
|
||||||
await setupRouter(app)
|
await setupRouter(app)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
15
src/utils/format/index.ts
Normal file
15
src/utils/format/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// 转义 HTML 字符
|
||||||
|
export function encodeHTML(source: string) {
|
||||||
|
return source
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.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))
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
@@ -3,6 +3,7 @@ import { computed } from 'vue'
|
|||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import hljs from 'highlight.js'
|
import hljs from 'highlight.js'
|
||||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
|
import { encodeHTML } from '@/utils/format'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
inversion?: boolean
|
inversion?: boolean
|
||||||
@@ -15,12 +16,19 @@ const props = defineProps<Props>()
|
|||||||
|
|
||||||
const { isMobile } = useBasicLayout()
|
const { isMobile } = useBasicLayout()
|
||||||
|
|
||||||
marked.setOptions({
|
const renderer = new marked.Renderer()
|
||||||
renderer: new marked.Renderer(),
|
|
||||||
highlight(code) {
|
renderer.html = (html) => {
|
||||||
return hljs.highlightAuto(code).value
|
return `<p>${encodeHTML(html)}</p>`
|
||||||
},
|
}
|
||||||
})
|
|
||||||
|
renderer.code = (code, language) => {
|
||||||
|
const validLang = !!(language && hljs.getLanguage(language))
|
||||||
|
const highlighted = validLang ? hljs.highlight(language, code).value : code
|
||||||
|
return `<pre><code class="hljs ${language}">${highlighted}</code></pre>`
|
||||||
|
}
|
||||||
|
|
||||||
|
marked.setOptions({ renderer })
|
||||||
|
|
||||||
const wrapClass = computed(() => {
|
const wrapClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
@@ -35,9 +43,10 @@ const wrapClass = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const text = computed(() => {
|
const text = computed(() => {
|
||||||
if (props.text && !props.inversion)
|
const value = props.text ?? ''
|
||||||
return marked(props.text)
|
if (!props.inversion)
|
||||||
return props.text
|
return marked(value)
|
||||||
|
return value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -48,7 +57,7 @@ const text = computed(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="leading-relaxed break-all">
|
<div class="leading-relaxed break-all">
|
||||||
<pre v-if="!inversion" class="markdown-body" v-html="text" />
|
<div v-if="!inversion" class="markdown-body" v-html="text" />
|
||||||
<div v-else class="whitespace-pre-wrap" v-text="text" />
|
<div v-else class="whitespace-pre-wrap" v-text="text" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user