feat: version 2.9.1 (#207)
* feat: i18n * chore: format * feat: 补充遗漏翻译 * chore: update deps * feat: 复制代码块[#196][#197] * chore: version 2.9.1
This commit is contained in:
parent
21cf1bdd9e
commit
f19998d59b
|
@ -45,5 +45,14 @@
|
||||||
"VITE",
|
"VITE",
|
||||||
"vueuse",
|
"vueuse",
|
||||||
"Zhao"
|
"Zhao"
|
||||||
]
|
],
|
||||||
|
"i18n-ally.enabledParsers": [
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"i18n-ally.sortKeys": true,
|
||||||
|
"i18n-ally.keepFulfilled": true,
|
||||||
|
"i18n-ally.localesPaths": [
|
||||||
|
"src/locales"
|
||||||
|
],
|
||||||
|
"i18n-ally.keystyle": "nested"
|
||||||
}
|
}
|
||||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -1,3 +1,19 @@
|
||||||
|
## v2.9.1
|
||||||
|
|
||||||
|
`2023-03-02`
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
- 代码块添加当前代码语言显示和复制功能[#197][#196]
|
||||||
|
- 完善多语言,现在可以切换中英文显示
|
||||||
|
|
||||||
|
## Enhancement
|
||||||
|
- 由[Zo3i](https://github.com/Chanzhaoyu/chatgpt-web/pull/187) 完善 `docker-compose` 部署文档
|
||||||
|
|
||||||
|
### BugFix
|
||||||
|
- 由 [ottocsb](https://github.com/Chanzhaoyu/chatgpt-web/pull/200) 修复头像修改不同步的问题
|
||||||
|
## Other
|
||||||
|
- 更新依赖至最新
|
||||||
|
- 修改 `README` 内容
|
||||||
## v2.9.0
|
## v2.9.0
|
||||||
|
|
||||||
`2023-03-02`
|
`2023-03-02`
|
||||||
|
|
|
@ -69,9 +69,9 @@ API_REVERSE_PROXY=
|
||||||
|
|
||||||
[✓] 对代码等消息类型的格式化美化处理
|
[✓] 对代码等消息类型的格式化美化处理
|
||||||
|
|
||||||
[✗] 界面多语言
|
[✓] 界面多语言
|
||||||
|
|
||||||
[✗] 界面主题
|
[✓] 界面主题
|
||||||
|
|
||||||
[✗] More...
|
[✗] More...
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: chenzhaoyu94/chatgpt-web # 总是使用latest,更新时重新pull该tag镜像即可
|
image: chenzhaoyu94/chatgpt-web # 总是使用 latest ,更新时重新 pull 该 tag 镜像即可
|
||||||
ports:
|
ports:
|
||||||
- 3002:3002
|
- 3002:3002
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -18,9 +18,9 @@ services:
|
||||||
build: nginx
|
build: nginx
|
||||||
image: chatgpt/nginx
|
image: chatgpt/nginx
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- '80:80'
|
||||||
expose:
|
expose:
|
||||||
- "80"
|
- '80'
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx/html/:/etc/nginx/html/
|
- ./nginx/html/:/etc/nginx/html/
|
||||||
links:
|
links:
|
||||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "chatgpt-web",
|
"name": "chatgpt-web",
|
||||||
"version": "2.9.0",
|
"version": "2.9.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "ChatGPT Web",
|
"description": "ChatGPT Web",
|
||||||
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
"author": "ChenZhaoYu <chenzhaoyu1994@gmail.com>",
|
||||||
|
@ -27,34 +27,34 @@
|
||||||
"highlight.js": "^11.7.0",
|
"highlight.js": "^11.7.0",
|
||||||
"marked": "^4.2.12",
|
"marked": "^4.2.12",
|
||||||
"naive-ui": "^2.34.3",
|
"naive-ui": "^2.34.3",
|
||||||
"pinia": "^2.0.30",
|
"pinia": "^2.0.32",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.35.2",
|
"@antfu/eslint-config": "^0.35.3",
|
||||||
"@commitlint/cli": "^17.4.4",
|
"@commitlint/cli": "^17.4.4",
|
||||||
"@commitlint/config-conventional": "^17.4.4",
|
"@commitlint/config-conventional": "^17.4.4",
|
||||||
"@iconify/vue": "^4.1.0",
|
"@iconify/vue": "^4.1.0",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/marked": "^4.0.8",
|
"@types/marked": "^4.0.8",
|
||||||
"@types/node": "^18.14.0",
|
"@types/node": "^18.14.4",
|
||||||
"@types/web-bluetooth": "^0.0.16",
|
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.4",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.35.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"lint-staged": "^13.1.2",
|
"lint-staged": "^13.1.2",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.21",
|
||||||
"rimraf": "^4.1.2",
|
"rimraf": "^4.1.3",
|
||||||
"tailwindcss": "^3.2.7",
|
"tailwindcss": "^3.2.7",
|
||||||
"typescript": "~4.9.5",
|
"typescript": "~4.9.5",
|
||||||
"vite": "^4.1.2",
|
"vite": "^4.1.4",
|
||||||
"vue-tsc": "^1.1.4"
|
"vue-tsc": "^1.2.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{ts,tsx,vue}": [
|
"*.{ts,tsx,vue}": [
|
||||||
|
|
520
pnpm-lock.yaml
520
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -24,7 +24,7 @@
|
||||||
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml"
|
"common:cleanup": "rimraf node_modules && rimraf pnpm-lock.yaml"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chatgpt": "^5.0.1",
|
"chatgpt": "^5.0.4",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"esno": "^0.16.3",
|
"esno": "^0.16.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.35.3",
|
"@antfu/eslint-config": "^0.35.3",
|
||||||
"@types/express": "^4.17.17",
|
"@types/express": "^4.17.17",
|
||||||
"@types/node": "^18.14.3",
|
"@types/node": "^18.14.4",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"rimraf": "^4.1.3",
|
"rimraf": "^4.1.3",
|
||||||
"tsup": "^6.6.3",
|
"tsup": "^6.6.3",
|
||||||
|
|
|
@ -3,8 +3,8 @@ lockfileVersion: 5.4
|
||||||
specifiers:
|
specifiers:
|
||||||
'@antfu/eslint-config': ^0.35.3
|
'@antfu/eslint-config': ^0.35.3
|
||||||
'@types/express': ^4.17.17
|
'@types/express': ^4.17.17
|
||||||
'@types/node': ^18.14.3
|
'@types/node': ^18.14.4
|
||||||
chatgpt: ^5.0.1
|
chatgpt: ^5.0.4
|
||||||
dotenv: ^16.0.3
|
dotenv: ^16.0.3
|
||||||
eslint: ^8.35.0
|
eslint: ^8.35.0
|
||||||
esno: ^0.16.3
|
esno: ^0.16.3
|
||||||
|
@ -17,7 +17,7 @@ specifiers:
|
||||||
typescript: ^4.9.5
|
typescript: ^4.9.5
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
chatgpt: 5.0.1
|
chatgpt: 5.0.4
|
||||||
dotenv: 16.0.3
|
dotenv: 16.0.3
|
||||||
esno: 0.16.3
|
esno: 0.16.3
|
||||||
express: 4.18.2
|
express: 4.18.2
|
||||||
|
@ -28,7 +28,7 @@ dependencies:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@antfu/eslint-config': 0.35.3_ycpbpc6yetojsgtrx3mwntkhsu
|
'@antfu/eslint-config': 0.35.3_ycpbpc6yetojsgtrx3mwntkhsu
|
||||||
'@types/express': 4.17.17
|
'@types/express': 4.17.17
|
||||||
'@types/node': 18.14.3
|
'@types/node': 18.14.4
|
||||||
eslint: 8.35.0
|
eslint: 8.35.0
|
||||||
rimraf: 4.1.3
|
rimraf: 4.1.3
|
||||||
tsup: 6.6.3_typescript@4.9.5
|
tsup: 6.6.3_typescript@4.9.5
|
||||||
|
@ -428,19 +428,19 @@ packages:
|
||||||
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
|
resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/connect': 3.4.35
|
'@types/connect': 3.4.35
|
||||||
'@types/node': 18.14.3
|
'@types/node': 18.14.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/connect/3.4.35:
|
/@types/connect/3.4.35:
|
||||||
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.14.3
|
'@types/node': 18.14.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/express-serve-static-core/4.17.33:
|
/@types/express-serve-static-core/4.17.33:
|
||||||
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
|
resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 18.14.3
|
'@types/node': 18.14.4
|
||||||
'@types/qs': 6.9.7
|
'@types/qs': 6.9.7
|
||||||
'@types/range-parser': 1.2.4
|
'@types/range-parser': 1.2.4
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -472,8 +472,8 @@ packages:
|
||||||
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
|
resolution: {integrity: sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/node/18.14.3:
|
/@types/node/18.14.4:
|
||||||
resolution: {integrity: sha512-1y36CC5iL5CMyKALzwX9cwwxcWIxvIBe3gzs4GrXWXEQ8klQnCZ2U/WDGiNrXHmQcUhnaun17XG9TEIDlGj2RA==}
|
resolution: {integrity: sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/normalize-package-data/2.4.1:
|
/@types/normalize-package-data/2.4.1:
|
||||||
|
@ -495,7 +495,7 @@ packages:
|
||||||
resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==}
|
resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mime': 3.0.1
|
'@types/mime': 3.0.1
|
||||||
'@types/node': 18.14.3
|
'@types/node': 18.14.4
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/unist/2.0.6:
|
/@types/unist/2.0.6:
|
||||||
|
@ -896,8 +896,8 @@ packages:
|
||||||
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
|
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chatgpt/5.0.1:
|
/chatgpt/5.0.4:
|
||||||
resolution: {integrity: sha512-Wy+/2XL0FobiJFaQ6N5WnhRCnOwrUJCpoVCn67qqhiWrM1QW6lgmpvtDDKKDyvj7D1MLMjc2xB/kK8aT27mL/w==}
|
resolution: {integrity: sha512-qkppO2IDYDJC1eaXfqupXdZcOPNqtBkToRcvr9CAGM1rdsKfBDpWLTx4Y6OMNH02sgWu48aJB//0lO1M17K58w==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
|
@ -47,16 +47,16 @@ onMounted(() => {
|
||||||
>
|
>
|
||||||
Github
|
Github
|
||||||
</a>
|
</a>
|
||||||
免费,并且没有任何形式分付费行为!
|
,免费且基于 MIT 协议,没有任何形式的付费行为!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
如果你觉得此项目对你有帮助,请在 Github 帮我点个 Star 或者给予一点赞助,谢谢!
|
如果你觉得此项目对你有帮助,请在 Github 帮我点个 Star 或者给予一点赞助,谢谢!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p>API方式:{{ config?.apiModel ?? '-' }}</p>
|
<p>{{ $t("setting.api") }}:{{ config?.apiModel ?? '-' }}</p>
|
||||||
<p>反向代理:{{ config?.reverseProxy ?? '-' }}</p>
|
<p>{{ $t("setting.reverseProxy") }}:{{ config?.reverseProxy ?? '-' }}</p>
|
||||||
<p>超时时间:{{ config?.timeoutMs ?? '-' }}</p>
|
<p>{{ $t("setting.timeout") }}:{{ config?.timeoutMs ?? '-' }}</p>
|
||||||
<p>Socks代理:{{ config?.socksProxy ?? '-' }}</p>
|
<p>{{ $t("setting.socks") }}:{{ config?.socksProxy ?? '-' }}</p>
|
||||||
</div>
|
</div>
|
||||||
</NSpin>
|
</NSpin>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import type { Language, Theme } from '@/store/modules/app/helper'
|
||||||
import { SvgIcon } from '@/components/common'
|
import { SvgIcon } from '@/components/common'
|
||||||
import { useAppStore, useUserStore } from '@/store'
|
import { useAppStore, useUserStore } from '@/store'
|
||||||
import type { UserInfo } from '@/store/modules/user/helper'
|
import type { UserInfo } from '@/store/modules/user/helper'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
|
||||||
interface Emit {
|
interface Emit {
|
||||||
(event: 'update'): void
|
(event: 'update'): void
|
||||||
|
@ -61,12 +62,12 @@ const languageOptions: { label: string; key: Language; value: Language }[] = [
|
||||||
|
|
||||||
function updateUserInfo(options: Partial<UserInfo>) {
|
function updateUserInfo(options: Partial<UserInfo>) {
|
||||||
userStore.updateUserInfo(options)
|
userStore.updateUserInfo(options)
|
||||||
ms.success('Update success')
|
ms.success(t('common.success'))
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
userStore.resetUserInfo()
|
userStore.resetUserInfo()
|
||||||
ms.success('Reset success')
|
ms.success(t('common.success'))
|
||||||
emit('update')
|
emit('update')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -75,40 +76,40 @@ function handleReset() {
|
||||||
<div class="p-4 space-y-5 min-h-[200px]">
|
<div class="p-4 space-y-5 min-h-[200px]">
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Avatar Link</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.avatarLink') }}</span>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<NInput v-model:value="avatar" placeholder="" />
|
<NInput v-model:value="avatar" placeholder="" />
|
||||||
</div>
|
</div>
|
||||||
<NButton size="tiny" text type="primary" @click="updateUserInfo({ avatar })">
|
<NButton size="tiny" text type="primary" @click="updateUserInfo({ avatar })">
|
||||||
Save
|
{{ $t('common.save') }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Name</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.name') }}</span>
|
||||||
<div class="w-[200px]">
|
<div class="w-[200px]">
|
||||||
<NInput v-model:value="name" placeholder="" />
|
<NInput v-model:value="name" placeholder="" />
|
||||||
</div>
|
</div>
|
||||||
<NButton size="tiny" text type="primary" @click="updateUserInfo({ name })">
|
<NButton size="tiny" text type="primary" @click="updateUserInfo({ name })">
|
||||||
Save
|
{{ $t('common.save') }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Description</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.description') }}</span>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<NInput v-model:value="description" placeholder="" />
|
<NInput v-model:value="description" placeholder="" />
|
||||||
</div>
|
</div>
|
||||||
<NButton size="tiny" text type="primary" @click="updateUserInfo({ description })">
|
<NButton size="tiny" text type="primary" @click="updateUserInfo({ description })">
|
||||||
Save
|
{{ $t('common.save') }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Reset UserInfo</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.resetUserInfo') }}</span>
|
||||||
<NButton text type="primary" @click="handleReset">
|
<NButton text type="primary" @click="handleReset">
|
||||||
Reset
|
{{ $t('common.reset') }}
|
||||||
</NButton>
|
</NButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Theme</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.theme') }}</span>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<template v-for="item of themeOptions" :key="item.key">
|
<template v-for="item of themeOptions" :key="item.key">
|
||||||
<a
|
<a
|
||||||
|
@ -124,7 +125,7 @@ function handleReset() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="flex-shrink-0 w-[100px]">Language</span>
|
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.language') }}</span>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<template v-for="item of languageOptions" :key="item.key">
|
<template v-for="item of languageOptions" :key="item.key">
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -5,6 +5,10 @@ import General from './General.vue'
|
||||||
import About from './About.vue'
|
import About from './About.vue'
|
||||||
import { SvgIcon } from '@/components/common'
|
import { SvgIcon } from '@/components/common'
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<Emit>()
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
}
|
}
|
||||||
|
@ -13,10 +17,6 @@ interface Emit {
|
||||||
(e: 'update:visible', visible: boolean): void
|
(e: 'update:visible', visible: boolean): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
|
|
||||||
const emit = defineEmits<Emit>()
|
|
||||||
|
|
||||||
const active = ref('General')
|
const active = ref('General')
|
||||||
|
|
||||||
const reload = ref(false)
|
const reload = ref(false)
|
||||||
|
@ -39,13 +39,13 @@ function handleReload() {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NModal v-model:show="show">
|
<NModal v-model:show="show" :auto-focus="false">
|
||||||
<NCard role="dialog" aria-modal="true" :bordered="false" style="width: 100%; max-width: 640px">
|
<NCard role="dialog" aria-modal="true" :bordered="false" style="width: 100%; max-width: 640px">
|
||||||
<NTabs v-model:value="active" type="line" animated>
|
<NTabs v-model:value="active" type="line" animated>
|
||||||
<NTabPane name="General" tab="General">
|
<NTabPane name="General" tab="General">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<SvgIcon class="text-lg" icon="ri:file-user-line" />
|
<SvgIcon class="text-lg" icon="ri:file-user-line" />
|
||||||
<span class="ml-2">General</span>
|
<span class="ml-2">{{ $t('setting.general') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="min-h-[100px]">
|
<div class="min-h-[100px]">
|
||||||
<General v-if="!reload" @update="handleReload" />
|
<General v-if="!reload" @update="handleReload" />
|
||||||
|
@ -54,7 +54,7 @@ function handleReload() {
|
||||||
<NTabPane name="Config" tab="Config">
|
<NTabPane name="Config" tab="Config">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<SvgIcon class="text-lg" icon="ri:list-settings-line" />
|
<SvgIcon class="text-lg" icon="ri:list-settings-line" />
|
||||||
<span class="ml-2">Config</span>
|
<span class="ml-2">{{ $t('setting.config') }}</span>
|
||||||
</template>
|
</template>
|
||||||
<About />
|
<About />
|
||||||
</NTabPane>
|
</NTabPane>
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { enUS, zhCN } from 'naive-ui'
|
import { enUS, zhCN } from 'naive-ui'
|
||||||
import { useAppStore } from '@/store'
|
import { useAppStore } from '@/store'
|
||||||
|
import { setLocale } from '@/locales'
|
||||||
|
|
||||||
export function useLanguage() {
|
export function useLanguage() {
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const language = computed(() => {
|
const language = computed(() => {
|
||||||
if (appStore.language === 'zh-CN')
|
switch (appStore.language) {
|
||||||
return zhCN
|
case 'en-US':
|
||||||
else
|
setLocale('en-US')
|
||||||
return enUS
|
return enUS
|
||||||
|
case 'zh-CN':
|
||||||
|
setLocale('zh-CN')
|
||||||
|
return zhCN
|
||||||
|
default:
|
||||||
|
setLocale('zh-CN')
|
||||||
|
return enUS
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return { language }
|
return { language }
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
export default {
|
||||||
|
common: {
|
||||||
|
delete: 'Delete',
|
||||||
|
save: 'Save',
|
||||||
|
reset: 'Reset',
|
||||||
|
yes: 'Yes',
|
||||||
|
no: 'No',
|
||||||
|
noData: 'No Data',
|
||||||
|
wrong: 'Something went wrong, please try again later.',
|
||||||
|
success: 'Success',
|
||||||
|
failed: 'Failed',
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
placeholder: 'Ask me anything...(Shift + Enter = line break)',
|
||||||
|
placeholderMobile: 'Ask me anything...',
|
||||||
|
copy: 'Copy',
|
||||||
|
copied: 'Copied',
|
||||||
|
copyCode: 'Copy Code',
|
||||||
|
clearChat: 'Clear Chat',
|
||||||
|
clearChatConfirm: 'Are you sure to clear this chat?',
|
||||||
|
deleteMessage: 'Delete Message',
|
||||||
|
deleteMessageConfirm: 'Are you sure to delete this message?',
|
||||||
|
deleteHistoryConfirm: 'Are you sure to clear this history?',
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
setting: 'Setting',
|
||||||
|
general: 'General',
|
||||||
|
config: 'Config',
|
||||||
|
avatarLink: 'Avatar Link',
|
||||||
|
name: 'Name',
|
||||||
|
description: 'Description',
|
||||||
|
resetUserInfo: 'Reset UserInfo',
|
||||||
|
theme: 'Theme',
|
||||||
|
language: 'Language',
|
||||||
|
api: 'API',
|
||||||
|
reverseProxy: 'Reverse Proxy',
|
||||||
|
timeout: 'Timeout',
|
||||||
|
socks: 'Socks',
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { App } from 'vue'
|
||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import en from './en-US'
|
||||||
|
import cn from './zh-CN'
|
||||||
|
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||||
|
import type { Language } from '@/store/modules/app/helper'
|
||||||
|
|
||||||
|
const appStore = useAppStoreWithOut()
|
||||||
|
|
||||||
|
const defaultLocale = appStore.language || 'zh-CN'
|
||||||
|
|
||||||
|
const i18n = createI18n({
|
||||||
|
locale: defaultLocale,
|
||||||
|
fallbackLocale: 'en-US',
|
||||||
|
allowComposition: true,
|
||||||
|
messages: {
|
||||||
|
'en-US': en,
|
||||||
|
'zh-CN': cn,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export function t(key: string) {
|
||||||
|
return i18n.global.t(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLocale(locale: Language) {
|
||||||
|
i18n.global.locale = locale
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setupI18n(app: App) {
|
||||||
|
app.use(i18n)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default i18n
|
|
@ -0,0 +1,41 @@
|
||||||
|
export default {
|
||||||
|
common: {
|
||||||
|
delete: '删除',
|
||||||
|
save: '保存',
|
||||||
|
reset: '重置',
|
||||||
|
yes: '是',
|
||||||
|
no: '否',
|
||||||
|
noData: '暂无数据',
|
||||||
|
wrong: '好像出错了,请稍后再试。',
|
||||||
|
success: '操作成功',
|
||||||
|
failed: '操作失败',
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
placeholder: '来说点什么...(Shift + Enter = 换行)',
|
||||||
|
placeholderMobile: '来说点什么...',
|
||||||
|
copy: '复制',
|
||||||
|
copied: '复制成功',
|
||||||
|
copyCode: '复制代码',
|
||||||
|
clearChat: '清空会话',
|
||||||
|
clearChatConfirm: '是否清空会话?',
|
||||||
|
deleteMessage: '删除消息',
|
||||||
|
deleteMessageConfirm: '是否删除此消息?',
|
||||||
|
deleteHistoryConfirm: '确定删除此记录?',
|
||||||
|
},
|
||||||
|
setting: {
|
||||||
|
setting: '设置',
|
||||||
|
general: '总览',
|
||||||
|
config: '配置',
|
||||||
|
avatarLink: '头像链接',
|
||||||
|
name: '名称',
|
||||||
|
description: '描述',
|
||||||
|
resetUserInfo: '重置用户信息',
|
||||||
|
theme: '主题',
|
||||||
|
language: '语言',
|
||||||
|
api: 'API',
|
||||||
|
reverseProxy: '反向代理',
|
||||||
|
timeout: '超时',
|
||||||
|
socks: 'Socks',
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { setupDirectives } from './directives'
|
import { setupDirectives } from './directives'
|
||||||
import { setupAssets } from '@/plugins'
|
import { setupI18n } from './locales'
|
||||||
import { setupStore } from '@/store'
|
import { setupAssets } from './plugins'
|
||||||
import { setupRouter } from '@/router'
|
import { setupStore } from './store'
|
||||||
|
import { setupRouter } from './router'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
@ -13,6 +14,8 @@ async function bootstrap() {
|
||||||
|
|
||||||
setupDirectives(app)
|
setupDirectives(app)
|
||||||
|
|
||||||
|
setupI18n(app)
|
||||||
|
|
||||||
await setupRouter(app)
|
await setupRouter(app)
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import type { App } from 'vue'
|
import type { App } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
export const store = createPinia()
|
||||||
|
|
||||||
export function setupStore(app: App) {
|
export function setupStore(app: App) {
|
||||||
const store = createPinia()
|
|
||||||
app.use(store)
|
app.use(store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { AppState, Language, Theme } from './helper'
|
import type { AppState, Language, Theme } from './helper'
|
||||||
import { getLocalSetting, setLocalSetting } from './helper'
|
import { getLocalSetting, setLocalSetting } from './helper'
|
||||||
|
import { store } from '@/store'
|
||||||
|
|
||||||
export const useAppStore = defineStore('app-store', {
|
export const useAppStore = defineStore('app-store', {
|
||||||
state: (): AppState => getLocalSetting(),
|
state: (): AppState => getLocalSetting(),
|
||||||
|
@ -27,3 +28,7 @@ export const useAppStore = defineStore('app-store', {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function useAppStoreWithOut() {
|
||||||
|
return useAppStore(store)
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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'
|
import { encodeHTML } from '@/utils/format'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
inversion?: boolean
|
inversion?: boolean
|
||||||
|
@ -26,8 +27,10 @@ renderer.html = (html) => {
|
||||||
|
|
||||||
renderer.code = (code, language) => {
|
renderer.code = (code, language) => {
|
||||||
const validLang = !!(language && hljs.getLanguage(language))
|
const validLang = !!(language && hljs.getLanguage(language))
|
||||||
if (validLang)
|
if (validLang) {
|
||||||
return `<pre><code class="hljs ${language}">${hljs.highlight(language, code).value}</code></pre>`
|
const lang = language ?? ''
|
||||||
|
return `<pre class="code-block-wrapper"><div class="code-block-header"><span class="code-block-header__lang">${lang}</span><span class="code-block-header__copy">${t('chat.copyCode')}</span></div><code class="hljs code-block-body ${language}">${hljs.highlight(lang, code).value}</code></pre>`
|
||||||
|
}
|
||||||
return `<pre style="background: none">${hljs.highlightAuto(code).value}</pre>`
|
return `<pre style="background: none">${hljs.highlightAuto(code).value}</pre>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { NDropdown, useMessage } from 'naive-ui'
|
import { NDropdown } from 'naive-ui'
|
||||||
import AvatarComponent from './Avatar.vue'
|
import AvatarComponent from './Avatar.vue'
|
||||||
import TextComponent from './Text.vue'
|
import TextComponent from './Text.vue'
|
||||||
import { SvgIcon } from '@/components/common'
|
import { SvgIcon } from '@/components/common'
|
||||||
import { copyText } from '@/utils/format'
|
import { copyText } from '@/utils/format'
|
||||||
import { useIconRender } from '@/hooks/useIconRender'
|
import { useIconRender } from '@/hooks/useIconRender'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dateTime?: string
|
dateTime?: string
|
||||||
|
@ -24,25 +25,18 @@ const props = defineProps<Props>()
|
||||||
|
|
||||||
const emit = defineEmits<Emit>()
|
const emit = defineEmits<Emit>()
|
||||||
|
|
||||||
const ms = useMessage()
|
|
||||||
|
|
||||||
const { iconRender } = useIconRender()
|
const { iconRender } = useIconRender()
|
||||||
|
|
||||||
const textRef = ref<HTMLElement>()
|
const textRef = ref<HTMLElement>()
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
label: 'Copy Raw',
|
label: t('chat.copy'),
|
||||||
key: 'copyRaw',
|
key: 'copyText',
|
||||||
icon: iconRender({ icon: 'ri:file-copy-2-line' }),
|
icon: iconRender({ icon: 'ri:file-copy-2-line' }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Copy Text',
|
label: t('common.delete'),
|
||||||
key: 'copyText',
|
|
||||||
icon: iconRender({ icon: 'ri:file-copy-line' }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Delete',
|
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
icon: iconRender({ icon: 'ri:delete-bin-line' }),
|
icon: iconRender({ icon: 'ri:delete-bin-line' }),
|
||||||
},
|
},
|
||||||
|
@ -50,15 +44,8 @@ const options = [
|
||||||
|
|
||||||
function handleSelect(key: 'copyRaw' | 'copyText' | 'delete') {
|
function handleSelect(key: 'copyRaw' | 'copyText' | 'delete') {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'copyRaw':
|
|
||||||
if (textRef.value && (textRef.value as any).textRef) {
|
|
||||||
copyText({ text: (textRef.value as any).textRef.innerText })
|
|
||||||
ms.success('Copied Raw')
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case 'copyText':
|
case 'copyText':
|
||||||
copyText({ text: props.text ?? '', origin: false })
|
copyText({ text: props.text ?? '' })
|
||||||
ms.success('Copied Text')
|
|
||||||
return
|
return
|
||||||
case 'delete':
|
case 'delete':
|
||||||
emit('delete')
|
emit('delete')
|
||||||
|
|
|
@ -24,9 +24,37 @@
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
code.hljs{
|
code.hljs {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
&-wrapper {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
color: #b3b3b3;
|
||||||
|
|
||||||
|
&__copy{
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
user-select: none;
|
||||||
|
&:hover {
|
||||||
|
color: #65a665;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark {
|
html.dark {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
|
export function useCopyCode() {
|
||||||
|
function copyCodeBlock() {
|
||||||
|
const codeBlockWrapper = document.querySelectorAll('.code-block-wrapper')
|
||||||
|
codeBlockWrapper.forEach((wrapper) => {
|
||||||
|
const copyBtn = wrapper.querySelector('.code-block-header__copy')
|
||||||
|
const codeBlock = wrapper.querySelector('.code-block-body')
|
||||||
|
if (copyBtn && codeBlock) {
|
||||||
|
copyBtn.addEventListener('click', () => {
|
||||||
|
navigator.clipboard.writeText(codeBlock.textContent ?? '')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => copyCodeBlock())
|
||||||
|
}
|
|
@ -1,23 +1,25 @@
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { NButton, NInput, useDialog, useMessage } from 'naive-ui'
|
import { NButton, NInput, useDialog } from 'naive-ui'
|
||||||
import { Message } from './components'
|
import { Message } from './components'
|
||||||
import { useScroll } from './hooks/useScroll'
|
import { useScroll } from './hooks/useScroll'
|
||||||
import { useChat } from './hooks/useChat'
|
import { useChat } from './hooks/useChat'
|
||||||
|
import { useCopyCode } from './hooks/useCopyCode'
|
||||||
import { HoverButton, SvgIcon } from '@/components/common'
|
import { HoverButton, SvgIcon } from '@/components/common'
|
||||||
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
import { useBasicLayout } from '@/hooks/useBasicLayout'
|
||||||
import { useChatStore } from '@/store'
|
import { useChatStore } from '@/store'
|
||||||
import { fetchChatAPIProcess } from '@/api'
|
import { fetchChatAPIProcess } from '@/api'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
|
||||||
let controller = new AbortController()
|
let controller = new AbortController()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const ms = useMessage()
|
|
||||||
|
|
||||||
const chatStore = useChatStore()
|
const chatStore = useChatStore()
|
||||||
|
|
||||||
|
useCopyCode()
|
||||||
const { isMobile } = useBasicLayout()
|
const { isMobile } = useBasicLayout()
|
||||||
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
|
const { addChat, updateChat, updateChatSome, getChatByUuidAndIndex } = useChat()
|
||||||
const { scrollRef, scrollToBottom } = useScroll()
|
const { scrollRef, scrollToBottom } = useScroll()
|
||||||
|
@ -118,7 +120,7 @@ async function onConversation() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
catch (error: any) {
|
catch (error: any) {
|
||||||
const errorMessage = error?.message ?? 'Something went wrong, please try again later.'
|
const errorMessage = error?.message ?? t('common.wrong')
|
||||||
|
|
||||||
if (error.message === 'canceled') {
|
if (error.message === 'canceled') {
|
||||||
updateChatSome(
|
updateChatSome(
|
||||||
|
@ -245,7 +247,7 @@ async function onRegenerate(index: number) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const errorMessage = error?.message ?? 'Something went wrong, please try again later.'
|
const errorMessage = error?.message ?? t('common.wrong')
|
||||||
|
|
||||||
updateChat(
|
updateChat(
|
||||||
+uuid,
|
+uuid,
|
||||||
|
@ -271,13 +273,12 @@ function handleDelete(index: number) {
|
||||||
return
|
return
|
||||||
|
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: 'Delete Message',
|
title: t('chat.deleteMessage'),
|
||||||
content: 'Are you sure to delete this message?',
|
content: t('chat.deleteMessageConfirm'),
|
||||||
positiveText: 'Yes',
|
positiveText: t('common.yes'),
|
||||||
negativeText: 'No',
|
negativeText: t('common.no'),
|
||||||
onPositiveClick: () => {
|
onPositiveClick: () => {
|
||||||
chatStore.deleteChatByUuid(+uuid, index)
|
chatStore.deleteChatByUuid(+uuid, index)
|
||||||
ms.success('Message deleted successfully.')
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -287,10 +288,10 @@ function handleClear() {
|
||||||
return
|
return
|
||||||
|
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: 'Clear Chat',
|
title: t('chat.clearChat'),
|
||||||
content: 'Are you sure to clear this chat?',
|
content: t('chat.clearChatConfirm'),
|
||||||
positiveText: 'Yes',
|
positiveText: t('common.yes'),
|
||||||
negativeText: 'No',
|
negativeText: t('common.no'),
|
||||||
onPositiveClick: () => {
|
onPositiveClick: () => {
|
||||||
chatStore.clearChatByUuid(+uuid)
|
chatStore.clearChatByUuid(+uuid)
|
||||||
},
|
},
|
||||||
|
@ -315,8 +316,8 @@ function handleStop() {
|
||||||
|
|
||||||
const placeholder = computed(() => {
|
const placeholder = computed(() => {
|
||||||
if (isMobile.value)
|
if (isMobile.value)
|
||||||
return 'Ask me anything...'
|
return t('chat.placeholderMobile')
|
||||||
return 'Ask me anything... (Shift + Enter = line break)'
|
return t('chat.placeholder')
|
||||||
})
|
})
|
||||||
|
|
||||||
const buttonDisabled = computed(() => {
|
const buttonDisabled = computed(() => {
|
||||||
|
|
|
@ -11,7 +11,7 @@ const show = ref(false)
|
||||||
<footer class="flex items-center justify-between min-w-0 p-4 overflow-hidden border-t dark:border-neutral-800">
|
<footer class="flex items-center justify-between min-w-0 p-4 overflow-hidden border-t dark:border-neutral-800">
|
||||||
<UserAvatar />
|
<UserAvatar />
|
||||||
|
|
||||||
<HoverButton tooltip="Setting" @click="show = true">
|
<HoverButton :tooltip="$t('setting.setting')" @click="show = true">
|
||||||
<span class="text-xl text-[#4f555e] dark:text-white">
|
<span class="text-xl text-[#4f555e] dark:text-white">
|
||||||
<SvgIcon icon="ri:settings-4-line" />
|
<SvgIcon icon="ri:settings-4-line" />
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -49,7 +49,7 @@ function isActive(uuid: number) {
|
||||||
<template v-if="!dataSources.length">
|
<template v-if="!dataSources.length">
|
||||||
<div class="flex flex-col items-center mt-4 text-center text-neutral-300">
|
<div class="flex flex-col items-center mt-4 text-center text-neutral-300">
|
||||||
<SvgIcon icon="ri:inbox-line" class="mb-2 text-3xl" />
|
<SvgIcon icon="ri:inbox-line" class="mb-2 text-3xl" />
|
||||||
<span>No history</span>
|
<span>{{ $t('common.noData') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
@ -87,7 +87,7 @@ function isActive(uuid: number) {
|
||||||
<SvgIcon icon="ri:delete-bin-line" />
|
<SvgIcon icon="ri:delete-bin-line" />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
Are you sure to clear this history?
|
{{ $t('chat.deleteHistoryConfirm') }}
|
||||||
</NPopconfirm>
|
</NPopconfirm>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,10 +13,11 @@
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"types": ["vite/client", "node", "naive-ui/volar", "web-bluetooth"]
|
"types": ["vite/client", "node", "naive-ui/volar"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules", "dist", "service"]
|
"exclude": ["node_modules", "dist", "service"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue