@lobehub/chat 0.85.3 → 0.86.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.dockerignore +10 -0
  2. package/.github/workflows/issue-check-inactive.yml +0 -1
  3. package/.github/workflows/issue-close-require.yml +0 -3
  4. package/.github/workflows/issue-remove-inactive.yml +0 -1
  5. package/.github/workflows/pre-welcome.yml +4 -5
  6. package/.github/workflows/release.yml +3 -0
  7. package/.github/workflows/sync.yml +0 -2
  8. package/.releaserc.js +14 -1
  9. package/CHANGELOG.md +7 -50
  10. package/Dockerfile +47 -0
  11. package/locales/en_US/error.json +3 -4
  12. package/locales/ja_JP/error.json +3 -4
  13. package/locales/ko_KR/error.json +3 -4
  14. package/locales/ru_RU/error.json +3 -4
  15. package/locales/zh_CN/error.json +2 -3
  16. package/locales/zh_TW/error.json +3 -4
  17. package/next.config.mjs +3 -0
  18. package/package.json +3 -3
  19. package/public/manifest.json +1 -1
  20. package/scripts/i18nWorkflow/genDiff.ts +1 -2
  21. package/scripts/i18nWorkflow/utils.ts +9 -3
  22. package/src/app/api/auth.ts +2 -2
  23. package/src/app/api/openai/chat/route.ts +5 -19
  24. package/src/app/api/openai/createAzureOpenai.ts +1 -4
  25. package/src/app/api/openai/createOpenai.ts +6 -6
  26. package/src/app/api/openai/errorResponse.ts +0 -1
  27. package/src/app/chat/(desktop)/index.tsx +1 -1
  28. package/src/app/chat/(desktop)/layout.desktop.tsx +1 -1
  29. package/src/app/chat/(mobile)/mobile/index.tsx +1 -1
  30. package/src/app/chat/features/Conversation/ChatList/Error/OpenAiBizError.tsx +9 -4
  31. package/src/app/chat/features/Conversation/ChatList/Error/index.tsx +0 -5
  32. package/src/app/chat/features/SessionListContent/Inbox/index.tsx +0 -1
  33. package/src/app/chat/features/SessionListContent/List/index.tsx +0 -1
  34. package/src/app/home/index.tsx +1 -1
  35. package/src/app/market/(desktop)/features/AgentDetail.tsx +1 -1
  36. package/src/app/market/(desktop)/features/Header.tsx +1 -1
  37. package/src/app/market/(desktop)/layout.desktop.tsx +2 -2
  38. package/src/app/market/features/AgentCard/AgentCardBanner.tsx +0 -1
  39. package/src/app/market/features/AgentDetailContent/AgentInfo/Header.tsx +1 -1
  40. package/src/app/settings/(desktop)/layout.desktop.tsx +1 -1
  41. package/src/app/settings/common/index.tsx +0 -4
  42. package/src/app/settings/features/SideBar/List.tsx +0 -1
  43. package/src/app/settings/features/SideBar/index.tsx +1 -1
  44. package/src/app/settings/features/UpgradeAlert.tsx +1 -1
  45. package/src/app/settings/llm/LLM/index.tsx +25 -7
  46. package/src/app/settings/llm/index.tsx +1 -16
  47. package/src/app/welcome/(desktop)/features/Showcase.tsx +1 -1
  48. package/src/app/welcome/(desktop)/layout.desktop.tsx +1 -1
  49. package/src/app/welcome/(mobile)/features/Showcase.tsx +1 -1
  50. package/src/app/welcome/(mobile)/layout.mobile.tsx +1 -1
  51. package/src/app/welcome/features/Banner/index.tsx +17 -16
  52. package/src/app/welcome/features/Banner/style.ts +1 -1
  53. package/src/const/layoutTokens.ts +1 -1
  54. package/src/features/FolderPanel/index.tsx +2 -2
  55. package/src/features/SideBar/index.tsx +1 -1
  56. package/src/layout/AppLayout.desktop.tsx +0 -1
  57. package/src/layout/AppLayout.mobile.tsx +1 -1
  58. package/src/layout/GlobalLayout/LocaleLoading.tsx +1 -1
  59. package/src/locales/default/error.ts +1 -2
  60. package/src/styles/global.ts +5 -6
  61. package/src/types/fetch.ts +4 -3
  62. package/src/app/chat/features/Conversation/ChatList/Error/OpenAPIKey.tsx +0 -12
  63. package/src/app/settings/features/Footer.tsx +0 -24
package/.dockerignore ADDED
@@ -0,0 +1,10 @@
1
+ Dockerfile
2
+ .dockerignore
3
+ node_modules
4
+ npm-debug.log
5
+ .next
6
+ .git
7
+ scripts
8
+ docs
9
+ .github
10
+ *.md
@@ -18,6 +18,5 @@ jobs:
18
18
  uses: actions-cool/issues-helper@v3
19
19
  with:
20
20
  actions: 'check-inactive'
21
- token: ${{ secrets.GH_TOKEN }}
22
21
  inactive-label: 'Inactive'
23
22
  inactive-day: 30
@@ -18,7 +18,6 @@ jobs:
18
18
  uses: actions-cool/issues-helper@v3
19
19
  with:
20
20
  actions: 'close-issues'
21
- token: ${{ secrets.GH_TOKEN }}
22
21
  labels: '✅ Fixed'
23
22
  inactive-day: 3
24
23
  body: |
@@ -29,7 +28,6 @@ jobs:
29
28
  uses: actions-cool/issues-helper@v3
30
29
  with:
31
30
  actions: 'close-issues'
32
- token: ${{ secrets.GH_TOKEN }}
33
31
  labels: '🤔 Need Reproduce'
34
32
  inactive-day: 3
35
33
  body: |
@@ -40,7 +38,6 @@ jobs:
40
38
  uses: actions-cool/issues-helper@v3
41
39
  with:
42
40
  actions: 'close-issues'
43
- token: ${{ secrets.GH_TOKEN }}
44
41
  labels: "🙅🏻‍♀️ WON'T DO"
45
42
  inactive-day: 3
46
43
  body: |
@@ -21,6 +21,5 @@ jobs:
21
21
  uses: actions-cool/issues-helper@v3
22
22
  with:
23
23
  actions: 'remove-labels'
24
- token: ${{ secrets.GH_TOKEN }}
25
24
  issue-number: ${{ github.event.issue.number }}
26
25
  labels: 'Inactive'
@@ -14,10 +14,9 @@ jobs:
14
14
  with:
15
15
  token: ${{ secrets.GITHUB_TOKEN }}
16
16
  comment: |
17
- 🎉 Great PR @${{ github.event.pull_request.user.login }} 🎉
17
+ Great PR @${{ github.event.pull_request.user.login }}. The growth of project is inseparable from user feedback and contribution, thanks for your contribution ❤️
18
18
 
19
- The growth of project is inseparable from user feedback and contribution, thanks for your contribution ❤️
20
-
21
- 项目的成长离不开用户反馈和贡献,感谢您的贡献 ❤️
22
- emoji: 'hooray'
19
+ 非常棒的 PR @${{ github.event.pull_request.user.login }}. 项目的成长离不开用户反馈和贡献,感谢您的贡献 ❤️
20
+ emoji: 'heart'
23
21
  pr-emoji: '+1, heart'
