Compare commits

3 Commits

Author SHA1 Message Date
Redon
21fb4f817c fix: HTML 渲染异常 (#152)
* fix: 修复 `API` 版本 HTML 会被渲染的问题[#146]

* chore: version 2.8.1
2023-02-27 19:25:15 +08:00
二丫讲梵
2c509c329f feat: 调整镜像构建的tag,让最新代码指向latest,让每次版本指向日期 (#144)
* feat: 调整镜像构建的tag,让最新代码指向latest,让每次版本指向日期

* feat: 触发时机为main分支push或者relases的create,镜像的tag为分支名或tag名

* fix: 修复变量引用方式

* fix: 修复变量引用方式

* fix: 使用github.ref_name 取触发action的来源
2023-02-27 19:24:46 +08:00
ChenZhaoYu
4769c31d09 fix: white space 2023-02-27 13:01:42 +08:00
10 changed files with 85 additions and 25 deletions

View File

@@ -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

View File

@@ -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`

View File

@@ -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:

View File

@@ -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>",

View 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)
}

View File

@@ -1 +1,6 @@
export function setupDirectives() {} import type { App } from 'vue'
import setupHighlightDirective from './highlight'
export function setupDirectives(app: App) {
setupHighlightDirective(app)
}

View File

@@ -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
View File

@@ -0,0 +1,15 @@
// 转义 HTML 字符
export function encodeHTML(source: string) {
return source
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
}
// 判断是否为代码块
export function includeCode(text: string | null | undefined) {
const regexp = /^(?:\s{4}|\t).+/gm
return !!(text?.includes(' = ') || text?.match(regexp))
}

View File

@@ -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

View File

@@ -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>