22
+ close: true
@@ -3,6 +3,7 @@ on:
3
3
  push:
4
4
  branches:
5
5
  - main
6
+ - beta
6
7
 
7
8
  jobs:
8
9
  release:
@@ -28,3 +29,5 @@ jobs:
28
29
  env:
29
30
  GH_TOKEN: ${{ secrets.GH_TOKEN }}
30
31
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
32
+ DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
33
+ DOCKER_REGISTRY_USER: ${{ secrets.DOCKER_REGISTRY_USER }}
@@ -47,8 +47,6 @@ jobs:
47
47
 
48
48
  由于 [LobeChat][lobechat] 上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次,请查看 [详细教程][tutorial-zh-CN]
49
49
 
50
- ![](https://github-production-user-asset-6210df.s3.amazonaws.com/17870709/273954625-df80c890-0822-4ac2-95e6-c990785cbed5.png)
51
-
52
50
  [lobechat]: https://github.com/lobehub/lobe-chat
53
51
  [tutorial-zh-CN]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync.zh-CN
54
52
  [tutorial-en-US]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync
package/.releaserc.js CHANGED
@@ -1 +1,14 @@
1
- module.exports = require('@lobehub/lint').semanticRelease;
1
+ const release = require('@lobehub/lint').semanticRelease;
2
+
3
+ module.exports = {
4
+ ...release,
5
+ plugins: [
6
+ ...release.plugins,
7
+ [
8
+ '@codedependant/semantic-release-docker',
9
+ {
10
+ dockerImage: 'lobe-chat',
11
+ },
12
+ ],
13
+ ],
14
+ };
package/CHANGELOG.md CHANGED
@@ -2,73 +2,30 @@
2
2
 
3
3
  # Changelog
4
4
 
5
- ### [Version 0.85.3](https://github.com/lobehub/lobe-chat/compare/v0.85.2...v0.85.3)
5
+ ## [Version 0.86.0-beta.1](https://github.com/lobehub/lobe-chat/compare/v0.85.0...v0.86.0-beta.1)
6
6
 
7
7
  <sup>Released on **2023-10-10**</sup>
8
8
 
9
- #### 💄 Styles
10
-
11
- - **misc**: Add new components, modify display properties, and update settings feature, Replace 100vh with 100% to fix mobile scroll problem.
12
-
13
- <br/>
14
-
15
- <details>
16
- <summary><kbd>Improvements and Fixes</kbd></summary>
17
-
18
- #### Styles
19
-
20
- - **misc**: Add new components, modify display properties, and update settings feature ([87a4a46](https://github.com/lobehub/lobe-chat/commit/87a4a46))
21
- - **misc**: Replace 100vh with 100% to fix mobile scroll problem ([2ef3c94](https://github.com/lobehub/lobe-chat/commit/2ef3c94))
22
-
23
- </details>
24
-
25
- <div align="right">
26
-
27
- [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
28
-
29
- </div>
30
-
31
- ### [Version 0.85.2](https://github.com/lobehub/lobe-chat/compare/v0.85.1...v0.85.2)
9
+ #### ✨ Features
32
10
 
33
- <sup>Released on **2023-10-10**</sup>
11
+ - **misc**: Dockerize lobe-chat.
34
12
 
35
13
  #### 🐛 Bug Fixes
36
14
 
37
- - **misc**: Add apikey form when there is no default api key in env.
15
+ - **misc**: Use bun to improve speed.
38
16
 
39
17
  <br/>
40
18
 
41
19
  <details>
42
20
  <summary><kbd>Improvements and Fixes</kbd></summary>
43
21
 
44
- #### What's fixed
45
-
46
- - **misc**: Add apikey form when there is no default api key in env, closes [#290](https://github.com/lobehub/lobe-chat/issues/290) ([2c907e9](https://github.com/lobehub/lobe-chat/commit/2c907e9))
47
-
48
- </details>
49
-
50
- <div align="right">
51
-
52
- [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
53
-
54
- </div>
55
-
56
- ### [Version 0.85.1](https://github.com/lobehub/lobe-chat/compare/v0.85.0...v0.85.1)
57
-
58
- <sup>Released on **2023-10-10**</sup>
59
-
60
- #### 🐛 Bug Fixes
61
-
62
- - **misc**: Fix mobile safearea (fix.
63
-
64
- <br/>
22
+ #### What's improved
65
23
 
66
- <details>
67
- <summary><kbd>Improvements and Fixes</kbd></summary>
24
+ - **misc**: Dockerize lobe-chat ([1b750f8](https://github.com/lobehub/lobe-chat/commit/1b750f8))
68
25
 
69
26
  #### What's fixed
70
27
 
71
- - **misc**: Fix mobile safearea (fix, closes [#211](https://github.com/lobehub/lobe-chat/issues/211) ([68775b8](https://github.com/lobehub/lobe-chat/commit/68775b8))
28
+ - **misc**: Use bun to improve speed ([8db2a3c](https://github.com/lobehub/lobe-chat/commit/8db2a3c))
72
29
 
73
30
  </details>
74
31
 
package/Dockerfile ADDED
@@ -0,0 +1,47 @@
1
+ FROM node:20-slim AS base
2
+ ENV PNPM_HOME="/pnpm"
3
+ ENV PATH="$PNPM_HOME:$PATH"
4
+ RUN corepack enable
5
+
6
+ ## Install dependencies only when needed
7
+ FROM base AS builder
8
+ WORKDIR /app
9
+
10
+ COPY package.json ./
11
+
12
+ RUN pnpm i -g bun
13
+ # If you want to build docker in China
14
+ #RUN npm config set registry https://registry.npmmirror.com/
15
+ RUN bun i
16
+
17
+ COPY . .
18
+ RUN pnpm run build:docker # run build standalone for docker version
19
+
20
+ ## Production image, copy all the files and run next
21
+ FROM base AS runner
22
+ WORKDIR /app
23
+
24
+ ENV NODE_ENV production
25
+
26
+ RUN addgroup --system --gid 1001 nodejs
27
+ RUN adduser --system --uid 1001 nextjs
28
+
29
+ COPY --from=builder /app/public ./public
30
+
31
+ # Set the correct permission for prerender cache
32
+ RUN mkdir .next
33
+ RUN chown nextjs:nodejs .next
34
+
35
+ # Automatically leverage output traces to reduce image size
36
+ # https://nextjs.org/docs/advanced-features/output-file-tracing
37
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
38
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
39
+
40
+ USER nextjs
41
+
42
+ EXPOSE 3000
43
+
44
+ # set hostname to localhost
45
+ ENV HOSTNAME "0.0.0.0"
46
+
47
+ CMD ["node", "server.js"]
@@ -25,13 +25,12 @@
25
25
  "PluginApiNotFound": "Sorry, the API does not exist in the plugin's manifest. Please check if your request method matches the plugin manifest API",
26
26
  "PluginApiParamsError": "Sorry, the input parameter validation for the plugin request failed. Please check if the input parameters match the API description",
27
27
  "PluginSettingsInvalid": "This plugin needs to be correctly configured before it can be used. Please check if your configuration is correct",
28
- "PluginServerError": "Plugin server request returned an error. Please check your plugin manifest file, plugin configuration, or server implementation based on the error information below",
29
- "NoAPIKey": "OpenAI API Key is empty, please add a custom OpenAI API Key"
28
+ "PluginServerError": "Plugin server request returned an error. Please check your plugin manifest file, plugin configuration, or server implementation based on the error information below"
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "title": "Use Custom API Key",
34
- "description": "Enter your OpenAI API Key to start the conversation. The application will not record your API Key."
32
+ "description": "Enter your OpenAI API Key to bypass password verification. The application will not record your API Key.",
33
+ "title": "Use Custom API Key"
35
34
  },
36
35
  "confirm": "Confirm and Retry",
37
36
  "password": {
@@ -25,13 +25,12 @@
25
25
  "PluginMarketIndexInvalid": "申し訳ありませんが、プラグインのインデックスの検証に失敗しました。インデックスファイルの形式が正しいかどうかを確認してください",
26
26
  "PluginManifestNotFound": "申し訳ありませんが、サーバーでプラグインのマニフェストファイル (manifest.json) が見つかりませんでした。プラグインのマニフェストファイルのアドレスが正しいかどうかを確認してください",
27
27
  "PluginManifestInvalid": "申し訳ありませんが、このプラグインのマニフェストの検証に失敗しました。マニフェストの形式が正しいかどうかを確認してください",
28
- "PluginApiNotFound": "申し訳ありませんが、プラグインのマニフェストに指定されたAPIが見つかりませんでした。リクエストメソッドとプラグインのマニフェストのAPIが一致しているかどうかを確認してください",
29
- "NoAPIKey": "OpenAI APIキーが空です。カスタムOpenAI APIキーを追加してください。"
28
+ "PluginApiNotFound": "申し訳ありませんが、プラグインのマニフェストに指定されたAPIが見つかりませんでした。リクエストメソッドとプラグインのマニフェストのAPIが一致しているかどうかを確認してください"
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "title": "カスタムAPIキーの使用",
34
- "description": "OpenAI APIキーを入力すると、会話を開始できます。アプリはあなたのAPIキーを記録しません。"
32
+ "description": "OpenAI APIキーを入力すると、パスワード認証をバイパスできます。アプリはAPIキーを記録しません",
33
+ "title": "カスタムAPIキーの使用"
35
34
  },
36
35
  "closeMessage": "ヒントを閉じる",
37
36
  "confirm": "確認して再試行",
@@ -25,13 +25,12 @@
25
25
  "PluginSettingsInvalid": "플러그인을 사용하려면 올바른 구성이 필요합니다. 구성이 올바른지 확인해주세요.",
26
26
  "PluginServerError": "플러그인 서버 요청이 오류로 반환되었습니다. 플러그인 설명 파일, 플러그인 구성 또는 서버 구현을 확인해주세요.",
27
27
  "InvalidAccessCode": "암호가 올바르지 않거나 비어 있습니다. 올바른 액세스 암호를 입력하거나 사용자 지정 OpenAI API 키를 추가해주세요.",
28
- "OpenAIBizError": "OpenAI 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 문제를 해결하거나 다시 시도해주세요.",
29
- "NoAPIKey": "OpenAI API 키가 비어 있습니다. 사용자 정의 OpenAI API 키를 추가해주세요."
28
+ "OpenAIBizError": "OpenAI 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 문제를 해결하거나 다시 시도해주세요."
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "title": "사용자 정의 API 키 사용",
34
- "description": "OpenAI API 키를 입력하면 대화를 시작할 수 있습니다. 애플리케이션은 API 키를 기록하지 않습니다."
32
+ "description": "OpenAI API 키를 입력하여 암호 확인을 우회할 수 있습니다. 애플리케이션은 API 키를 기록하지 않습니다.",
33
+ "title": "사용자 정의 API 키 사용"
35
34
  },
36
35
  "closeMessage": "알림 닫기",
37
36
  "confirm": "확인 및 다시 시도",
@@ -25,13 +25,12 @@
25
25
  "PluginApiNotFound": "К сожалению, API не существует в манифесте плагина. Пожалуйста, проверьте, соответствует ли ваш метод запроса API манифеста плагина",
26
26
  "PluginApiParamsError": "К сожалению, проверка входных параметров для запроса плагина не удалась. Пожалуйста, проверьте, соответствуют ли входные параметры описанию API",
27
27
  "PluginSettingsInvalid": "Этот плагин необходимо правильно настроить, прежде чем его можно будет использовать. Пожалуйста, проверьте правильность вашей конфигурации",
28
- "PluginServerError": "Запрос сервера плагина возвратил ошибку. Проверьте файл манифеста плагина, конфигурацию плагина или реализацию сервера на основе информации об ошибке ниже",
29
- "NoAPIKey": "Ключ OpenAI API пуст, пожалуйста, добавьте свой собственный ключ OpenAI API"
28
+ "PluginServerError": "Запрос сервера плагина возвратил ошибку. Проверьте файл манифеста плагина, конфигурацию плагина или реализацию сервера на основе информации об ошибке ниже"
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "title": "Использовать собственный ключ API",
34
- "description": "Введите свой ключ API OpenAI, чтобы начать сеанс. Приложение не будет сохранять ваш ключ API."
32
+ "description": "Введите свой ключ API OpenAI, чтобы обойти проверку пароля. Приложение не будет записывать ваш ключ API.",
33
+ "title": "Использовать собственный ключ API"
35
34
  },
36
35
  "confirm": "Подтвердить и повторить попытку",
37
36
  "password": {
@@ -25,12 +25,11 @@
25
25
  "PluginSettingsInvalid": "该插件需要正确配置后才可以使用,请检查你的配置是否正确",
26
26
  "PluginServerError": "插件服务端请求返回出错,请检查根据下面的报错信息检查你的插件描述文件、插件配置或服务端实现",
27
27
  "InvalidAccessCode": "密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key",
28
- "OpenAIBizError": "请求 OpenAI 服务出错,请根据以下信息排查或重试",
29
- "NoAPIKey": "OpenAI API Key 为空,请添加自定义 OpenAI API Key"
28
+ "OpenAIBizError": "请求 OpenAI 服务出错,请根据以下信息排查或重试"
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "description": "输入你的 OpenAI API Key 即可开始会话。应用不会记录你的 API Key",
32
+ "description": "输入你的 OpenAI API Key 即可绕过密码验证。应用不会记录你的 API Key",
34
33
  "title": "使用自定义 API Key"
35
34
  },
36
35
  "closeMessage": "关闭提示",
@@ -25,13 +25,12 @@
25
25
  "PluginApiNotFound": "抱歉,外掛描述檔案中不存在該 API。請檢查您的請求方法與外掛清單 API 是否相符",
26
26
  "PluginApiParamsError": "抱歉,該外掛請求的輸入參數驗證失敗。請檢查輸入參數與 API 描述資訊是否相符",
27
27
  "PluginSettingsInvalid": "該外掛需要正確設定後才可以使用。請檢查您的設定是否正確",
28
- "PluginServerError": "外掛伺服器請求回傳錯誤。請根據下面的錯誤資訊檢查您的外掛描述檔案、外掛設定或伺服器實作",
29
- "NoAPIKey": "OpenAI API 金鑰為空,請添加自訂 OpenAI API 金鑰"
28
+ "PluginServerError": "外掛伺服器請求回傳錯誤。請根據下面的錯誤資訊檢查您的外掛描述檔案、外掛設定或伺服器實作"
30
29
  },
31
30
  "unlock": {
32
31
  "apikey": {
33
- "title": "使用自定義 API Key",
34
- "description": "輸入你的 OpenAI API Key 即可開始會話。應用程式不會記錄你的 API Key"
32
+ "description": "輸入您的 OpenAI API Key 以繞過密碼驗證。應用程式不會記錄您的 API Key。",
33
+ "title": "使用自定義 API Key"
35
34
  },
36
35
  "confirm": "確認並重試",
37
36
  "password": {
package/next.config.mjs CHANGED
@@ -2,6 +2,7 @@ import analyzer from '@next/bundle-analyzer';
2
2
  import nextPWA from 'next-pwa';
3
3
 
4
4
  const isProd = process.env.NODE_ENV === 'production';
5
+ const buildWithDocker = process.env.DOCKER === 'true';
5
6
 
6
7
  const withBundleAnalyzer = analyzer({
7
8
  enabled: process.env.ANALYZE === 'true',
@@ -32,6 +33,8 @@ const nextConfig = {
32
33
  AGENTS_INDEX_URL: process.env.AGENTS_INDEX_URL,
33
34
  PLUGINS_INDEX_URL: process.env.PLUGINS_INDEX_URL,
34
35
  },
36
+
37
+ output: buildWithDocker ? 'standalone' : undefined,
35
38
  };
36
39
 
37
40
  export default isProd ? withBundleAnalyzer(withPWA(nextConfig)) : nextConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.85.3",
3
+ "version": "0.86.0-beta.1",
4
4
  "description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -25,6 +25,7 @@
25
25
  "scripts": {
26
26
  "build": "next build",
27
27
  "build:analyze": "ANALYZE=true next build",
28
+ "build:docker": "DOCKER=true next build",
28
29
  "dev": "next dev -p 3010",
29
30
  "i18n": "npm run i18n:workflow && lobe-i18n",
30
31
  "i18n:workflow": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
@@ -120,6 +121,7 @@
120
121
  "zustand-utils": "^1"
121
122
  },
122
123
  "devDependencies": {
124
+ "@codedependant/semantic-release-docker": "^4",
123
125
  "@commitlint/cli": "^17",
124
126
  "@lobehub/i18n-cli": "latest",
125
127
  "@lobehub/lint": "latest",
@@ -128,7 +130,6 @@
128
130
  "@testing-library/jest-dom": "^6",
129
131
  "@testing-library/react": "^14",
130
132
  "@types/chroma-js": "^2",
131
- "@types/lodash": "^4",
132
133
  "@types/lodash-es": "^4",
133
134
  "@types/mixpanel-browser": "^2",
134
135
  "@types/node": "^20",
@@ -147,7 +148,6 @@
147
148
  "husky": "^8",
148
149
  "jsdom": "^22",
149
150
  "lint-staged": "^14",
150
- "lodash": "^4",
151
151
  "next-pwa": "^5",
152
152
  "node-fetch": "^3",
153
153
  "prettier": "^3",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "background_color": "#000000",
3
3
  "description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
4
- "display": "standalone",
4
+ "display": "fullscreen",
5
5
  "icons": [
6
6
  {
7
7
  "src": "/icons/icon-192x192.png",
@@ -1,6 +1,5 @@
1
1
  import { consola } from 'consola';
2
2
  import { colors } from 'consola/utils';
3
- import { unset } from 'lodash';
4
3
  import { existsSync } from 'node:fs';
5
4
 
6
5
  import {
@@ -9,7 +8,7 @@ import {
9
8
  localesResourcesFilepath,
10
9
  outputLocaleJsonFilepath,
11
10
  } from './const';
12
- import { diff, readJSON, tagWhite, tagYellow, writeJSON } from './utils';
11
+ import { diff, readJSON, tagWhite, tagYellow, unset, writeJSON } from './utils';
13
12
 
14
13
  export const genDiff = () => {
15
14
  consola.start(`Diff between Dev/Prod local...`);
@@ -1,7 +1,6 @@
1
1
  import { consola } from 'consola';
2
2
  import { colors } from 'consola/utils';
3
3
  import { tocForResources } from 'i18next-resources-for-ts';
4
- import { isObject } from 'lodash';
5
4
  import { readFileSync, writeFileSync } from 'node:fs';
6
5
  import { resolve } from 'node:path';
7
6
 
@@ -59,8 +58,8 @@ export const diff = (obj1: any, obj2: any, prefix: string = ''): string[] => {
59
58
  let result: string[] = [];
60
59
  for (const key in obj1) {
61
60
  if (!obj2[key]) continue;
62
- if (isObject(obj1[key]) && isObject(obj2[key])) {
63
- result = [...result, ...diff(obj1[key], obj2[key], `${prefix}${key}.`)];
61
+ if (typeof obj1[key] === 'object' && obj1[key] !== null && obj2[key]) {
62
+ result = result.concat(diff(obj1[key], obj2[key], `${key}.`));
64
63
  } else if (obj1[key] !== obj2[key]) {
65
64
  result.push(`${prefix}${key}`);
66
65
  }
@@ -68,6 +67,13 @@ export const diff = (obj1: any, obj2: any, prefix: string = ''): string[] => {
68
67
  return result;
69
68
  };
70
69
 
70
+ export const unset = (obj: any, key: string) => {
71
+ const keys = key.split('.');
72
+ const lastKey = keys.pop();
73
+ const lastObj = keys.reduce((obj, key) => (obj[key] = obj[key] || {}), obj);
74
+ if (lastKey) delete lastObj[lastKey];
75
+ };
76
+
71
77
  export const tagBlue = (text: string) => colors.bgBlueBright(colors.black(` ${text} `));
72
78
  export const tagYellow = (text: string) => colors.bgYellowBright(colors.black(` ${text} `));
73
79
  export const tagGreen = (text: string) => colors.bgGreenBright(colors.black(` ${text} `));
@@ -9,12 +9,12 @@ interface AuthConfig {
9
9
  export const checkAuth = ({ apiKey, accessCode }: AuthConfig) => {
10
10
  const { ACCESS_CODE } = getServerConfig();
11
11
 
12
- // if apiKey exist
12
+ // 如果存在 apiKey
13
13
  if (apiKey) {
14
14
  return { auth: true };
15
15
  }
16
16
 
17
- // if accessCode doesn't exist
17
+ // 如果不存在,则检查 accessCode
18
18
  if (!ACCESS_CODE) return { auth: true };
19
19
 
20
20
  if (accessCode !== ACCESS_CODE) {
@@ -2,7 +2,7 @@ import OpenAI from 'openai';
2
2
 
3
3
  import { getServerConfig } from '@/config/server';
4
4
  import { getOpenAIAuthFromRequest } from '@/const/fetch';
5
- import { ChatErrorType, ErrorType } from '@/types/fetch';
5
+ import { ErrorType } from '@/types/fetch';
6
6
  import { OpenAIStreamPayload } from '@/types/openai';
7
7
 
8
8
  import { checkAuth } from '../../auth';
@@ -29,24 +29,10 @@ export const POST = async (req: Request) => {
29
29
  const { USE_AZURE_OPENAI } = getServerConfig();
30
30
  const useAzureOpenAI = useAzure || USE_AZURE_OPENAI;
31
31
 
32
- try {
33
- if (useAzureOpenAI) {
34
- openai = createAzureOpenai({
35
- apiVersion,
36
- endpoint,
37
- model: payload.model,
38
- userApiKey: apiKey,
39
- });
40
- } else {
41
- openai = createOpenai(apiKey, endpoint);
42
- }
43
- } catch (error) {
44
- if ((error as Error).cause === ChatErrorType.NoAPIKey) {
45
- return createErrorResponse(ChatErrorType.NoAPIKey);
46
- }
47
-
48
- console.error(error); // log error to trace it
49
- return createErrorResponse(ChatErrorType.InternalServerError);
32
+ if (useAzureOpenAI) {
33
+ openai = createAzureOpenai({ apiVersion, endpoint, model: payload.model, userApiKey: apiKey });
34
+ } else {
35
+ openai = createOpenai(apiKey, endpoint);
50
36
  }
51
37
 
52
38
  return createChatCompletion({ openai, payload });
@@ -2,7 +2,6 @@ import OpenAI, { ClientOptions } from 'openai';
2
2
  import urlJoin from 'url-join';
3
3
 
4
4
  import { getServerConfig } from '@/config/server';
5
- import { ChatErrorType } from '@/types/fetch';
6
5
 
7
6
  // 创建 Azure OpenAI 实例
8
7
  export const createAzureOpenai = (params: {
@@ -18,9 +17,7 @@ export const createAzureOpenai = (params: {
18
17
 
19
18
  const defaultApiVersion = AZURE_API_VERSION || '2023-08-01-preview';
20
19
  const apiVersion = !params.apiVersion ? defaultApiVersion : params.apiVersion;
21
- const apiKey = !params.userApiKey ? AZURE_API_KEY : params.userApiKey;
22
-
23
- if (!apiKey) throw new Error('AZURE_API_KEY is empty', { cause: ChatErrorType.NoAPIKey });
20
+ const apiKey = !params.userApiKey ? AZURE_API_KEY ?? '' : params.userApiKey;
24
21
 
25
22
  const config: ClientOptions = {
26
23
  apiKey,
@@ -1,7 +1,6 @@
1
- import OpenAI from 'openai';
1
+ import OpenAI, { ClientOptions } from 'openai';
2
2
 
3
3
  import { getServerConfig } from '@/config/server';
4
- import { ChatErrorType } from '@/types/fetch';
5
4
 
6
5
  // 创建 OpenAI 实例
7
6
  export const createOpenai = (userApiKey: string | null, endpoint?: string | null) => {
@@ -9,9 +8,10 @@ export const createOpenai = (userApiKey: string | null, endpoint?: string | null
9
8
 
10
9
  const baseURL = endpoint ? endpoint : OPENAI_PROXY_URL ? OPENAI_PROXY_URL : undefined;
11
10
 
12
- const apiKey = !userApiKey ? OPENAI_API_KEY : userApiKey;
11
+ const config: ClientOptions = {
12
+ apiKey: !userApiKey ? OPENAI_API_KEY : userApiKey,
13
+ baseURL,
14
+ };
13
15
 
14
- if (!apiKey) throw new Error('OPENAI_API_KEY is empty', { cause: ChatErrorType.NoAPIKey });
15
-
16
- return new OpenAI({ apiKey, baseURL });
16
+ return new OpenAI(config);
17
17
  };
@@ -2,7 +2,6 @@ import { ChatErrorType, ErrorResponse, ErrorType } from '@/types/fetch';
2
2
 
3
3
  const getStatus = (errorType: ErrorType) => {
4
4
  switch (errorType) {
5
- case ChatErrorType.NoAPIKey:
6
5
  case ChatErrorType.InvalidAccessCode: {
7
6
  return 401;
8
7
  }
@@ -18,7 +18,7 @@ const DesktopPage = memo(() => (
18
18
  <Layout>
19
19
  <PageTitle />
20
20
  <ChatHeader />
21
- <Flexbox flex={1} height={'calc(100% - 64px)'} horizontal>
21
+ <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
22
22
  <Conversation chatInput={<ChatInput />} />
23
23
  <SideBar />
24
24
  </Flexbox>
@@ -16,7 +16,7 @@ export default memo(({ children }: PropsWithChildren) => {
16
16
  <ResponsiveSessionList />
17
17
  <Flexbox
18
18
  flex={1}
19
- height={'100%'}
19
+ height={'100vh'}
20
20
  id={'lobe-conversion-container'}
21
21
  style={{ position: 'relative' }}
22
22
  >
@@ -10,7 +10,7 @@ import Layout from './layout.mobile';
10
10
 
11
11
  const Chat = memo(() => (
12
12
  <Layout>
13
- <Flexbox height={'calc(100% - 44px)'} horizontal>
13
+ <Flexbox height={'calc(100vh - 44px)'} horizontal>
14
14
  <Conversation chatInput={<ChatInput />} mobile />
15
15
  <TopicList />
16
16
  </Flexbox>
@@ -2,8 +2,8 @@ import { Highlighter } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
- import OpenAPIKey from './OpenAPIKey';
6
- import { useStyles } from './style';
5
+ import APIKeyForm from './ApiKeyForm';
6
+ import { ErrorActionContainer, useStyles } from './style';
7
7
 
8
8
  interface OpenAIError {
9
9
  code: 'invalid_api_key' | string;
@@ -21,8 +21,13 @@ const OpenAiBizError = memo<{ content: OpenAIErrorResponse; id: string }>(({ con
21
21
 
22
22
  const errorCode = content.error?.code;
23
23
 
24
- if (errorCode === 'invalid_api_key') return <OpenAPIKey id={id} />;
25
-
24
+ if (errorCode === 'invalid_api_key') {
25
+ return (
26
+ <ErrorActionContainer>
27
+ <APIKeyForm id={id} />
28
+ </ErrorActionContainer>
29
+ );
30
+ }
26
31
  return (
27
32
  <Flexbox className={styles.container} style={{ maxWidth: 600 }}>
28
33
  <Highlighter language={'json'}>{JSON.stringify(content, null, 2)}</Highlighter>
@@ -5,7 +5,6 @@ import { ChatMessage } from '@/types/chatMessage';
5
5
  import { ChatErrorType, ErrorType } from '@/types/fetch';
6
6
 
7
7
  import InvalidAccess from './InvalidAccess';
8
- import OpenAPIKey from './OpenAPIKey';
9
8
  import OpenAiBizError from './OpenAiBizError';
10
9
  import PluginError from './Plugin/PluginError';
11
10
  import PluginSettings from './Plugin/PluginSettings';
@@ -37,10 +36,6 @@ export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessa
37
36
  return <InvalidAccess id={message.id} />;
38
37
  }
39
38
 
40
- case ChatErrorType.NoAPIKey: {
41
- return <OpenAPIKey id={message.id} />;
42
- }
43
-
44
39
  case ChatErrorType.OpenAIBizError: {
45
40
  return <OpenAiBizError content={(error as any).body} id={message.id} />;
46
41
  }
@@ -37,7 +37,6 @@ const Inbox = memo(() => {
37
37
 
38
38
  return (
39
39
  <Link
40
- aria-label={INBOX_SESSION_ID}
41
40
  href={SESSION_CHAT_URL(INBOX_SESSION_ID, mobile)}
42
41
  onClick={(e) => {
43
42
  e.preventDefault();
@@ -24,7 +24,6 @@ const SessionList = memo(() => {
24
24
  ) : list.length > 0 ? (
25
25
  list.map(({ id }) => (
26
26
  <Link
27
- aria-label={id}
28
27
  href={SESSION_CHAT_URL(id, mobile)}
29
28
  key={id}
30
29
  onClick={(e) => {
@@ -9,7 +9,7 @@ const Loading = () => {
9
9
  const { t } = useTranslation();
10
10
 
11
11
  return (
12
- <Flexbox height={'100%'} width={'100%'}>
12
+ <Flexbox height={'100vh'} width={'100%'}>
13
13
  <Center flex={1} gap={12} width={'100%'}>
14
14
  <Logo extra={'Chat'} size={48} type={'combine'} />
15
15
  <Center gap={16} horizontal>
@@ -53,7 +53,7 @@ const SideBar = memo(() => {
53
53
  <DraggablePanelContainer
54
54
  style={{
55
55
  flex: 'none',
56
- height: '100%',
56
+ height: '100vh',
57
57
  minWidth: MARKET_SIDEBAR_WIDTH,
58
58
  }}
59
59
  >
@@ -22,7 +22,7 @@ const Header = memo(() => {
22
22
  return (
23
23
  <ChatHeader
24
24
  left={
25
- <Link aria-label={'home'} href={'/'}>
25
+ <Link href={'/'}>
26
26
  <Logo className={styles.logo} extra={'Discover'} size={36} type={'text'} />
27
27
  </Link>
28
28
  }
@@ -34,12 +34,12 @@ const MarketLayout = memo<PropsWithChildren>(({ children }) => {
34
34
  <AppLayoutDesktop>
35
35
  <Flexbox
36
36
  flex={1}
37
- height={'100%'}
37
+ height={'100vh'}
38
38
  id={'lobe-market-container'}
39
39
  style={{ position: 'relative' }}
40
40
  >
41
41
  <Header />
42
- <Flexbox flex={1} height={'calc(100% - 64px)'} horizontal>
42
+ <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
43
43
  <Flexbox align={'center'} flex={1} style={{ overflow: 'auto', padding: 16 }}>
44
44
  <SafeSpacing />
45
45
 
@@ -50,7 +50,6 @@ const AgentCardBanner = memo<AgentCardBannerProps>(
50
50
  {...props}
51
51
  >
52
52
  <Avatar
53
- alt={'banner'}
54
53
  avatar={meta.avatar}
55
54
  background={meta.backgroundColor || theme.colorBgContainer}
56
55
  className={styles.bannerImg}
@@ -47,7 +47,7 @@ const Header = memo(() => {
47
47
  ))}
48
48
  </Center>
49
49
  <div className={styles.desc}>{description}</div>
50
- <Link aria-label={author} className={styles.author} href={homepage} target={'_blank'}>
50
+ <Link className={styles.author} href={homepage} target={'_blank'}>
51
51
  @{author}
52
52
  </Link>
53
53
  <Button
@@ -14,7 +14,7 @@ export default memo(({ children }: PropsWithChildren) => {
14
14
  return (
15
15
  <AppLayoutDesktop>
16
16
  <SideBar />
17
- <Flexbox flex={1} height={'100%'} style={{ position: 'relative' }}>
17
+ <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
18
18
  <Header />
19
19
  <Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
20
20
  <SafeSpacing />
@@ -4,24 +4,20 @@ import { memo } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import PageTitle from '@/components/PageTitle';
7
- import { CURRENT_VERSION } from '@/const/version';
8
7
  import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
9
8
  import { SettingsTabs } from '@/store/global/initialState';
10
9
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
11
10
 
12
- import Footer from '../features/Footer';
13
11
  import Common from './Common';
14
12
 
15
13
  export default memo(() => {
16
14
  useSwitchSideBarOnInit(SettingsTabs.Common);
17
15
  const { t } = useTranslation('setting');
18
16
  const pageTitle = genSiteHeadTitle(t('tab.common'));
19
-
20
17
  return (
21
18
  <>
22
19
  <PageTitle title={pageTitle} />
23
20
  <Common />
24
- <Footer>LobeChat v{CURRENT_VERSION}</Footer>
25
21
  </>
26
22
  );
27
23
  });
@@ -22,7 +22,6 @@ const List = memo(() => {
22
22
 
23
23
  return items.map(({ value, icon, label }) => (
24
24
  <Link
25
- aria-label={label}
26
25
  href={`/settings/${value}`}
27
26
  key={value}
28
27
  onClick={() => {
@@ -27,7 +27,7 @@ const SideBar = memo(() => {
27
27
  <DraggablePanelBody className={styles.body} style={{ padding: 0 }}>
28
28
  <Flexbox className={styles.top} padding={16}>
29
29
  <div>
30
- <Logo className={styles.logo} extra={'Settings'} size={36} type={'text'} />
30
+ <Logo className={styles.logo} size={36} type={'text'} />
31
31
  </div>
32
32
  </Flexbox>
33
33
  <UpgradeAlert />
@@ -14,7 +14,7 @@ const UpgradeAlert = memo(() => {
14
14
  hasNewVersion && (
15
15
  <Alert
16
16
  action={
17
- <Link aria-label={t('upgradeVersion.action')} href={MANUAL_UPGRADE_URL} target={'_blank'}>
17
+ <Link href={MANUAL_UPGRADE_URL} target={'_blank'}>
18
18
  <Button size={'small'} type={'primary'}>
19
19
  {t('upgradeVersion.action')}
20
20
  </Button>
@@ -3,8 +3,10 @@ import { Form as AntForm, AutoComplete, Input, Switch } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import { debounce } from 'lodash-es';
5
5
  import { Webhook } from 'lucide-react';
6
+ import Link from 'next/link';
6
7
  import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
+ import { Trans, useTranslation } from 'react-i18next';
9
+ import { Flexbox } from 'react-layout-kit';
8
10
 
9
11
  import { FORM_STYLE } from '@/const/layoutTokens';
10
12
  import { settingsSelectors, useEffectAfterGlobalHydrated, useGlobalStore } from '@/store/global';
@@ -22,6 +24,9 @@ const useStyles = createStyles(({ css, token }) => ({
22
24
  color: ${token.colorTextDescription} !important;
23
25
  }
24
26
  `,
27
+ plan: css`
28
+ color: ${token.colorTextDescription};
29
+ `,
25
30
  tip: css`
26
31
  font-size: 12px;
27
32
  color: ${token.colorTextDescription};
@@ -134,12 +139,25 @@ const LLM = memo(() => {
134
139
  };
135
140
 
136
141
  return (
137
- <Form
138
- form={form}
139
- items={[openAI]}
140
- onValuesChange={debounce(setSettings, 100)}
141
- {...FORM_STYLE}
142
- />
142
+ <Flexbox align={'center'} gap={24} width={'100%'}>
143
+ <Form
144
+ form={form}
145
+ items={[openAI]}
146
+ onValuesChange={debounce(setSettings, 100)}
147
+ {...FORM_STYLE}
148
+ />
149
+ <Flexbox align={'center'} className={styles.plan}>
150
+ <div>
151
+ <Trans i18nKey="llm.waitingForMore" ns={'setting'}>
152
+ 更多模型正在
153
+ <Link href="https://github.com/lobehub/lobe-chat/issues/151" target="_blank">
154
+ 计划接入
155
+ </Link>
156
+ 中 ,敬请期待 ✨
157
+ </Trans>
158
+ </div>
159
+ </Flexbox>
160
+ </Flexbox>
143
161
  );
144
162
  });
145
163
 
@@ -1,10 +1,8 @@
1
1
  'use client';
2
2
 
3
- import Link from 'next/link';
4
3
  import { memo } from 'react';
5
- import { Trans, useTranslation } from 'react-i18next';
4
+ import { useTranslation } from 'react-i18next';
6
5
 
7
- import Footer from '@/app/settings/features/Footer';
8
6
  import PageTitle from '@/components/PageTitle';
9
7
  import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
10
8
  import { SettingsTabs } from '@/store/global/initialState';
@@ -20,19 +18,6 @@ export default memo(() => {
20
18
  <>
21
19
  <PageTitle title={pageTitle} />
22
20
  <LLM />
23
- <Footer>
24
- <Trans i18nKey="llm.waitingForMore" ns={'setting'}>
25
- 更多模型正在
26
- <Link
27
- aria-label={'todo'}
28
- href="https://github.com/lobehub/lobe-chat/issues/151"
29
- target="_blank"
30
- >
31
- 计划接入
32
- </Link>
33
- 中 ,敬请期待 ✨
34
- </Trans>
35
- </Footer>
36
21
  </>
37
22
  );
38
23
  });
@@ -10,7 +10,7 @@ const Showcase = memo(() => (
10
10
  <Flexbox
11
11
  flex={1}
12
12
  justify={'center'}
13
- style={{ height: '100%', position: 'relative', width: '100%' }}
13
+ style={{ height: '100vh', position: 'relative', width: '100%' }}
14
14
  >
15
15
  <GridShowcase>
16
16
  <Banner />
@@ -13,7 +13,7 @@ const Desktop = memo<PropsWithChildren>(({ children }) => {
13
13
  <Center
14
14
  className={styles.layout}
15
15
  flex={1}
16
- height={'100%'}
16
+ height={'100vh'}
17
17
  horizontal
18
18
  style={{ position: 'relative' }}
19
19
  >
@@ -4,7 +4,7 @@ import { Center, Flexbox } from 'react-layout-kit';
4
4
  import Banner from '@/app/welcome/features/Banner';
5
5
 
6
6
  const Showcase = memo(() => (
7
- <Flexbox align={'center'} justify={'center'} style={{ height: 'calc(100% - 44px)' }}>
7
+ <Flexbox align={'center'} justify={'center'} style={{ height: 'calc(100vh - 44px)' }}>
8
8
  <Center gap={16}>
9
9
  <Banner mobile />
10
10
  </Center>
@@ -12,7 +12,7 @@ const MobileLayout = memo<PropsWithChildren>(({ children }) => {
12
12
 
13
13
  return (
14
14
  <AppLayoutMobile navBar={<Header />} showTabBar style={{ background: theme.colorBgLayout }}>
15
- <div style={{ height: '100%', paddingInline: 16 }}>{children}</div>
15
+ <div style={{ paddingInline: 16 }}>{children}</div>
16
16
  </AppLayoutMobile>
17
17
  );
18
18
  });
@@ -4,10 +4,12 @@ import { Icon } from '@lobehub/ui';
4
4
  import { useSize } from 'ahooks';
5
5
  import { Button, Upload } from 'antd';
6
6
  import { SendHorizonal } from 'lucide-react';
7
+ import Link from 'next/link';
7
8
  import { memo, useCallback, useRef } from 'react';
8
9
  import { useTranslation } from 'react-i18next';
9
10
  import { Flexbox } from 'react-layout-kit';
10
11
 
12
+ import { SESSION_CHAT_URL } from '@/const/url';
11
13
  import { useImportConfig } from '@/hooks/useImportConfig';
12
14
  import { useSessionStore } from '@/store/session';
13
15
 
@@ -21,12 +23,8 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
21
23
  const width = domSize?.width || 1024;
22
24
  const { t } = useTranslation('welcome');
23
25
  const { styles } = useStyles();
24
- const [switchSession, switchBackToChat, router, isMobile] = useSessionStore((s) => [
25
- s.switchSession,
26
- s.switchBackToChat,
27
- s.router,
28
- s.isMobile,
29
- ]);
26
+
27
+ const switchSession = useSessionStore((s) => s.switchSession);
30
28
  const handleImport = useCallback((e: any) => {
31
29
  importConfig(e);
32
30
  switchSession();
@@ -49,17 +47,20 @@ const Banner = memo<{ mobile?: boolean }>(({ mobile }) => {
49
47
  {t('button.import')}
50
48
  </Button>
51
49
  </Upload>
52
- <Button
53
- block={mobile}
54
- onClick={() => (isMobile ? router?.push('/chat') : switchBackToChat())}
55
- size={'large'}
56
- type={'primary'}
50
+ <Link
51
+ href={SESSION_CHAT_URL()}
52
+ onClick={(e) => {
53
+ e.preventDefault();
54
+ switchSession();
55
+ }}
57
56
  >
58
- <Flexbox align={'center'} gap={4} horizontal justify={'center'}>
59
- {t('button.start')}
60
- <Icon icon={SendHorizonal} />
61
- </Flexbox>
62
- </Button>
57
+ <Button block={mobile} size={'large'} type={'primary'}>
58
+ <Flexbox align={'center'} gap={4} horizontal justify={'center'}>
59
+ {t('button.start')}
60
+ <Icon icon={SendHorizonal} />
61
+ </Flexbox>
62
+ </Button>
63
+ </Link>
63
64
  </Flexbox>
64
65
  </>
65
66
  );
@@ -60,7 +60,7 @@ export const useStyles = createStyles(({ css, token, stylish, cx, prefixCls }) =
60
60
  overflow-y: auto;
61
61
 
62
62
  max-width: 1024px;
63
- height: 100%;
63
+ height: 100vh;
64
64
  padding: 32px 16px;
65
65
  `,
66
66
  ),
@@ -2,7 +2,7 @@ import type { FormProps } from '@lobehub/ui';
2
2
 
3
3
  export const HEADER_HEIGHT = 64;
4
4
  export const MOBILE_NABBAR_HEIGHT = 44;
5
- export const MOBILE_TABBAR_HEIGHT = 60;
5
+ export const MOBILE_TABBAR_HEIGHT = 48;
6
6
  export const CHAT_TEXTAREA_HEIGHT = 200;
7
7
  export const CHAT_TEXTAREA_HEIGHT_MOBILE = 120;
8
8
  export const CHAT_SIDEBAR_WIDTH = 280;
@@ -8,7 +8,7 @@ import { useGlobalStore } from '@/store/global';
8
8
 
9
9
  export const useStyles = createStyles(({ css, token }) => ({
10
10
  panel: css`
11
- height: 100%;
11
+ height: 100vh;
12
12
  color: ${token.colorTextSecondary};
13
13
  background: ${token.colorBgContainer};
14
14
  `,
@@ -48,7 +48,7 @@ const FolderPanel = memo<PropsWithChildren>(({ children }) => {
48
48
  updatePreference({ sessionsWidth: nextWidth });
49
49
  }}
50
50
  placement="left"
51
- size={{ height: '100%', width: sessionsWidth }}
51
+ size={{ height: '100vh', width: sessionsWidth }}
52
52
  >
53
53
  <DraggablePanelContainer style={{ flex: 'none', height: '100%', minWidth: FOLDER_WIDTH }}>
54
54
  {children}
@@ -14,7 +14,7 @@ export default memo(() => {
14
14
  <SideNav
15
15
  avatar={<AvatarWithUpload id={'avatar'} />}
16
16
  bottomActions={<BottomActions setTab={setTab} tab={tab} />}
17
- style={{ height: '100%' }}
17
+ style={{ height: '100vh' }}
18
18
  topActions={<TopActions setTab={setTab} tab={tab} />}
19
19
  />
20
20
  );
@@ -10,7 +10,6 @@ const AppLayoutDesktop = memo<PropsWithChildren>(({ children }) => {
10
10
  const theme = useTheme();
11
11
  return (
12
12
  <Flexbox
13
- height={'100%'}
14
13
  horizontal
15
14
  style={isPWA ? { borderTop: `1px solid ${theme.colorBorder}` } : {}}
16
15
  width={'100%'}
@@ -16,7 +16,7 @@ const useStyles = createStyles(({ css, cx, stylish }) => ({
16
16
  overflow-y: auto;
17
17
 
18
18
  width: 100vw;
19
- height: 100%;
19
+ height: 100vh;
20
20
  `,
21
21
  ),
22
22
  mobileNavBar: css`
@@ -7,7 +7,7 @@ const Loading = memo(() => (
7
7
  <div
8
8
  style={{
9
9
  backdropFilter: 'blur(3px)',
10
- height: '100%',
10
+ height: '100vh',
11
11
  width: '100vw',
12
12
  zIndex: 100,
13
13
  }}
@@ -32,12 +32,11 @@ export default {
32
32
 
33
33
  InvalidAccessCode: '密码不正确或为空,请输入正确的访问密码,或者添加自定义 OpenAI API Key',
34
34
  OpenAIBizError: '请求 OpenAI 服务出错,请根据以下信息排查或重试',
35
- NoAPIKey: 'OpenAI API Key 为空,请添加自定义 OpenAI API Key',
36
35
  /* eslint-enable */
37
36
  },
38
37
  unlock: {
39
38
  apikey: {
40
- description: '输入你的 OpenAI API Key 即可开始会话。应用不会记录你的 API Key',
39
+ description: '输入你的 OpenAI API Key 即可绕过密码验证。应用不会记录你的 API Key',
41
40
  title: '使用自定义 API Key',
42
41
  },
43
42
  closeMessage: '关闭提示',
@@ -3,20 +3,20 @@ import { css } from 'antd-style';
3
3
  export default ({ prefixCls }: { prefixCls: string }) => css`
4
4
  html,
5
5
  body,
6
- #__next,
7
6
  .${prefixCls}-app {
8
- position: relative;
7
+ overflow: hidden;
9
8
  overscroll-behavior: none;
10
- height: 100% !important;
11
- min-height: 100% !important;
12
9
 
13
10
  ::-webkit-scrollbar {
14
- display: none;
15
11
  width: 0;
16
12
  height: 0;
17
13
  }
18
14
  }
19
15
 
16
+ #__next {
17
+ height: 100%;
18
+ }
19
+
20
20
  p {
21
21
  margin-bottom: 0;
22
22
  }
@@ -24,7 +24,6 @@ export default ({ prefixCls }: { prefixCls: string }) => css`
24
24
  @media (max-width: 575px) {
25
25
  * {
26
26
  ::-webkit-scrollbar {
27
- display: none;
28
27
  width: 0;
29
28
  height: 0;
30
29
  }
@@ -2,9 +2,10 @@
2
2
  export const ChatErrorType = {
3
3
  // ******* 业务错误语义 ******* //
4
4
 
5
- InvalidAccessCode: 'InvalidAccessCode', // 密码无效
6
- OpenAIBizError: 'OpenAIBizError', // OpenAI 返回的业务错误
7
- NoAPIKey: 'NoAPIKey',
5
+ // 密码无效
6
+ InvalidAccessCode: 'InvalidAccessCode',
7
+ // OpenAI 返回的业务错误
8
+ OpenAIBizError: 'OpenAIBizError',
8
9
 
9
10
  // ******* 客户端错误 ******* //
10
11
  BadRequest: 400,
@@ -1,12 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import APIKeyForm from './ApiKeyForm';
4
- import { ErrorActionContainer } from './style';
5
-
6
- const OpenAPIKey = memo<{ id: string }>(({ id }) => (
7
- <ErrorActionContainer>
8
- <APIKeyForm id={id} />
9
- </ErrorActionContainer>
10
- ));
11
-
12
- export default OpenAPIKey;
@@ -1,24 +0,0 @@
1
- import { Divider } from 'antd';
2
- import { createStyles } from 'antd-style';
3
- import { PropsWithChildren, memo } from 'react';
4
- import { Center } from 'react-layout-kit';
5
-
6
- const useStyles = createStyles(
7
- ({ css, token }) => css`
8
- font-size: 14px;
9
- color: ${token.colorTextDescription};
10
- `,
11
- );
12
-
13
- const Footer = memo<PropsWithChildren>(({ children }) => {
14
- const { styles } = useStyles();
15
- return (
16
- <Center style={{ maxWidth: 600, width: '100%' }}>
17
- <Divider>
18
- <span className={styles}>{children}</span>
19
- </Divider>
20
- </Center>
21
- );
22
- });
23
-
24
- export default Footer;