Compare commits

..

236 Commits

Author SHA1 Message Date
Jamie Stivala 0b39093e5a Merge branch 'feat/okta' 2025-07-24 13:39:13 +02:00
Jamie Stivala e9b66bdb3a Added Okta to SSO providers list 2025-07-24 13:38:52 +02:00
Jamie Stivala c0742491f4 Merge branch 'feat/okta' 2025-07-24 13:36:30 +02:00
Jamie Stivala 8a46d41d60 Reverted a micro-change which was changed during some testing (back to original) 2025-07-24 13:36:13 +02:00
Jamie Stivala 8f5b2eb141 Removed Okta references from auth envs (deprecated) 2025-07-24 13:28:55 +02:00
Jamie Stivala 77bea167b1 Merge branch 'feat/okta' 2025-07-24 13:26:46 +02:00
Jamie Stivala c57074d725 Removed Okta Test 2025-07-24 13:23:41 +02:00
Jamie Stivala cb6861ef04 Added Okta as SSO Provider 2025-07-24 13:12:01 +02:00
Jamie Stivala 5f367e1242 Removed deprecated env variables 2025-07-24 13:11:14 +02:00
Jamie Stivala 27917bcca5 Added documentation 2025-07-24 13:07:05 +02:00
lobehubbot 3f8b1dde09 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-24 09:56:37 +00:00
semantic-release-bot 7189d8a81c 🔖 chore(release): v1.103.2 [skip ci]
### [Version 1.103.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.103.1...v1.103.2)
<sup>Released on **2025-07-24**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix chat stream in desktop and update shortcut.

#### 💄 Styles

- **misc**: Add cached token count to usage of GoogleAI and VertexAI, fix desktop titlebar style in window, fix sub topic width in md responsive.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix chat stream in desktop and update shortcut, closes [#8520](https://github.com/jaworldwideorg/OneJA-Bot/issues/8520) ([0192140](https://github.com/jaworldwideorg/OneJA-Bot/commit/0192140))

#### Styles

* **misc**: Add cached token count to usage of GoogleAI and VertexAI, closes [#8545](https://github.com/jaworldwideorg/OneJA-Bot/issues/8545) ([66dbb24](https://github.com/jaworldwideorg/OneJA-Bot/commit/66dbb24))
* **misc**: Fix desktop titlebar style in window, closes [#8439](https://github.com/jaworldwideorg/OneJA-Bot/issues/8439) ([fd7662c](https://github.com/jaworldwideorg/OneJA-Bot/commit/fd7662c))
* **misc**: Fix sub topic width in md responsive, closes [#8443](https://github.com/jaworldwideorg/OneJA-Bot/issues/8443) ([9bae13b](https://github.com/jaworldwideorg/OneJA-Bot/commit/9bae13b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-24 09:56:10 +00:00
Jamie Stivala 7767bbbedc Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-24 11:39:49 +02:00
semantic-release-bot a6127a9d82 🔖 chore(release): v1.101.0 [skip ci]
## [Version&nbsp;1.101.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.100.1...v1.101.0)
<sup>Released on **2025-07-23**</sup>

#### ♻ Code Refactoring

- **misc**: Add badge and improve document.

####  Features

- **misc**: Add image generation capabilities using Google AI Imagen API, add Qwen image generation capabilities.

#### 🐛 Bug Fixes

- **groq**: Enable streaming for tool calls and add Kimi K2 model.
- **misc**: Remove debug logging from ModelRuntime and async caller.

#### 💄 Styles

- **misc**: Add notification for desktop, fix lobehub provider `/chat` in desktop, modal list header sticky style, update i18n, Update tray icon.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Add badge and improve document, closes [#8528](https://github.com/jaworldwideorg/OneJA-Bot/issues/8528) ([9fb4b0d](https://github.com/jaworldwideorg/OneJA-Bot/commit/9fb4b0d))

#### What's improved

* **misc**: Add image generation capabilities using Google AI Imagen API, closes [#8503](https://github.com/jaworldwideorg/OneJA-Bot/issues/8503) ([cef8208](https://github.com/jaworldwideorg/OneJA-Bot/commit/cef8208))
* **misc**: Add Qwen image generation capabilities, closes [#8534](https://github.com/jaworldwideorg/OneJA-Bot/issues/8534) ([7e8e5ef](https://github.com/jaworldwideorg/OneJA-Bot/commit/7e8e5ef))

#### What's fixed

* **groq**: Enable streaming for tool calls and add Kimi K2 model, closes [#8510](https://github.com/jaworldwideorg/OneJA-Bot/issues/8510) ([60739bc](https://github.com/jaworldwideorg/OneJA-Bot/commit/60739bc))
* **misc**: Remove debug logging from ModelRuntime and async caller, closes [#8525](https://github.com/jaworldwideorg/OneJA-Bot/issues/8525) ([dd1a635](https://github.com/jaworldwideorg/OneJA-Bot/commit/dd1a635))

#### Styles

* **misc**: Add notification for desktop, closes [#8523](https://github.com/jaworldwideorg/OneJA-Bot/issues/8523) ([4917d17](https://github.com/jaworldwideorg/OneJA-Bot/commit/4917d17))
* **misc**: Fix lobehub provider `/chat` in desktop, closes [#8508](https://github.com/jaworldwideorg/OneJA-Bot/issues/8508) ([c801f9c](https://github.com/jaworldwideorg/OneJA-Bot/commit/c801f9c))
* **misc**: Modal list header sticky style, closes [#8514](https://github.com/jaworldwideorg/OneJA-Bot/issues/8514) ([75273d5](https://github.com/jaworldwideorg/OneJA-Bot/commit/75273d5))
* **misc**: Update i18n, closes [#8537](https://github.com/jaworldwideorg/OneJA-Bot/issues/8537) ([b16f19b](https://github.com/jaworldwideorg/OneJA-Bot/commit/b16f19b))
* **misc**: Update tray icon, closes [#8530](https://github.com/jaworldwideorg/OneJA-Bot/issues/8530) ([2696de4](https://github.com/jaworldwideorg/OneJA-Bot/commit/2696de4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-23 14:25:29 +00:00
GitHub Actions ca841b9fc8 Merge branch 'fix/dynamic-test' 2025-07-23 16:07:24 +02:00
GitHub Actions a506e60458 Merge remote-tracking branch 'origin/main' 2025-07-23 16:00:30 +02:00
GitHub Actions 8a1c21f216 Remove custom git sync script and .gitattributes, switch to using Fork-Sync-With-Upstream GitHub Action in workflow 2025-07-23 16:00:14 +02:00
GitHub Actions 06a1cc2adf Update plugin action tests to use DEFAULT_INBOX_AVATAR constant instead of hardcoded path 2025-07-23 15:53:25 +02:00
semantic-release-bot b5616f0581 🔖 chore(release): v1.101.0 [skip ci]
## [Version&nbsp;1.101.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.100.1...v1.101.0)
<sup>Released on **2025-07-23**</sup>

#### ♻ Code Refactoring

- **misc**: Add badge and improve document.

####  Features

- **misc**: Add image generation capabilities using Google AI Imagen API, add Qwen image generation capabilities.

#### 🐛 Bug Fixes

- **groq**: Enable streaming for tool calls and add Kimi K2 model.
- **misc**: Remove debug logging from ModelRuntime and async caller.

#### 💄 Styles

- **misc**: Add notification for desktop, fix lobehub provider `/chat` in desktop, modal list header sticky style, update i18n, Update tray icon.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Add badge and improve document, closes [#8528](https://github.com/jaworldwideorg/OneJA-Bot/issues/8528) ([9fb4b0d](https://github.com/jaworldwideorg/OneJA-Bot/commit/9fb4b0d))

#### What's improved

* **misc**: Add image generation capabilities using Google AI Imagen API, closes [#8503](https://github.com/jaworldwideorg/OneJA-Bot/issues/8503) ([cef8208](https://github.com/jaworldwideorg/OneJA-Bot/commit/cef8208))
* **misc**: Add Qwen image generation capabilities, closes [#8534](https://github.com/jaworldwideorg/OneJA-Bot/issues/8534) ([7e8e5ef](https://github.com/jaworldwideorg/OneJA-Bot/commit/7e8e5ef))

#### What's fixed

* **groq**: Enable streaming for tool calls and add Kimi K2 model, closes [#8510](https://github.com/jaworldwideorg/OneJA-Bot/issues/8510) ([60739bc](https://github.com/jaworldwideorg/OneJA-Bot/commit/60739bc))
* **misc**: Remove debug logging from ModelRuntime and async caller, closes [#8525](https://github.com/jaworldwideorg/OneJA-Bot/issues/8525) ([dd1a635](https://github.com/jaworldwideorg/OneJA-Bot/commit/dd1a635))

#### Styles

* **misc**: Add notification for desktop, closes [#8523](https://github.com/jaworldwideorg/OneJA-Bot/issues/8523) ([4917d17](https://github.com/jaworldwideorg/OneJA-Bot/commit/4917d17))
* **misc**: Fix lobehub provider `/chat` in desktop, closes [#8508](https://github.com/jaworldwideorg/OneJA-Bot/issues/8508) ([c801f9c](https://github.com/jaworldwideorg/OneJA-Bot/commit/c801f9c))
* **misc**: Modal list header sticky style, closes [#8514](https://github.com/jaworldwideorg/OneJA-Bot/issues/8514) ([75273d5](https://github.com/jaworldwideorg/OneJA-Bot/commit/75273d5))
* **misc**: Update i18n, closes [#8537](https://github.com/jaworldwideorg/OneJA-Bot/issues/8537) ([b16f19b](https://github.com/jaworldwideorg/OneJA-Bot/commit/b16f19b))
* **misc**: Update tray icon, closes [#8530](https://github.com/jaworldwideorg/OneJA-Bot/issues/8530) ([2696de4](https://github.com/jaworldwideorg/OneJA-Bot/commit/2696de4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-23 13:52:23 +00:00
GitHub Actions b39eaf2686 Merge branch 'fix/dynamic-test' 2025-07-23 15:37:17 +02:00
GitHub Actions 78176978cd Update tests to replace hardcoded avatar paths with constants for inbox and user avatars 2025-07-23 15:34:38 +02:00
semantic-release-bot a7bac06436 🔖 chore(release): v1.101.0 [skip ci]
## [Version&nbsp;1.101.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.100.1...v1.101.0)
<sup>Released on **2025-07-23**</sup>

#### ♻ Code Refactoring

- **misc**: Add badge and improve document.

####  Features

- **misc**: Add image generation capabilities using Google AI Imagen API, add Qwen image generation capabilities.

#### 🐛 Bug Fixes

- **groq**: Enable streaming for tool calls and add Kimi K2 model.
- **misc**: Remove debug logging from ModelRuntime and async caller.

#### 💄 Styles

- **misc**: Add notification for desktop, fix lobehub provider `/chat` in desktop, modal list header sticky style, update i18n, Update tray icon.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Add badge and improve document, closes [#8528](https://github.com/jaworldwideorg/OneJA-Bot/issues/8528) ([9fb4b0d](https://github.com/jaworldwideorg/OneJA-Bot/commit/9fb4b0d))

#### What's improved

* **misc**: Add image generation capabilities using Google AI Imagen API, closes [#8503](https://github.com/jaworldwideorg/OneJA-Bot/issues/8503) ([cef8208](https://github.com/jaworldwideorg/OneJA-Bot/commit/cef8208))
* **misc**: Add Qwen image generation capabilities, closes [#8534](https://github.com/jaworldwideorg/OneJA-Bot/issues/8534) ([7e8e5ef](https://github.com/jaworldwideorg/OneJA-Bot/commit/7e8e5ef))

#### What's fixed

* **groq**: Enable streaming for tool calls and add Kimi K2 model, closes [#8510](https://github.com/jaworldwideorg/OneJA-Bot/issues/8510) ([60739bc](https://github.com/jaworldwideorg/OneJA-Bot/commit/60739bc))
* **misc**: Remove debug logging from ModelRuntime and async caller, closes [#8525](https://github.com/jaworldwideorg/OneJA-Bot/issues/8525) ([dd1a635](https://github.com/jaworldwideorg/OneJA-Bot/commit/dd1a635))

#### Styles

* **misc**: Add notification for desktop, closes [#8523](https://github.com/jaworldwideorg/OneJA-Bot/issues/8523) ([4917d17](https://github.com/jaworldwideorg/OneJA-Bot/commit/4917d17))
* **misc**: Fix lobehub provider `/chat` in desktop, closes [#8508](https://github.com/jaworldwideorg/OneJA-Bot/issues/8508) ([c801f9c](https://github.com/jaworldwideorg/OneJA-Bot/commit/c801f9c))
* **misc**: Modal list header sticky style, closes [#8514](https://github.com/jaworldwideorg/OneJA-Bot/issues/8514) ([75273d5](https://github.com/jaworldwideorg/OneJA-Bot/commit/75273d5))
* **misc**: Update i18n, closes [#8537](https://github.com/jaworldwideorg/OneJA-Bot/issues/8537) ([b16f19b](https://github.com/jaworldwideorg/OneJA-Bot/commit/b16f19b))
* **misc**: Update tray icon, closes [#8530](https://github.com/jaworldwideorg/OneJA-Bot/issues/8530) ([2696de4](https://github.com/jaworldwideorg/OneJA-Bot/commit/2696de4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-23 13:08:53 +00:00
GitHub Actions bbc161db30 Merge branch 'fix/dynamic-test' 2025-07-23 14:53:06 +02:00
GitHub Actions 33f838a59b Update tests to use BRANDING_NAME constant instead of hardcoded 'LobeChat' and update avatar icon path in chat message tests 2025-07-23 14:52:14 +02:00
GitHub Actions e8a7edea76 Update test data for plugin action to use avatar icon path (rather than hard coded) 2025-07-23 14:40:50 +02:00
GitHub Actions e3766e94d0 Update dependencies, replace vi-canvas-mock with vitest-canvas-mock, and refine test assertions in knowledgeBase and aiProvider models. 2025-07-23 14:39:04 +02:00
GitHub Actions 76ff1f6da4 Add Okta support to auth config and tests 2025-07-23 14:29:56 +02:00
GitHub Actions 2f1f3a846e Remove unused schemas, tests, documentation, and references related to the deprecated meta-schema, ModelParamsSchema, and associated configurations. 2025-07-23 14:24:16 +02:00
GitHub Actions 0ffa190d2b Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx
2025-07-23 14:15:40 +02:00
GitHub Actions 89253d1e5c Simplify SeedNumberInput by removing unused props (min, max, step). 2025-07-23 14:15:20 +02:00
GitHub Actions 21f997fc0e Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-23 12:12:16 +00:00
GitHub Actions 883982754f Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx
2025-07-23 14:11:55 +02:00
GitHub Actions 80b72bb2ee Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-21 00:34:24 +00:00
lobehubbot 87ea15bba5 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-20 18:23:10 +00:00
GitHub Actions 7c1b1cefed Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-20 18:08:23 +00:00
lobehubbot 5b64c3be3c 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-20 12:26:04 +00:00
GitHub Actions 7e391d8a57 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-20 12:10:56 +00:00
lobehubbot 1aa1484c25 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-20 06:24:51 +00:00
GitHub Actions 8cb9e0d542 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-20 06:09:41 +00:00
lobehubbot ce9f766cde 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-20 00:50:35 +00:00
GitHub Actions c250721b69 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-20 00:35:40 +00:00
lobehubbot 8c9b8b5f3c 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-19 18:23:41 +00:00
semantic-release-bot cc77ca3ff7 🔖 chore(release): v1.100.1 [skip ci]
### [Version&nbsp;1.100.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.100.0...v1.100.1)
<sup>Released on **2025-07-19**</sup>

#### 🐛 Bug Fixes

- **misc**: Try fix authorization code exchange & pin next-auto to `beta.29`.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Try fix authorization code exchange & pin next-auto to `beta.29`, closes [#8496](https://github.com/jaworldwideorg/OneJA-Bot/issues/8496) ([27c4881](https://github.com/jaworldwideorg/OneJA-Bot/commit/27c4881))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-19 18:23:15 +00:00
GitHub Actions 098654742b Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-19 18:08:18 +00:00
lobehubbot ffc21dc86e 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-19 12:25:43 +00:00
GitHub Actions 262fcf2945 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-19 12:10:46 +00:00
lobehubbot 7cf5922a15 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-19 06:24:22 +00:00
GitHub Actions 5bb2514e4f Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-19 06:09:24 +00:00
lobehubbot 886cb69436 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-19 00:46:29 +00:00
GitHub Actions 4596d2ce32 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-19 00:31:27 +00:00
lobehubbot 171fcd7a48 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-18 18:24:57 +00:00
semantic-release-bot 5be34ff5ae 🔖 chore(release): v1.100.0 [skip ci]
## [Version&nbsp;1.100.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.99.2...v1.100.0)
<sup>Released on **2025-07-18**</sup>

####  Features

- **misc**: Add zhipu cogview4.

#### 🐛 Bug Fixes

- **misc**: Some ai image bugs.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add zhipu cogview4, closes [#8486](https://github.com/jaworldwideorg/OneJA-Bot/issues/8486) ([0b1557d](https://github.com/jaworldwideorg/OneJA-Bot/commit/0b1557d))

#### What's fixed

* **misc**: Some ai image bugs, closes [#8490](https://github.com/jaworldwideorg/OneJA-Bot/issues/8490) ([5d852be](https://github.com/jaworldwideorg/OneJA-Bot/commit/5d852be))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-18 18:24:30 +00:00
GitHub Actions 4c6630a6d8 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-18 18:09:09 +00:00
lobehubbot a6486d41f6 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-18 12:27:34 +00:00
GitHub Actions abac12f89f Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-18 12:12:31 +00:00
lobehubbot ea90b4cedb 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-18 10:29:09 +00:00
semantic-release-bot d8d3e98e74 🔖 chore(release): v1.99.2 [skip ci]
### [Version&nbsp;1.99.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.99.1...v1.99.2)
<sup>Released on **2025-07-18**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix webapi proxy with clerk.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix webapi proxy with clerk, closes [#8479](https://github.com/jaworldwideorg/OneJA-Bot/issues/8479) ([7dd65f0](https://github.com/jaworldwideorg/OneJA-Bot/commit/7dd65f0))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-18 10:28:42 +00:00
GitHub Actions 5136e7a7ee Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-18 10:13:16 +00:00
GitHub Actions 54ba981bd4 Enhance sync-upstream script with advanced conflict resolution and detailed logging:
- Implement `.gitattributes` parsing for dynamic merge strategies.
- Add functions for automatic conflict resolution based on merge strategies.
- Improve error handling and diagnostics for troubleshooting.
- Ensure cross-environment compatibility using portable shell commands.
2025-07-18 12:10:45 +02:00
GitHub Actions d774d39066 Merge upstream changes from lobehub/lobe-chat/main with automatic conflict resolution 2025-07-18 12:08:23 +02:00
Jamie Stivala 468a507d74 Improve sync-upstream script: fetch specific branch, handle unrelated histories, honor .gitattributes 2025-07-18 11:56:36 +02:00
Jamie Stivala cafbba3e25 Replace Fork-Sync-With-Upstream action with custom sync script [skip-ci] 2025-07-18 11:52:00 +02:00
Jamie Stivala 9da0e6bad8 Merge README.md and keep theirs [skip-ci] 2025-07-18 11:45:35 +02:00
semantic-release-bot 651a899c87 🔖 chore(release): v1.99.1 [skip ci]
### [Version&nbsp;1.99.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.99.0...v1.99.1)
<sup>Released on **2025-07-17**</sup>

#### 🐛 Bug Fixes

- **misc**: Use server env config image models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Use server env config image models, closes [#8478](https://github.com/jaworldwideorg/OneJA-Bot/issues/8478) ([768ee2b](https://github.com/jaworldwideorg/OneJA-Bot/commit/768ee2b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-17 19:27:28 +00:00
Jamie Stivala a31531fd91 Update sync.yml
On Sync finish, trigger release workflow
2025-07-17 21:12:03 +02:00
GH Action - Upstream Sync 75b084cee3 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-07-17 19:09:38 +00:00
semantic-release-bot 02354c9eda 🔖 chore(release): v1.99.0 [skip ci]
## [Version&nbsp;1.99.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.98.1...v1.99.0)
<sup>Released on **2025-07-17**</sup>

####  Features

- **misc**: Refactor desktop oauth and use JWTs token to support remote chat.

#### 🐛 Bug Fixes

- **misc**: Desktop local db can't upload image, fix apikey issue on server log, fix page error when url is not defined in web search plugin.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Refactor desktop oauth and use JWTs token to support remote chat, closes [#8446](https://github.com/jaworldwideorg/OneJA-Bot/issues/8446) ([054ca5f](https://github.com/jaworldwideorg/OneJA-Bot/commit/054ca5f))

#### What's fixed

* **misc**: Desktop local db can't upload image, closes [#8459](https://github.com/jaworldwideorg/OneJA-Bot/issues/8459) ([25bfc80](https://github.com/jaworldwideorg/OneJA-Bot/commit/25bfc80))
* **misc**: Fix apikey issue on server log, closes [#8457](https://github.com/jaworldwideorg/OneJA-Bot/issues/8457) ([43be2d1](https://github.com/jaworldwideorg/OneJA-Bot/commit/43be2d1))
* **misc**: Fix page error when url is not defined in web search plugin, closes [#8441](https://github.com/jaworldwideorg/OneJA-Bot/issues/8441) ([a55b65b](https://github.com/jaworldwideorg/OneJA-Bot/commit/a55b65b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-17 10:17:28 +00:00
Jamie Stivala 8ecbbe7899 Configure Git merge strategies for changelog and package.json files in sync workflow 2025-07-17 12:02:11 +02:00
Jamie Stivala 9884510fa3 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-17 11:55:05 +02:00
Jamie Stivala dff27b0ac0 Remove custom package.json merge handling from sync workflow
- Delete the `mergePackageJson.js` script and its associated tests.
- Simplify workflow by removing logic for backing up and restoring files.
- Streamline sync workflow inputs and steps for improved maintainability.
2025-07-17 11:54:57 +02:00
lobehubbot 6046d755ad 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-16 12:26:34 +00:00
semantic-release-bot 17bf2990b0 🔖 chore(release): v1.98.1 [skip ci]
### [Version&nbsp;1.98.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.98.0...v1.98.1)
<sup>Released on **2025-07-16**</sup>

#### 🐛 Bug Fixes

- **misc**: Chat model list should not show image model, some ai image generation feedback issues.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Chat model list should not show image model, closes [#8448](https://github.com/jaworldwideorg/OneJA-Bot/issues/8448) ([2bb1506](https://github.com/jaworldwideorg/OneJA-Bot/commit/2bb1506))
* **misc**: Some ai image generation feedback issues, closes [#8440](https://github.com/jaworldwideorg/OneJA-Bot/issues/8440) ([bc41329](https://github.com/jaworldwideorg/OneJA-Bot/commit/bc41329))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-16 12:26:06 +00:00
Jamie Stivala a3c2cf5223 Add custom package.json merge handling during sync workflow
- Implement logic to back up `package.json` for special handling during sync.
- Introduce a script for merging `package.json` with custom dependencies preserved.
- Add tests to validate `package.json` merge logic.
2025-07-16 14:10:57 +02:00
Jamie Stivala 768b401ceb Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-07-16 14:01:25 +02:00
lobehubbot d987b81f0c 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-15 08:26:39 +00:00
semantic-release-bot d914b7cd00 🔖 chore(release): v1.98.0 [skip ci]
## [Version&nbsp;1.98.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.97.0...v1.98.0)
<sup>Released on **2025-07-15**</sup>

####  Features

- **plugin**: Support Streamable HTTP MCP Server Auth.
- **misc**:  support AI Image.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **plugin**: Support Streamable HTTP MCP Server Auth, closes [#8425](https://github.com/jaworldwideorg/OneJA-Bot/issues/8425) ([853a09a](https://github.com/jaworldwideorg/OneJA-Bot/commit/853a09a))
* **misc**:  support AI Image, closes [#8312](https://github.com/jaworldwideorg/OneJA-Bot/issues/8312) ([095de57](https://github.com/jaworldwideorg/OneJA-Bot/commit/095de57))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-15 08:26:14 +00:00
Jamie Stivala c37027c07f Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-07-15 10:10:47 +02:00
semantic-release-bot a884dad265 🔖 chore(release): v1.97.0 [skip ci]
## [Version&nbsp;1.97.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.96.3...v1.97.0)
<sup>Released on **2025-07-14**</sup>

####  Features

- **misc**: Add network proxy for desktop.

#### 🐛 Bug Fixes

- **misc**: Add vision support to Grok 4, Revert "💄 style: Open new topic by tap Just Chat again".

#### 💄 Styles

- **misc**: Add Kimi K2 model, fix discover translation, Support Hunyuan A13B thinking model, Support new Doubao thinking models, update i18n, update i18n, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add network proxy for desktop, closes [#7848](https://github.com/jaworldwideorg/OneJA-Bot/issues/7848) ([46d2509](https://github.com/jaworldwideorg/OneJA-Bot/commit/46d2509))

#### What's fixed

* **misc**: Add vision support to Grok 4, closes [#8386](https://github.com/jaworldwideorg/OneJA-Bot/issues/8386) ([8512f5a](https://github.com/jaworldwideorg/OneJA-Bot/commit/8512f5a))
* **misc**: Revert "💄 style: Open new topic by tap Just Chat again", closes [#8402](https://github.com/jaworldwideorg/OneJA-Bot/issues/8402) ([55462b9](https://github.com/jaworldwideorg/OneJA-Bot/commit/55462b9))

#### Styles

* **misc**: Add Kimi K2 model, closes [#8401](https://github.com/jaworldwideorg/OneJA-Bot/issues/8401) ([4cb1a18](https://github.com/jaworldwideorg/OneJA-Bot/commit/4cb1a18))
* **misc**: Fix discover translation, closes [#8423](https://github.com/jaworldwideorg/OneJA-Bot/issues/8423) ([15ae35c](https://github.com/jaworldwideorg/OneJA-Bot/commit/15ae35c))
* **misc**: Support Hunyuan A13B thinking model, closes [#8278](https://github.com/jaworldwideorg/OneJA-Bot/issues/8278) ([09ca978](https://github.com/jaworldwideorg/OneJA-Bot/commit/09ca978))
* **misc**: Support new Doubao thinking models, closes [#8174](https://github.com/jaworldwideorg/OneJA-Bot/issues/8174) ([637d75c](https://github.com/jaworldwideorg/OneJA-Bot/commit/637d75c))
* **misc**: Update i18n, closes [#8422](https://github.com/jaworldwideorg/OneJA-Bot/issues/8422) ([5b89ec8](https://github.com/jaworldwideorg/OneJA-Bot/commit/5b89ec8))
* **misc**: Update i18n, closes [#8410](https://github.com/jaworldwideorg/OneJA-Bot/issues/8410) ([2515875](https://github.com/jaworldwideorg/OneJA-Bot/commit/2515875))
* **misc**: Update i18n, closes [#8400](https://github.com/jaworldwideorg/OneJA-Bot/issues/8400) ([790eeb8](https://github.com/jaworldwideorg/OneJA-Bot/commit/790eeb8))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-14 14:34:04 +00:00
Jamie Stivala 53975efcab Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-14 10:31:52 +02:00
lobehubbot fbb92a667f 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-11 09:09:37 +00:00
semantic-release-bot 5cf2e3c6fd 🔖 chore(release): v1.96.3 [skip ci]
### [Version&nbsp;1.96.3](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.96.2...v1.96.3)
<sup>Released on **2025-07-11**</sup>

#### 🐛 Bug Fixes

- **misc**: Grok-4 reasoning model universal matching.

#### 💄 Styles

- **misc**: Open new topic by tap Just Chat again, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Grok-4 reasoning model universal matching, closes [#8390](https://github.com/jaworldwideorg/OneJA-Bot/issues/8390) ([d6f17f8](https://github.com/jaworldwideorg/OneJA-Bot/commit/d6f17f8))

#### Styles

* **misc**: Open new topic by tap Just Chat again, closes [#8311](https://github.com/jaworldwideorg/OneJA-Bot/issues/8311) ([7e2f4ce](https://github.com/jaworldwideorg/OneJA-Bot/commit/7e2f4ce))
* **misc**: Update i18n, closes [#8387](https://github.com/jaworldwideorg/OneJA-Bot/issues/8387) ([00215c0](https://github.com/jaworldwideorg/OneJA-Bot/commit/00215c0))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-11 09:09:11 +00:00
Jamie Stivala dcb29ab16c Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-07-11 10:55:24 +02:00
lobehubbot 5604704d0a 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-10 12:33:22 +00:00
semantic-release-bot 08949c5757 🔖 chore(release): v1.96.2 [skip ci]
### [Version&nbsp;1.96.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.96.1...v1.96.2)
<sup>Released on **2025-07-10**</sup>

#### ♻ Code Refactoring

- **misc**: Replace `utility-types` with `type-fest`.

#### 💄 Styles

- **misc**: Add google search grounding for Vertex AI, fix: solve the loading was strange spin when switch show, integrate Amazon Cognito for user authentication.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Replace `utility-types` with `type-fest`, closes [#8370](https://github.com/jaworldwideorg/OneJA-Bot/issues/8370) ([a072b53](https://github.com/jaworldwideorg/OneJA-Bot/commit/a072b53))

#### Styles

* **misc**: Add google search grounding for Vertex AI, closes [#8313](https://github.com/jaworldwideorg/OneJA-Bot/issues/8313) ([afd5900](https://github.com/jaworldwideorg/OneJA-Bot/commit/afd5900))
* **misc**: Fix: solve the loading was strange spin when switch show, closes [#8333](https://github.com/jaworldwideorg/OneJA-Bot/issues/8333) ([07197e7](https://github.com/jaworldwideorg/OneJA-Bot/commit/07197e7))
* **misc**: Integrate Amazon Cognito for user authentication, closes [#7472](https://github.com/jaworldwideorg/OneJA-Bot/issues/7472) ([56f4e98](https://github.com/jaworldwideorg/OneJA-Bot/commit/56f4e98))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-10 12:32:53 +00:00
Jamie Stivala 23710714c1 Add Cognito as a new SSO provider to ssoProviders array 2025-07-10 14:18:51 +02:00
Jamie Stivala 41d1b45549 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	src/libs/next-auth/sso-providers/index.ts
2025-07-10 14:18:13 +02:00
lobehubbot 7b9f36aba5 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-10 08:50:04 +00:00
semantic-release-bot 8c44806b31 🔖 chore(release): v1.96.1 [skip ci]
### [Version&nbsp;1.96.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.96.0...v1.96.1)
<sup>Released on **2025-07-10**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix locale hydration error in SSR.

#### 💄 Styles

- **misc**: Add `grok-4-0709` model from xAI, fix theme issue in desktop, implement data analytics event tracking framework.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix locale hydration error in SSR, closes [#8365](https://github.com/jaworldwideorg/OneJA-Bot/issues/8365) ([63f482a](https://github.com/jaworldwideorg/OneJA-Bot/commit/63f482a))

#### Styles

* **misc**: Add `grok-4-0709` model from xAI, closes [#8379](https://github.com/jaworldwideorg/OneJA-Bot/issues/8379) ([b7ca447](https://github.com/jaworldwideorg/OneJA-Bot/commit/b7ca447))
* **misc**: Fix theme issue in desktop, closes [#8380](https://github.com/jaworldwideorg/OneJA-Bot/issues/8380) ([c7ae78b](https://github.com/jaworldwideorg/OneJA-Bot/commit/c7ae78b))
* **misc**: Implement data analytics event tracking framework, closes [#8352](https://github.com/jaworldwideorg/OneJA-Bot/issues/8352) ([f433aca](https://github.com/jaworldwideorg/OneJA-Bot/commit/f433aca))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-10 08:49:34 +00:00
Jamie Stivala 5dd6cf9bff Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-10 10:35:06 +02:00
GH Action - Upstream Sync bb4668038b Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-07-09 12:11:57 +00:00
GH Action - Upstream Sync ed0b98cc5b Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-07-09 06:10:11 +00:00
lobehubbot f57cb2f6f3 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-08 12:52:37 +00:00
semantic-release-bot 297216961a 🔖 chore(release): v1.96.0 [skip ci]
## [Version&nbsp;1.96.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.95.2...v1.96.0)
<sup>Released on **2025-07-08**</sup>

####  Features

- **misc**: Add MCP marketplace and mcp plugin one-click installation in desktop.

#### 💄 Styles

- **misc**: Add `MCP_TOOL_TIMEOUT` env and improve debug usage guide.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add MCP marketplace and mcp plugin one-click installation in desktop, closes [#8334](https://github.com/jaworldwideorg/OneJA-Bot/issues/8334) ([416a4b1](https://github.com/jaworldwideorg/OneJA-Bot/commit/416a4b1))

#### Styles

* **misc**: Add `MCP_TOOL_TIMEOUT` env and improve debug usage guide, closes [#8357](https://github.com/jaworldwideorg/OneJA-Bot/issues/8357) ([d4baae5](https://github.com/jaworldwideorg/OneJA-Bot/commit/d4baae5))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-08 12:52:13 +00:00
Jamie Stivala fdc239c2c7 Merge remote-tracking branch 'origin/main' 2025-07-08 14:37:47 +02:00
Jamie Stivala 39809f92bb Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-08 14:37:38 +02:00
lobehubbot 650514421f 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-07 09:44:24 +00:00
semantic-release-bot c980dd1c41 🔖 chore(release): v1.95.2 [skip ci]
### [Version&nbsp;1.95.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.95.1...v1.95.2)
<sup>Released on **2025-07-07**</sup>

#### 🐛 Bug Fixes

- **misc**: Change the wrong github checkmodel name, pin `officeparser@5.1.1` to fix server error.

#### 💄 Styles

- **misc**: Files hello pages should scroll.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Change the wrong github checkmodel name, closes [#8339](https://github.com/jaworldwideorg/OneJA-Bot/issues/8339) ([f07d912](https://github.com/jaworldwideorg/OneJA-Bot/commit/f07d912))
* **misc**: Pin `officeparser@5.1.1` to fix server error, closes [#8354](https://github.com/jaworldwideorg/OneJA-Bot/issues/8354) ([3f4e935](https://github.com/jaworldwideorg/OneJA-Bot/commit/3f4e935))

#### Styles

* **misc**: Files hello pages should scroll, closes [#8340](https://github.com/jaworldwideorg/OneJA-Bot/issues/8340) ([df9b7df](https://github.com/jaworldwideorg/OneJA-Bot/commit/df9b7df))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-07 09:43:59 +00:00
Jamie Stivala 06b2b76963 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-07-07 11:29:58 +02:00
lobehubbot cd9f7848c1 📝 docs(bot): Auto sync agents & plugin to readme 2025-07-03 12:41:02 +00:00
semantic-release-bot c5dbde3912 🔖 chore(release): v1.95.1 [skip ci]
### [Version&nbsp;1.95.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.95.0...v1.95.1)
<sup>Released on **2025-07-03**</sup>

#### ♻ Code Refactoring

- **misc**: Migrate to `@google/genai` SDK for Google Gemini API and Vertex AI.

#### 🐛 Bug Fixes

- **mermaid**: Firefox mermaid show error.
- **misc**: Fix desktop chunk issue, pin `antd@5.26.2` to fix build error, Wrong Gemini 2.5 Pro thinkbudget.

#### 💄 Styles

- **misc**: Add DeepResearch models from OpenAI, update i18n, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Migrate to `@google/genai` SDK for Google Gemini API and Vertex AI, closes [#7884](https://github.com/jaworldwideorg/OneJA-Bot/issues/7884) ([fef3e5f](https://github.com/jaworldwideorg/OneJA-Bot/commit/fef3e5f))

#### What's fixed

* **mermaid**: Firefox mermaid show error, closes [#8270](https://github.com/jaworldwideorg/OneJA-Bot/issues/8270) ([d9c5e7b](https://github.com/jaworldwideorg/OneJA-Bot/commit/d9c5e7b))
* **misc**: Fix desktop chunk issue, closes [#8280](https://github.com/jaworldwideorg/OneJA-Bot/issues/8280) ([c193e65](https://github.com/jaworldwideorg/OneJA-Bot/commit/c193e65))
* **misc**: Pin `antd@5.26.2` to fix build error, closes [#8303](https://github.com/jaworldwideorg/OneJA-Bot/issues/8303) ([44b6b01](https://github.com/jaworldwideorg/OneJA-Bot/commit/44b6b01))
* **misc**: Wrong Gemini 2.5 Pro thinkbudget, closes [#8296](https://github.com/jaworldwideorg/OneJA-Bot/issues/8296) ([18920c5](https://github.com/jaworldwideorg/OneJA-Bot/commit/18920c5))

#### Styles

* **misc**: Add DeepResearch models from OpenAI, closes [#8291](https://github.com/jaworldwideorg/OneJA-Bot/issues/8291) ([87a5cbc](https://github.com/jaworldwideorg/OneJA-Bot/commit/87a5cbc))
* **misc**: Update i18n, closes [#8322](https://github.com/jaworldwideorg/OneJA-Bot/issues/8322) ([0c6b885](https://github.com/jaworldwideorg/OneJA-Bot/commit/0c6b885))
* **misc**: Update i18n, closes [#8306](https://github.com/jaworldwideorg/OneJA-Bot/issues/8306) ([80aad1d](https://github.com/jaworldwideorg/OneJA-Bot/commit/80aad1d))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-03 12:40:36 +00:00
Jamie Stivala 786331d3f4 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-07-03 14:25:28 +02:00
Jamie Stivala 378dceefa4 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-06-30 11:21:04 +02:00
lobehubbot 809a60d90c 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-25 11:50:43 +00:00
semantic-release-bot 24f47f83f9 🔖 chore(release): v1.95.0 [skip ci]
## [Version&nbsp;1.95.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.5...v1.95.0)
<sup>Released on **2025-06-25**</sup>

####  Features

- **misc**: Add Brave & Google PSE & Kagi as build-in Search Provider, add v0 (Vercel) provider support.

#### 🐛 Bug Fixes

- **misc**: Fix `MiniMax-M1` reasoning tag missing, fix inputTemplate behavior, Google Gemini tools declarations, Remove unsupported parameters of Hunyuan.

#### 💄 Styles

- **openrouter**: Add stable versions of Gemini 2.5 models.
- **misc**: Add `blockAds` & `stealth` params for Browserless, Optimized Gemini thinkingBudget configuration, update i18n, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add Brave & Google PSE & Kagi as build-in Search Provider, closes [#8172](https://github.com/jaworldwideorg/OneJA-Bot/issues/8172) ([16ae521](https://github.com/jaworldwideorg/OneJA-Bot/commit/16ae521))
* **misc**: Add v0 (Vercel) provider support, closes [#8235](https://github.com/jaworldwideorg/OneJA-Bot/issues/8235) ([5842a18](https://github.com/jaworldwideorg/OneJA-Bot/commit/5842a18))

#### What's fixed

* **misc**: Fix `MiniMax-M1` reasoning tag missing, closes [#8240](https://github.com/jaworldwideorg/OneJA-Bot/issues/8240) ([ea76c11](https://github.com/jaworldwideorg/OneJA-Bot/commit/ea76c11))
* **misc**: Fix inputTemplate behavior, closes [#8204](https://github.com/jaworldwideorg/OneJA-Bot/issues/8204) ([61c2c3c](https://github.com/jaworldwideorg/OneJA-Bot/commit/61c2c3c))
* **misc**: Google Gemini tools declarations, closes [#8256](https://github.com/jaworldwideorg/OneJA-Bot/issues/8256) ([08f5d73](https://github.com/jaworldwideorg/OneJA-Bot/commit/08f5d73))
* **misc**: Remove unsupported parameters of Hunyuan, closes [#8247](https://github.com/jaworldwideorg/OneJA-Bot/issues/8247) ([826d724](https://github.com/jaworldwideorg/OneJA-Bot/commit/826d724))

#### Styles

* **openrouter**: Add stable versions of Gemini 2.5 models, closes [#8239](https://github.com/jaworldwideorg/OneJA-Bot/issues/8239) ([d34ecab](https://github.com/jaworldwideorg/OneJA-Bot/commit/d34ecab))
* **misc**: Add `blockAds` & `stealth` params for Browserless, closes [#8255](https://github.com/jaworldwideorg/OneJA-Bot/issues/8255) ([2ff3efa](https://github.com/jaworldwideorg/OneJA-Bot/commit/2ff3efa))
* **misc**: Optimized Gemini thinkingBudget configuration, closes [#8224](https://github.com/jaworldwideorg/OneJA-Bot/issues/8224) ([03625e8](https://github.com/jaworldwideorg/OneJA-Bot/commit/03625e8))
* **misc**: Update i18n, closes [#8253](https://github.com/jaworldwideorg/OneJA-Bot/issues/8253) ([b86dc9b](https://github.com/jaworldwideorg/OneJA-Bot/commit/b86dc9b))
* **misc**: Update i18n, closes [#8242](https://github.com/jaworldwideorg/OneJA-Bot/issues/8242) ([2d1babc](https://github.com/jaworldwideorg/OneJA-Bot/commit/2d1babc))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-25 11:50:11 +00:00
Jamie Stivala c3d386691a Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.zh-CN.md
#	changelog/v1.json
#	src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx
2025-06-25 13:34:22 +02:00
lobehubbot 139323ffc1 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-20 10:45:55 +00:00
semantic-release-bot c9019f23bf 🔖 chore(release): v1.94.5 [skip ci]
### [Version&nbsp;1.94.5](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.4...v1.94.5)
<sup>Released on **2025-06-20**</sup>

#### 🐛 Bug Fixes

- **misc**: Correctly pass `reasoning.summary`.

#### 💄 Styles

- **misc**: Add MiniMax-M1 model, Update Gemini 2.5 Pro, Flash GA models. Add Gemini 2.5 Flash-Lite Preview model, update i18n, update i18n, update model card for Gemini 2.5 Pro via OpenRouter.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Correctly pass `reasoning.summary`, closes [#8221](https://github.com/jaworldwideorg/OneJA-Bot/issues/8221) ([da79815](https://github.com/jaworldwideorg/OneJA-Bot/commit/da79815))

#### Styles

* **misc**: Add MiniMax-M1 model, closes [#8209](https://github.com/jaworldwideorg/OneJA-Bot/issues/8209) ([41a0178](https://github.com/jaworldwideorg/OneJA-Bot/commit/41a0178))
* **misc**: Update Gemini 2.5 Pro, Flash GA models. Add Gemini 2.5 Flash-Lite Preview model, closes [#8213](https://github.com/jaworldwideorg/OneJA-Bot/issues/8213) ([39ef8be](https://github.com/jaworldwideorg/OneJA-Bot/commit/39ef8be))
* **misc**: Update i18n, closes [#8233](https://github.com/jaworldwideorg/OneJA-Bot/issues/8233) ([88c4362](https://github.com/jaworldwideorg/OneJA-Bot/commit/88c4362))
* **misc**: Update i18n, closes [#8225](https://github.com/jaworldwideorg/OneJA-Bot/issues/8225) ([53e1784](https://github.com/jaworldwideorg/OneJA-Bot/commit/53e1784))
* **misc**: Update model card for Gemini 2.5 Pro via OpenRouter, closes [#8129](https://github.com/jaworldwideorg/OneJA-Bot/issues/8129) ([c96d9ef](https://github.com/jaworldwideorg/OneJA-Bot/commit/c96d9ef))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-20 10:45:30 +00:00
Jamie Stivala 8cf8c418bc Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.zh-CN.md
#	changelog/v1.json
2025-06-20 18:30:42 +08:00
semantic-release-bot 51ba55dfb4 🔖 chore(release): v1.94.4 [skip ci]
### [Version&nbsp;1.94.4](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.3...v1.94.4)
<sup>Released on **2025-06-18**</sup>

#### 🐛 Bug Fixes

- **misc**: Enhance the multi-display window opening experience.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Enhance the multi-display window opening experience, closes [#8176](https://github.com/jaworldwideorg/OneJA-Bot/issues/8176) ([b132e66](https://github.com/jaworldwideorg/OneJA-Bot/commit/b132e66))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-18 09:25:52 +00:00
Jamie Stivala 0db1f753a0 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-18 16:50:32 +08:00
semantic-release-bot 624ab716e6 🔖 chore(release): v1.94.3 [skip ci]
### [Version&nbsp;1.94.3](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.2...v1.94.3)
<sup>Released on **2025-06-16**</sup>

#### 🐛 Bug Fixes

- **misc**: Correctly handle `reasoning_effort`, improve chat selectors and enhance topic handling logic.

#### 💄 Styles

- **misc**: Add `kimi-thinking-preview` model from Moonshot.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Correctly handle `reasoning_effort`, closes [#8180](https://github.com/jaworldwideorg/OneJA-Bot/issues/8180) ([1c04736](https://github.com/jaworldwideorg/OneJA-Bot/commit/1c04736))
* **misc**: Improve chat selectors and enhance topic handling logic, closes [#8133](https://github.com/jaworldwideorg/OneJA-Bot/issues/8133) [#8117](https://github.com/jaworldwideorg/OneJA-Bot/issues/8117) ([15b24f1](https://github.com/jaworldwideorg/OneJA-Bot/commit/15b24f1))

#### Styles

* **misc**: Add `kimi-thinking-preview` model from Moonshot, closes [#8171](https://github.com/jaworldwideorg/OneJA-Bot/issues/8171) ([93d677c](https://github.com/jaworldwideorg/OneJA-Bot/commit/93d677c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-16 10:32:37 +00:00
Jamie Stivala e148fd9c97 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-16 18:17:32 +08:00
semantic-release-bot b652a8fb0a 🔖 chore(release): v1.94.2 [skip ci]
### [Version&nbsp;1.94.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.1...v1.94.2)
<sup>Released on **2025-06-13**</sup>

#### 🐛 Bug Fixes

- **misc**: Abort the Gemini request correctly & Add openai o3-pro.

#### 💄 Styles

- **misc**: Add Doubao Seed 1.6 model.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Abort the Gemini request correctly & Add openai o3-pro, closes [#8135](https://github.com/jaworldwideorg/OneJA-Bot/issues/8135) ([c79f1b9](https://github.com/jaworldwideorg/OneJA-Bot/commit/c79f1b9))

#### Styles

* **misc**: Add Doubao Seed 1.6 model, closes [#8167](https://github.com/jaworldwideorg/OneJA-Bot/issues/8167) ([bdfa44b](https://github.com/jaworldwideorg/OneJA-Bot/commit/bdfa44b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-13 01:20:57 +00:00
Jamie Stivala 62dd97b688 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-13 08:57:20 +08:00
lobehubbot 6481f0bb7a 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-12 14:06:53 +00:00
semantic-release-bot 0e39773557 🔖 chore(release): v1.94.1 [skip ci]
### [Version&nbsp;1.94.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.94.0...v1.94.1)
<sup>Released on **2025-06-12**</sup>

#### 🐛 Bug Fixes

- **chat**: Improve response animation merging logic.
- **misc**: Update Gemini range of thinkingBudget.

#### 💄 Styles

- **ModelSelect**: Improve mobile layout and text overflow handling.
- **misc**: Support `web_search_preview` & fix some bug form OpenAI Response API, Transition animation switch, update pplx abilities tags, support `vision`.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **chat**: Improve response animation merging logic, closes [#8160](https://github.com/jaworldwideorg/OneJA-Bot/issues/8160) ([9d81cdc](https://github.com/jaworldwideorg/OneJA-Bot/commit/9d81cdc))
* **misc**: Update Gemini range of thinkingBudget, closes [#8122](https://github.com/jaworldwideorg/OneJA-Bot/issues/8122) ([7331e8a](https://github.com/jaworldwideorg/OneJA-Bot/commit/7331e8a))

#### Styles

* **ModelSelect**: Improve mobile layout and text overflow handling, closes [#8118](https://github.com/jaworldwideorg/OneJA-Bot/issues/8118) ([d97aa49](https://github.com/jaworldwideorg/OneJA-Bot/commit/d97aa49))
* **misc**: Support `web_search_preview` & fix some bug form OpenAI Response API, closes [#8131](https://github.com/jaworldwideorg/OneJA-Bot/issues/8131) ([b2983f0](https://github.com/jaworldwideorg/OneJA-Bot/commit/b2983f0))
* **misc**: Transition animation switch, closes [#7981](https://github.com/jaworldwideorg/OneJA-Bot/issues/7981) ([dd4ab3f](https://github.com/jaworldwideorg/OneJA-Bot/commit/dd4ab3f))
* **misc**: Update pplx abilities tags, support `vision`, closes [#8119](https://github.com/jaworldwideorg/OneJA-Bot/issues/8119) ([5c2e5f7](https://github.com/jaworldwideorg/OneJA-Bot/commit/5c2e5f7))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-12 14:06:25 +00:00
Jamie Stivala e58affe613 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-12 21:41:47 +08:00
Jamie Stivala 0797fac217 Merge remote-tracking branch 'upstream/main' 2025-06-12 21:41:08 +08:00
lobehubbot adbd822851 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-10 12:27:22 +00:00
semantic-release-bot a31f6167bb 🔖 chore(release): v1.94.0 [skip ci]
## [Version&nbsp;1.94.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.93.2...v1.94.0)
<sup>Released on **2025-06-10**</sup>

####  Features

- **misc**: Support google sso as auth provider.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support google sso as auth provider, closes [#8074](https://github.com/jaworldwideorg/OneJA-Bot/issues/8074) ([43ab03a](https://github.com/jaworldwideorg/OneJA-Bot/commit/43ab03a))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-10 12:27:02 +00:00
Jamie Stivala 6ce07b21be Add Okta as a new SSO provider to ssoProviders array 2025-06-10 20:12:25 +08:00
Jamie Stivala 908b4be918 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
#	src/libs/next-auth/sso-providers/index.ts
2025-06-10 20:10:53 +08:00
Jamie Stivala 50c7570440 Auto-redirect the user to the SSO Provider login page if one provider 2025-06-10 19:58:01 +08:00
lobehubbot a021d0ee0e 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-10 08:45:53 +00:00
semantic-release-bot 72925ecd0c 🔖 chore(release): v1.93.2 [skip ci]
### [Version&nbsp;1.93.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.93.1...v1.93.2)
<sup>Released on **2025-06-10**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor `<think>` & `</think>` handling, refactor branding info.

#### 🐛 Bug Fixes

- **misc**: Restore reasoningEffort in setting.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Refactor `<think>` & `</think>` handling, closes [#8121](https://github.com/jaworldwideorg/OneJA-Bot/issues/8121) ([04ac353](https://github.com/jaworldwideorg/OneJA-Bot/commit/04ac353))
* **misc**: Refactor branding info, closes [#8134](https://github.com/jaworldwideorg/OneJA-Bot/issues/8134) ([3baa966](https://github.com/jaworldwideorg/OneJA-Bot/commit/3baa966))

#### What's fixed

* **misc**: Restore reasoningEffort in setting, closes [#8123](https://github.com/jaworldwideorg/OneJA-Bot/issues/8123) ([3be609c](https://github.com/jaworldwideorg/OneJA-Bot/commit/3be609c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-10 08:45:32 +00:00
Jamie Stivala 0633286211 Updated branding 2025-06-10 16:30:53 +08:00
Jamie Stivala e93d27b14d Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-10 16:26:01 +08:00
Jamie Stivala e7c0372191 Removed unused LobeChat import from AuthSignInBox.tsx. 2025-06-09 17:57:25 +08:00
lobehubbot 45eae70926 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-09 08:45:54 +00:00
semantic-release-bot 7cec77ae2c 🔖 chore(release): v1.92.0 [skip ci]
## [Version&nbsp;1.92.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.91.3...v1.92.0)
<sup>Released on **2025-06-09**</sup>

####  Features

- **misc**: Support OpenAI Responses API mode, support placeholder variables in prompts and input.

#### 🐛 Bug Fixes

- **misc**: Fix client s3 getObject throw error, fix openai default Responses API issue.

#### 💄 Styles

- **ModelSelect**: Add responsive layout for mobile devices.
- **misc**: Add support to azureopenai embedding, improve `{{username}}` placeholder variable, Support OpenRouter Claude 4 reasoning, Update Gemini & Qwen models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support OpenAI Responses API mode, closes [#8048](https://github.com/jaworldwideorg/OneJA-Bot/issues/8048) ([5bf0921](https://github.com/jaworldwideorg/OneJA-Bot/commit/5bf0921))
* **misc**: Support placeholder variables in prompts and input, closes [#8060](https://github.com/jaworldwideorg/OneJA-Bot/issues/8060) ([3752739](https://github.com/jaworldwideorg/OneJA-Bot/commit/3752739))

#### What's fixed

* **misc**: Fix client s3 getObject throw error, closes [#8009](https://github.com/jaworldwideorg/OneJA-Bot/issues/8009) ([b91ca8c](https://github.com/jaworldwideorg/OneJA-Bot/commit/b91ca8c))
* **misc**: Fix openai default Responses API issue, closes [#8124](https://github.com/jaworldwideorg/OneJA-Bot/issues/8124) ([7f6ccf2](https://github.com/jaworldwideorg/OneJA-Bot/commit/7f6ccf2))

#### Styles

* **ModelSelect**: Add responsive layout for mobile devices, closes [#7960](https://github.com/jaworldwideorg/OneJA-Bot/issues/7960) ([cb84c3e](https://github.com/jaworldwideorg/OneJA-Bot/commit/cb84c3e))
* **misc**: Add support to azureopenai embedding, closes [#8075](https://github.com/jaworldwideorg/OneJA-Bot/issues/8075) ([0725f94](https://github.com/jaworldwideorg/OneJA-Bot/commit/0725f94))
* **misc**: Improve `{{username}}` placeholder variable, closes [#8100](https://github.com/jaworldwideorg/OneJA-Bot/issues/8100) ([95fd588](https://github.com/jaworldwideorg/OneJA-Bot/commit/95fd588))
* **misc**: Support OpenRouter Claude 4 reasoning, closes [#8087](https://github.com/jaworldwideorg/OneJA-Bot/issues/8087) ([039be1d](https://github.com/jaworldwideorg/OneJA-Bot/commit/039be1d))
* **misc**: Update Gemini & Qwen models, closes [#8083](https://github.com/jaworldwideorg/OneJA-Bot/issues/8083) ([6308237](https://github.com/jaworldwideorg/OneJA-Bot/commit/6308237))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-09 08:45:27 +00:00
Jamie Stivala f7ef381bbb Replaced hardcoded username with dynamic branding constant in auth selectors test. 2025-06-09 16:30:44 +08:00
Jamie Stivala 19edff11d7 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.md
#	README.zh-CN.md
#	changelog/v1.json
2025-06-09 16:10:32 +08:00
Jamie Stivala 7cbea6da8a Merge remote-tracking branch 'origin/main' 2025-06-09 16:10:18 +08:00
semantic-release-bot 65259e566c 🔖 chore(release): v1.91.3 [skip ci]
### [Version&nbsp;1.91.3](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.91.2...v1.91.3)
<sup>Released on **2025-06-06**</sup>

#### 🐛 Bug Fixes

- **misc**: Some web search bugs.

#### 💄 Styles

- **misc**: Support Vertex AI thought summaries.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Some web search bugs, closes [#8068](https://github.com/jaworldwideorg/OneJA-Bot/issues/8068) ([bebe7a3](https://github.com/jaworldwideorg/OneJA-Bot/commit/bebe7a3))

#### Styles

* **misc**: Support Vertex AI thought summaries, closes [#8090](https://github.com/jaworldwideorg/OneJA-Bot/issues/8090) ([1355a2e](https://github.com/jaworldwideorg/OneJA-Bot/commit/1355a2e))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-06 12:25:30 +00:00
GH Action - Upstream Sync 09592304f8 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-06-06 12:11:19 +00:00
Jamie Stivala 19ab3fef16 Replaced static branding elements with dynamic values sourced from constants. 2025-06-06 18:51:34 +08:00
Jamie Stivala 4884f26ec5 Merge remote-tracking branch 'origin/main' 2025-06-06 18:26:27 +08:00
Jamie Stivala b07803d6a8 Fixed the issue with auto login being looped 2025-06-06 18:26:16 +08:00
lobehubbot 3925d15fa2 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-06 08:56:01 +00:00
semantic-release-bot 010280afdd 🔖 chore(release): v1.91.2 [skip ci]
### [Version&nbsp;1.91.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.91.1...v1.91.2)
<sup>Released on **2025-06-06**</sup>

#### 🐛 Bug Fixes

- **misc**: Correct deepseek R1 fc support display.

#### 💄 Styles

- **misc**: Add openAI websearch and claude 4 to modelproviders.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Correct deepseek R1 fc support display, closes [#8069](https://github.com/jaworldwideorg/OneJA-Bot/issues/8069) ([ed5bb5f](https://github.com/jaworldwideorg/OneJA-Bot/commit/ed5bb5f))

#### Styles

* **misc**: Add openAI websearch and claude 4 to modelproviders, closes [#7988](https://github.com/jaworldwideorg/OneJA-Bot/issues/7988) ([95994f4](https://github.com/jaworldwideorg/OneJA-Bot/commit/95994f4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-06 08:55:42 +00:00
Jamie Stivala f2e79fe809 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-06 16:38:37 +08:00
lobehubbot cde421edc7 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-05 12:26:00 +00:00
semantic-release-bot f1ac9bf38c 🔖 chore(release): v1.91.1 [skip ci]
### [Version&nbsp;1.91.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.91.0...v1.91.1)
<sup>Released on **2025-06-05**</sup>

#### 💄 Styles

- **misc**: Add Volcengine & OpenAI-like Provider (e.g. oneapi) model fetch support, improve loading state.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add Volcengine & OpenAI-like Provider (e.g. oneapi) model fetch support, closes [#8064](https://github.com/jaworldwideorg/OneJA-Bot/issues/8064) ([d3dafe1](https://github.com/jaworldwideorg/OneJA-Bot/commit/d3dafe1))
* **misc**: Improve loading state, closes [#8072](https://github.com/jaworldwideorg/OneJA-Bot/issues/8072) ([f0a7193](https://github.com/jaworldwideorg/OneJA-Bot/commit/f0a7193))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-05 12:25:41 +00:00
GH Action - Upstream Sync 8ae66242b5 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-06-05 12:11:48 +00:00
GH Action - Upstream Sync b9c489a115 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-06-05 06:09:48 +00:00
GH Action - Upstream Sync 62f83a6230 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-06-04 18:09:01 +00:00
lobehubbot abcc820239 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-04 14:46:31 +00:00
Jamie Stivala 17f56df3cf Auto-triggered sign-in for single SSO provider using useLayoutEffect instead of useEffect. 2025-06-04 22:31:55 +08:00
Jamie Stivala 66c6e506dc Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-06-04 21:52:33 +08:00
Jamie Stivala 9ad33bdc6e Added searxng-settings.yml for development environment in Docker Compose configuration. 2025-06-03 22:49:12 +08:00
Jamie Stivala 86bac0654c Removed auto-triggered sign-in behavior for single SSO provider. 2025-06-03 22:47:55 +08:00
lobehubbot a4281e53ef 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-03 14:07:58 +00:00
semantic-release-bot a9ed1a634d 🔖 chore(release): v1.89.0 [skip ci]
## [Version&nbsp;1.89.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.88.0...v1.89.0)
<sup>Released on **2025-06-03**</sup>

#### ♻ Code Refactoring

- **misc**: Rename the createOpenAICompatibleRuntime.

####  Features

- **misc**: Add more provider support for search & crawl.

#### 🐛 Bug Fixes

- **misc**: Update the clerk middleware to support route protection.

#### 💄 Styles

- **misc**: Update modelscope models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Rename the createOpenAICompatibleRuntime, closes [#8049](https://github.com/jaworldwideorg/OneJA-Bot/issues/8049) ([ee660d6](https://github.com/jaworldwideorg/OneJA-Bot/commit/ee660d6))

#### What's improved

* **misc**: Add more provider support for search & crawl, closes [#8033](https://github.com/jaworldwideorg/OneJA-Bot/issues/8033) ([23fade3](https://github.com/jaworldwideorg/OneJA-Bot/commit/23fade3))

#### What's fixed

* **misc**: Update the clerk middleware to support route protection, closes [#8044](https://github.com/jaworldwideorg/OneJA-Bot/issues/8044) ([309f973](https://github.com/jaworldwideorg/OneJA-Bot/commit/309f973))

#### Styles

* **misc**: Update modelscope models, closes [#8057](https://github.com/jaworldwideorg/OneJA-Bot/issues/8057) ([3e02c25](https://github.com/jaworldwideorg/OneJA-Bot/commit/3e02c25))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-03 14:07:37 +00:00
Jamie Stivala 16dcf8edcd Merge remote-tracking branch 'upstream/main' 2025-06-03 21:53:03 +08:00
Jamie Stivala 8aeb49fd2d Updated vitest-canvas-mock to vi-canvas-mock in package.json. 2025-06-03 21:52:30 +08:00
Jamie Stivala 946cd085ac Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
#	package.json
2025-06-03 21:36:25 +08:00
Jamie Stivala 6ba03bf8c3 Auto-triggered sign-in for single SSO provider using useEffect. 2025-06-03 21:35:08 +08:00
lobehubbot 9e532232d7 📝 docs(bot): Auto sync agents & plugin to readme 2025-06-02 09:57:17 +00:00
semantic-release-bot e76ade32e3 🔖 chore(release): v1.88.0 [skip ci]
## [Version&nbsp;1.88.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.87.2...v1.88.0)
<sup>Released on **2025-06-02**</sup>

####  Features

- **misc**:  Support ModelScope Provider, support protect page.

#### 🐛 Bug Fixes

- **misc**: Agent automatic completion meta not working error, disable LaTeX and Mermaid rendering in SystemRoleContent to prevent lag caused by massive rendering tasks when switching topics, fix DeepSeek new R1 Search error.

#### 💄 Styles

- **misc**:  `+` in the welcome message can be clicked to create an assistant, Enable deploymentName for Aliyun Bailian, Enhanced reasoning_effort Slider Component, support `web_search` tool for MiniMax & Zhipu, support 01.ai proxy url, Update Hunyuan models & deepseek-r1-0528, use default deployment name when parseModelString doesn't contain deployment name.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**:  Support ModelScope Provider, closes [#8026](https://github.com/jaworldwideorg/OneJA-Bot/issues/8026) ([7b91dfd](https://github.com/jaworldwideorg/OneJA-Bot/commit/7b91dfd))
* **misc**: Support protect page, closes [#8024](https://github.com/jaworldwideorg/OneJA-Bot/issues/8024) ([d61a9f5](https://github.com/jaworldwideorg/OneJA-Bot/commit/d61a9f5))

#### What's fixed

* **misc**: Agent automatic completion meta not working error, closes [#8003](https://github.com/jaworldwideorg/OneJA-Bot/issues/8003) ([c5307bf](https://github.com/jaworldwideorg/OneJA-Bot/commit/c5307bf))
* **misc**: Disable LaTeX and Mermaid rendering in SystemRoleContent to prevent lag caused by massive rendering tasks when switching topics, closes [#8034](https://github.com/jaworldwideorg/OneJA-Bot/issues/8034) ([5b42ee2](https://github.com/jaworldwideorg/OneJA-Bot/commit/5b42ee2))
* **misc**: Fix DeepSeek new R1 Search error, closes [#8035](https://github.com/jaworldwideorg/OneJA-Bot/issues/8035) ([cf58628](https://github.com/jaworldwideorg/OneJA-Bot/commit/cf58628))

#### Styles

* **misc**:  `+` in the welcome message can be clicked to create an assistant, closes [#7984](https://github.com/jaworldwideorg/OneJA-Bot/issues/7984) ([9f07e4c](https://github.com/jaworldwideorg/OneJA-Bot/commit/9f07e4c))
* **misc**: Enable deploymentName for Aliyun Bailian, closes [#7576](https://github.com/jaworldwideorg/OneJA-Bot/issues/7576) ([169e598](https://github.com/jaworldwideorg/OneJA-Bot/commit/169e598))
* **misc**: Enhanced reasoning_effort Slider Component, closes [#7998](https://github.com/jaworldwideorg/OneJA-Bot/issues/7998) ([750b26a](https://github.com/jaworldwideorg/OneJA-Bot/commit/750b26a))
* **misc**: Support `web_search` tool for MiniMax & Zhipu, closes [#7980](https://github.com/jaworldwideorg/OneJA-Bot/issues/7980) ([28cdafb](https://github.com/jaworldwideorg/OneJA-Bot/commit/28cdafb))
* **misc**: Support 01.ai proxy url, closes [#8025](https://github.com/jaworldwideorg/OneJA-Bot/issues/8025) ([e0442b8](https://github.com/jaworldwideorg/OneJA-Bot/commit/e0442b8))
* **misc**: Update Hunyuan models & deepseek-r1-0528, closes [#7993](https://github.com/jaworldwideorg/OneJA-Bot/issues/7993) ([2eb198c](https://github.com/jaworldwideorg/OneJA-Bot/commit/2eb198c))
* **misc**: Use default deployment name when parseModelString doesn't contain deployment name, closes [#7719](https://github.com/jaworldwideorg/OneJA-Bot/issues/7719) ([aef19f4](https://github.com/jaworldwideorg/OneJA-Bot/commit/aef19f4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-06-02 09:56:48 +00:00
Jamie Stivala a7b89493e4 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.zh-CN.md
#	changelog/v1.json
2025-06-02 17:19:20 +08:00
lobehubbot bcc9c54356 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-30 06:56:51 +00:00
semantic-release-bot e53a8db7c9 🔖 chore(release): v1.87.2 [skip ci]
### [Version&nbsp;1.87.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.87.1...v1.87.2)
<sup>Released on **2025-05-30**</sup>

#### 💄 Styles

- **misc**: Support Web Search Tools and Beta Header from Anthropic.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Support Web Search Tools and Beta Header from Anthropic, closes [#7964](https://github.com/jaworldwideorg/OneJA-Bot/issues/7964) ([a47ddc5](https://github.com/jaworldwideorg/OneJA-Bot/commit/a47ddc5))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-30 06:56:31 +00:00
Jamie Stivala e2ad515379 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-05-30 14:41:52 +08:00
Jamie Stivala f542b49955 Merge remote-tracking branch 'origin/main' 2025-05-30 13:50:39 +08:00
Jamie Stivala 77259e2245 Updated test to reflect JA Logo change 2025-05-30 13:50:17 +08:00
Jamie Stivala d0385e25f1 Updated test to reflect JA Logo change 2025-05-30 13:48:48 +08:00
Jamie Stivala bd9c6e37fc Fixed issue with JA Worldwide logo not loading properly 2025-05-30 13:23:51 +08:00
lobehubbot 0df9aff7db 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-30 04:13:45 +00:00
semantic-release-bot 8a000ac0d8 🔖 chore(release): v1.87.1 [skip ci]
### [Version&nbsp;1.87.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.87.0...v1.87.1)
<sup>Released on **2025-05-30**</sup>

#### 🐛 Bug Fixes

- **misc**: Close historySummary correctly, cmd + click chat tab not open new tab, Enable thinking output only for supported Gemini thinking models.

#### 💄 Styles

- **misc**: Add fc ability to deepseek-reasoner model, increase the history limit, Update GitHub models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Close historySummary correctly, closes [#7010](https://github.com/jaworldwideorg/OneJA-Bot/issues/7010) ([90a6f68](https://github.com/jaworldwideorg/OneJA-Bot/commit/90a6f68))
* **misc**: Cmd + click chat tab not open new tab, closes [#8001](https://github.com/jaworldwideorg/OneJA-Bot/issues/8001) ([d6d2129](https://github.com/jaworldwideorg/OneJA-Bot/commit/d6d2129))
* **misc**: Enable thinking output only for supported Gemini thinking models, closes [#7987](https://github.com/jaworldwideorg/OneJA-Bot/issues/7987) ([f503c53](https://github.com/jaworldwideorg/OneJA-Bot/commit/f503c53))

#### Styles

* **misc**: Add fc ability to deepseek-reasoner model, closes [#8006](https://github.com/jaworldwideorg/OneJA-Bot/issues/8006) ([1511c75](https://github.com/jaworldwideorg/OneJA-Bot/commit/1511c75))
* **misc**: Increase the history limit, closes [#8007](https://github.com/jaworldwideorg/OneJA-Bot/issues/8007) ([5ec7c8d](https://github.com/jaworldwideorg/OneJA-Bot/commit/5ec7c8d))
* **misc**: Update GitHub models, closes [#8002](https://github.com/jaworldwideorg/OneJA-Bot/issues/8002) ([7b8f533](https://github.com/jaworldwideorg/OneJA-Bot/commit/7b8f533))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-30 04:13:22 +00:00
Jamie Stivala 77f82a37a0 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-05-30 11:58:31 +08:00
Jamie Stivala 4ceb2ec3ab Merge remote-tracking branch 'upstream/main' 2025-05-29 16:54:36 +08:00
lobehubbot 554fa612b5 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-28 10:17:05 +00:00
semantic-release-bot f9f994f9ff 🔖 chore(release): v1.87.0 [skip ci]
## [Version&nbsp;1.87.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.86.1...v1.87.0)
<sup>Released on **2025-05-28**</sup>

####  Features

- **misc**: Add claude 4 series.

#### 🐛 Bug Fixes

- **docs**: Rename and update Google Gemini documentation.
- **DragUpload**: Resolve issue with pasting clipboard images in Safari.
- **misc**: Auto sync theme mode in desktop, cant invoke the application after OIDC authorization in Windows 11, fix chat header in the desktop, fix draggable issue with agent header, fix message refresh 401 on desktop, fix missing email field to user, update agent config of client db will override old config, user nickName & username selector in desktop.

#### 💄 Styles

- **DevPanel**: Improve json display.
- **misc**: Add gemini & hunyuan & Claude models, add live search support for xAI, Allow `SliderWithInput` to have no input limit, correct model name `SenseChat-5-1202`, fix a few typos in the model tooltips, improve thread flicker when first-time loading, resolve InputNumber display overlap issue, support adjust thinkingBudget in gemini 2.5 flash, Support Gemini 2.5 thought reasoning, support share single message.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add claude 4 series, closes [#7939](https://github.com/jaworldwideorg/OneJA-Bot/issues/7939) ([9b4f950](https://github.com/jaworldwideorg/OneJA-Bot/commit/9b4f950))

#### What's fixed

* **docs**: Rename and update Google Gemini documentation, closes [#7957](https://github.com/jaworldwideorg/OneJA-Bot/issues/7957) ([432c28d](https://github.com/jaworldwideorg/OneJA-Bot/commit/432c28d))
* **DragUpload**: Resolve issue with pasting clipboard images in Safari, closes [#7961](https://github.com/jaworldwideorg/OneJA-Bot/issues/7961) ([3c3cc75](https://github.com/jaworldwideorg/OneJA-Bot/commit/3c3cc75))
* **misc**: Auto sync theme mode in desktop, closes [#7970](https://github.com/jaworldwideorg/OneJA-Bot/issues/7970) ([a16fa02](https://github.com/jaworldwideorg/OneJA-Bot/commit/a16fa02))
* **misc**: Cant invoke the application after OIDC authorization in Windows 11, closes [#7900](https://github.com/jaworldwideorg/OneJA-Bot/issues/7900) ([585e386](https://github.com/jaworldwideorg/OneJA-Bot/commit/585e386))
* **misc**: Fix chat header in the desktop, closes [#7973](https://github.com/jaworldwideorg/OneJA-Bot/issues/7973) ([63c3a71](https://github.com/jaworldwideorg/OneJA-Bot/commit/63c3a71))
* **misc**: Fix draggable issue with agent header, closes [#7968](https://github.com/jaworldwideorg/OneJA-Bot/issues/7968) ([cd84241](https://github.com/jaworldwideorg/OneJA-Bot/commit/cd84241))
* **misc**: Fix message refresh 401 on desktop, closes [#7958](https://github.com/jaworldwideorg/OneJA-Bot/issues/7958) ([b4b426f](https://github.com/jaworldwideorg/OneJA-Bot/commit/b4b426f))
* **misc**: Fix missing email field to user, closes [#7913](https://github.com/jaworldwideorg/OneJA-Bot/issues/7913) ([d314130](https://github.com/jaworldwideorg/OneJA-Bot/commit/d314130))
* **misc**: Update agent config of client db will override old config, closes [#7918](https://github.com/jaworldwideorg/OneJA-Bot/issues/7918) ([f7cda68](https://github.com/jaworldwideorg/OneJA-Bot/commit/f7cda68))
* **misc**: User nickName & username selector in desktop, closes [#7899](https://github.com/jaworldwideorg/OneJA-Bot/issues/7899) ([bf51746](https://github.com/jaworldwideorg/OneJA-Bot/commit/bf51746))

#### Styles

* **DevPanel**: Improve json display, closes [#7978](https://github.com/jaworldwideorg/OneJA-Bot/issues/7978) ([db800d2](https://github.com/jaworldwideorg/OneJA-Bot/commit/db800d2))
* **misc**: Add gemini & hunyuan & Claude models, closes [#7908](https://github.com/jaworldwideorg/OneJA-Bot/issues/7908) ([5244f22](https://github.com/jaworldwideorg/OneJA-Bot/commit/5244f22))
* **misc**: Add live search support for xAI, closes [#7907](https://github.com/jaworldwideorg/OneJA-Bot/issues/7907) ([dff4b7b](https://github.com/jaworldwideorg/OneJA-Bot/commit/dff4b7b))
* **misc**: Allow `SliderWithInput` to have no input limit, closes [#7708](https://github.com/jaworldwideorg/OneJA-Bot/issues/7708) ([bdb02b2](https://github.com/jaworldwideorg/OneJA-Bot/commit/bdb02b2))
* **misc**: Correct model name `SenseChat-5-1202`, closes [#7979](https://github.com/jaworldwideorg/OneJA-Bot/issues/7979) ([d9e1336](https://github.com/jaworldwideorg/OneJA-Bot/commit/d9e1336))
* **misc**: Fix a few typos in the model tooltips, closes [#7952](https://github.com/jaworldwideorg/OneJA-Bot/issues/7952) ([8416fec](https://github.com/jaworldwideorg/OneJA-Bot/commit/8416fec))
* **misc**: Improve thread flicker when first-time loading, closes [#7963](https://github.com/jaworldwideorg/OneJA-Bot/issues/7963) ([4cacacd](https://github.com/jaworldwideorg/OneJA-Bot/commit/4cacacd))
* **misc**: Resolve InputNumber display overlap issue, closes [#7892](https://github.com/jaworldwideorg/OneJA-Bot/issues/7892) ([5486663](https://github.com/jaworldwideorg/OneJA-Bot/commit/5486663))
* **misc**: Support adjust thinkingBudget in gemini 2.5 flash, closes [#7947](https://github.com/jaworldwideorg/OneJA-Bot/issues/7947) ([a9db548](https://github.com/jaworldwideorg/OneJA-Bot/commit/a9db548))
* **misc**: Support Gemini 2.5 thought reasoning, closes [#7686](https://github.com/jaworldwideorg/OneJA-Bot/issues/7686) ([f34c4de](https://github.com/jaworldwideorg/OneJA-Bot/commit/f34c4de))
* **misc**: Support share single message, closes [#7967](https://github.com/jaworldwideorg/OneJA-Bot/issues/7967) ([660a5ad](https://github.com/jaworldwideorg/OneJA-Bot/commit/660a5ad))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-28 10:16:25 +00:00
Jamie Stivala 3fa2cc1ec2 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.zh-CN.md
#	changelog/v1.json
2025-05-28 18:01:44 +08:00
lobehubbot 6e83440d68 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-22 18:23:40 +00:00
semantic-release-bot 9f1ae50f71 🔖 chore(release): v1.86.1 [skip ci]
### [Version&nbsp;1.86.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.86.0...v1.86.1)
<sup>Released on **2025-05-22**</sup>

#### 🐛 Bug Fixes

- **misc**: 'top_p' is not supported with o4-mini, bump  @lobehub/ui to 2.1.7, pin zustand version to avoid type error.

#### 💄 Styles

- **misc**: Improve tools display.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: 'top_p' is not supported with o4-mini, closes [#7747](https://github.com/jaworldwideorg/OneJA-Bot/issues/7747) ([4e04399](https://github.com/jaworldwideorg/OneJA-Bot/commit/4e04399))
* **misc**: Bump  @lobehub/ui to 2.1.7, closes [#7912](https://github.com/jaworldwideorg/OneJA-Bot/issues/7912) ([457b645](https://github.com/jaworldwideorg/OneJA-Bot/commit/457b645))
* **misc**: Pin zustand version to avoid type error, closes [#7929](https://github.com/jaworldwideorg/OneJA-Bot/issues/7929) ([4f6e286](https://github.com/jaworldwideorg/OneJA-Bot/commit/4f6e286))

#### Styles

* **misc**: Improve tools display, closes [#7906](https://github.com/jaworldwideorg/OneJA-Bot/issues/7906) ([af8a05b](https://github.com/jaworldwideorg/OneJA-Bot/commit/af8a05b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-22 18:23:19 +00:00
GH Action - Upstream Sync e1c4a934dc Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-05-22 18:08:52 +00:00
GH Action - Upstream Sync d8d1cc6ddd Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-05-22 06:09:50 +00:00
Jamie Stivala 05c70c6388 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-05-21 18:06:43 +07:00
lobehubbot 0758d68d74 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-20 12:38:28 +00:00
semantic-release-bot 26d3c3eabd 🔖 chore(release): v1.86.0 [skip ci]
## [Version&nbsp;1.86.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.85.2...v1.86.0)
<sup>Released on **2025-05-20**</sup>

#### ♻ Code Refactoring

- **misc**: Clean code with new antd api, refactor agent runtime to model runtime.

####  Features

- **misc**: Add Qiniu Provider, support custom language and Mermaid Appearance.

#### 🐛 Bug Fixes

- **misc**: Fix desktop open issue on linux like Fedora42, fix oidc redirect url, supported SenseNova v6 models correctly & update Gemini models.

#### 💄 Styles

- **misc**: Support Doubao 1.5 Thinking Vision Pro model, update internlm model list, add  series, update Spark X1 model list & fix build-in search params.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean code with new antd api, closes [#7870](https://github.com/jaworldwideorg/OneJA-Bot/issues/7870) ([c543884](https://github.com/jaworldwideorg/OneJA-Bot/commit/c543884))
* **misc**: Refactor agent runtime to model runtime, closes [#7846](https://github.com/jaworldwideorg/OneJA-Bot/issues/7846) ([a3b9448](https://github.com/jaworldwideorg/OneJA-Bot/commit/a3b9448))

#### What's improved

* **misc**: Add Qiniu Provider, closes [#7649](https://github.com/jaworldwideorg/OneJA-Bot/issues/7649) ([c9b8e9f](https://github.com/jaworldwideorg/OneJA-Bot/commit/c9b8e9f))
* **misc**: Support custom language and Mermaid Appearance, closes [#7850](https://github.com/jaworldwideorg/OneJA-Bot/issues/7850) ([bee2b2d](https://github.com/jaworldwideorg/OneJA-Bot/commit/bee2b2d))

#### What's fixed

* **misc**: Fix desktop open issue on linux like Fedora42, closes [#7883](https://github.com/jaworldwideorg/OneJA-Bot/issues/7883) ([5b0154f](https://github.com/jaworldwideorg/OneJA-Bot/commit/5b0154f))
* **misc**: Fix oidc redirect url, closes [#7855](https://github.com/jaworldwideorg/OneJA-Bot/issues/7855) ([3156538](https://github.com/jaworldwideorg/OneJA-Bot/commit/3156538))
* **misc**: Supported SenseNova v6 models correctly & update Gemini models, closes [#7778](https://github.com/jaworldwideorg/OneJA-Bot/issues/7778) ([e2b5ed3](https://github.com/jaworldwideorg/OneJA-Bot/commit/e2b5ed3))

#### Styles

* **misc**: Support Doubao 1.5 Thinking Vision Pro model, closes [#7784](https://github.com/jaworldwideorg/OneJA-Bot/issues/7784) ([9cf0d6f](https://github.com/jaworldwideorg/OneJA-Bot/commit/9cf0d6f))
* **misc**: Update internlm model list, add  series, closes [#7566](https://github.com/jaworldwideorg/OneJA-Bot/issues/7566) ([4eaddf4](https://github.com/jaworldwideorg/OneJA-Bot/commit/4eaddf4))
* **misc**: Update Spark X1 model list & fix build-in search params, closes [#7480](https://github.com/jaworldwideorg/OneJA-Bot/issues/7480) ([7050c81](https://github.com/jaworldwideorg/OneJA-Bot/commit/7050c81))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-20 12:37:58 +00:00
Jamie Stivala ef2e2dd1c0 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-05-20 19:23:07 +07:00
Jamie Stivala d4b7668823 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	package.json
2025-05-20 19:22:21 +07:00
lobehubbot 0adcf550d9 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-14 18:23:51 +00:00
semantic-release-bot 5a5484b145 🔖 chore(release): v1.85.2 [skip ci]
### [Version&nbsp;1.85.2](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.85.1...v1.85.2)
<sup>Released on **2025-05-14**</sup>

#### 💄 Styles

- **misc**: Improve smoothing on completion, update electron style on windows.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve smoothing on completion, closes [#7833](https://github.com/jaworldwideorg/OneJA-Bot/issues/7833) ([6434686](https://github.com/jaworldwideorg/OneJA-Bot/commit/6434686))
* **misc**: Update electron style on windows, closes [#7839](https://github.com/jaworldwideorg/OneJA-Bot/issues/7839) ([474de56](https://github.com/jaworldwideorg/OneJA-Bot/commit/474de56))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-14 18:23:31 +00:00
GH Action - Upstream Sync 558eac8c21 Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-05-14 18:08:54 +00:00
Jamie Stivala 1c67cf3e05 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	changelog/v1.json
2025-05-14 21:12:42 +07:00
semantic-release-bot 8b6fac26d4 🔖 chore(release): v1.85.1 [skip ci]
### [Version&nbsp;1.85.1](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.85.0...v1.85.1)
<sup>Released on **2025-05-14**</sup>

#### 🐛 Bug Fixes

- **misc**: Redirect unauthorized next-auth user to signin page.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Redirect unauthorized next-auth user to signin page, closes [#7813](https://github.com/jaworldwideorg/OneJA-Bot/issues/7813) ([6160784](https://github.com/jaworldwideorg/OneJA-Bot/commit/6160784))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-14 06:23:49 +00:00
GH Action - Upstream Sync e1d318f56d Merge branch 'main' of https://github.com/lobehub/lobe-chat 2025-05-14 06:09:34 +00:00
lobehubbot 632d687b63 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-12 18:23:27 +00:00
semantic-release-bot 7e721ba86d 🔖 chore(release): v1.85.0 [skip ci]
## [Version&nbsp;1.85.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.84.16...v1.85.0)
<sup>Released on **2025-05-12**</sup>

#### ♻ Code Refactoring

- **misc**: Add perf stat support for openai factory, Remove doubao Provider, upgrade anthropic sdk.

####  Features

- **misc**: Support upload files direct into chat context.

#### 🐛 Bug Fixes

- **misc**: Fix changelog issue on desktop app, fix config import issue in the desktop version, fix desktop upload image on macOS, fix electron state init on window, fix nothing return when reset the client db, fix streamable http url valid and refactor local files to local system, fix window close issue and release Window/Linux beta, remove mcp client cache.

#### 💄 Styles

- **misc**: Add new gemini & Mistral models, add qwen3 for ollama, add Qwen3 models for infiniai, add reasoning tokens and token usage statistics for Google Gemini, add write file tool to local-file plugin, add Xiaohongshu crawler rules, fix init state of loading, improve pdf and xlsx file content parser, Show Aliyun Bailian tokens usage tracking.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Add perf stat support for openai factory, closes [#7677](https://github.com/jaworldwideorg/OneJA-Bot/issues/7677) ([40464d1](https://github.com/jaworldwideorg/OneJA-Bot/commit/40464d1))
* **misc**: Remove doubao Provider, closes [#7573](https://github.com/jaworldwideorg/OneJA-Bot/issues/7573) ([0cf3bcc](https://github.com/jaworldwideorg/OneJA-Bot/commit/0cf3bcc))
* **misc**: Upgrade anthropic sdk, closes [#7773](https://github.com/jaworldwideorg/OneJA-Bot/issues/7773) ([39e871f](https://github.com/jaworldwideorg/OneJA-Bot/commit/39e871f))

#### What's improved

* **misc**: Support upload files direct into chat context, closes [#7751](https://github.com/jaworldwideorg/OneJA-Bot/issues/7751) ([39b790e](https://github.com/jaworldwideorg/OneJA-Bot/commit/39b790e))

#### What's fixed

* **misc**: Fix changelog issue on desktop app, closes [#7740](https://github.com/jaworldwideorg/OneJA-Bot/issues/7740) ([f0a12af](https://github.com/jaworldwideorg/OneJA-Bot/commit/f0a12af))
* **misc**: Fix config import issue in the desktop version, closes [#7800](https://github.com/jaworldwideorg/OneJA-Bot/issues/7800) ([2cb8635](https://github.com/jaworldwideorg/OneJA-Bot/commit/2cb8635))
* **misc**: Fix desktop upload image on macOS, closes [#7741](https://github.com/jaworldwideorg/OneJA-Bot/issues/7741) ([07d5374](https://github.com/jaworldwideorg/OneJA-Bot/commit/07d5374))
* **misc**: Fix electron state init on window, closes [#7707](https://github.com/jaworldwideorg/OneJA-Bot/issues/7707) ([ef05b49](https://github.com/jaworldwideorg/OneJA-Bot/commit/ef05b49))
* **misc**: Fix nothing return when reset the client db, closes [#7738](https://github.com/jaworldwideorg/OneJA-Bot/issues/7738) ([90efb13](https://github.com/jaworldwideorg/OneJA-Bot/commit/90efb13))
* **misc**: Fix streamable http url valid and refactor local files to local system, closes [#7794](https://github.com/jaworldwideorg/OneJA-Bot/issues/7794) ([37fd5fe](https://github.com/jaworldwideorg/OneJA-Bot/commit/37fd5fe))
* **misc**: Fix window close issue and release Window/Linux beta, closes [#7780](https://github.com/jaworldwideorg/OneJA-Bot/issues/7780) ([82c48b9](https://github.com/jaworldwideorg/OneJA-Bot/commit/82c48b9))
* **misc**: Remove mcp client cache, closes [#7776](https://github.com/jaworldwideorg/OneJA-Bot/issues/7776) ([0582134](https://github.com/jaworldwideorg/OneJA-Bot/commit/0582134))

#### Styles

* **misc**: Add new gemini & Mistral models, closes [#7730](https://github.com/jaworldwideorg/OneJA-Bot/issues/7730) ([b7753e2](https://github.com/jaworldwideorg/OneJA-Bot/commit/b7753e2))
* **misc**: Add qwen3 for ollama, closes [#7746](https://github.com/jaworldwideorg/OneJA-Bot/issues/7746) ([806d905](https://github.com/jaworldwideorg/OneJA-Bot/commit/806d905))
* **misc**: Add Qwen3 models for infiniai, closes [#7657](https://github.com/jaworldwideorg/OneJA-Bot/issues/7657) ([edd1732](https://github.com/jaworldwideorg/OneJA-Bot/commit/edd1732))
* **misc**: Add reasoning tokens and token usage statistics for Google Gemini, closes [#7501](https://github.com/jaworldwideorg/OneJA-Bot/issues/7501) ([b466b42](https://github.com/jaworldwideorg/OneJA-Bot/commit/b466b42))
* **misc**: Add write file tool to local-file plugin, closes [#7684](https://github.com/jaworldwideorg/OneJA-Bot/issues/7684) ([e22e932](https://github.com/jaworldwideorg/OneJA-Bot/commit/e22e932))
* **misc**: Add Xiaohongshu crawler rules, closes [#7717](https://github.com/jaworldwideorg/OneJA-Bot/issues/7717) ([cc3724d](https://github.com/jaworldwideorg/OneJA-Bot/commit/cc3724d))
* **misc**: Fix init state of loading, closes [#7694](https://github.com/jaworldwideorg/OneJA-Bot/issues/7694) ([1d97a68](https://github.com/jaworldwideorg/OneJA-Bot/commit/1d97a68))
* **misc**: Improve pdf and xlsx file content parser, closes [#7783](https://github.com/jaworldwideorg/OneJA-Bot/issues/7783) ([0376870](https://github.com/jaworldwideorg/OneJA-Bot/commit/0376870))
* **misc**: Show Aliyun Bailian tokens usage tracking, closes [#7660](https://github.com/jaworldwideorg/OneJA-Bot/issues/7660) ([3ef0542](https://github.com/jaworldwideorg/OneJA-Bot/commit/3ef0542))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-12 18:22:39 +00:00
Jamie Stivala a8ffdefcdd Updated sync to also ignore changelog/* 2025-05-13 01:09:01 +07:00
Jamie Stivala 58f4e0ed8d Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
#	README.zh-CN.md
#	changelog/v1.json
2025-05-13 01:03:00 +07:00
Jamie Stivala 3b48bf4551 Updated upstream sync repo 2025-05-13 00:59:47 +07:00
Jamie Stivala 48d0e01434 Changed ACR 2025-05-13 00:56:13 +07:00
Jamie Stivala 7d82bb16b9 Running upstream sync should trigger release.yml 2025-05-13 00:49:06 +07:00
lobehubbot 9923a38d84 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-02 02:34:14 +00:00
semantic-release-bot 97588e6cf4 🔖 chore(release): v1.84.16 [skip ci]
### [Version&nbsp;1.84.16](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.84.15...v1.84.16)
<sup>Released on **2025-05-02**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix desktop quiting with reopen window.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix desktop quiting with reopen window, closes [#7675](https://github.com/jaworldwideorg/OneJA-Bot/issues/7675) ([edeabcf](https://github.com/jaworldwideorg/OneJA-Bot/commit/edeabcf))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-05-02 02:33:56 +00:00
Jamie Stivala acfe5ea8b0 On sync, added the ability to ignore files such as CHANGELOG.md 2025-05-02 09:20:18 +07:00
Jamie Stivala ef6651e305 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	CHANGELOG.md
2025-05-02 09:09:07 +07:00
Jamie Stivala 059e78ba2c Target sync branch to main. 2025-05-02 09:07:25 +07:00
Jamie Stivala 765198e00f Merge remote-tracking branch 'origin/main' 2025-05-02 01:07:27 +07:00
lobehubbot 2cda3e77bc 📝 docs(bot): Auto sync agents & plugin to readme 2025-05-01 18:06:40 +00:00
Jamie Stivala 6d9278a018 Target sync branch to upstream still. 2025-05-02 01:03:44 +07:00
Jamie Stivala e118e0fa7f Update repository url on release to use JA Worldwide One-JA Bot 2025-05-02 01:02:31 +07:00
Jamie Stivala 7687b21ff0 Sync directly into main branch 2025-05-02 01:01:09 +07:00
Jamie Stivala 387ac1e778 Readded vi-canvas-mock 2025-05-02 00:52:33 +07:00
Jamie Stivala 6e444c6e5e Removed depricated npmrc files 2025-05-02 00:46:49 +07:00
Jamie Stivala 3d29f8324a Changed precommit file 2025-05-02 00:46:35 +07:00
Jamie Stivala 9d04179123 Merge branch 'upstream'
# Conflicts:
#	CHANGELOG.md
#	package.json
#	src/libs/next-auth/sso-providers/index.ts
2025-05-02 00:45:55 +07:00
Jamie Stivala d598f68313 Attempt to fix upstream syncing 2025-05-02 00:18:07 +07:00
Jamie Stivala 1cec875a8d Added test mode 2025-04-02 17:25:56 -04:00
semantic-release-bot 336957ec63 🔖 chore(release): v1.73.0 [skip ci]
## [Version&nbsp;1.73.0](https://github.com/jaworldwideorg/OneJA-Bot/compare/v1.72.3...v1.73.0)
<sup>Released on **2025-03-20**</sup>

####  Features

- **misc**: Add Cohere provider support, add search1api crawler implementation for WeChat Sogou links.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add Cohere provider support, closes [#7016](https://github.com/jaworldwideorg/OneJA-Bot/issues/7016) ([2a4e2ed](https://github.com/jaworldwideorg/OneJA-Bot/commit/2a4e2ed))
* **misc**: Add search1api crawler implementation for WeChat Sogou links, closes [#7036](https://github.com/jaworldwideorg/OneJA-Bot/issues/7036) ([7327138](https://github.com/jaworldwideorg/OneJA-Bot/commit/7327138))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-03-20 16:20:40 +00:00
Jamie Stivala e678340e57 Merge pull request #53 from jaworldwideorg/feat/branding
 feat: Updated branding to be inline with JA Worldwide
2025-03-20 17:10:55 +01:00
Jamie Stivala 8905367222 Updated dependencies 2025-03-20 16:55:44 +01:00
Jamie Stivala 76f3dddf85 Removed vite-canvas-mock and added vi-canvas-mock 2025-03-20 16:55:17 +01:00
Jamie Stivala 5eac228e01 Updated tests to reference branding_name 2025-03-20 14:50:20 +01:00
Jamie Stivala 76d546305b Updated favicon and touch icon 2025-03-20 14:26:30 +01:00
Jamie Stivala da928c78dc Updated branding to reflect JA 2025-03-20 14:21:16 +01:00
Jamie Stivala 52c2fdb6db Mounting local file system 2025-03-20 14:12:18 +01:00
Jamie Stivala 2dd5a72ccd Updated dockerfile 2025-03-20 13:09:21 +01:00
Jamie Stivala f3ab6b8bd7 Added COHERE Environment to testing docker file 2025-03-20 13:08:09 +01:00
Jamie Stivala 5ecf59e7e7 Merge branch 'upstream' into feat/branding
# Conflicts:
#	package.json
2025-03-20 13:05:20 +01:00
Jamie Stivala d0ea7aa45c Fixed dockerfile location 2025-03-20 13:03:48 +01:00
Jamie Stivala f8ab18d8da Merge pull request #47 from jaworldwideorg/test/includes
🔨 tests - Updated tests to use deep array matching
2025-03-19 13:50:09 +01:00
Jamie Stivala 965d2829eb Updated tests to use deep array matching 2025-03-19 13:37:31 +01:00
Jamie Stivala 33e9767c16 Merge pull request #44 from jaworldwideorg/chore/ci-updates
📝 docs & 🔨 chore: Added a way to run Docker Local Development and Fixed CI/CD to work with Azure ACR
2025-03-19 11:58:55 +01:00
Jamie Stivala 70e54c98bf Updated package.json reference 2025-03-19 11:56:36 +01:00
Jamie Stivala 49f1b97b67 Merge pull request #43 from jaworldwideorg/feat/okta-oidc
 feat - Added Okta as an OIDC Provider
2025-03-19 11:52:52 +01:00
Jamie Stivala 0ed5a6b5ec Updated Lighthouse repo branch 2025-03-19 11:48:13 +01:00
Jamie Stivala bec44875f7 Updated docker-database builder location 2025-03-19 11:47:53 +01:00
Jamie Stivala f849d0e102 Fixed syncing upstream branch 2025-03-19 11:47:35 +01:00
Jamie Stivala 15102da85d Remove NPM from Semantic Release 2025-03-19 11:47:04 +01:00
Jamie Stivala 7eee6d1cb2 Added a way to run local development 2025-03-19 11:42:22 +01:00
Jamie Stivala b0e8c4fbb8 Updated documentation 2025-03-19 11:15:23 +01:00
Jamie Stivala f1468b7d5a Added Okta as an SSO Provider 2025-03-19 11:14:41 +01:00
1636 changed files with 27326 additions and 75207 deletions
-183
View File
@@ -1,183 +0,0 @@
---
description: Complete guide for adding a new AI provider documentation to LobeChat
alwaysApply: false
---
# Adding New AI Provider Documentation
This document provides a step-by-step guide for adding documentation for a new AI provider to LobeChat, based on the complete workflow used for adding providers like BFL (Black Forest Labs) and FAL.
## Overview
Adding a new provider requires creating both user-facing documentation and technical configuration files. The process involves:
1. Creating usage documentation (EN + CN)
2. Adding environment variable documentation (EN + CN)
3. Updating Docker configuration files
4. Updating .env.example file
5. Preparing image resources
## Step 1: Create Provider Usage Documentation
Create user-facing documentation that explains how to use the new provider.
### Required Files
Create both English and Chinese versions:
- `docs/usage/providers/{provider-name}.mdx` (English)
- `docs/usage/providers/{provider-name}.zh-CN.mdx` (Chinese)
### Documentation Structure
Follow the structure and format used in existing provider documentation. For reference, see:
- `docs/usage/providers/fal.mdx` (English template)
- `docs/usage/providers/fal.zh-CN.mdx` (Chinese template)
### Key Requirements
- **Images**: Prepare 5-6 screenshots showing the process
- **Cover Image**: Create or obtain a cover image for the provider
- **Accurate URLs**: Use real registration and dashboard URLs
- **Service Type**: Specify whether it's for image generation, text generation, etc.
- **Pricing Warning**: Include pricing information callout
### Important Notes
- **🔒 API Key Security**: Never include real API keys in documentation. Always use placeholder format (e.g., `bfl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`)
- **🖼️ Image Hosting**: Use LobeHub's CDN for all images: `hub-apac-1.lobeobjects.space`
## Step 2: Update Environment Variables Documentation
Add the new provider's environment variables to the self-hosting documentation.
### Files to Update
- `docs/self-hosting/environment-variables/model-provider.mdx` (English)
- `docs/self-hosting/environment-variables/model-provider.zh-CN.mdx` (Chinese)
### Content to Add
Add two sections for each provider:
```markdown
### `{PROVIDER}_API_KEY`
- Type: Required
- Description: This is the API key you applied for in the {Provider Name} service.
- Default: -
- Example: `{api-key-format-example}`
### `{PROVIDER}_MODEL_LIST`
- Type: Optional
- Description: Used to control the {Provider Name} model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model. Separate multiple entries with commas. The definition syntax follows the same rules as other providers' model lists.
- Default: `-`
- Example: `-all,+{model-id-1},+{model-id-2}={display-name}`
The above example disables all models first, then enables `{model-id-1}` and `{model-id-2}` (displayed as `{display-name}`).
[model-list]: /docs/self-hosting/advanced/model-list
```
### Important Notes
- **API Key Format**: Use proper UUID format for examples (e.g., `12345678-1234-1234-1234-123456789abc`)
- **Real Model IDs**: Use actual model IDs from the codebase, not placeholders
- **Consistent Naming**: Follow the pattern `{PROVIDER}_API_KEY` and `{PROVIDER}_MODEL_LIST`
## Step 3: Update Docker Configuration Files
Add environment variables to all Docker configuration files to ensure the provider works in containerized deployments.
### Files to Update
All Dockerfile variants must be updated:
- `Dockerfile`
- `Dockerfile.database`
- `Dockerfile.pglite`
### Changes Required
Add the new provider's environment variables at the **end** of the ENV section, just before the final line:
```dockerfile
# Previous providers...
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# {New Provider 1}
{PROVIDER1}_API_KEY="" {PROVIDER1}_MODEL_LIST="" \
# {New Provider 2}
{PROVIDER2}_API_KEY="" {PROVIDER2}_MODEL_LIST=""
```
### Important Rules
- **Position**: Add new providers at the **end** of the list
- **Ordering**: When adding multiple providers, use alphabetical order (e.g., FAL before BFL)
- **Consistency**: Maintain identical ordering across all Dockerfile variants
- **Format**: Follow the pattern `{PROVIDER}_API_KEY="" {PROVIDER}_MODEL_LIST="" \`
## Step 4: Update .env.example File
Add example configuration entries to help users understand how to configure the provider locally.
### File to Update
- `.env.example`
### Content to Add
Add new sections before the "Market Service" section:
```bash
### {Provider Name} ###
# {PROVIDER}_API_KEY={provider-prefix}-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
### Format Guidelines
- **Section Header**: Use `### {Provider Name} ###` format
- **Commented Example**: Use `#` to comment out the example
- **Key Format**: Use appropriate prefix for the provider (e.g., `bfl-`, `fal-`, `sk-`)
- **Position**: Add before the Market Service section
- **Spacing**: Maintain consistent spacing with existing entries
## Step 5: Image Resources
Prepare all necessary image resources for the documentation.
### Required Images
1. **Cover Image**: Provider logo or branded image
2. **API Dashboard Screenshots**: 3-4 screenshots showing API key creation process
3. **LobeChat Configuration Screenshots**: 2-3 screenshots showing provider setup in LobeChat
### Image Guidelines
- **Quality**: Use high-resolution screenshots
- **Consistency**: Maintain consistent styling across all screenshots
- **Privacy**: Remove or blur any sensitive information
- **Format**: Use PNG format for screenshots
- **Hosting**: Use LobeHub's CDN (`hub-apac-1.lobeobjects.space`) for all images
## Checklist
Before submitting your provider documentation:
- [ ] Created both English and Chinese usage documentation
- [ ] Added environment variable documentation (EN + CN)
- [ ] Updated all 3 Dockerfile variants with consistent ordering
- [ ] Updated .env.example with proper key format
- [ ] Prepared all required screenshots and images
- [ ] Used actual model IDs from the codebase
- [ ] Verified no real API keys are included in documentation
- [ ] Used LobeHub CDN for all image hosting
- [ ] Tested the documentation for clarity and accuracy
## Reference
This guide was created based on the implementation of BFL (Black Forest Labs) provider documentation. For a complete example, refer to:
- Commits: `d2da03e1a` (documentation) and `6a2e95868` (environment variables)
- Files: `docs/usage/providers/bfl.mdx`, `docs/usage/providers/bfl.zh-CN.mdx`
- PR: Current branch `tj/feat/bfl-docs`
+17 -2
View File
@@ -26,11 +26,26 @@ Gather the modified code and context. Please strictly follow the process below:
### Code Style
read [typescript.mdc](mdc:.cursor/rules/typescript.mdc) for the consolidated project code style and optimization rules.
- Ensure JSDoc comments accurately reflect the implementation; update them when needed.
- Look for opportunities to simplify or modernize code with the latest JavaScript/TypeScript features.
- Prefer `async`/`await` over callbacks or chained `.then` promises.
- Use consistent, descriptive naming—avoid obscure abbreviations.
- Replace magic numbers or strings with well-named constants.
- Use semantically meaningful variable, function, and class names.
- Ignore purely formatting issues and other autofixable lint problems.
### Code Optimization
The optimization checklist has been consolidated into [typescript.mdc](mdc:.cursor/rules/typescript.mdc): loops, debouncing/throttling, design system components, theming tokens, concurrency with `Promise.*`, minimal DB column selection, and package reuse.
- Prefer `for…of` loops to index-based `for` loops when feasible.
- Decide whether callbacks should be **debounced** or **throttled**.
- Use components from `@lobehub/ui`, Ant Design, or the existing design system instead of raw HTML tags (e.g., `Button` vs. `button`).
- reuse npm packages already installed (e.g., `lodash/omit`) rather than reinventing the wheel.
- Design for dark mode and mobile responsiveness:
- Use the `antd-style` token system instead of hard-coded colors.
- Select the proper component variants.
- Performance considerations:
- Where safe, convert sequential async flows to concurrent ones with `Promise.all`, `Promise.race`, etc.
- Query only the required columns from a database rather than selecting entire rows.
### Obvious Bugs
+72
View File
@@ -30,3 +30,75 @@ alwaysApply: true
- Good: `git show commit_hash -- file.txt | cat`
- Good: `git log --oneline | cat`
- Reason: Some git commands use pagers by default, which may prevent output from being captured properly
## Mermaid Diagram Generation: Strict Syntax Validation Checklist
Before producing any Mermaid diagram, you **must** compare your final code line-by-line against every rule in the following checklist to ensure 100% compliance. **This is a hard requirement and takes precedence over other stylistic suggestions.** Please follow these action steps:
1. Plan the Mermaid diagram logic in your mind.
2. Write the Mermaid code.
3. **Carefully review your code line-by-line against the entire checklist below.**
4. Fix any aspect of your code that doesn't comply.
5. Use the `validateMermaid` tool to check your code for syntax errors. Only proceed if validation passes.
6. Output the final, compliant, and copy-ready Mermaid code block.
7. Immediately after the Mermaid code block, output:
I have checked that the Mermaid syntax fully complies with the validation checklist.
---
### Checklist Details
#### Rule 1: Edge Labels Must Be Plain Text Only
> **Essence:** Anything inside `|...|` must contain pure, unformatted text. Absolutely NO Markdown, list markers, or parentheses/brackets allowed—these often cause rendering failures.
- **✅ Do:** `A -->|Process plain text data| B`
- **❌ Don't:** `A -->|1. Ordered list item| B` (No numbered lists)
- **❌ Don't:** `CC --"1. fetch('/api/...')"--> API` (No square brackets)
- **❌ Don't:** `A -->|- Unordered list item| B` (No hyphen lists)
- **❌ Don't:** `A -->|Transform (important)| B` (No parentheses)
- **❌ Don't:** `A -->|Transform [important]| B` (No square brackets)
#### Rule 2: Node Definition Handle Special Characters with Care
> **Essence:** When node text or subgraph titles contain special characters like `()` or `[]`, wrap the text in quotes to avoid conflicts with Mermaid shape syntax.
- **When your node text includes parentheses (e.g., 'React (JSX)'):**
- **✅ Do:** `I_REACT["<b>React component (JSX)</b>"]` (Quotes wrap all text)
- **❌ Don't:** `I_REACT(<b>React component (JSX)</b>)` (Wrong, Mermaid parses this as a shape)
- **❌ Don't:** `subgraph Plugin Features (Plugins)` (Wrong, subgraph titles with parentheses must also be wrapped in quotes)
#### Rule 3: Double Quotes in Text Must Be Escaped
> **Essence:** Use `&quot;` for double quotes **inside node text**.
- **✅ Do:** `A[This node contains &quot;quotes&quot;]`
- **❌ Don't:** `A[This node contains "quotes"]`
#### Rule 4: All Formatting Must Use HTML Tags (NOT Markdown!)
> **Essence:** For newlines, bold, and other text formatting in nodes, use HTML tags only. Markdown is not supported.
- **✅ Do (robust):** `A["<b>Bold</b> and <code>code</code><br>This is a new line"]`
- **❌ Don't (not rendered):** `C["# This is a heading"]`
- **❌ Don't (not rendered):** ``C["`const` means constant"]``
- **⚠️ Warning (unreliable):** `B["Markdown **bold** might sometimes work but DON'T rely on it"]`
#### Rule 5: No HTML Tags for Participants and Message Labels (Sequence Diagrams)
> **Important Addition:**
> In Mermaid sequence diagrams, you MUST NOT use any HTML tags (such as `<b>`, `<code>`, etc.) in:
>
> - `participant` display names (`as` part)
> - Message labels (the text after `:` in diagram flows)
>
> These tags are generally not rendered—they may appear as-is or cause compatibility issues.
- **✅ Do:** `participant A as Client`
- **❌ Don't:** `participant A as <b>Client</b>`
- **✅ Do:** `A->>B: 1. Establish connection`
- **❌ Don't:** `A->>B: 1. <code>Establish connection</code>`
---
**Validate each Mermaid code block by running it through the `validateMermaid` tool before delivering your output!**
+193
View File
@@ -0,0 +1,193 @@
---
description: Debug 调试指南
globs:
alwaysApply: false
---
# Debug 调试指南
## 💡 调试流程概览
当遇到问题时,请按照以下优先级进行处理:
1. **快速判断** - 对于熟悉的错误,直接提供解决方案
2. **信息收集** - 使用工具搜索相关代码和配置
3. **网络搜索** - 查找现有解决方案
4. **定位调试** - 添加日志进行问题定位
5. **临时方案** - 如果找不到根本解决方案,提供临时解决方案
6. **解决实施** - 提供可维护的最终解决方案
## 🔍 错误信息分析
### 错误来源识别
错误信息可能来自:
- **Terminal 输出** - 构建、运行时错误
- **浏览器控制台** - 前端 JavaScript 错误
- **开发工具** - ESLint、TypeScript、测试框架等
- **服务器日志** - API、数据库连接等后端错误
- **截图或文本** - 用户直接提供的错误信息
## 🛠️ 信息收集工具
### 代码搜索工具
使用以下工具收集相关信息,并根据场景选择最合适的工具:
- **`codebase_search` (语义搜索)**
- **何时使用**: 当你不确定具体的代码实现,想要寻找相关概念、功能或逻辑时。
- **示例**: `查询"文件上传"功能的实现`
- **`grep_search` (精确/正则搜索)**
- **何时使用**: 当你知道要查找的确切字符串、函数名、变量名或一个特定的模式时。
- **示例**: `查找所有使用了 'useState' 的地方`
- **`file_search` (文件搜索)**
- **何时使用**: 当你知道文件名的一部分,需要快速定位文件时。
- **示例**: `查找 'Button.tsx' 组件`
- **`read_file` (内容读取)**
- **何时使用**: 在定位到具体文件后,用于查看其完整内容和上下文。
- **`web_search` (网络搜索)**
- **何时使用**: 当错误信息可能与第三方库、API 或常见问题相关时,用于获取外部信息。
### 环境与依赖检查
- **检查 `package.json`**: 查看 `scripts` 了解项目如何运行、构建和测试。查看 `dependencies` 和 `devDependencies` 确认库版本,版本冲突有时是问题的根源。
- **运行测试**: 使用 `ni vitest` 运行单元测试和集成测试,这可以快速定位功能回归或组件错误。
### 项目特定搜索目标
针对 lobe-chat 项目,重点关注:
- **配置文件**: [package.json](mdc:package.json), [next.config.mjs](mdc:next.config.mjs)
- **核心功能**: `src/features/` 下的相关模块
- **状态管理**: `src/store/` 下的 Zustand stores
- **数据库**: `src/database/` 和 `src/migrations/`
- **类型定义**: `src/types/` 下的类型文件
- **服务层**: `src/services/` 下的 API 服务
- **启动流程**: [apps/desktop/src/main/core/App.ts](mdc:apps/desktop/src/main/core/App.ts) - 了解应用启动流程
## 🌐 网络搜索策略
### 搜索顺序优先级
1. **和问题相关的项目的 github issue**
2. **技术社区**
- Stack Overflow
- GitHub Discussions
- Reddit
3. **官方文档**
- 使用 `mcp_context7_resolve-library-id` 和 `mcp_context7_get-library-docs` 工具
- 查阅官方文档网站
### 搜索关键词策略
- **错误信息**: 完整的错误消息
- **技术栈**: "Next.js 15" + "error message"
- **上下文**: 添加功能相关的关键词
## 🔧 问题定位与结构化思考
如果问题比较复杂,我们要按照先定位问题,再解决问题的大方向进行。
### 结构化思考工具
对于复杂或多步骤的调试任务,使用 `mcp_sequential-thinking_sequentialthinking` 工具来结构化思考过程。这有助于:
- **分解问题**: 将大问题拆解成可管理的小步骤。
- **清晰追踪**: 记录每一步的发现和决策,避免遗漏。
- **自我修正**: 在过程中评估和调整调试路径。
### 日志调试
在问题产生的路径上添加日志,可以简单使用 `console.log` 或者参考 [debug-usage.mdc](mdc:.cursor/rules/debug-usage.mdc) 使用 `debug` 模块。添加完日志后,请求我运行相关的代码并提供关键输出和错误信息。
### 引导式交互调试
虽然我无法直接操作浏览器开发者工具,但我可以引导你进行交互式调试:
1. **设置断点**: 我会告诉你可以在哪些关键代码行设置断点。
2. **检查变量**: 我会请你在断点处检查特定变量的值或 `props`/`state`。
3. **分析调用栈**: 我会请你提供调用栈信息,以帮助理解代码执行流程。
## 💡 临时解决方案策略
当无法找到根本解决方案时,提供临时解决方案:
### 临时方案准则
- **快速修复** - 优先让功能可用
- **最小修改** - 减少对现有代码的影响
- **清晰标记** - 明确标注这是临时方案
- **后续计划** - 说明后续如何找到更好的解决方案
### 临时方案模板
```markdown
## 临时解决方案 ⚠️
**问题**: [简要描述问题]
**临时修复**:
[具体的临时修复步骤]
**风险说明**:
- [可能的副作用或限制]
- [需要注意的事项]
**后续计划**:
- [ ] 深入调研根本原因
- [ ] 寻找更优雅的解决方案
- [ ] 监控是否有其他影响
```
## ✅ 解决方案准则
### 方案质量标准
提供的解决方案应该:
- **✅ 低侵入性** - 最小化对现有代码的修改
- **✅ 可维护性** - 易于理解和后续维护
- **✅ 类型安全** - 符合 TypeScript 规范
- **✅ 最佳实践** - 遵循项目的编码规范
- **✅ 测试友好** - 便于编写和运行测试
- **❌ 避免长期 Hack** - 临时方案可以 hack,但要明确标注
### 解决方案模板
```markdown
## 问题原因
[简要说明问题产生的根本原因]
## 解决方案
[详细的解决步骤]
## 代码修改
[具体的代码变更]
## 验证方法
[如何验证问题已解决]
## 预防措施
[如何避免类似问题再次发生]
```
## 🔄 迭代调试流程
如果初次解决方案无效:
1. **重新收集信息** - 基于新的错误信息搜索
2. **深入代码分析** - 查看更多相关代码文件
3. **运行相关测试** - 编写或运行一个失败的测试来稳定复现问题。
4. **扩大搜索范围** - 搜索更广泛的相关问题
5. **请求更多日志** - 添加更详细的调试信息
6. **提供临时方案** - 如果根本解决方案复杂,先提供临时修复
7. **分解问题** - 将复杂问题拆解为更小的子问题
+2 -1
View File
@@ -1,5 +1,6 @@
---
globs: *.tsx
description: i18n workflow and troubleshooting
globs:
alwaysApply: false
---
# LobeChat 国际化指南
+15 -1
View File
@@ -10,7 +10,7 @@ Emoji logo: 🤯
## Project Technologies Stack
read [package.json](mdc:package.json) to know all npm packages you can use.
read [package.json](mdc:package.json) to know all npm packages you can use. read [folder-structure.mdx](mdc:docs/development/basic/folder-structure.mdx) to learn project structure.
The project uses the following technologies:
@@ -42,3 +42,17 @@ The project uses the following technologies:
- Cursor AI for code editing and AI coding assistance
Note: All tools and libraries used are the latest versions. The application only needs to be compatible with the latest browsers;
## Often used npm scripts
```bash
# type check
bun type-check
# install dependencies
pnpm install
# !: don't any build script to check weather code can work after modify
```
check [testing guide](./testing-guide/testing-guide.mdc) to learn test scripts.
-239
View File
@@ -1,239 +0,0 @@
---
description: Project directory structure overview
alwaysApply: false
---
# LobeChat Project Structure
## Directory Structure
note: some files are not shown for simplicity.
```plaintext
lobe-chat/
├── apps/ # Applications directory
│ └── desktop/ # Electron desktop application
│ ├── src/ # Desktop app source code
│ └── resources/ # Desktop app resources
├── docs/ # Project documentation
│ ├── development/ # Development docs
│ ├── self-hosting/ # Self-hosting docs
│ └── usage/ # Usage guides
├── locales/ # Internationalization files (multiple locales)
│ ├── en-US/ # English (example)
│ └── zh-CN/ # Simplified Chinese (example)
├── packages/ # Monorepo packages directory
│ ├── const/ # Constants definition package
│ ├── database/ # Database related package
│ ├── electron-client-ipc/ # Electron renderer ↔ main IPC client
│ ├── electron-server-ipc/ # Electron main process IPC server
│ ├── model-bank/ # Built-in model presets/catalog exports
│ ├── model-runtime/ # AI model runtime package
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions package
│ ├── file-loaders/ # File processing packages
│ ├── prompts/ # AI prompt management
│ └── web-crawler/ # Web crawling functionality
├── public/ # Static assets
│ ├── icons/ # Application icons
│ ├── images/ # Image resources
│ └── screenshots/ # Application screenshots
├── scripts/ # Build and tool scripts
├── src/ # Main application source code (see below)
├── .cursor/ # Cursor AI configuration
├── docker-compose/ # Docker configuration
├── package.json # Project dependencies
├── pnpm-workspace.yaml # pnpm monorepo configuration
├── next.config.ts # Next.js configuration
├── drizzle.config.ts # Drizzle ORM configuration
└── tsconfig.json # TypeScript configuration
```
## Core Source Directory (`src/`)
```plaintext
src/
├── app/ # Next.js App Router routes
│ ├── (backend)/ # Backend API routes
│ │ ├── api/ # REST API endpoints
│ │ │ ├── auth/ # Authentication endpoints
│ │ │ └── webhooks/ # Webhook handlers for various auth providers
│ │ ├── middleware/ # Request middleware
│ │ ├── oidc/ # OpenID Connect endpoints
│ │ ├── trpc/ # tRPC API routes
│ │ │ ├── async/ # Async tRPC endpoints
│ │ │ ├── desktop/ # Desktop runtime endpoints
│ │ │ ├── edge/ # Edge runtime endpoints
│ │ │ ├── lambda/ # Lambda runtime endpoints
│ │ │ └── tools/ # Tools-specific endpoints
│ │ └── webapi/ # Web API endpoints
│ │ ├── chat/ # Chat-related APIs for various providers
│ │ ├── models/ # Model management APIs
│ │ ├── plugin/ # Plugin system APIs
│ │ ├── stt/ # Speech-to-text APIs
│ │ ├── text-to-image/ # Image generation APIs
│ │ └── tts/ # Text-to-speech APIs
│ ├── [variants]/ # Page route variants
│ │ ├── (main)/ # Main application routes
│ │ │ ├── chat/ # Chat interface and workspace
│ │ │ ├── discover/ # Discover page (assistants, models, providers)
│ │ │ ├── files/ # File management interface
│ │ │ ├── image/ # Image generation interface
│ │ │ ├── profile/ # User profile and stats
│ │ │ ├── repos/ # Knowledge base repositories
│ │ │ └── settings/ # Application settings
│ │ └── @modal/ # Modal routes
│ └── manifest.ts # PWA configuration
├── components/ # Global shared components
│ ├── Analytics/ # Analytics tracking components
│ ├── Error/ # Error handling components
│ └── Loading/ # Loading state components
├── config/ # Application configuration
│ ├── featureFlags/ # Feature flags & experiments
│ └── modelProviders/ # Model provider configurations
├── features/ # Feature components (UI Layer)
│ ├── AgentSetting/ # Agent configuration and management
│ ├── ChatInput/ # Chat input with file upload and tools
│ ├── Conversation/ # Message display and interaction
│ ├── FileManager/ # File upload and knowledge base
│ └── PluginStore/ # Plugin marketplace and management
├── hooks/ # Custom React hooks
├── layout/ # Global layout components
│ ├── AuthProvider/ # Authentication provider
│ └── GlobalProvider/ # Global state provider
├── libs/ # External library integrations
│ ├── analytics/ # Analytics services integration
│ ├── next-auth/ # NextAuth.js configuration
│ └── oidc-provider/ # OIDC provider implementation
├── locales/ # Internationalization resources
│ └── default/ # Default language definitions
├── migrations/ # Client-side data migrations
├── server/ # Server-side code
│ ├── modules/ # Server modules
│ ├── routers/ # tRPC routers
│ └── services/ # Server services
├── services/ # Service layer (per-domain, client/server split)
│ ├── user/ # User services
│ │ ├── client.ts # Client DB (PGLite) implementation
│ │ └── server.ts # Server DB implementation (via tRPC)
│ ├── aiModel/ # AI model services
│ ├── session/ # Session services
│ └── message/ # Message services
├── store/ # Zustand state management
│ ├── agent/ # Agent state
│ ├── chat/ # Chat state
│ └── user/ # User state
├── styles/ # Global styles
├── tools/ # Built-in tool system
│ ├── artifacts/ # Code artifacts and preview
│ └── web-browsing/ # Web search and browsing
├── types/ # TypeScript type definitions
└── utils/ # Utility functions
├── client/ # Client-side utilities
└── server/ # Server-side utilities
```
## Key Monorepo Packages
```plaintext
packages/
├── const/ # Global constants and configurations
├── database/ # Database schemas and models
│ ├── src/models/ # Data models (CRUD operations)
│ ├── src/schemas/ # Drizzle database schemas
│ ├── src/repositories/ # Complex query layer
│ └── migrations/ # Database migration files
├── model-runtime/ # AI model runtime
│ └── src/
│ ├── openai/ # OpenAI provider integration
│ ├── anthropic/ # Anthropic provider integration
│ ├── google/ # Google AI provider integration
│ ├── ollama/ # Ollama local model integration
│ ├── types/ # Runtime type definitions
│ └── utils/ # Runtime utilities
├── types/ # Shared TypeScript type definitions
│ └── src/
│ ├── agent/ # Agent-related types
│ ├── message/ # Message and chat types
│ ├── user/ # User and session types
│ └── tool/ # Tool and plugin types
├── utils/ # Shared utility functions
│ └── src/
│ ├── client/ # Client-side utilities
│ ├── server/ # Server-side utilities
│ ├── fetch/ # HTTP request utilities
│ └── tokenizer/ # Token counting utilities
├── file-loaders/ # File loaders (PDF, DOCX, etc.)
├── prompts/ # AI prompt management
└── web-crawler/ # Web crawling functionality
```
## Architecture Map
- Presentation: `src/features`, `src/components`, `src/layout` — UI composition, global providers
- State: `src/store` — Zustand slices, selectors, middleware
- Client Services: `src/services/<domain>/{client|server}.ts` — client: PGLite; server: tRPC bridge
- API Routers: `src/app/(backend)/webapi` (REST), `src/app/(backend)/trpc/{edge|lambda|async|desktop|tools}`; Lambda router triggers Async router for long-running tasks (e.g., image)
- Server Services: `src/server/services` (business logic), `src/server/modules` (infra adapters)
- Data Access: `packages/database/src/{schemas,models,repositories}` — Schema (Drizzle), Model (CRUD), Repository (complex queries)
- Integrations: `src/libs` — analytics, auth, trpc, logging, runtime helpers
## Data Flow Architecture
### Unified Flow Pattern
```
UI Layer → State Management → Client Service → [Environment Branch] → Database
↓ ↓ ↓ ↓ ↓
React Zustand Environment Local/Remote PGLite/
Components Store Adaptation Routing PostgreSQL
```
### Environment-Specific Routing
| Mode | UI | Service Route | Database |
| --------------- | -------- | ---------------------- | ------------------- |
| **Browser/PWA** | React | Direct Model Access | PGLite (Local) |
| **Server** | React | tRPC → Server Services | PostgreSQL (Remote) |
| **Desktop** | Electron | tRPC → Local Node.js | PGLite/PostgreSQL\* |
_\*Depends on cloud sync configuration_
### Key Characteristics
- **Type Safety**: End-to-end type safety via tRPC and Drizzle ORM
- **Local/Remote Dual Mode**: PGLite enables user data ownership and local control
## Quick Map
- App Routes: `src/app` — UI routes (App Router) and backend routes under `(backend)`
- Web API: `src/app/(backend)/webapi` — REST-like endpoints
- tRPC Routers: `src/server/routers` — typed RPC endpoints by runtime
- Client Services: `src/services` — environment-adaptive client-side business logic
- Server Services: `src/server/services` — platform-agnostic business logic
- Database: `packages/database` — schemas/models/repositories/migrations
- State: `src/store` — Zustand stores and slices
- Integrations: `src/libs` — analytics/auth/trpc/logging/runtime helpers
- Tools: `src/tools` — built-in tool system
## Common Tasks
- Add Web API route: `src/app/(backend)/webapi/<module>/route.ts`
- Add tRPC endpoint: `src/server/routers/{edge|lambda|desktop}/...`
- Add client/server service: `src/services/<domain>/{client|server}.ts` (client: PGLite; server: tRPC)
- Add server service: `src/server/services/<domain>`
- Add a new model/provider: `src/config/modelProviders/<provider>.ts` + `packages/model-bank/src/aiModels/<provider>.ts` + `packages/model-runtime/src/<provider>/index.ts`
- Add DB schema/model/repository: `packages/database/src/{schemas|models|repositories}`
- Add Zustand slice: `src/store/<domain>/slices`
## Env Modes
- `NEXT_PUBLIC_CLIENT_DB`: selects client DB mode (e.g., `pglite`) vs server-backed
- `NEXT_PUBLIC_IS_DESKTOP_APP`: enables desktop-specific routes and behavior
- `NEXT_PUBLIC_SERVICE_MODE`: controls service routing preference (client/server)
## Boundaries
- Keep client logic in `src/services`; server-only logic stays in `src/server/services`
- Dont mix Web API (`webapi/`) with tRPC (`src/server/routers/`)
- Place business UI under `src/features`, global reusable UI under `src/components`
+73 -92
View File
@@ -3,7 +3,6 @@ description:
globs: *.tsx
alwaysApply: false
---
# react component 编写指南
- 如果要写复杂样式的话用 antd-style ,简单的话可以用 style 属性直接写内联样式
@@ -21,20 +20,18 @@ import { useTheme } from 'antd-style';
const MyComponent = () => {
const theme = useTheme();
return (
<div
style={{
color: theme.colorPrimary,
backgroundColor: theme.colorBgContainer,
padding: theme.padding,
borderRadius: theme.borderRadius,
}}
>
<div style={{
color: theme.colorPrimary,
backgroundColor: theme.colorBgContainer,
padding: theme.padding,
borderRadius: theme.borderRadius
}}>
使用主题 token 的组件
</div>
);
};
}
```
#### 使用 antd-style 的 createStyles
@@ -56,13 +53,13 @@ const useStyles = createStyles(({ css, token }) => {
content: css`
font-size: ${token.fontSize}px;
line-height: ${token.lineHeight};
`,
`
};
});
const Card: FC<CardProps> = ({ title, content }) => {
const { styles } = useStyles();
return (
<Flexbox className={styles.container}>
<div className={styles.title}>{title}</div>
@@ -77,96 +74,80 @@ const Card: FC<CardProps> = ({ title, content }) => {
请注意使用下面的 token 而不是 css 字面值。可以访问 https://ant.design/docs/react/customize-theme-cn 了解所有 token
- 动画类
- token.motionDurationMid
- token.motionEaseInOut
- token.motionDurationMid
- token.motionEaseInOut
- 包围盒属性
- token.paddingSM
- token.marginLG
- token.paddingSM
- token.marginLG
## Lobe UI 包含的组件
- 不知道 @lobehub/ui 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜,大部分组件都是在 antd 的基础上扩展了属性
- 具体用法不懂可以联网搜索,例如 ActionIcon 就爬取 https://ui.lobehub.com/components/action-icon
- 可以阅读 node_modules/@lobehub/ui/es/index.js 了解有哪些组件,每个组件的属性是什么
- General
- ActionIcon
- ActionIconGroup
- Block
- Button
- DownloadButton
- Icon
ActionIcon
ActionIconGroup
Block
Button
Icon
- Data Display
- Avatar
- AvatarGroup
- GroupAvatar
- Collapse
- FileTypeIcon
- FluentEmoji
- GuideCard
- Highlighter
- Hotkey
- Image
- List
- Markdown
- SearchResultCards
- MaterialFileTypeIcon
- Mermaid
- Typography
- Text
- Segmented
- Snippet
- SortableList
- Tag
- Tooltip
- Video
Avatar
Collapse
FileTypeIcon
FluentEmoji
GuideCard
Highlighter
Hotkey
Image
List
Markdown
MaterialFileTypeIcon
Mermaid
Segmented
Snippet
SortableList
Tag
Tooltip
Video
- Data Entry
- AutoComplete
- CodeEditor
- ColorSwatches
- CopyButton
- DatePicker
- EditableText
- EmojiPicker
- Form
- FormModal
- HotkeyInput
- ImageSelect
- Input
- SearchBar
- Select
- SliderWithInput
- ThemeSwitch
AutoComplete
CodeEditor
ColorSwatches
CopyButton
DatePicker
EditableText
EmojiPicker
Form
FormModal
HotkeyInput
ImageSelect
Input
SearchBar
Select
SliderWithInput
ThemeSwitch
- Feedback
- Alert
- Drawer
- Modal
Alert
Drawer
Modal
- Layout
- DraggablePanel
- DraggablePanelBody
- DraggablePanelContainer
- DraggablePanelFooter
- DraggablePanelHeader
- Footer
- Grid
- Header
- Layout
- LayoutFooter
- LayoutHeader
- LayoutMain
- LayoutSidebar
- LayoutSidebarInner
- LayoutToc
- MaskShadow
- ScrollShadow
DraggablePanel
Footer
Grid
Header
Layout
MaskShadow
ScrollShadow
- Navigation
- Burger
- Dropdown
- Menu
- SideNav
- Tabs
- Toc
Burger
Dropdown
Menu
SideNav
Tabs
Toc
- Theme
- ConfigProvider
- FontLoader
- ThemeProvider
ConfigProvider
FontLoader
ThemeProvider
+63 -30
View File
@@ -4,49 +4,82 @@ globs:
alwaysApply: true
---
# 📋 Available Rules Index
# LobeChat Cursor Rules System Guide
This document explains how the LobeChat project's Cursor rules system works and serves as an index for manually accessible rules.
## 🎯 Core Principle
**All rules are equal** - there are no priorities or "recommendations" between different rule sources. You should follow all applicable rules simultaneously.
## 📚 Four Ways to Access Rules
### 1. **Always Applied Rules** - `always_applied_workspace_rules`
- **What**: Core project guidelines that are always active
- **Content**: Project tech stack, basic coding standards, output formatting rules
- **Access**: No tools needed - automatically provided in every conversation
### 2. **Dynamic Context Rules** - `cursor_rules_context`
- **What**: Rules automatically matched based on files referenced in the conversation
- **Trigger**: Only when user **explicitly @ mentions files** or **opens files in Cursor**
- **Content**: May include brief descriptions or full rule content, depending on relevance
- **Access**: No tools needed - automatically updated when files are referenced
### 3. **Agent Requestable Rules** - `agent_requestable_workspace_rules`
- **What**: Detailed operational guides that can be requested on-demand
- **Access**: Use `fetch_rules` tool with rule names
- **Examples**: `debug`, `i18n/i18n`, `code-review`
### 4. **Manual Rules Index** - This file + `read_file`
- **What**: Additional rules not covered by the above mechanisms
- **Why needed**: Cursor's rule system only supports "agent request" or "auto attach" modes
- **Access**: Use `read_file` tool to read specific `.mdc` files
## 🔧 When to Use `read_file` for Rules
Use `read_file` to access rules from the index below when:
1. **Gap identification**: You determine a rule is needed for the current task
2. **No auto-trigger**: The rule isn't provided in `cursor_rules_context` (because relevant files weren't @ mentioned)
3. **Not agent-requestable**: The rule isn't available via `fetch_rules`
## 📋 Available Rules Index
The following rules are available via `read_file` from the `.cursor/rules/` directory:
## General
- `project-introduce.mdc` Project description and tech stack
- `cursor-rules.mdc` Cursor rules authoring and optimization guide
- `code-review.mdc` How to code review
## Backend
- `backend-architecture.mdc` Backend layer architecture and design guidelines
- `define-database-model.mdc` Database model definition guidelines
- `drizzle-schema-style-guide.mdc` Style guide for defining Drizzle ORM schemas
## Frontend
- `react-component.mdc` React component style guide and conventions
- `i18n.mdc` Internationalization guide using react-i18next
- `testing-guide.mdc` Comprehensive testing guide for Vitest environment
- `typescript.mdc` TypeScript code style guide
- `packages/react-layout-kit.mdc` Usage guide for react-layout-kit
## State Management
- `zustand-action-patterns.mdc` Recommended patterns for organizing Zustand actions
- `zustand-slice-organization.mdc` Best practices for structuring Zustand slices
## Desktop (Electron)
## ❌ Common Misunderstandings to Avoid
- `desktop-feature-implementation.mdc` Implementing new Electron desktop features
- `desktop-controller-tests.mdc` Desktop controller unit testing guide
- `desktop-local-tools-implement.mdc` Workflow to add new desktop local tools
- `desktop-menu-configuration.mdc` Desktop menu configuration guide
- `desktop-window-management.mdc` Desktop window management guide
1. **"Priority confusion"**: There's no hierarchy between rule sources - they're complementary, not competitive
2. **"Dynamic expectations"**: `cursor_rules_context` only updates when you @ files - it won't automatically include rules for tasks you're thinking about
3. **"Tool redundancy"**: Each access method serves a different purpose - they're not alternatives to choose from
## Debugging
## 🛠️ Practical Workflow
- `debug.mdc` General debugging guide
- `debug-usage.mdc` Using the debug package and namespace conventions
```
1. Start with always_applied_workspace_rules (automatic)
2. Check cursor_rules_context for auto-matched rules (automatic)
3. If you need specific guides: fetch_rules (manual)
4. If you identify gaps: consult this index → read_file (manual)
```
## Testing
## Example Decision Flow
- `testing-guide/testing-guide.mdc` Comprehensive testing guide for Vitest
- `testing-guide/electron-ipc-test.mdc` Electron IPC interface testing strategy
- `testing-guide/db-model-test.mdc` Database Model testing guide
**Scenario**: Working on a new Zustand store slice
1. Follow always_applied_workspace_rules ✅
2. If store files were @ mentioned → use cursor_rules_context rules ✅
3. Need detailed Zustand guidance → `read_file('.cursor/rules/zustand-slice-organization.mdc')` ✅
4. All rules apply simultaneously - no conflicts ✅
+20 -8
View File
@@ -6,26 +6,38 @@ alwaysApply: true
## System Role
You are an expert in full-stack Web development, proficient in JavaScript, TypeScript, CSS, React, Node.js, Next.js, Postgresql, Redis, S3, all kinds of network protocols.
You are an expert in full-stack Web development, proficient in JavaScript, TypeScript, CSS, React, Node.js, Next.js, Postgresql, all kinds of network protocols.
You are an LLM expert, you are familiar with all kinds of LLM models, ai agents, ai workflow, prompt engineering and context engineering.
You are an expert in Ai art. In Ai image generation, you are proficient in Stable Diffusion and ComfyUI's architectural principles, workflows, model structures, parameter configurations, training methods, and inference optimization.
You are an expert in LLM and Ai art. In Ai image generation, you are proficient in Stable Diffusion and ComfyUI's architectural principles, workflows, model structures, parameter configurations, training methods, and inference optimization.
You are an expert in UI/UX design, proficient in web interaction patterns, responsive design, accessibility, and user behavior optimization. You excel at improving user retention and paid conversion rates through various interaction details.
## Problem Solving
- Before formulating any response, you must first gather context by using tools like codebase_search, grep_search, file_search, web_search, fetch_rules, context7, and read_file to avoid making assumptions.
- When modifying existing code, clearly describe the differences and reasons for the changes
- Provide alternative solutions that may be better overall or superior in specific aspects
- Provide optimization suggestions for deprecated API usage
- Cite sources whenever possible at the end, not inline
- When you provide multiple solutions, provide the recommended solution first, and note it as `Recommended`
- Express uncertainty when there might not be a correct answer, instead of take action by guessing and assuming
- Express uncertainty when there might not be a correct answer
- Admit when you don't know something instead of guessing
## Code Implementation
- Write correct, up-to-date, bug-free, fully functional, secure, maintainable and efficient code
- First, think step-by-step: describe your plan in detailed pseudocode before implementation
- Confirm the plan before writing code
- Focus on maintainable over being performant
- Be sure to reference file path
- If doc links or required files are missing, ask for them before proceeding with the task rather than making assumptions
- If you're unable to get valid result when using tools, please clearly state in the output
- Leave NO TODOs, placeholders, or missing pieces
- Be sure to reference file names
- When you notice I have manually modified the code, that was definitely on purpose and do not revert them
- If documentation links or required files are missing, ask for them before proceeding with the task rather than making assumptions
- If you're unable to access or retrieve content from websites, please inform me immediately and request the specific information needed rather than making assumptions
- You can use emojis, npm packages like `chalk`/`chalk-animation`/`terminal-link`/`gradient-string`/`log-symbols`/`boxen`/`consola`/`@clack/prompts` to create beautiful terminal output
- Don't run `tsc --noEmit` to check ts syntax error, because our project is very large and the validate very slow
## Some logging rules
- Never log user private information like api key, etc
- Don't use `import { log } from 'debug'` to log messages, because it will directly log the message to the console.
@@ -5,8 +5,6 @@ alwaysApply: false
## 🗃️ 数据库 Model 测试指南
测试 `packages/database` 下的数据库 Model 层。
### 测试环境选择 💡
数据库 Model 层通过环境变量控制数据库类型,在两种测试环境下有不同的数据库后端:客户端环境 (PGLite) 和 服务端环境 (PostgreSQL)
@@ -19,10 +17,10 @@ alwaysApply: false
```bash
# 1. 先在客户端环境测试(快速验证)
cd packages/database && TEST_SERVER_DB=0 bunx vitest run --silent='passed-only' src/database/models/__tests__/myModel.test.ts
npx vitest run --config vitest.config.ts src/database/models/__tests__/myModel.test.ts
# 2. 再在服务端环境测试(兼容性验证), 需要设置环境变量 `TEST_SERVER_DB=1`
cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent='passed-only' src/database/models/__tests__/myModel.test.ts #
# 2. 再在服务端环境测试(兼容性验证)
npx vitest run --config vitest.config.server.ts src/database/models/__tests__/myModel.test.ts
```
### 创建新 Model 测试的最佳实践 📋
+168 -182
View File
@@ -5,11 +5,11 @@ alwaysApply: false
# 测试指南 - LobeChat Testing Guide
## 测试环境概览
## 🧪 测试环境概览
LobeChat 项目使用 Vitest 测试库,配置了两种不同的测试环境:
### 客户端数据库测试环境 (DOM Environment)
### 客户端测试环境 (DOM Environment)
- **配置文件**: [vitest.config.ts](mdc:vitest.config.ts)
- **环境**: Happy DOM (浏览器环境模拟)
@@ -17,71 +17,60 @@ LobeChat 项目使用 Vitest 测试库,配置了两种不同的测试环境:
- **用途**: 测试前端组件、客户端逻辑、React 组件等
- **设置文件**: [tests/setup.ts](mdc:tests/setup.ts)
### 服务端数据库测试环境 (Node Environment)
### 服务端测试环境 (Node Environment)
目前只有 `packages/database` 下的测试可以通过配置 `TEST_SERVER_DB=1` 环境变量来使用服务端数据库测试
- **配置文件**: [packages/database/vitest.config.mts](mdc:packages/database/vitest.config.mts) 并且设置环境变量 `TEST_SERVER_DB=1`
- **配置文件**: [vitest.config.server.ts](mdc:vitest.config.server.ts)
- **环境**: Node.js
- **数据库**: 真实的 PostgreSQL 数据库
- **并发限制**: 单线程运行 (`singleFork: true`)
- **用途**: 测试数据库模型、服务端逻辑、API 端点等
- **设置文件**: [packages/database/tests/setup-db.ts](mdc:packages/database/tests/setup-db.ts)
- **设置文件**: [tests/setup-db.ts](mdc:tests/setup-db.ts)
## 测试运行命令
## 🚀 测试运行命令
** 性能警告**: 项目包含 3000+ 测试用例,完整运行需要约 10 分钟。务必使用文件过滤或测试名称过滤。
**🚨 性能警告**: 项目包含 3000+ 测试用例,完整运行需要约 10 分钟。务必使用文件过滤或测试名称过滤。
### 正确的命令格式
### 正确的命令格式
```bash
# 运行所有客户端/服务端测试
bunx vitest run --silent='passed-only' # 客户端测试
cd packages/database && TEST_SERVER_DB=1 bunx vitest run --silent='passed-only' # 服务端测试
npx vitest run --config vitest.config.ts # 客户端测试
npx vitest run --config vitest.config.server.ts # 服务端测试
# 运行特定测试文件 (支持模糊匹配)
bunx vitest run --silent='passed-only' user.test.ts
npx vitest run --config vitest.config.ts user.test.ts
# 运行特定测试用例名称 (使用 -t 参数)
bunx vitest run --silent='passed-only' -t "test case name"
npx vitest run --config vitest.config.ts -t "test case name"
# 组合使用文件和测试名称过滤
bunx vitest run --silent='passed-only' filename.test.ts -t "specific test"
npx vitest run --config vitest.config.ts filename.test.ts -t "specific test"
# 生成覆盖率报告 (使用 --coverage 参数)
bunx vitest run --silent='passed-only' --coverage
npx vitest run --config vitest.config.ts --coverage
```
### 避免的命令格式
### 避免的命令格式
```bash
# 这些命令会运行所有 3000+ 测试用例,耗时约 10 分钟!
# 这些命令会运行所有 3000+ 测试用例,耗时约 10 分钟!
npm test
npm test some-file.test.ts
# 不要使用裸 vitest (会进入 watch 模式)
# 不要使用裸 vitest (会进入 watch 模式)
vitest test-file.test.ts
```
## 测试修复原则
## 🔧 测试修复原则
### 核心原则
### 核心原则 ⚠️
1. **收集足够的上下文**
在修复测试之前,务必做到:
- 完整理解测试的意图和实现
- 强烈建议阅读当前的 git diff 和 PR diff
1. **充分阅读测试代码**: 在修复测试之前,必须完整理解测试的意图和实现
2. **测试优先修复**: 如果是测试本身写错了,修改测试而不是实现代码
3. **专注单一问题**: 只修复指定的测试,不要添加额外测试或功能
4. **不自作主张**: 不要因为发现其他问题就直接修改,先提出再讨论
2. **测试优先修复**
如果是测试本身写错了,应优先修改测试,而不是实现代码。
3. **专注单一问题**
只修复指定的测试,不要顺带添加额外测试。
4. **不自作主张**
发现其他问题时,不要直接修改,需先提出并讨论。
### 测试协作最佳实践
### 测试协作最佳实践 🤝
基于实际开发经验总结的重要协作原则:
@@ -95,10 +84,10 @@ vitest test-file.test.ts
- **避免陷阱**: 不要陷入"不断尝试相同或类似方法"的循环
```typescript
// 错误做法:连续失败后继续盲目尝试
// 错误做法:连续失败后继续盲目尝试
// 第3次、第4次仍在用相似的方法修复同一个问题
// 正确做法:失败1-2次后总结问题
// 正确做法:失败1-2次后总结问题
/*
问题总结:
1. 尝试过的方法:修改 mock 数据结构
@@ -117,7 +106,7 @@ vitest test-file.test.ts
- **保持稳定性**: 测试名称应该在代码重构后仍然有意义
```typescript
// 错误的测试命名
// 错误的测试命名
describe('User component coverage', () => {
it('covers line 45-50 in getUserData', () => {
// 为了覆盖第45-50行而写的测试
@@ -128,7 +117,7 @@ describe('User component coverage', () => {
});
});
// 正确的测试命名
// 正确的测试命名
describe('<UserAvatar />', () => {
it('should render fallback icon when image url is not provided', () => {
// 测试具体的业务场景,自然会覆盖相关代码分支
@@ -142,8 +131,8 @@ describe('<UserAvatar />', () => {
**覆盖率提升的正确思路**:
- 通过设计各种业务场景(正常流程、边缘情况、错误处理)来自然提升覆盖率
- 不要为了达到覆盖率数字而写测试,更不要在测试中注释"为了覆盖 xxx 行"
- 通过设计各种业务场景(正常流程、边缘情况、错误处理)来自然提升覆盖率
- 不要为了达到覆盖率数字而写测试,更不要在测试中注释"为了覆盖 xxx 行"
#### 3. 测试组织结构
@@ -154,7 +143,7 @@ describe('<UserAvatar />', () => {
- **避免碎片化**: 不要为了单个测试用例就创建新的顶级 `describe` 块
```typescript
// 错误的组织方式:创建过多顶级块
// 错误的组织方式:创建过多顶级块
describe('<UserProfile />', () => {
it('should render user name', () => {});
});
@@ -169,7 +158,7 @@ describe('UserProfile edge cases', () => {
it('should handle missing avatar', () => {});
});
// 正确的组织方式:合并相关测试
// 正确的组织方式:合并相关测试
describe('<UserProfile />', () => {
it('should render user name', () => {});
@@ -225,9 +214,9 @@ describe('<UserProfile />', () => {
**修复方法**: 更新了测试文件中的 mock 数据结构,使其与最新的 API 响应格式保持一致。具体修改了 `user.test.ts` 中的 `mockUserData` 对象结构。
```
## 测试编写最佳实践
## 🎯 测试编写最佳实践
### Mock 数据策略:追求"低成本的真实性"
### Mock 数据策略:追求"低成本的真实性" 📋
**核心原则**: 测试数据应默认追求真实性,只有在引入"高昂的测试成本"时才进行简化。
@@ -239,10 +228,10 @@ describe('<UserProfile />', () => {
- **网络请求**:HTTP 调用、数据库连接
- **系统调用**:获取系统时间、环境变量等
#### 推荐做法:Mock 依赖,保留真实数据
#### 推荐做法:Mock 依赖,保留真实数据
```typescript
// 好的做法:Mock I/O 操作,但使用真实的文件内容格式
// 好的做法:Mock I/O 操作,但使用真实的文件内容格式
describe('parseContentType', () => {
beforeEach(() => {
// Mock 文件读取操作(避免真实 I/O)
@@ -260,7 +249,7 @@ describe('parseContentType', () => {
});
});
// 过度简化:使用不真实的数据
// 过度简化:使用不真实的数据
describe('parseContentType', () => {
it('should detect PDF content type correctly', () => {
// 这种简化数据没有测试价值
@@ -270,116 +259,78 @@ describe('parseContentType', () => {
});
```
#### 真实标识符的价值
#### 🎯 真实标识符的价值
```typescript
// ✅ 使用真实标识符
const result = parseModelString('openai', '+gpt-4,+gpt-3.5-turbo');
// ✅ 使用真实的提供商标识符
it('should parse OpenAI model list correctly', () => {
const result = parseModelString('openai', '+gpt-4,+gpt-3.5-turbo');
expect(result.add).toHaveLength(2);
expect(result.add[0].id).toBe('gpt-4');
});
// ❌ 使用占位符(价值较低)
const result = parseModelString('test-provider', '+model1,+model2');
```
### 现代化Mock技巧:环境设置与Mock方法
**环境设置 + Mock方法结合使用**
客户端代码测试时,推荐使用环境注释配合现代化Mock方法:
```typescript
/**
* @vitest-environment happy-dom // 提供浏览器API
*/
import { beforeEach, vi } from 'vitest';
beforeEach(() => {
// 现代方法1:使用vi.stubGlobal替代global.xxx = ...
const mockImage = vi.fn().mockImplementation(() => ({
addEventListener: vi.fn(),
naturalHeight: 600,
naturalWidth: 800,
}));
vi.stubGlobal('Image', mockImage);
// 现代方法2:使用vi.spyOn保留原功能,只mock特定方法
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mock-url');
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
// ❌ 使用占位符标识符(价值较低)
it('should parse model list correctly', () => {
const result = parseModelString('test-provider', '+model1,+model2');
expect(result.add).toHaveLength(2);
// 这种测试对理解真实场景帮助不大
});
```
**环境选择优先级**
1. **@vitest-environment happy-dom** (推荐) - 轻量、快速,项目已安装
2. **@vitest-environment jsdom** - 功能完整,但需要额外安装jsdom包
3. **不设置环境** - Node.js环境,需要手动mock所有浏览器API
**Mock方法对比**
```typescript
// ❌ 旧方法:直接操作global对象(类型问题)
global.Image = mockImage;
global.URL = { ...global.URL, createObjectURL: mockFn };
// ✅ 现代方法:类型安全的vi API
vi.stubGlobal('Image', mockImage); // 完全替换全局对象
vi.spyOn(URL, 'createObjectURL'); // 部分mock,保留其他功能
```
### 测试覆盖率原则:代码分支优于用例数量
**核心原则**: 优先覆盖所有代码分支,而非编写大量重复用例
```typescript
// ❌ 过度测试:29个测试用例都验证相同分支
describe('getImageDimensions', () => {
it('should reject .txt files');
it('should reject .pdf files');
// ... 25个类似测试,都走相同的验证分支
});
// ✅ 精简测试:4个核心用例覆盖所有分支
describe('getImageDimensions', () => {
it('should return dimensions for valid File object'); // 成功路径 - File
it('should return dimensions for valid data URI'); // 成功路径 - String
it('should return undefined for invalid inputs'); // 输入验证分支
it('should return undefined when image fails to load'); // 错误处理分支
});
```
**分支覆盖策略**
1. **成功路径** - 每种输入类型1个测试即可
2. **边界条件** - 合并类似场景到单个测试
3. **错误处理** - 测试代表性错误即可
4. **业务逻辑** - 覆盖所有if/else分支
**合理测试数量**
- 简单工具函数:2-5个测试
- 复杂业务逻辑:5-10个测试
- 核心安全功能:适当增加,但避免重复路径
### 错误处理测试:测试"行为"而非"文本"
### 错误处理测试:测试"行为"而非"文本" ⚠️
**核心原则**: 测试应该验证程序在错误发生时的行为是可预测的,而不是验证易变的错误信息文本。
#### 推荐的错误测试方式
#### 推荐的错误测试方式
```typescript
// ✅ 测试错误类型和属性
expect(() => validateUser({})).toThrow(ValidationError);
expect(() => processPayment({})).toThrow(
expect.objectContaining({
code: 'INVALID_PAYMENT_DATA',
statusCode: 400,
}),
);
// ✅ 测试是否抛出错误
it('should throw error when invalid input provided', () => {
expect(() => processInput(null)).toThrow();
});
// ❌ 避免测试具体错误文本
expect(() => processUser({})).toThrow('用户数据不能为空,请检查输入参数');
// ✅ 测试错误类型(最推荐)
it('should throw ValidationError for invalid data', () => {
expect(() => validateUser({})).toThrow(ValidationError);
});
// ✅ 测试错误属性而非消息文本
it('should throw error with correct error code', () => {
expect(() => processPayment({})).toThrow(
expect.objectContaining({
code: 'INVALID_PAYMENT_DATA',
statusCode: 400,
}),
);
});
```
### 疑难解答:警惕模块污染
#### ❌ 应避免的做法
```typescript
// ❌ 过度依赖具体错误信息文本
it('should throw specific error message', () => {
expect(() => processUser({})).toThrow('用户数据不能为空,请检查输入参数');
// 这种测试很脆弱,错误文案稍有修改就会失败
});
```
#### 🎯 例外情况:何时可以测试错误信息
```typescript
// ✅ 测试标准 API 错误(这是契约的一部分)
it('should return proper HTTP error for API', () => {
expect(response.statusCode).toBe(400);
expect(response.error).toBe('Bad Request');
});
// ✅ 测试错误信息的关键部分(使用正则)
it('should include field name in validation error', () => {
expect(() => validateField('email', '')).toThrow(/email/i);
});
```
### 疑难解答:警惕模块污染 🚨
**识别信号**: 当你的测试出现以下"灵异"现象时,优先怀疑模块污染:
@@ -390,25 +341,55 @@ expect(() => processUser({})).toThrow('用户数据不能为空,请检查输
#### 典型场景:动态 Mock 同一模块
```typescript
// ❌ 问题:动态Mock同一模块
it('dev mode', async () => {
vi.doMock('./config', () => ({ isDev: true }));
const { getSettings } = await import('./service'); // 可能使用缓存
// ❌ 容易出现模块污染的写法
describe('ConfigService', () => {
it('should work in development mode', async () => {
vi.doMock('./config', () => ({ isDev: true }));
const { getSettings } = await import('./configService'); // 第一次加载
expect(getSettings().debugMode).toBe(true);
});
it('should work in production mode', async () => {
vi.doMock('./config', () => ({ isDev: false }));
const { getSettings } = await import('./configService'); // 可能使用缓存的旧版本!
expect(getSettings().debugMode).toBe(false); // ❌ 可能失败
});
});
// ✅ 解决:清除模块缓存
beforeEach(() => {
vi.resetModules(); // 确保每个测试都是干净环境
// ✅ 使用 resetModules 解决模块污染
describe('ConfigService', () => {
beforeEach(() => {
vi.resetModules(); // 清除模块缓存,确保每个测试都是干净的环境
});
it('should work in development mode', async () => {
vi.doMock('./config', () => ({ isDev: true }));
const { getSettings } = await import('./configService');
expect(getSettings().debugMode).toBe(true);
});
it('should work in production mode', async () => {
vi.doMock('./config', () => ({ isDev: false }));
const { getSettings } = await import('./configService');
expect(getSettings().debugMode).toBe(false); // ✅ 测试通过
});
});
```
**记住**: `vi.resetModules()` 是解决测试"灵异"失败的终极武器。
#### 🔧 排查和解决步骤
## 测试文件组织
1. **识别问题**: 测试失败时,首先问自己:"是否有多个测试在 Mock 同一个模块?"
2. **添加隔离**: 在 `beforeEach` 中添加 `vi.resetModules()`
3. **验证修复**: 重新运行测试,确认问题解决
**记住**: `vi.resetModules()` 是解决测试"灵异"失败的终极武器,当常规调试方法都无效时,它往往能一针见血地解决问题。
## 📂 测试文件组织
### 文件命名约定
`*.test.ts`, `*.test.tsx` (任意位置)
- **客户端测试**: `*.test.ts`, `*.test.tsx` (任意位置)
- **服务端测试**: `src/database/models/**/*.test.ts`, `src/database/server/**/*.test.ts` (限定路径)
### 测试文件组织风格
@@ -425,10 +406,7 @@ src/components/Button/
└── index.test.tsx # 测试文件
```
- 也有少数情况会统一放到 `__tests__` 文件夹, 例如 `packages/database/src/models/__tests__`
- 测试使用的辅助文件放到 fixtures 文件夹
## 测试调试技巧
## 🛠️ 测试调试技巧
### 测试调试步骤
@@ -437,59 +415,67 @@ src/components/Button/
3. **分析错误**: 仔细阅读错误信息、堆栈跟踪和最近的文件修改记录
4. **添加调试**: 在测试中添加 `console.log` 了解执行流程
### TypeScript 类型处理
### TypeScript 类型处理 📝
在测试中,为了提高编写效率和可读性,可以适当放宽 TypeScript 类型检测:
#### 推荐的类型放宽策略
#### 推荐的类型放宽策略
```typescript
// 使用非空断言访问测试中确定存在的属性
// 使用非空断言访问测试中确定存在的属性
const result = await someFunction();
expect(result!.data).toBeDefined();
expect(result!.status).toBe('success');
// 使用 any 类型简化复杂的 Mock 设置
// 使用 any 类型简化复杂的 Mock 设置
const mockStream = new ReadableStream() as any;
mockStream.toReadableStream = () => mockStream;
// 访问私有成员
await instance['getFromCache']('key'); // 推荐中括号
await (instance as any).getFromCache('key'); // 避免as any
```
#### 适用场景
#### 🎯 适用场景
- **Mock 对象**: 对于测试用的 Mock 数据,使用 `as any` 避免复杂的类型定义
- **第三方库**: 处理复杂的第三方库类型时,适当使用 `any` 提高效率
- **测试断言**: 在确定对象存在的测试场景中,使用 `!` 非空断言
- **私有成员访问**: 优先使用中括号 `instance['privateMethod']()` 而不是 `(instance as any).privateMethod()`
- **临时调试**: 快速编写测试时,先用 `any` 保证功能,后续可选择性地优化类型
#### 注意事项
#### ⚠️ 注意事项
- **适度使用**: 不要过度依赖 `any`,核心业务逻辑的类型仍应保持严格
- **私有成员访问优先级**: 中括号访问 > `as any` 转换,保持更好的类型安全性
- **文档说明**: 对于使用 `any` 的复杂场景,添加注释说明原因
- **测试覆盖**: 确保即使使用了 `any`,测试仍能有效验证功能正确性
### 检查最近修改记录
### 检查最近修改记录 🔍
**核心原则**:测试突然失败时,优先检查最近的代码修改
系统性地检查相关文件的修改历史是问题定位的关键步骤
#### 快速检查
#### 三步检查法
**Step 1: 查看当前状态**
```bash
git status # 查看当前修改状态
git diff HEAD -- '*.test.*' # 查测试文件改
git diff main...HEAD # 对比主分支差异
gh pr diff # 查看PR中的所有改动
git status # 查看未提交的修改
git diff path/to/component.test.ts | cat # 查测试文件
git diff path/to/component.ts | cat # 查看实现文件修改
```
#### 常见原因与解决
**Step 2: 查看提交历史**
- **最新提交引入bug** → 检查并修复实现代码
- **分支代码滞后** → `git rebase main` 同步主分支
```bash
git log --pretty=format:"%h %ad %s" --date=relative -3 path/to/component.ts | cat
```
**Step 3: 查看具体修改内容**
```bash
git show HEAD -- path/to/component.ts | cat # 查看最新提交的修改
```
#### 时间相关性判断
- **24小时内的提交**: 🔴 **高度相关** - 很可能是直接原因
- **1-7天内的提交**: 🟡 **中等相关** - 需要仔细分析
- **超过1周的提交**: ⚪ **低相关性** - 除非重大重构
## 特殊场景的测试
@@ -498,9 +484,9 @@ gh pr diff # 查看PR中的所有改动
- [Electron IPC 接口测试策略](mdc:./electron-ipc-test.mdc)
- [数据库 Model 测试指南](mdc:./db-model-test.mdc)
## 核心要点
## 🎯 核心要点
- **命令格式**: 使用 `bunx vitest run --silent='passed-only'` 并指定文件过滤
- **命令格式**: 使用 `npx vitest run --config [config-file]` 并指定文件过滤
- **修复原则**: 失败1-2次后寻求帮助,测试命名关注行为而非实现细节
- **调试流程**: 复现 → 分析 → 假设 → 修复 → 验证 → 总结
- **文件组织**: 优先在现有 `describe` 块中添加测试,避免创建冗余顶级块
+12 -55
View File
@@ -1,62 +1,19 @@
---
description: TypeScript code style and optimization guidelines
description:
globs: *.ts,*.tsx,*.mts
alwaysApply: false
---
# TypeScript Code Style Guide
## Types and Type Safety
TypeScript Code Style Guide:
- Avoid explicit type annotations when TypeScript can infer types.
- Avoid implicitly `any` variables; explicitly type when necessary (e.g., `let a: number` instead of `let a`).
- Use the most accurate type possible (e.g., prefer `Record<PropertyKey, unknown>` over `object`).
- Prefer `interface` over `type` for object shapes (e.g., React component props). Keep `type` for unions, intersections, and utility types.
- Prefer `as const satisfies XyzInterface` over plain `as const` when suitable.
## Imports and Modules
- When importing a directory module, prefer the explicit index path like `@/db/index` instead of `@/db`.
## Asynchronous Patterns and Concurrency
- Prefer `async`/`await` over callbacks or chained `.then` promises.
- Prefer async APIs over sync ones (avoid `*Sync`).
- Prefer promise-based variants (e.g., `import { readFile } from 'fs/promises'`) over callback-based APIs from `fs`.
- Where safe, convert sequential async flows to concurrent ones with `Promise.all`, `Promise.race`, etc.
## Code Structure and Readability
- Refactor repeated logic into reusable functions.
- Prefer object destructuring when accessing and using properties.
- Use consistent, descriptive naming; avoid obscure abbreviations.
- Use semantically meaningful variable, function, and class names.
- Replace magic numbers or strings with well-named constants.
- Keep meaningful code comments; do not remove them when applying edits. Update comments when behavior changes.
- Ensure JSDoc comments accurately reflect the implementation.
- Look for opportunities to simplify or modernize code with the latest JavaScript/TypeScript features where it improves clarity.
- Defer formatting to tooling; ignore purely formatting-only issues and autofixable lint problems.
- Respect project Prettier settings.
## UI and Theming
- Use components from `@lobehub/ui`, Ant Design, or existing design system components instead of raw HTML tags (e.g., `Button` vs. `button`).
- Design for dark mode and mobile responsiveness:
- Use the `antd-style` token system instead of hard-coded colors.
- Select appropriate component variants.
## Performance
- Prefer `for…of` loops to index-based `for` loops when feasible.
- Decide whether callbacks should be debounced or throttled based on UX and performance needs.
- Reuse existing npm packages rather than reinventing the wheel (e.g., `lodash-es/omit`).
- Query only the required columns from a database rather than selecting entire rows.
## Time and Consistency
- Instead of calling `Date.now()` multiple times, assign it to a constant once and reuse it to ensure consistency and improve readability.
## Some logging rules
- Never log user private information like api key, etc
- Don't use `import { log } from 'debug'` to log messages, because it will directly log the message to the console.
- Avoid defining `any` type variables (e.g., `let a: number;` instead of `let a;`).
- Use the most accurate type possible (e.g., use `Record<PropertyKey, unknown>` instead of `object`).
- Prefer `interface` over `type` (e.g., define react component props).
- Use `as const satisfies XyzInterface` instead of `as const` when suitable
- import index.ts module(directory module) like `@/db/index` instead of `@/db`
- Instead of calling Date.now() multiple times, assign it to a constant once and reuse it. This ensures consistency and improves readability
- Always refactor repeated logic into a reusable function
- Don't remove meaningful code comments, be sure to keep original comments when providing applied code
- Update the code comments when needed after you modify the related code
- Please respect my prettier preferences when you provide code
-33
View File
@@ -4,14 +4,6 @@
# Specify your API Key selection method, currently supporting `random` and `turn`.
# API_KEY_SELECT_MODE=random
########################################
########### Security Settings ###########
########################################
# Control Content Security Policy headers
# Set to '1' to enable X-Frame-Options and Content-Security-Policy headers
# Default is '0' (enabled)
# ENABLED_CSP=1
########################################
########## AI Provider Service #########
@@ -148,31 +140,6 @@ OPENAI_API_KEY=sk-xxxxxxxxx
# INFINIAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### 302.AI ###
# AI302_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### ModelScope ###
# MODELSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### AiHubMix ###
# AIHUBMIX_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### BFL ###
# BFL_API_KEY=bfl-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### FAL ###
# FAL_API_KEY=fal-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
### Nebius ###
# NEBIUS_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
########################################
############ Market Service ############
########################################
-122
View File
@@ -1,122 +0,0 @@
# LobeChat Development Server Configuration
# This file contains environment variables for both LobeChat server mode and Docker compose setup
COMPOSE_FILE="docker-compose.development.yml"
# ⚠️⚠️⚠️ DO NOT USE THE SECRETS BELOW IN PRODUCTION!
UNSAFE_SECRET="ww+0igxjGRAAR/eTNFQ55VmhQB5KE5trFZseuntThJs="
UNSAFE_PASSWORD="CHANGE_THIS_PASSWORD_IN_PRODUCTION"
# Core Server Configuration
# Service mode - set to 'server' for server-side deployment
NEXT_PUBLIC_SERVICE_MODE=server
# Service Ports Configuration
LOBE_PORT=3010
# Application URL - the base URL where LobeChat will be accessible
APP_URL=http://localhost:${LOBE_PORT}
# Secret key for encrypting vault data (generate with: openssl rand -base64 32)
KEY_VAULTS_SECRET=${UNSAFE_SECRET}
# Database Configuration
# Database name for LobeChat
LOBE_DB_NAME=lobechat
# PostgreSQL password
POSTGRES_PASSWORD=${UNSAFE_PASSWORD}
# PostgreSQL database connection URL
DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@localhost:5432/${LOBE_DB_NAME}
# Database driver type
DATABASE_DRIVER=node
# Authentication Configuration
# Enable NextAuth authentication
NEXT_PUBLIC_ENABLE_NEXT_AUTH=1
# NextAuth secret for JWT signing (generate with: openssl rand -base64 32)
NEXT_AUTH_SECRET=${UNSAFE_SECRET}
NEXTAUTH_URL=${APP_URL}
# Authentication URL
AUTH_URL=${APP_URL}/api/auth
# SSO providers configuration - using Casdoor for development
NEXT_AUTH_SSO_PROVIDERS=casdoor
# Casdoor Configuration
# Casdoor service port
CASDOOR_PORT=8000
# Casdoor OIDC issuer URL
AUTH_CASDOOR_ISSUER=http://localhost:${CASDOOR_PORT}
# Casdoor application client ID
AUTH_CASDOOR_ID=a387a4892ee19b1a2249 # DO NOT USE IN PROD
# Casdoor application client secret
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354 # DO NOT USE IN PROD
# Origin URL for Casdoor internal configuration
origin=http://localhost:${CASDOOR_PORT}
# MinIO Storage Configuration
# MinIO service port
MINIO_PORT=9000
# MinIO root user (admin username)
MINIO_ROOT_USER=admin
# MinIO root password
MINIO_ROOT_PASSWORD=${UNSAFE_PASSWORD}
# MinIO bucket for LobeChat files
MINIO_LOBE_BUCKET=lobe
# S3/MinIO Configuration for LobeChat
# S3/MinIO access key ID
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
# S3/MinIO secret access key
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
# S3/MinIO endpoint URL
S3_ENDPOINT=http://localhost:${MINIO_PORT}
# S3 bucket name for storing files
S3_BUCKET=${MINIO_LOBE_BUCKET}
# Public domain for S3 file access
S3_PUBLIC_DOMAIN=http://localhost:${MINIO_PORT}
# Enable path-style S3 requests (required for MinIO)
S3_ENABLE_PATH_STYLE=1
# Disable S3 ACL setting (for MinIO compatibility)
S3_SET_ACL=0
# Use base64 encoding for LLM vision images
LLM_VISION_IMAGE_USE_BASE64=1
# Search Service Configuration
# SearXNG search engine URL
SEARXNG_URL=http://searxng:8080
# Development Options
# Uncomment to skip authentication during development
# Proxy Configuration (Optional)
# Uncomment if you need proxy support (e.g., for GitHub auth or API access)
# HTTP_PROXY=http://localhost:7890
# HTTPS_PROXY=http://localhost:7890
# AI Model Configuration (Optional)
# Add your AI model API keys and configurations here
# ⚠️ WARNING: Never commit real API keys to version control!
# OPENAI_API_KEY=sk-NEVER_USE_REAL_API_KEYS_IN_CONFIG_FILES
# OPENAI_PROXY_URL=https://api.openai.com/v1
# OPENAI_MODEL_LIST=...
-17
View File
@@ -29,20 +29,3 @@ logs
# misc
# add other ignore file below
.next
# temporary directories
tmp
temp
.temp
.local
docs/.local
# cache directories
.cache
# AI coding tools directories
.claude
.serena
# MCP tools
/.serena/**
-1
View File
@@ -8,7 +8,6 @@
- [ ] 💄 style
- [ ] 👷 build
- [ ] ⚡️ perf
- [ ] ✅ test
- [ ] 📝 docs
- [ ] 🔨 chore
-89
View File
@@ -1,89 +0,0 @@
/**
* Generate or update PR comment with Docker build info
*/
module.exports = async ({
github,
context,
dockerMetaJson,
image,
version,
dockerhubUrl,
platforms,
}) => {
const COMMENT_IDENTIFIER = '<!-- DOCKER-BUILD-COMMENT -->';
const parseTags = () => {
try {
if (dockerMetaJson) {
const parsed = JSON.parse(dockerMetaJson);
if (Array.isArray(parsed.tags) && parsed.tags.length > 0) {
return parsed.tags;
}
}
} catch (e) {
// ignore parsing error, fallback below
}
if (image && version) {
return [`${image}:${version}`];
}
return [];
};
const generateCommentBody = () => {
const tags = parseTags();
const buildTime = new Date().toISOString();
// Use the first tag as the main version
const mainTag = tags.length > 0 ? tags[0] : `${image}:${version}`;
const tagVersion = mainTag.includes(':') ? mainTag.split(':')[1] : version;
return [
COMMENT_IDENTIFIER,
'',
'### 🐳 Database Docker Build Completed!',
`**Version**: \`${tagVersion || 'N/A'}\``,
`**Build Time**: \`${buildTime}\``,
'',
dockerhubUrl ? `🔗 View all tags on Docker Hub: ${dockerhubUrl}` : '',
'',
'### Pull Image',
'Download the Docker image to your local machine:',
'',
'```bash',
`docker pull ${mainTag}`,
'```',
'> [!IMPORTANT]',
'> This build is for testing and validation purposes.',
]
.filter(Boolean)
.join('\n');
};
const body = generateCommentBody();
// List comments on the PR
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
const existing = comments.find((c) => c.body && c.body.includes(COMMENT_IDENTIFIER));
if (existing) {
await github.rest.issues.updateComment({
comment_id: existing.id,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
return { updated: true, id: existing.id };
}
const result = await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body,
});
return { updated: false, id: result.data.id };
};
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
git config --global user.name "lobehubbot"
git config --global user.email "i@lobehub.com"
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
-64
View File
@@ -1,64 +0,0 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@beta
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
# model: 'claude-opus-4-1-20250805'
allowed_bots: 'bot'
# Optional: Customize the trigger phrase (default: @claude)
# trigger_phrase: "/claude"
# Optional: Trigger when specific user is assigned to an issue
# assignee_trigger: "claude-bot"
# Optional: Allow Claude to run specific commands
allowed_tools: 'Bash(bun run:*),Bash(pnpm run:*),Bash(npm run:*),Bash(npx:*),Bash(bunx:*),Bash(vitest:*),Bash(rg:*),Bash(find:*),Bash(sed:*),Bash(grep:*),Bash(awk:*),Bash(wc:*),Bash(xargs:*)'
# Optional: Add custom instructions for Claude to customize its behavior for your project
# custom_instructions: |
# Follow our coding standards
# Ensure all new code has tests
# Use TypeScript for new files
# Optional: Custom environment variables for Claude
# claude_env: |
# NODE_ENV: test
+6 -14
View File
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
steps:
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -35,7 +35,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
version: 9
- name: Install deps
run: pnpm install
@@ -56,7 +56,7 @@ jobs:
# 输出版本信息,供后续 job 使用
version: ${{ steps.set_version.outputs.version }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -95,7 +95,7 @@ jobs:
matrix:
os: [macos-latest, windows-2025, ubuntu-latest]
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -107,7 +107,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
version: 9
# node-linker=hoisted 模式将可以确保 asar 压缩可用
- name: Install deps
@@ -183,10 +183,6 @@ jobs:
apps/desktop/release/*.zip*
apps/desktop/release/*.exe*
apps/desktop/release/*.AppImage
apps/desktop/release/*.deb*
apps/desktop/release/*.snap*
apps/desktop/release/*.rpm*
apps/desktop/release/*.tar.gz*
retention-days: 5
publish-pr:
@@ -200,7 +196,7 @@ jobs:
outputs:
artifact_path: ${{ steps.set_path.outputs.path }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -249,10 +245,6 @@ jobs:
release/*.zip*
release/*.exe*
release/*.AppImage
release/*.deb*
release/*.snap*
release/*.rpm*
release/*.tar.gz*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+11 -29
View File
@@ -12,7 +12,8 @@ concurrency:
cancel-in-progress: true
env:
REGISTRY_IMAGE: lobehub/lobe-chat-database
REGISTRY_URL: jaworldwide.azurecr.io
REGISTRY_IMAGE: jaworldwide.azurecr.io/oneja/ai/bot-database
PR_TAG_PREFIX: pr-
jobs:
@@ -39,7 +40,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -70,8 +71,9 @@ jobs:
- name: Docker login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
registry: ${{ env.REGISTRY_URL }}
username: ${{ secrets.CONTAINER_REGISTRY_USER }}
password: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
- name: Get commit SHA
if: github.ref == 'refs/heads/main'
@@ -80,7 +82,7 @@ jobs:
- name: Build and export
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v5
with:
platforms: ${{ matrix.platform }}
context: .
@@ -111,7 +113,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -147,8 +149,9 @@ jobs:
- name: Docker login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
registry: ${{ env.REGISTRY_URL }}
username: ${{ secrets.CONTAINER_REGISTRY_USER }}
password: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
- name: Create manifest list and push
working-directory: /tmp/digests
@@ -159,24 +162,3 @@ jobs:
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
- name: Comment on PR with Docker build info
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prComment = require('${{ github.workspace }}/.github/scripts/docker-pr-comment.js');
const result = await prComment({
github,
context,
dockerMetaJson: ${{ toJSON(steps.meta.outputs.json) }},
image: "${{ env.REGISTRY_IMAGE }}",
version: "${{ steps.meta.outputs.version }}",
dockerhubUrl: "https://hub.docker.com/r/${{ env.REGISTRY_IMAGE }}/tags",
platforms: "linux/amd64, linux/arm64",
});
core.info(`Status: ${result.updated ? 'Updated' : 'Created'}, ID: ${result.id}`);
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -80,7 +80,7 @@ jobs:
- name: Build and export
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v5
with:
platforms: ${{ matrix.platform }}
context: .
@@ -111,7 +111,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -80,7 +80,7 @@ jobs:
- name: Build and export
id: build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v5
with:
platforms: ${{ matrix.platform }}
context: .
@@ -111,7 +111,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
+3 -3
View File
@@ -2,7 +2,7 @@ name: Lighthouse Badger
env:
TOKEN_NAME: 'GH_TOKEN'
REPO_BRANCH: 'lobehub/lobe-chat lighthouse'
REPO_BRANCH: 'jaworldwideorg/OneJA-Bot lighthouse'
USER_NAME: 'lobehubbot'
USER_EMAIL: 'i@lobehub.com'
AUDIT_TYPE: 'both'
@@ -42,12 +42,12 @@ jobs:
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
env:
REPO_BRANCH: ${{ matrix.REPO_BRANCH || env.REPO_BRANCH }}
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
repository: ${{ env.REPOSITORY }}
token: ${{ secrets[matrix.TOKEN_NAME] || secrets[env.TOKEN_NAME] }}
ref: ${{ env.BRANCH }}
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
repository: 'myactionway/lighthouse-badges'
path: temp_lighthouse_badges_nested
+5 -13
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
steps:
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -31,7 +31,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
version: 9
- name: Install deps
run: pnpm install
@@ -47,7 +47,7 @@ jobs:
version: ${{ steps.set_version.outputs.version }}
is_pr_build: ${{ steps.set_version.outputs.is_pr_build }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -82,7 +82,7 @@ jobs:
matrix:
os: [macos-latest, windows-2025, ubuntu-latest]
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0
@@ -94,7 +94,7 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 10
version: 9
# node-linker=hoisted 模式将可以确保 asar 压缩可用
- name: Install deps
@@ -165,10 +165,6 @@ jobs:
apps/desktop/release/*.zip*
apps/desktop/release/*.exe*
apps/desktop/release/*.AppImage
apps/desktop/release/*.deb*
apps/desktop/release/*.snap*
apps/desktop/release/*.rpm*
apps/desktop/release/*.tar.gz*
retention-days: 5
# 正式版发布 job
@@ -205,9 +201,5 @@ jobs:
release/*.zip*
release/*.exe*
release/*.AppImage
release/*.deb*
release/*.snap*
release/*.rpm*
release/*.tar.gz*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+13 -10
View File
@@ -1,8 +1,15 @@
name: Release CI
permissions:
contents: write
issues: write
actions: write
on:
push:
branches:
- main
workflow_dispatch:
jobs:
release:
@@ -11,20 +18,16 @@ jobs:
services:
postgres:
image: paradedb/paradedb:latest
image: pgvector/pgvector:pg16
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v5
with:
token: ${{ secrets.GH_TOKEN }}
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -42,8 +45,8 @@ jobs:
- name: Lint
run: bun run lint
- name: Test Database Coverage
run: bun run --filter @lobechat/database test
- name: Test Server Coverage
run: bun run test-server:coverage
env:
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
DATABASE_DRIVER: node
@@ -52,8 +55,8 @@ jobs:
S3_PUBLIC_DOMAIN: https://example.com
APP_URL: https://home.com
- name: Test App
run: bun run test-app
- name: Test App Coverage
run: bun run test-app:coverage
- name: Release
run: bun run release
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Install dbdocs
run: sudo npm install -g dbdocs
+10 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: ${{ github.event.repository.fork }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Clean issue notice
uses: actions-cool/issues-helper@v3
@@ -52,3 +52,12 @@ jobs:
[lobechat]: https://github.com/lobehub/lobe-chat
[tutorial-zh-CN]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync.zh-CN
[tutorial-en-US]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync
- name: Trigger Release Workflow
if: success() && steps.sync.outputs.has_new_commits == 'true'
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/${{ github.repository }}/actions/workflows/release.yml/dispatches \
-d '{"ref":"main"}'
+17 -121
View File
@@ -2,115 +2,8 @@ name: Test CI
on: [push, pull_request]
permissions:
contents: read
jobs:
# Package tests - using each package's own test script
test-intenral-packages:
runs-on: ubuntu-latest
strategy:
matrix:
package: [file-loaders, prompts, model-runtime, web-crawler, electron-server-ipc, utils]
name: Test package ${{ matrix.package }}
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install bun
uses: oven-sh/setup-bun@v1
with:
bun-version: ${{ secrets.BUN_VERSION }}
- name: Install deps
run: bun i
- name: Test ${{ matrix.package }} package with coverage
run: bun run --filter @lobechat/${{ matrix.package }} test:coverage
- name: Upload ${{ matrix.package }} coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/${{ matrix.package }}/coverage/lcov.info
flags: packages/${{ matrix.package }}
test-packages:
runs-on: ubuntu-latest
strategy:
matrix:
package: [model-bank]
name: Test package ${{ matrix.package }}
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install bun
uses: oven-sh/setup-bun@v1
with:
bun-version: ${{ secrets.BUN_VERSION }}
- name: Install deps
run: bun i
- name: Test ${{ matrix.package }} package with coverage
run: bun run --filter ${{ matrix.package }} test:coverage
- name: Upload ${{ matrix.package }} coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/${{ matrix.package }}/coverage/lcov.info
flags: packages/${{ matrix.package }}
# App tests
test-website:
name: Test Website
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install bun
uses: oven-sh/setup-bun@v1
with:
bun-version: ${{ secrets.BUN_VERSION }}
- name: Install deps
run: bun i
- name: Test App Coverage
run: bun run test-app:coverage
- name: Upload App Coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/app/lcov.info
flags: app
test-databsae:
name: Test Database
test:
runs-on: ubuntu-latest
services:
@@ -126,7 +19,7 @@ jobs:
- 5432:5432
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -144,15 +37,8 @@ jobs:
- name: Lint
run: bun run lint
- name: Test Client DB
run: bun run --filter @lobechat/database test:client-db
env:
KEY_VAULTS_SECRET: LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=
S3_PUBLIC_DOMAIN: https://example.com
APP_URL: https://home.com
- name: Test Coverage
run: bun run --filter @lobechat/database test:coverage
- name: Test Server Coverage
run: bun run test-server:coverage
env:
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
DATABASE_DRIVER: node
@@ -161,9 +47,19 @@ jobs:
S3_PUBLIC_DOMAIN: https://example.com
APP_URL: https://home.com
- name: Upload Database coverage to Codecov
- name: Upload Server coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/database/coverage/lcov.info
flags: database
files: ./coverage/server/lcov.info
flags: server
- name: Test App Coverage
run: bun run test-app:coverage
- name: Upload App Coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/app/lcov.info
flags: app
+19
View File
@@ -0,0 +1,19 @@
name: Wiki Sync
on:
workflow_dispatch:
push:
paths:
- 'docs/wiki/**'
branches:
- main
jobs:
update-wiki:
runs-on: ubuntu-latest
name: Wiki sync
steps:
- uses: OrlovM/Wiki-Action@v1
with:
path: 'docs/wiki'
token: ${{ secrets.GH_TOKEN }}
+58 -94
View File
@@ -1,114 +1,78 @@
# Gitignore for LobeHub
################################################################
# System files
# general
.DS_Store
Thumbs.db
ehthumbs.db
Desktop.ini
# Linux/Ubuntu system files
*~
*.swp
*.swo
.fuse_hidden*
.directory
.Trash-*
.nfs*
.gvfs-fuse-daemon-*
# IDE and editors
.idea/
*.sublime-*
.history/
.windsurfrules
*.code-workspace
# Temporary files
.temp/
temp/
tmp/
*.tmp
*.temp
*.log
*.cache
.cache/
# Environment files
.env
.idea
.vscode
.history
.temp
.env.local
.env*.local
venv/
.venv/
venv
temp
tmp
.windsurfrules
# Dependencies
node_modules/
# dependencies
node_modules
*.log
*.lock
package-lock.json
bun.lockb
.pnpm-store/
# Build outputs
dist/
es/
lib/
.next/
logs/
test-output/
*.tsbuildinfo
next-env.d.ts
# Framework specific
# Umi
.umi/
.umi-production/
.umi-test/
.dumi/tmp*/
# Vercel
.vercel/
# Testing and CI
coverage/
.coverage/
.nyc_output/
# ci
coverage
.coverage
.eslintcache
.stylelintcache
# Service Worker
# production
dist
es
lib
logs
test-output
# umi
.umi
.umi-production
.umi-test
.dumi/tmp*
# husky
.husky/prepare-commit-msg
# misc
# add other ignore file below
CLAUDE.md
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
.next
.env
public/*.js
public/sitemap.xml
public/sitemap-index.xml
bun.lockb
sitemap*.xml
robots.txt
# Serwist
public/sw*
public/swe-worker*
# Generated files
public/*.js
public/sitemap.xml
public/sitemap-index.xml
sitemap*.xml
robots.txt
# Git hooks
.husky/prepare-commit-msg
# Documents and media
*.patch
*.pdf
# Cloud service keys
vertex-ai-key.json
# AI coding tools
.local/
.claude/
.mcp.json
CLAUDE.local.md
# MCP tools
.serena/**
# Misc
.pnpm-store
./packages/lobe-ui
*.ppt*
*.doc*
*.xls*
# for local prd docs
docs/prd
+5 -2
View File
@@ -1,2 +1,5 @@
npm run type-check
npx --no-install lint-staged
# .husky/post-merge
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run type-check || echo "Type check failed, please fix issues!"
-3
View File
@@ -4,9 +4,6 @@ resolution-mode=highest
ignore-workspace-root-check=true
enable-pre-post-scripts=true
# Load dotenv files for all the npm scripts
node-options="--require dotenv-expand/config"
public-hoist-pattern[]=*@umijs/lint*
public-hoist-pattern[]=*changelog*
public-hoist-pattern[]=*commitlint*
+1
View File
@@ -5,6 +5,7 @@
.DS_Store
.editorconfig
.idea
.vscode
.history
.temp
.env.local
+33
View File
@@ -1,5 +1,10 @@
const config = require('@lobehub/lint').semanticRelease;
// Remove NPM publishing by excluding "@semantic-release/npm" plugin
// Keep or add other plugins like GitHub Releases
config.plugins = config.plugins.filter((plugin) => plugin !== '@semantic-release/npm');
// Add GitHub only if required
config.plugins.push([
'@semantic-release/exec',
{
@@ -7,4 +12,32 @@ config.plugins.push([
},
]);
// Override GitHub repository URL without modifying package.json
// Make sure @semantic-release/github is present in the plugins
if (!config.plugins.some(plugin => Array.isArray(plugin) ? plugin[0] === '@semantic-release/github' : plugin === '@semantic-release/github')) {
config.plugins.push([
'@semantic-release/github',
{
repositoryUrl: 'https://github.com/jaworldwideorg/OneJA-Bot.git'
}
]);
} else {
// Find and update the existing GitHub plugin configuration
config.plugins = config.plugins.map(plugin => {
if (Array.isArray(plugin) && plugin[0] === '@semantic-release/github') {
return [
'@semantic-release/github',
{
...(plugin[1] || {}),
repositoryUrl: 'https://github.com/jaworldwideorg/OneJA-Bot.git'
}
];
}
return plugin;
});
}
// Set repository URL in global config
config.repositoryUrl = 'https://github.com/jaworldwideorg/OneJA-Bot.git';
module.exports = config;
-39
View File
@@ -1,39 +0,0 @@
# Stylelintignore for LobeHub
################################################################
# dependencies
node_modules
# ci
coverage
.coverage
# production
dist
es
lib
logs
# framework specific
.next
.umi
.umi-production
.umi-test
.dumi/tmp*
# temporary directories
tmp
temp
.temp
.local
docs/.local
# cache directories
.cache
# AI coding tools directories
.claude
.serena
# MCP tools
/.serena/**
-13
View File
@@ -1,13 +0,0 @@
{
"recommendations": [
"Anthropic.claude-code",
"dbaeumer.vscode-eslint",
"jrr997.antd-docs",
"seatonjiang.gitmoji-vscode",
"styled-components.vscode-styled-components",
"stylelint.vscode-stylelint",
"unifiedjs.vscode-mdx",
"unifiedjs.vscode-remark",
"vitest.explorer",
]
}
-93
View File
@@ -1,93 +0,0 @@
{
"editor.codeActionsOnSave": {
"source.addMissingImports": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
"editor.formatOnSave": true,
// don't show errors, but fix when save and git pre commit
"eslint.rules.customizations": [
{ "rule": "import/order", "severity": "off" },
{ "rule": "prettier/prettier", "severity": "off" },
{ "rule": "react/jsx-sort-props", "severity": "off" },
{ "rule": "sort-keys-fix/sort-keys-fix", "severity": "off" },
{ "rule": "typescript-sort-keys/interface", "severity": "off" }
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
// support mdx
"mdx"
],
"npm.packageManager": "pnpm",
"search.exclude": {
"**/node_modules": true,
// useless to search this big folder
"locales": true
},
"stylelint.validate": [
"css",
"postcss",
// make stylelint work with tsx antd-style css template string
"typescriptreact"
],
"vitest.maximumConfigs": 20,
"workbench.editor.customLabels.patterns": {
"**/app/**/[[]*[]]/[[]*[]]/page.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • page component",
"**/app/**/[[]*[]]/page.tsx": "${dirname(1)}/${dirname} • page component",
"**/app/**/page.tsx": "${dirname} • page component",
"**/app/**/[[]*[]]/[[]*[]]/layout.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • page layout",
"**/app/**/[[]*[]]/layout.tsx": "${dirname(1)}/${dirname} • page layout",
"**/app/**/layout.tsx": "${dirname} • page layout",
"**/app/**/[[]*[]]/[[]*[]]/default.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • slot default",
"**/app/**/[[]*[]]/default.tsx": "${dirname(1)}/${dirname} • slot default",
"**/app/**/default.tsx": "${dirname} • slot default",
"**/app/**/[[]*[]]/[[]*[]]/error.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • error component",
"**/app/**/[[]*[]]/error.tsx": "${dirname(1)}/${dirname} • error component",
"**/app/**/error.tsx": "${dirname} • error component",
"**/app/**/[[]*[]]/[[]*[]]/loading.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • loading component",
"**/app/**/[[]*[]]/loading.tsx": "${dirname(1)}/${dirname} • loading component",
"**/app/**/loading.tsx": "${dirname} • loading component",
"**/src/**/route.ts": "${dirname(1)}/${dirname} • route",
"**/src/**/index.tsx": "${dirname} • component",
"**/packages/database/src/repositories/*/index.ts": "${dirname} • db repository",
"**/packages/database/src/models/*.ts": "${filename} • db model",
"**/packages/database/src/schemas/*.ts": "${filename} • db schema",
"**/src/services/*.ts": "${filename} • service",
"**/src/services/*/client.ts": "${dirname} • client service",
"**/src/services/*/server.ts": "${dirname} • server service",
"**/src/store/*/action.ts": "${dirname} • action",
"**/src/store/*/slices/*/action.ts": "${dirname(2)}/${dirname} • action",
"**/src/store/*/slices/*/actions/*.ts": "${dirname(1)}/${dirname}/${filename} • action",
"**/src/store/*/initialState.ts": "${dirname} • state",
"**/src/store/*/slices/*/initialState.ts": "${dirname(2)}/${dirname} • state",
"**/src/store/*/selectors.ts": "${dirname} • selectors",
"**/src/store/*/slices/*/selectors.ts": "${dirname(2)}/${dirname} • selectors",
"**/src/store/*/reducer.ts": "${dirname} • reducer",
"**/src/store/*/slices/*/reducer.ts": "${dirname(2)}/${dirname} • reducer",
"**/src/config/modelProviders/*.ts": "${filename} • provider",
"**/packages/model-bank/src/aiModels/*.ts": "${filename} • model",
"**/packages/model-runtime/src/*/index.ts": "${dirname} • runtime",
"**/src/server/services/*/index.ts": "${dirname} • server/service",
"**/src/server/routers/lambda/*.ts": "${filename} • lambda",
"**/src/server/routers/async/*.ts": "${filename} • async",
"**/src/server/routers/edge/*.ts": "${filename} • edge",
"**/src/locales/default/*.ts": "${filename} • locale"
}
}
-133
View File
@@ -1,133 +0,0 @@
# LobeChat Development Guidelines
This document serves as a comprehensive guide for all team members when developing LobeChat.
## Tech Stack
Built with modern technologies:
- **Frontend**: Next.js 15, React 19, TypeScript
- **UI Components**: Ant Design, @lobehub/ui, antd-style
- **State Management**: Zustand, SWR
- **Database**: PostgreSQL, PGLite, Drizzle ORM
- **Testing**: Vitest, Testing Library
- **Package Manager**: pnpm (monorepo structure)
- **Build Tools**: Next.js (Turbopack in dev, Webpack in prod), Vitest
## Directory Structure
The project follows a well-organized monorepo structure:
- `apps/` - Main applications
- `packages/` - Shared packages and libraries
- `src/` - Main source code
- `docs/` - Documentation
- `.cursor/rules/` - Development rules and guidelines
## Development Workflow
### Git Workflow
- Use rebase for git pull: `git pull --rebase`
- Git commit messages should prefix with gitmoji
- Git branch name format: `username/feat/feature-name`
- Use `.github/PULL_REQUEST_TEMPLATE.md` for PR descriptions
### Package Management
- Use `pnpm` as the primary package manager
- Use `bun` to run npm scripts
- Use `bunx` to run executable npm packages
- Navigate to specific packages using `cd packages/<package-name>`
### Code Style Guidelines
#### TypeScript
- Follow strict TypeScript practices for type safety and code quality
- Use proper type annotations
- Prefer interfaces over types for object shapes
- Use generics for reusable components
#### React Components
- Use functional components with hooks
- Follow the component structure guidelines
- Use antd-style & @lobehub/ui for styling
- Implement proper error boundaries
#### Database Schema
- Follow Drizzle ORM naming conventions
- Use plural snake_case for table names
- Implement proper foreign key relationships
- Follow the schema style guide
### Testing Strategy
**Required Rule**: `testing-guide/testing-guide.mdc`
**Commands**:
- Web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'`
- Packages: `cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path-pattern]'`
**Important Notes**:
- Wrap file paths in single quotes to avoid shell expansion
- Never run `bun run test` - this runs all tests and takes ~10 minutes
- If a test fails twice, stop and ask for help
- Always add tests for new code
### Type Checking
- Use `bun run type-check` to check for type errors
- Ensure all TypeScript errors are resolved before committing
### Internationalization
- Add new keys to `src/locales/default/namespace.ts`
- Translate at least `zh-CN` files for development preview
- Use hierarchical nested objects, not flat keys
- Don't run `pnpm i18n` manually (handled by CI)
## Available Development Rules
The project provides comprehensive rules in `.cursor/rules/` directory:
### Core Development
- `backend-architecture.mdc` - Three-layer architecture and data flow
- `react-component.mdc` - Component patterns and UI library usage
- `drizzle-schema-style-guide.mdc` - Database schema conventions
- `define-database-model.mdc` - Model templates and CRUD patterns
- `i18n.mdc` - Internationalization workflow
### State Management & UI
- `zustand-slice-organization.mdc` - Store organization patterns
- `zustand-action-patterns.mdc` - Action implementation patterns
- `packages/react-layout-kit.mdc` - Flex layout component usage
### Testing & Quality
- `testing-guide/testing-guide.mdc` - Comprehensive testing strategy
- `code-review.mdc` - Code review process and standards
### Desktop (Electron)
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
- `desktop-local-tools-implement.mdc` - Tool integration workflow
- `desktop-menu-configuration.mdc` - Menu system configuration
- `desktop-window-management.mdc` - Window management patterns
- `desktop-controller-tests.mdc` - Controller testing guide
## Best Practices
- **Conservative for existing code, modern approaches for new features**
- **Code Language**: Use Chinese for files with existing Chinese comments, American English for new files
- Always add tests for new functionality
- Follow the established patterns in the codebase
- Use proper error handling and logging
- Implement proper accessibility features
- Consider internationalization from the start
+295 -3390
View File
File diff suppressed because it is too large Load Diff
-100
View File
@@ -1,100 +0,0 @@
# CLAUDE.md
This document serves as a shared guideline for all team members when using Claude Code in this repository.
## Tech Stack
read @.cursor/rules/project-introduce.mdc
## Directory Structure
read @.cursor/rules/project-structure.mdc
## Development
### Git Workflow
- use rebase for git pull
- git commit message should prefix with gitmoji
- git branch name format example: tj/feat/feature-name
- use .github/PULL_REQUEST_TEMPLATE.md to generate pull request description
### Package Management
This repository adopts a monorepo structure.
- Use `pnpm` as the primary package manager for dependency management
- Use `bun` to run npm scripts
- Use `bunx` to run executable npm packages
### TypeScript Code Style Guide
see @.cursor/rules/typescript.mdc
### Modify Code Rules
- **Code Language**:
- For files with existing Chinese comments: Continue using Chinese to maintain consistency
- For new files or files without Chinese comments: MUST use American English.
- eg: new react tsx file and new test file
- Conservative for existing code, modern approaches for new features
### Testing
Testing work follows the Rule-Aware Task Execution system above.
- **Required Rule**: `testing-guide/testing-guide.mdc`
- **Command**:
- web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'`
- packages(eg: database): `cd packages/database && bunx vitest run --silent='passed-only' '[file-path-pattern]'`
**Important**:
- wrapped the file path in single quotes to avoid shell expansion
- Never run `bun run test` etc to run tests, this will run all tests and cost about 10mins
- If try to fix the same test twice, but still failed, stop and ask for help.
### Typecheck
- use `bun run type-check` to check type errors.
### i18n
- **Keys**: Add to `src/locales/default/namespace.ts`
- **Dev**: Translate `locales/zh-CN/namespace.json` locale file only for preview
- DON'T run `pnpm i18n`, let CI auto handle it
## Rules Index
Some useful rules of this project. Read them when needed.
**IMPORTANT**: All rule files referenced in this document are located in the `.cursor/rules/` directory. Throughout this document, rule files are referenced by their filename only for brevity.
### 📋 Complete Rule Files
**Core Development**
- `backend-architecture.mdc` - Three-layer architecture, data flow
- `react-component.mdc` - antd-style, Lobe UI usage
- `drizzle-schema-style-guide.mdc` - Schema naming, patterns
- `define-database-model.mdc` - Model templates, CRUD patterns
- `i18n.mdc` - Internationalization workflow
**State & UI**
- `zustand-slice-organization.mdc` - Store organization
- `zustand-action-patterns.mdc` - Action patterns
- `packages/react-layout-kit.mdc` - flex layout components usage
**Testing & Quality**
- `testing-guide/testing-guide.mdc` - Test strategy, mock patterns
- `code-review.mdc` - Review process and standards
**Desktop (Electron)**
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
- `desktop-local-tools-implement.mdc` - Tool integration workflow
- `desktop-menu-configuration.mdc` - App menu, context menu, tray menu
- `desktop-window-management.mdc` - Window creation, state management, multi-window
- `desktop-controller-tests.mdc` - Controller unit testing guide
+2 -12
View File
@@ -66,7 +66,7 @@ ENV NEXT_PUBLIC_ANALYTICS_UMAMI="${NEXT_PUBLIC_ANALYTICS_UMAMI}" \
NEXT_PUBLIC_UMAMI_WEBSITE_ID="${NEXT_PUBLIC_UMAMI_WEBSITE_ID}"
# Node
ENV NODE_OPTIONS="--max-old-space-size=6144"
ENV NODE_OPTIONS="--max-old-space-size=8192"
WORKDIR /app
@@ -150,8 +150,6 @@ ENV \
AI21_API_KEY="" AI21_MODEL_LIST="" \
# Ai360
AI360_API_KEY="" AI360_MODEL_LIST="" \
# AiHubMix
AIHUBMIX_API_KEY="" AIHUBMIX_MODEL_LIST="" \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
@@ -194,8 +192,6 @@ ENV \
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
# Moonshot
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
# Nebius
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
# Novita
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
# Nvidia NIM
@@ -249,13 +245,7 @@ ENV \
# Tencent Cloud
TENCENT_CLOUD_API_KEY="" TENCENT_CLOUD_MODEL_LIST="" \
# Infini-AI
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST="" \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST=""
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST=""
USER nextjs
+3 -13
View File
@@ -74,7 +74,7 @@ ENV NEXT_PUBLIC_ANALYTICS_UMAMI="${NEXT_PUBLIC_ANALYTICS_UMAMI}" \
NEXT_PUBLIC_UMAMI_WEBSITE_ID="${NEXT_PUBLIC_UMAMI_WEBSITE_ID}"
# Node
ENV NODE_OPTIONS="--max-old-space-size=6144"
ENV NODE_OPTIONS="--max-old-space-size=8192"
WORKDIR /app
@@ -120,7 +120,7 @@ COPY --from=base /distroless/ /
COPY --from=builder /app/.next/standalone /app/
# Copy database migrations
COPY --from=builder /app/packages/database/migrations /app/migrations
COPY --from=builder /app/src/database/migrations /app/migrations
COPY --from=builder /app/scripts/migrateServerDB/docker.cjs /app/docker.cjs
COPY --from=builder /app/scripts/migrateServerDB/errorHint.js /app/errorHint.js
@@ -192,8 +192,6 @@ ENV \
AI21_API_KEY="" AI21_MODEL_LIST="" \
# Ai360
AI360_API_KEY="" AI360_MODEL_LIST="" \
# AiHubMix
AIHUBMIX_API_KEY="" AIHUBMIX_MODEL_LIST="" \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
@@ -236,8 +234,6 @@ ENV \
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
# Moonshot
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
# Nebius
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
# Novita
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
# Nvidia NIM
@@ -291,13 +287,7 @@ ENV \
# Tencent Cloud
TENCENT_CLOUD_API_KEY="" TENCENT_CLOUD_MODEL_LIST="" \
# Infini-AI
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST="" \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST=""
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST=""
USER nextjs
+2 -12
View File
@@ -68,7 +68,7 @@ ENV NEXT_PUBLIC_ANALYTICS_UMAMI="${NEXT_PUBLIC_ANALYTICS_UMAMI}" \
NEXT_PUBLIC_UMAMI_WEBSITE_ID="${NEXT_PUBLIC_UMAMI_WEBSITE_ID}"
# Node
ENV NODE_OPTIONS="--max-old-space-size=6144"
ENV NODE_OPTIONS="--max-old-space-size=8192"
WORKDIR /app
@@ -152,8 +152,6 @@ ENV \
AI21_API_KEY="" AI21_MODEL_LIST="" \
# Ai360
AI360_API_KEY="" AI360_MODEL_LIST="" \
# AiHubMix
AIHUBMIX_API_KEY="" AIHUBMIX_MODEL_LIST="" \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
@@ -196,8 +194,6 @@ ENV \
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
# Moonshot
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
# Nebius
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
# Novita
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
# Nvidia NIM
@@ -247,13 +243,7 @@ ENV \
# Tencent Cloud
TENCENT_CLOUD_API_KEY="" TENCENT_CLOUD_MODEL_LIST="" \
# Infini-AI
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST="" \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST=""
INFINIAI_API_KEY="" INFINIAI_MODEL_LIST=""
USER nextjs
+14 -15
View File
@@ -150,7 +150,7 @@ From productivity tools to development environments, discover new ways to extend
**Peak Performance, Zero Distractions**
Get the full LobeChat experience without browser limitations—comprehensive, focused, and always ready to go. Our desktop application provides a dedicated environment for your AI interactions, ensuring optimal performance and minimal distractions.
Get the full LobeChat experience without browser limitations—lightweight, focused, and always ready to go. Our desktop application provides a dedicated environment for your AI interactions, ensuring optimal performance and minimal distractions.
Experience faster response times, better resource management, and a more stable connection to your AI assistant. The desktop app is designed for users who demand the best performance from their AI tools.
@@ -237,7 +237,7 @@ In this way, LobeChat can more flexibly adapt to the needs of different users, w
We have implemented support for the following model service providers:
<!-- PROVIDER LIST -->
<!-- PROVIDER LIST -->
- **[OpenAI](https://lobechat.com/discover/provider/openai)**: OpenAI is a global leader in artificial intelligence research, with models like the GPT series pushing the frontiers of natural language processing. OpenAI is committed to transforming multiple industries through innovative and efficient AI solutions. Their products demonstrate significant performance and cost-effectiveness, widely used in research, business, and innovative applications.
- **[Ollama](https://lobechat.com/discover/provider/ollama)**: Ollama provides models that cover a wide range of fields, including code generation, mathematical operations, multilingual processing, and conversational interaction, catering to diverse enterprise-level and localized deployment needs.
@@ -245,17 +245,15 @@ We have implemented support for the following model service providers:
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock is a service provided by Amazon AWS, focusing on delivering advanced AI language and visual models for enterprises. Its model family includes Anthropic's Claude series, Meta's Llama 3.1 series, and more, offering a range of options from lightweight to high-performance, supporting tasks such as text generation, conversation, and image processing for businesses of varying scales and needs.
- **[Google](https://lobechat.com/discover/provider/google)**: Google's Gemini series represents its most advanced, versatile AI models, developed by Google DeepMind, designed for multimodal capabilities, supporting seamless understanding and processing of text, code, images, audio, and video. Suitable for various environments from data centers to mobile devices, it significantly enhances the efficiency and applicability of AI models.
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek is a company focused on AI technology research and application, with its latest model DeepSeek-V2.5 integrating general dialogue and code processing capabilities, achieving significant improvements in human preference alignment, writing tasks, and instruction following.
- **[Moonshot](https://lobechat.com/discover/provider/moonshot)**: Moonshot is an open-source platform launched by Beijing Dark Side Technology Co., Ltd., providing various natural language processing models with a wide range of applications, including but not limited to content creation, academic research, intelligent recommendations, and medical diagnosis, supporting long text processing and complex generation tasks.
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter is a service platform providing access to various cutting-edge large model interfaces, supporting OpenAI, Anthropic, LLaMA, and more, suitable for diverse development and application needs. Users can flexibly choose the optimal model and pricing based on their requirements, enhancing the AI experience.
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: The HuggingFace Inference API provides a fast and free way for you to explore thousands of models for various tasks. Whether you are prototyping for a new application or experimenting with the capabilities of machine learning, this API gives you instant access to high-performance models across multiple domains.
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter is a service platform providing access to various cutting-edge large model interfaces, supporting OpenAI, Anthropic, LLaMA, and more, suitable for diverse development and application needs. Users can flexibly choose the optimal model and pricing based on their requirements, enhancing the AI experience.
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: Run serverless GPU-powered machine learning models on Cloudflare's global network.
<details><summary><kbd>See more providers (+32)</kbd></summary>
- **[GitHub](https://lobechat.com/discover/provider/github)**: With GitHub Models, developers can become AI engineers and leverage the industry's leading AI models.
<details><summary><kbd>See more providers (+31)</kbd></summary>
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI is a platform providing a variety of large language models and AI image generation API services, flexible, reliable, and cost-effective. It supports the latest open-source models like Llama3 and Mistral, offering a comprehensive, user-friendly, and auto-scaling API solution for generative AI application development, suitable for the rapid growth of AI startups.
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc.
- **[302.AI](https://lobechat.com/discover/provider/ai302)**: 302.AI is an on-demand AI application platform offering the most comprehensive AI APIs and online AI applications available on the market.
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI is dedicated to achieving leading performance through innovative AI models, offering extensive customization capabilities, including rapid scaling support and intuitive deployment processes to meet various enterprise needs.
- **[Fireworks AI](https://lobechat.com/discover/provider/fireworksai)**: Fireworks AI is a leading provider of advanced language model services, focusing on functional calling and multimodal processing. Its latest model, Firefunction V2, is based on Llama-3, optimized for function calling, conversation, and instruction following. The visual language model FireLLaVA-13B supports mixed input of images and text. Other notable models include the Llama series and Mixtral series, providing efficient multilingual instruction following and generation support.
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq's LPU inference engine has excelled in the latest independent large language model (LLM) benchmarks, redefining the standards for AI solutions with its remarkable speed and efficiency. Groq represents instant inference speed, demonstrating strong performance in cloud-based deployments.
@@ -274,12 +272,13 @@ We have implemented support for the following model service providers:
- **[Spark](https://lobechat.com/discover/provider/spark)**: iFlytek's Spark model provides powerful AI capabilities across multiple domains and languages, utilizing advanced natural language processing technology to build innovative applications suitable for smart hardware, smart healthcare, smart finance, and other vertical scenarios.
- **[SenseNova](https://lobechat.com/discover/provider/sensenova)**: SenseNova, backed by SenseTime's robust infrastructure, offers efficient and user-friendly full-stack large model services.
- **[Stepfun](https://lobechat.com/discover/provider/stepfun)**: StepFun's large model possesses industry-leading multimodal and complex reasoning capabilities, supporting ultra-long text understanding and powerful autonomous scheduling search engine functions.
- **[Moonshot](https://lobechat.com/discover/provider/moonshot)**: Moonshot is an open-source platform launched by Beijing Dark Side Technology Co., Ltd., providing various natural language processing models with a wide range of applications, including but not limited to content creation, academic research, intelligent recommendations, and medical diagnosis, supporting long text processing and complex generation tasks.
- **[Baichuan](https://lobechat.com/discover/provider/baichuan)**: Baichuan Intelligence is a company focused on the research and development of large AI models, with its models excelling in domestic knowledge encyclopedias, long text processing, and generative creation tasks in Chinese, surpassing mainstream foreign models. Baichuan Intelligence also possesses industry-leading multimodal capabilities, performing excellently in multiple authoritative evaluations. Its models include Baichuan 4, Baichuan 3 Turbo, and Baichuan 3 Turbo 128k, each optimized for different application scenarios, providing cost-effective solutions.
- **[Minimax](https://lobechat.com/discover/provider/minimax)**: MiniMax is a general artificial intelligence technology company established in 2021, dedicated to co-creating intelligence with users. MiniMax has independently developed general large models of different modalities, including trillion-parameter MoE text models, voice models, and image models, and has launched applications such as Conch AI.
- **[InternLM](https://lobechat.com/discover/provider/internlm)**: An open-source organization dedicated to the research and development of large model toolchains. It provides an efficient and user-friendly open-source platform for all AI developers, making cutting-edge large models and algorithm technologies easily accessible.
- **[Higress](https://lobechat.com/discover/provider/higress)**: Higress is a cloud-native API gateway that was developed internally at Alibaba to address the issues of Tengine reload affecting long-lived connections and the insufficient load balancing capabilities for gRPC/Dubbo.
- **[Gitee AI](https://lobechat.com/discover/provider/giteeai)**: Gitee AI's Serverless API provides AI developers with an out of the box large model inference API service.
- **[Taichu](https://lobechat.com/discover/provider/taichu)**: The Institute of Automation, Chinese Academy of Sciences, and Wuhan Artificial Intelligence Research Institute have launched a new generation of multimodal large models, supporting comprehensive question-answering tasks such as multi-turn Q\&A, text creation, image generation, 3D understanding, and signal analysis, with stronger cognitive, understanding, and creative abilities, providing a new interactive experience.
- **[Taichu](https://lobechat.com/discover/provider/taichu)**: The Institute of Automation, Chinese Academy of Sciences, and Wuhan Artificial Intelligence Research Institute have launched a new generation of multimodal large models, supporting comprehensive question-answering tasks such as multi-turn Q&A, text creation, image generation, 3D understanding, and signal analysis, with stronger cognitive, understanding, and creative abilities, providing a new interactive experience.
- **[360 AI](https://lobechat.com/discover/provider/ai360)**: 360 AI is an AI model and service platform launched by 360 Company, offering various advanced natural language processing models, including 360GPT2 Pro, 360GPT Pro, 360GPT Turbo, and 360GPT Turbo Responsibility 8K. These models combine large-scale parameters and multimodal capabilities, widely applied in text generation, semantic understanding, dialogue systems, and code generation. With flexible pricing strategies, 360 AI meets diverse user needs, supports developer integration, and promotes the innovation and development of intelligent applications.
- **[Search1API](https://lobechat.com/discover/provider/search1api)**: Search1API provides access to the DeepSeek series of models that can connect to the internet as needed, including standard and fast versions, supporting a variety of model sizes.
- **[InfiniAI](https://lobechat.com/discover/provider/infiniai)**: Provides high-performance, easy-to-use, and secure large model services for application developers, covering the entire process from large model development to service deployment.
@@ -287,7 +286,7 @@ We have implemented support for the following model service providers:
</details>
> 📊 Total providers: [<kbd>**42**</kbd>](https://lobechat.com/discover/providers)
> 📊 Total providers: [<kbd>**41**</kbd>](https://lobechat.com/discover/providers)
<!-- PROVIDER LIST -->
@@ -380,16 +379,16 @@ In addition, these plugins are not limited to news aggregation, but can also ext
>
> Learn more about [📘 Plugin Usage][docs-usage-plugin] by checking it out.
<!-- PLUGIN LIST -->
<!-- PLUGIN LIST -->
| Recent Submits | Description |
| ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-07-21**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
| [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2025-07-18**</sup> | Learn how to say anything in another language with Speak, your AI-powered language tutor.<br/>`education` `language` |
| [Web](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | Smart web search that reads and analyzes pages to deliver comprehensive answers from Google results.<br/>`web` `search` |
| [Bing_websearch](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | Search for information from the internet base BingApi<br/>`bingsearch` |
| [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
> 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
<!-- PLUGIN LIST -->
@@ -419,7 +418,7 @@ Our marketplace is not just a showcase platform but also a collaborative space.
> We welcome all users to join this growing ecosystem and participate in the iteration and optimization of agents.
> Together, we can create more interesting, practical, and innovative agents, further enriching the diversity and practicality of the agent offerings.
<!-- AGENT LIST -->
<!-- AGENT LIST -->
| Recent Submits | Description |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -481,7 +480,7 @@ We deeply understand the importance of providing a seamless experience for users
Therefore, we have adopted Progressive Web Application ([PWA](https://support.google.com/chrome/answer/9658361)) technology,
a modern web technology that elevates web applications to an experience close to that of native apps.
Through PWA, LobeChat can offer a highly optimized user experience on both desktop and mobile devices while maintaining high-performance characteristics.
Through PWA, LobeChat can offer a highly optimized user experience on both desktop and mobile devices while maintaining its lightweight and high-performance characteristics.
Visually and in terms of feel, we have also meticulously designed the interface to ensure it is indistinguishable from native apps,
providing smooth animations, responsive layouts, and adapting to different device screen resolutions.
+29 -30
View File
@@ -237,49 +237,48 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
我们已经实现了对以下模型服务商的支持:
<!-- PROVIDER LIST -->
<!-- PROVIDER LIST -->
- **[OpenAI](https://lobechat.com/discover/provider/openai)**: OpenAI 是全球领先的人工智能研究机构,其开发的模型如 GPT 系列推动了自然语言处理的前沿。OpenAI 致力于通过创新和高效的 AI 解决方案改变多个行业。他们的产品具有显著的性能和经济性,广泛用于研究、商业和创新应用。
- **[OpenAI](https://lobechat.com/discover/provider/openai)**: OpenAI 是全球领先的人工智能研究机构,其开发的模型如GPT系列推动了自然语言处理的前沿。OpenAI 致力于通过创新和高效的AI解决方案改变多个行业。他们的产品具有显著的性能和经济性,广泛用于研究、商业和创新应用。
- **[Ollama](https://lobechat.com/discover/provider/ollama)**: Ollama 提供的模型广泛涵盖代码生成、数学运算、多语种处理和对话互动等领域,支持企业级和本地化部署的多样化需求。
- **[Anthropic](https://lobechat.com/discover/provider/anthropic)**: Anthropic 是一家专注于人工智能研究和开发的公司,提供了一系列先进的语言模型,如 Claude 3.5 Sonnet、Claude 3 Sonnet、Claude 3 Opus 和 Claude 3 Haiku。这些模型在智能、速度和成本之间取得了理想的平衡,适用于从企业级工作负载到快速响应的各种应用场景。Claude 3.5 Sonnet 作为其最新模型,在多项评估中表现优异,同时保持了较高的性价比。
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock 是亚马逊 AWS 提供的一项服务,专注于为企业提供先进的 AI 语言模型和视觉模型。其模型家族包括 Anthropic 的 Claude 系列、Meta 的 Llama 3.1 系列等,涵盖从轻量级到高性能的多种选择,支持文本生成、对话、图像处理等多种任务,适用于不同规模和需求的企业应用。
- **[Google](https://lobechat.com/discover/provider/google)**: Google 的 Gemini 系列是其最先进、通用的 AI 模型,由 Google DeepMind 打造,专为多模态设计,支持文本、代码、图像、音频和视频的无缝理解与处理。适用于从数据中心到移动设备的多种环境,极大提升了 AI 模型的效率与应用广泛性。
- **[Google](https://lobechat.com/discover/provider/google)**: Google 的 Gemini 系列是其最先进、通用的 AI模型,由 Google DeepMind 打造,专为多模态设计,支持文本、代码、图像、音频和视频的无缝理解与处理。适用于从数据中心到移动设备的多种环境,极大提升了AI模型的效率与应用广泛性。
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek 是一家专注于人工智能技术研究和应用的公司,其最新模型 DeepSeek-V3 多项评测成绩超越 Qwen2.5-72B 和 Llama-3.1-405B 等开源模型,性能对齐领军闭源模型 GPT-4o 与 Claude-3.5-Sonnet。
- **[Moonshot](https://lobechat.com/discover/provider/moonshot)**: Moonshot 是由北京月之暗面科技有限公司推出的开源平台,提供多种自然语言处理模型,应用领域广泛,包括但不限于内容创作、学术研究、智能推荐、医疗诊断等,支持长文本处理和复杂生成任务。
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter 是一个提供多种前沿大模型接口的服务平台,支持 OpenAI、Anthropic、LLaMA 及更多,适合多样化的开发和应用需求。用户可根据自身需求灵活选择最优的模型和价格,助力 AI 体验的提升。
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: HuggingFace Inference API 提供了一种快速且免费的方式,让您可以探索成千上万种模型,适用于各种任务。无论您是在为新应用程序进行原型设计,还是在尝试机器学习的功能,这个 API 都能让您即时访问多个领域的高性能模型。
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter 是一个提供多种前沿大模型接口的服务平台,支持 OpenAI、Anthropic、LLaMA 及更多,适合多样化的开发和应用需求。用户可根据自身需求灵活选择最优的模型和价格,助力AI体验的提升。
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: 在 Cloudflare 的全球网络上运行由无服务器 GPU 驱动的机器学习模型。
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过GitHub模型,开发人员可以成为AI工程师,并使用行业领先的AI模型进行构建。
<details><summary><kbd>See more providers (+32)</kbd></summary>
<details><summary><kbd>See more providers (+31)</kbd></summary>
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过 GitHub 模型,开发人员可以成为 AI 工程师,并使用行业领先的 AI 模型进行构建。
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI 是一个提供多种大语言模型与 AI 图像生成的 API 服务的平台,灵活、可靠且具有成本效益。它支持 Llama3、Mistral 等最新的开源模型,并为生成式 AI 应用开发提供了全面、用户友好且自动扩展的 API 解决方案,适合 AI 初创公司的快速发展。
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO 派欧云提供稳定、高性价比的开源模型 API 服务,支持 DeepSeek 全系列、Llama、Qwen 等行业领先大模型。
- **[302.AI](https://lobechat.com/discover/provider/ai302)**: 302.AI 是一个按需付费的 AI 应用平台,提供市面上最全的 AI API 和 AI 在线应用
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI 致力于通过创新的 AI 模型实现领先的性能,提供广泛的自定义能力,包括快速扩展支持和直观的部署流程,满足企业的各种需求。
- **[Fireworks AI](https://lobechat.com/discover/provider/fireworksai)**: Fireworks AI 是一家领先的高级语言模型服务商,专注于功能调用和多模态处理。其最新模型 Firefunction V2 基于 Llama-3,优化用于函数调用、对话及指令跟随。视觉语言模型 FireLLaVA-13B 支持图像和文本混合输入。其他 notable 模型包括 Llama 系列和 Mixtral 系列,提供高效的多语言指令跟随与生成支持。
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq 的 LPU 推理引擎在最新的独立大语言模型(LLM)基准测试中表现卓越,以其惊人的速度和效率重新定义了 AI 解决方案的标准。Groq 是一种即时推理速度的代表,在基于云的部署中展现了良好的性能。
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity 是一家领先的对话生成模型提供商,提供多种先进的 Llama 3.1 模型,支持在线和离线应用,特别适用于复杂的自然语言处理任务。
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity 是一家领先的对话生成模型提供商,提供多种先进的Llama 3.1模型,支持在线和离线应用,特别适用于复杂的自然语言处理任务。
- **[Mistral](https://lobechat.com/discover/provider/mistral)**: Mistral 提供先进的通用、专业和研究型模型,广泛应用于复杂推理、多语言任务、代码生成等领域,通过功能调用接口,用户可以集成自定义功能,实现特定应用。
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope 是阿里云推出的模型即服务平台,提供丰富的 AI 模型和推理服务。
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope是阿里云推出的模型即服务平台,提供丰富的AI模型和推理服务。
- **[Ai21Labs](https://lobechat.com/discover/provider/ai21)**: AI21 Labs 为企业构建基础模型和人工智能系统,加速生成性人工智能在生产中的应用。
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage 专注于为各种商业需求开发 AI 模型,包括 Solar LLM 和文档 AI,旨在实现工作的人造通用智能(AGI)。通过 Chat API 创建简单的对话代理,并支持功能调用、翻译、嵌入以及特定领域应用。
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage 专注于为各种商业需求开发AI模型,包括 Solar LLM 和文档 AI,旨在实现工作的人造通用智能(AGI)。通过 Chat API 创建简单的对话代理,并支持功能调用、翻译、嵌入以及特定领域应用。
- **[xAI (Grok)](https://lobechat.com/discover/provider/xai)**: xAI 是一家致力于构建人工智能以加速人类科学发现的公司。我们的使命是推动我们对宇宙的共同理解。
- **[Aliyun Bailian](https://lobechat.com/discover/provider/qwen)**: 通义千问是阿里云自主研发的超大规模语言模型,具有强大的自然语言理解和生成能力。它可以回答各种问题、创作文字内容、表达观点看法、撰写代码等,在多个领域发挥作用。
- **[Wenxin](https://lobechat.com/discover/provider/wenxin)**: 企业级一站式大模型与 AI 原生应用开发及服务平台,提供最全面易用的生成式人工智能模型开发、应用开发全流程工具链
- **[Wenxin](https://lobechat.com/discover/provider/wenxin)**: 企业级一站式大模型与AI原生应用开发及服务平台,提供最全面易用的生成式人工智能模型开发、应用开发全流程工具链
- **[Hunyuan](https://lobechat.com/discover/provider/hunyuan)**: 由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力
- **[ZhiPu](https://lobechat.com/discover/provider/zhipu)**: 智谱 AI 提供多模态与语言模型的开放平台,支持广泛的 AI 应用场景,包括文本处理、图像理解与编程辅助等。
- **[ZhiPu](https://lobechat.com/discover/provider/zhipu)**: 智谱 AI 提供多模态与语言模型的开放平台,支持广泛的AI应用场景,包括文本处理、图像理解与编程辅助等。
- **[SiliconCloud](https://lobechat.com/discover/provider/siliconcloud)**: SiliconCloud,基于优秀开源基础模型的高性价比 GenAI 云服务
- **[01.AI](https://lobechat.com/discover/provider/zeroone)**: 零一万物致力于推动以人为本的 AI 2.0 技术革命,旨在通过大语言模型创造巨大的经济和社会价值,并开创新的 AI 生态与商业模式。
- **[01.AI](https://lobechat.com/discover/provider/zeroone)**: 零一万物致力于推动以人为本的AI 2.0技术革命,旨在通过大语言模型创造巨大的经济和社会价值,并开创新的AI生态与商业模式。
- **[Spark](https://lobechat.com/discover/provider/spark)**: 科大讯飞星火大模型提供多领域、多语言的强大 AI 能力,利用先进的自然语言处理技术,构建适用于智能硬件、智慧医疗、智慧金融等多种垂直场景的创新应用。
- **[SenseNova](https://lobechat.com/discover/provider/sensenova)**: 商汤日日新,依托商汤大装置的强大的基础支撑,提供高效易用的全栈大模型服务。
- **[Stepfun](https://lobechat.com/discover/provider/stepfun)**: 阶级星辰大模型具备行业领先的多模态及复杂推理能力,支持超长文本理解和强大的自主调度搜索引擎功能。
- **[Moonshot](https://lobechat.com/discover/provider/moonshot)**: Moonshot 是由北京月之暗面科技有限公司推出的开源平台,提供多种自然语言处理模型,应用领域广泛,包括但不限于内容创作、学术研究、智能推荐、医疗诊断等,支持长文本处理和复杂生成任务。
- **[Baichuan](https://lobechat.com/discover/provider/baichuan)**: 百川智能是一家专注于人工智能大模型研发的公司,其模型在国内知识百科、长文本处理和生成创作等中文任务上表现卓越,超越了国外主流模型。百川智能还具备行业领先的多模态能力,在多项权威评测中表现优异。其模型包括 Baichuan 4、Baichuan 3 Turbo 和 Baichuan 3 Turbo 128k 等,分别针对不同应用场景进行优化,提供高性价比的解决方案。
- **[Minimax](https://lobechat.com/discover/provider/minimax)**: MiniMax 是 2021 年成立的通用人工智能科技公司,致力于与用户共创智能。MiniMax 自主研发了不同模态的通用大模型,其中包括万亿参数的 MoE 文本大模型、语音大模型以及图像大模型。并推出了海螺 AI 等应用。
- **[InternLM](https://lobechat.com/discover/provider/internlm)**: 致力于大模型研究与开发工具链的开源组织。为所有 AI 开发者提供高效、易用的开源平台,让最前沿的大模型与算法技术触手可及
- **[Higress](https://lobechat.com/discover/provider/higress)**: Higress 是一款云原生 API 网关,在阿里内部为解决 Tengine reload 对长连接业务有损,以及 gRPC/Dubbo 负载均衡能力不足而诞生。
- **[Gitee AI](https://lobechat.com/discover/provider/giteeai)**: Gitee AI 的 Serverless API 为 AI 开发者提供开箱即用的大模型推理 API 服务。
- **[Taichu](https://lobechat.com/discover/provider/taichu)**: 中科院自动化研究所和武汉人工智能研究院推出新一代多模态大模型,支持多轮问答、文本创作、图像生成、3D 理解、信号分析等全面问答任务,拥有更强的认知、理解、创作能力,带来全新互动体验。
- **[Taichu](https://lobechat.com/discover/provider/taichu)**: 中科院自动化研究所和武汉人工智能研究院推出新一代多模态大模型,支持多轮问答、文本创作、图像生成、3D理解、信号分析等全面问答任务,拥有更强的认知、理解、创作能力,带来全新互动体验。
- **[360 AI](https://lobechat.com/discover/provider/ai360)**: 360 AI 是 360 公司推出的 AI 模型和服务平台,提供多种先进的自然语言处理模型,包括 360GPT2 Pro、360GPT Pro、360GPT Turbo 和 360GPT Turbo Responsibility 8K。这些模型结合了大规模参数和多模态能力,广泛应用于文本生成、语义理解、对话系统与代码生成等领域。通过灵活的定价策略,360 AI 满足多样化用户需求,支持开发者集成,推动智能化应用的革新和发展。
- **[Search1API](https://lobechat.com/discover/provider/search1api)**: Search1API 提供可根据需要自行联网的 DeepSeek 系列模型的访问,包括标准版和快速版本,支持多种参数规模的模型选择。
- **[InfiniAI](https://lobechat.com/discover/provider/infiniai)**: 为应用开发者提供高性能、易上手、安全可靠的大模型服务,覆盖从大模型开发到大模型服务化部署的全流程。
@@ -287,7 +286,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
</details>
> 📊 Total providers: [<kbd>**42**</kbd>](https://lobechat.com/discover/providers)
> 📊 Total providers: [<kbd>**41**</kbd>](https://lobechat.com/discover/providers)
<!-- PROVIDER LIST -->
@@ -373,16 +372,16 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
> 通过文档了解更多 [📘 插件使用][docs-usage-plugin]
<!-- PLUGIN LIST -->
<!-- PLUGIN LIST -->
| 最近新增 | 描述 |
| -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-07-21**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
| [网页](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | 智能网页搜索,读取和分析页面,以提供来自 Google 结果的全面答案。<br/>`网页` `搜索` |
| [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过 BingApi 搜索互联网上的信息<br/>`bingsearch` |
| [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
| 最近新增 | 描述 |
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-07-21**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
| [Speak](https://lobechat.com/discover/plugin/speak)<br/><sup>By **speak** on **2025-07-18**</sup> | 使用Speak,您的AI语言导师,学习如何用另一种语言说任何事情。<br/>`教育` `语言` |
| [网页](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | 智能网页搜索,读取和分析页面,以提供来自Google结果的全面答案。<br/>`网页` `搜索` |
| [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过BingApi搜索互联网上的信息<br/>`bingsearch` |
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
> 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
<!-- PLUGIN LIST -->
@@ -408,14 +407,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
>
> 我欢迎所有用户加入这个不断成长的生态系统,共同参与到助手的迭代与优化中来。共同创造出更多有趣、实用且具有创新性的助手,进一步丰富助手的多样性和实用性。
<!-- AGENT LIST -->
<!-- AGENT LIST -->
| 最近新增 | 描述 |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| 最近新增 | 描述 |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| [海龟汤主持人](https://lobechat.com/discover/assistant/lateral-thinking-puzzle)<br/><sup>By **[CSY2022](https://github.com/CSY2022)** on **2025-06-19**</sup> | 一个海龟汤主持人,需要自己提供汤面,汤底与关键点(猜中的判定条件)。<br/>`海龟汤` `推理` `互动` `谜题` `角色扮演` |
| [美食评论员🍟](https://lobechat.com/discover/assistant/food-reviewer)<br/><sup>By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17**</sup> | 美食评价专家<br/>`美食` `评价` `写作` |
| [学术写作助手](https://lobechat.com/discover/assistant/academic-writing-assistant)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2025-06-17**</sup> | 专业的学术研究论文写作和正式文档编写专家<br/>`学术写作` `研究` `正式风格` |
| [Minecraft 资深开发者](https://lobechat.com/discover/assistant/java-development)<br/><sup>By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17**</sup> | 擅长高级 Java 开发及 Minecraft 开发<br/>`开发` `编程` `minecraft` `java` |
| [美食评论员🍟](https://lobechat.com/discover/assistant/food-reviewer)<br/><sup>By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17**</sup> | 美食评价专家<br/>`美食` `评价` `写作` |
| [学术写作助手](https://lobechat.com/discover/assistant/academic-writing-assistant)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2025-06-17**</sup> | 专业的学术研究论文写作和正式文档编写专家<br/>`学术写作` `研究` `正式风格` |
| [Minecraft资深开发者](https://lobechat.com/discover/assistant/java-development)<br/><sup>By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17**</sup> | 擅长高级 Java 开发及 Minecraft 开发<br/>`开发` `编程` `minecraft` `java` |
> 📊 Total agents: [<kbd>**505**</kbd> ](https://lobechat.com/discover/assistants)
+1 -2
View File
@@ -23,9 +23,8 @@ module.exports = defineConfig({
'vi-VN',
'fa-IR',
],
saveImmediately: true,
temperature: 0,
modelName: 'gpt-4.1-mini',
modelName: 'gpt-4o-mini',
experimental: {
jsonMode: true,
},
+1 -23
View File
@@ -11,16 +11,6 @@ console.log(`🚄 Build Version ${packageJSON.version}, Channel: ${channel}`);
const isNightly = channel === 'nightly';
const isBeta = packageJSON.name.includes('beta');
// 根据版本类型确定协议 scheme
const getProtocolScheme = () => {
if (isNightly) return 'lobehub-nightly';
if (isBeta) return 'lobehub-beta';
return 'lobehub';
};
const protocolScheme = getProtocolScheme();
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration
@@ -64,18 +54,12 @@ const config = {
linux: {
category: 'Utility',
maintainer: 'electronjs.org',
target: ['AppImage', 'snap', 'deb', 'rpm', 'tar.gz'],
target: ['AppImage', 'snap', 'deb'],
},
mac: {
compression: 'maximum',
entitlementsInherit: 'build/entitlements.mac.plist',
extendInfo: {
CFBundleURLTypes: [
{
CFBundleURLName: 'LobeHub Protocol',
CFBundleURLSchemes: [protocolScheme],
},
],
NSCameraUsageDescription: "Application requests access to the device's camera.",
NSDocumentsFolderUsageDescription:
"Application requests access to the user's Documents folder.",
@@ -107,12 +91,6 @@ const config = {
uninstallDisplayName: '${productName}',
uninstallerSidebar: './build/nsis-sidebar.bmp',
},
protocols: [
{
name: 'LobeHub Protocol',
schemes: [protocolScheme],
},
],
publish: [
{
owner: 'lobehub',
+3 -3
View File
@@ -20,10 +20,11 @@
"electron:dev": "electron-vite dev",
"electron:run-unpack": "electron .",
"format": "prettier --write ",
"i18n": "tsx scripts/i18nWorkflow/index.ts && lobe-i18n",
"i18n": "bun run scripts/i18nWorkflow/index.ts && lobe-i18n",
"postinstall": "electron-builder install-app-deps",
"install-isolated": "pnpm install",
"lint": "eslint --cache ",
"pg-server": "bun run scripts/pglite-server.ts",
"start": "electron-vite preview",
"test": "vitest --run",
"typecheck": "tsgo --noEmit -p tsconfig.json"
@@ -31,7 +32,6 @@
"dependencies": {
"electron-updater": "^6.6.2",
"electron-window-state": "^5.0.3",
"fetch-socks": "^1.3.2",
"get-port-please": "^3.1.2",
"pdfjs-dist": "4.10.38"
},
@@ -52,7 +52,7 @@
"@typescript/native-preview": "7.0.0-dev.20250711.1",
"consola": "^3.1.0",
"cookie": "^1.0.2",
"electron": "^37.4.0",
"electron": "~37.1.0",
"electron-builder": "^26.0.12",
"electron-is": "^3.0.0",
"electron-log": "^5.3.3",
@@ -14,7 +14,6 @@
"edit": {
"copy": "نسخ",
"cut": "قص",
"delete": "حذف",
"paste": "لصق",
"redo": "إعادة",
"selectAll": "تحديد الكل",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Копиране",
"cut": "Изрязване",
"delete": "Изтрий",
"paste": "Поставяне",
"redo": "Повторно",
"selectAll": "Избери всичко",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Kopieren",
"cut": "Ausschneiden",
"delete": "Löschen",
"paste": "Einfügen",
"redo": "Wiederherstellen",
"selectAll": "Alles auswählen",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Copy",
"cut": "Cut",
"delete": "Delete",
"paste": "Paste",
"redo": "Redo",
"selectAll": "Select All",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Copiar",
"cut": "Cortar",
"delete": "Eliminar",
"paste": "Pegar",
"redo": "Rehacer",
"selectAll": "Seleccionar todo",
@@ -14,7 +14,6 @@
"edit": {
"copy": "کپی",
"cut": "برش",
"delete": "حذف",
"paste": "چسباندن",
"redo": "انجام مجدد",
"selectAll": "انتخاب همه",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Copier",
"cut": "Couper",
"delete": "Supprimer",
"paste": "Coller",
"redo": "Rétablir",
"selectAll": "Tout sélectionner",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Copia",
"cut": "Taglia",
"delete": "Elimina",
"paste": "Incolla",
"redo": "Ripeti",
"selectAll": "Seleziona tutto",
@@ -14,7 +14,6 @@
"edit": {
"copy": "コピー",
"cut": "切り取り",
"delete": "削除",
"paste": "貼り付け",
"redo": "やり直し",
"selectAll": "すべて選択",
@@ -14,7 +14,6 @@
"edit": {
"copy": "복사",
"cut": "잘라내기",
"delete": "삭제",
"paste": "붙여넣기",
"redo": "다시 실행",
"selectAll": "모두 선택",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Kopiëren",
"cut": "Knippen",
"delete": "Verwijderen",
"paste": "Plakken",
"redo": "Opnieuw doen",
"selectAll": "Alles selecteren",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Kopiuj",
"cut": "Wytnij",
"delete": "Usuń",
"paste": "Wklej",
"redo": "Ponów",
"selectAll": "Zaznacz wszystko",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Copiar",
"cut": "Cortar",
"delete": "Excluir",
"paste": "Colar",
"redo": "Refazer",
"selectAll": "Selecionar Tudo",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Копировать",
"cut": "Вырезать",
"delete": "Удалить",
"paste": "Вставить",
"redo": "Повторить",
"selectAll": "Выбрать все",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Kopyala",
"cut": "Kes",
"delete": "Sil",
"paste": "Yapıştır",
"redo": "Yinele",
"selectAll": "Tümünü Seç",
@@ -14,7 +14,6 @@
"edit": {
"copy": "Sao chép",
"cut": "Cắt",
"delete": "Xóa",
"paste": "Dán",
"redo": "Làm lại",
"selectAll": "Chọn tất cả",
@@ -14,7 +14,6 @@
"edit": {
"copy": "复制",
"cut": "剪切",
"delete": "删除",
"paste": "粘贴",
"redo": "重做",
"selectAll": "全选",
@@ -14,7 +14,6 @@
"edit": {
"copy": "複製",
"cut": "剪下",
"delete": "刪除",
"paste": "貼上",
"redo": "重做",
"selectAll": "全選",
-1
View File
@@ -25,7 +25,6 @@ export const defaultProxySettings: NetworkProxySettings = {
* 存储默认值
*/
export const STORE_DEFAULTS: ElectronMainStore = {
autoUpdateNotificationEnabled: true,
dataSyncConfig: { storageMode: 'local' },
encryptedTokens: {},
locale: 'auto',
@@ -7,7 +7,7 @@ import { IpcClientEventSender } from '@/types/ipcClientEvent';
import { ControllerModule, ipcClientEvent, shortcut } from './index';
export default class BrowserWindowsCtr extends ControllerModule {
@shortcut('showApp')
@shortcut('showMainWindow')
async toggleMainWindow() {
const mainWindow = this.app.browserManager.getMainWindow();
mainWindow.toggleVisible();
@@ -1,41 +0,0 @@
import { createLogger } from '@/utils/logger';
import { ControllerModule, ipcClientEvent } from './index';
// Create logger
const logger = createLogger('controllers:DownloadCtr');
/**
* 下载控制器
* 处理桌面应用的下载相关功能,包括自动更新通知设置
*/
export default class DownloadCtr extends ControllerModule {
/**
* 获取自动更新通知设置
*/
@ipcClientEvent('getAutoUpdateNotificationEnabled')
async getAutoUpdateNotificationEnabled(): Promise<boolean> {
try {
const enabled = this.app.storeManager.get('autoUpdateNotificationEnabled', true);
logger.debug('Retrieved auto update notification setting:', enabled);
return enabled;
} catch (error) {
logger.error('Failed to get auto update notification setting:', error);
return true; // 默认启用
}
}
/**
* 设置自动更新通知
*/
@ipcClientEvent('setAutoUpdateNotificationEnabled')
async setAutoUpdateNotificationEnabled(enabled: boolean): Promise<void> {
try {
this.app.storeManager.set('autoUpdateNotificationEnabled', enabled);
logger.info('Auto update notification setting updated:', enabled);
} catch (error) {
logger.error('Failed to set auto update notification setting:', error);
throw error;
}
}
}
@@ -1,153 +0,0 @@
import { createLogger } from '@/utils/logger';
import { ControllerModule, createProtocolHandler } from '.';
import { McpSchema } from '../types/protocol';
const logger = createLogger('controllers:McpInstallCtr');
const protocolHandler = createProtocolHandler('plugin');
/**
* 验证 MCP Schema 对象结构
*/
function validateMcpSchema(schema: any): schema is McpSchema {
if (!schema || typeof schema !== 'object') return false;
// 必填字段验证
if (typeof schema.identifier !== 'string' || !schema.identifier) return false;
if (typeof schema.name !== 'string' || !schema.name) return false;
if (typeof schema.author !== 'string' || !schema.author) return false;
if (typeof schema.description !== 'string' || !schema.description) return false;
if (typeof schema.version !== 'string' || !schema.version) return false;
// 可选字段验证
if (schema.homepage !== undefined && typeof schema.homepage !== 'string') return false;
if (schema.icon !== undefined && typeof schema.icon !== 'string') return false;
// config 字段验证
if (!schema.config || typeof schema.config !== 'object') return false;
const config = schema.config;
if (config.type === 'stdio') {
if (typeof config.command !== 'string' || !config.command) return false;
if (config.args !== undefined && !Array.isArray(config.args)) return false;
if (config.env !== undefined && typeof config.env !== 'object') return false;
} else if (config.type === 'http') {
if (typeof config.url !== 'string' || !config.url) return false;
try {
new URL(config.url); // 验证URL格式
} catch {
return false;
}
if (config.headers !== undefined && typeof config.headers !== 'object') return false;
} else {
return false; // 未知的 config type
}
return true;
}
interface McpInstallParams {
id: string;
marketId?: string;
schema?: any;
}
/**
* MCP 插件安装控制器
* 负责处理 MCP 插件安装流程
*/
export default class McpInstallController extends ControllerModule {
/**
* 处理 MCP 插件安装请求
* @param parsedData 解析后的协议数据
* @returns 是否处理成功
*/
@protocolHandler('install')
public async handleInstallRequest(parsedData: McpInstallParams): Promise<boolean> {
try {
// 从参数中提取必需字段
const { id, schema: schemaParam, marketId } = parsedData;
if (!id) {
logger.warn(`🔧 [McpInstall] Missing required MCP parameters:`, {
id: !!id,
});
return false;
}
// 映射协议来源
const isOfficialMarket = marketId === 'lobehub';
// 对于官方市场,schema 是可选的;对于第三方市场,schema 是必需的
if (!isOfficialMarket && !schemaParam) {
logger.warn(`🔧 [McpInstall] Schema is required for third-party marketplace:`, {
marketId,
});
return false;
}
let mcpSchema: McpSchema | undefined;
// 如果提供了 schema 参数,则解析和验证
if (schemaParam) {
try {
mcpSchema = JSON.parse(schemaParam);
} catch (error) {
logger.error(`🔧 [McpInstall] Failed to parse MCP schema:`, error);
return false;
}
if (!validateMcpSchema(mcpSchema)) {
logger.error(`🔧 [McpInstall] Invalid MCP Schema structure`);
return false;
}
// 验证 identifier 与 id 参数匹配
if (mcpSchema.identifier !== id) {
logger.error(`🔧 [McpInstall] Schema identifier does not match URL id parameter:`, {
schemaId: mcpSchema.identifier,
urlId: id,
});
return false;
}
}
logger.debug(`🔧 [McpInstall] MCP install request validated:`, {
hasSchema: !!mcpSchema,
marketId,
pluginId: id,
pluginName: mcpSchema?.name || 'Unknown',
pluginVersion: mcpSchema?.version || 'Unknown',
});
// 广播安装请求到前端
const installRequest = {
marketId,
pluginId: id,
schema: mcpSchema,
};
logger.debug(`🔧 [McpInstall] Broadcasting install request:`, {
hasSchema: !!installRequest.schema,
marketId: installRequest.marketId,
pluginId: installRequest.pluginId,
pluginName: installRequest.schema?.name || 'Unknown',
});
// 通过应用实例广播到前端
if (this.app?.browserManager) {
this.app.browserManager.broadcastToWindow('chat', 'mcpInstallRequest', installRequest);
logger.debug(`🔧 [McpInstall] Install request broadcasted successfully`);
return true;
} else {
logger.error(`🔧 [McpInstall] App or browserManager not available`);
return false;
}
} catch (error) {
logger.error(`🔧 [McpInstall] Error processing install request:`, error);
return false;
}
}
}
+2 -2
View File
@@ -14,8 +14,8 @@ export default class MenuController extends ControllerModule {
* 显示上下文菜单
*/
@ipcClientEvent('showContextMenu')
showContextMenu(params: { data?: any; type: string }) {
return this.app.menuManager.showContextMenu(params.type, params.data);
showContextMenu(type: string, data?: any) {
return this.app.menuManager.showContextMenu(type, data);
}
/**
@@ -1,5 +1,3 @@
import { ShortcutUpdateResult } from '@/core/ui/ShortcutManager';
import { ControllerModule, ipcClientEvent } from '.';
export default class ShortcutController extends ControllerModule {
@@ -15,13 +13,7 @@ export default class ShortcutController extends ControllerModule {
* 更新单个快捷键配置
*/
@ipcClientEvent('updateShortcutConfig')
updateShortcutConfig({
id,
accelerator,
}: {
accelerator: string;
id: string;
}): ShortcutUpdateResult {
updateShortcutConfig(id: string, accelerator: string): boolean {
return this.app.shortcutManager.updateShortcutConfig(id, accelerator);
}
}
@@ -6,12 +6,16 @@ import {
import { createLogger } from '@/utils/logger';
import { ControllerModule, ipcClientEvent } from './index';
import { ControllerModule, ipcClientEvent, shortcut } from './index';
// 创建日志记录器
const logger = createLogger('controllers:TrayMenuCtr');
export default class TrayMenuCtr extends ControllerModule {
/**
* 使用快捷键切换窗口可见性
*/
@shortcut('showMainWindow')
async toggleMainWindow() {
logger.debug('通过快捷键切换主窗口可见性');
const mainWindow = this.app.browserManager.getMainWindow();
@@ -29,9 +29,9 @@ describe('MenuController', () => {
it('should call menuManager.refreshMenus', () => {
// 模拟返回值
mockRefreshMenus.mockReturnValueOnce(true);
const result = menuController.refreshAppMenu();
expect(mockRefreshMenus).toHaveBeenCalled();
expect(result).toBe(true);
});
@@ -41,9 +41,9 @@ describe('MenuController', () => {
it('should call menuManager.showContextMenu with type only', () => {
const menuType = 'chat';
mockShowContextMenu.mockReturnValueOnce({ shown: true });
const result = menuController.showContextMenu({ type: menuType });
const result = menuController.showContextMenu(menuType);
expect(mockShowContextMenu).toHaveBeenCalledWith(menuType, undefined);
expect(result).toEqual({ shown: true });
});
@@ -52,9 +52,9 @@ describe('MenuController', () => {
const menuType = 'file';
const menuData = { fileId: '123', filePath: '/path/to/file.txt' };
mockShowContextMenu.mockReturnValueOnce({ shown: true });
const result = menuController.showContextMenu({ type: menuType, data: menuData });
const result = menuController.showContextMenu(menuType, menuData);
expect(mockShowContextMenu).toHaveBeenCalledWith(menuType, menuData);
expect(result).toEqual({ shown: true });
});
@@ -63,20 +63,20 @@ describe('MenuController', () => {
describe('setDevMenuVisibility', () => {
it('should call menuManager.rebuildAppMenu with showDevItems true', () => {
mockRebuildAppMenu.mockReturnValueOnce(true);
const result = menuController.setDevMenuVisibility(true);
expect(mockRebuildAppMenu).toHaveBeenCalledWith({ showDevItems: true });
expect(result).toBe(true);
});
it('should call menuManager.rebuildAppMenu with showDevItems false', () => {
mockRebuildAppMenu.mockReturnValueOnce(true);
const result = menuController.setDevMenuVisibility(false);
expect(mockRebuildAppMenu).toHaveBeenCalledWith({ showDevItems: false });
expect(result).toBe(true);
});
});
});
});
@@ -5,9 +5,9 @@ import type { App } from '@/core/App';
import ShortcutController from '../ShortcutCtr';
// 模拟 App 及其依赖项
const mockGetShortcutsConfig = vi.fn().mockReturnValue({
const mockGetShortcutsConfig = vi.fn().mockReturnValue({
toggleMainWindow: 'CommandOrControl+Shift+L',
openSettings: 'CommandOrControl+,',
openSettings: 'CommandOrControl+,'
});
const mockUpdateShortcutConfig = vi.fn().mockImplementation((id, accelerator) => {
// 简单模拟更新成功
@@ -32,11 +32,11 @@ describe('ShortcutController', () => {
describe('getShortcutsConfig', () => {
it('should return shortcuts config from shortcutManager', () => {
const result = shortcutController.getShortcutsConfig();
expect(mockGetShortcutsConfig).toHaveBeenCalled();
expect(result).toEqual({
toggleMainWindow: 'CommandOrControl+Shift+L',
openSettings: 'CommandOrControl+,',
openSettings: 'CommandOrControl+,'
});
});
});
@@ -45,9 +45,9 @@ describe('ShortcutController', () => {
it('should call shortcutManager.updateShortcutConfig with correct parameters', () => {
const id = 'toggleMainWindow';
const accelerator = 'CommandOrControl+Alt+L';
const result = shortcutController.updateShortcutConfig({ id, accelerator });
const result = shortcutController.updateShortcutConfig(id, accelerator);
expect(mockUpdateShortcutConfig).toHaveBeenCalledWith(id, accelerator);
expect(result).toBe(true);
});
@@ -55,13 +55,10 @@ describe('ShortcutController', () => {
it('should return the result from shortcutManager.updateShortcutConfig', () => {
// 模拟更新失败的情况
mockUpdateShortcutConfig.mockReturnValueOnce(false);
const result = shortcutController.updateShortcutConfig({
id: 'invalidKey',
accelerator: 'invalid+combo',
});
const result = shortcutController.updateShortcutConfig('invalidKey', 'invalid+combo');
expect(result).toBe(false);
});
});
});
});
@@ -44,30 +44,11 @@ const shortcutDecorator = (name: string) => (target: any, methodName: string, de
*/
export const shortcut = (method: ShortcutActionType) => shortcutDecorator(method);
const protocolDecorator =
(urlType: string, action: string) => (target: any, methodName: string, descriptor?: any) => {
const handlers = IoCContainer.protocolHandlers.get(target.constructor) || [];
handlers.push({ action, methodName, urlType });
IoCContainer.protocolHandlers.set(target.constructor, handlers);
return descriptor;
};
/**
* Protocol handler decorator
* @param urlType 协议URL类型 (如: 'plugin')
* @param action 操作类型 (如: 'install')
*/
export const createProtocolHandler = (urlType: string) => (action: string) =>
protocolDecorator(urlType, action);
interface IControllerModule {
afterAppReady?(): void;
app: App;
beforeAppReady?(): void;
}
export class ControllerModule implements IControllerModule {
constructor(public app: App) {
this.app = app;
-46
View File
@@ -16,7 +16,6 @@ import { CustomRequestHandler, createHandler } from '@/utils/next-electron-rsc';
import { BrowserManager } from './browser/BrowserManager';
import { I18nManager } from './infrastructure/I18nManager';
import { IoCContainer } from './infrastructure/IoCContainer';
import { ProtocolManager } from './infrastructure/ProtocolManager';
import { StaticFileServerManager } from './infrastructure/StaticFileServerManager';
import { StoreManager } from './infrastructure/StoreManager';
import { UpdaterManager } from './infrastructure/UpdaterManager';
@@ -28,7 +27,6 @@ const logger = createLogger('core:App');
export type IPCEventMap = Map<string, { controller: any; methodName: string }>;
export type ShortcutMethodMap = Map<string, () => Promise<void>>;
export type ProtocolHandlerMap = Map<string, { controller: any; methodName: string }>;
type Class<T> = new (...args: any[]) => T;
@@ -45,7 +43,6 @@ export class App {
shortcutManager: ShortcutManager;
trayManager: TrayManager;
staticFileServerManager: StaticFileServerManager;
protocolManager: ProtocolManager;
chromeFlags: string[] = ['OverlayScrollbar', 'FluentOverlayScrollbar', 'FluentScrollbar'];
/**
@@ -103,15 +100,11 @@ export class App {
this.shortcutManager = new ShortcutManager(this);
this.trayManager = new TrayManager(this);
this.staticFileServerManager = new StaticFileServerManager(this);
this.protocolManager = new ProtocolManager(this);
// register the schema to interceptor url
// it should register before app ready
this.registerNextHandler();
// initialize protocol handlers
this.protocolManager.initialize();
// 统一处理 before-quit 事件
app.on('before-quit', this.handleBeforeQuit);
@@ -167,10 +160,6 @@ export class App {
});
app.on('activate', this.onActivate);
// Process any pending protocol URLs after everything is ready
await this.protocolManager.processPendingUrls();
logger.info('Application bootstrap completed');
};
@@ -182,32 +171,6 @@ export class App {
return this.controllers.get(controllerClass);
}
/**
* Handle protocol request by dispatching to registered handlers
* @param urlType 协议URL类型 (如: 'plugin')
* @param action 操作类型 (如: 'install')
* @param data 解析后的协议数据
* @returns 是否成功处理
*/
async handleProtocolRequest(urlType: string, action: string, data: any): Promise<boolean> {
const key = `${urlType}:${action}`;
const handler = this.protocolHandlerMap.get(key);
if (!handler) {
logger.warn(`No protocol handler found for ${key}`);
return false;
}
try {
logger.debug(`Dispatching protocol request ${key} to controller`);
const result = await handler.controller[handler.methodName](data);
return result !== false; // 假设控制器返回 false 表示处理失败
} catch (error) {
logger.error(`Error handling protocol request ${key}:`, error);
return false;
}
}
private onActivate = () => {
logger.debug('Application activated');
this.browserManager.showMainWindow();
@@ -270,7 +233,6 @@ export class App {
private ipcClientEventMap: IPCEventMap = new Map();
private ipcServerEventMap: IPCEventMap = new Map();
shortcutMethodMap: ShortcutMethodMap = new Map();
protocolHandlerMap: ProtocolHandlerMap = new Map();
/**
* use in next router interceptor in prod browser render
@@ -346,14 +308,6 @@ export class App {
controller[shortcut.methodName]();
});
});
IoCContainer.protocolHandlers.get(ControllerClass)?.forEach((handler) => {
const key = `${handler.urlType}:${handler.action}`;
this.protocolHandlerMap.set(key, {
controller,
methodName: handler.methodName,
});
});
};
private addService = (ServiceClass: IServiceModule) => {
+4 -17
View File
@@ -9,7 +9,7 @@ import {
import { join } from 'node:path';
import { buildDir, preloadDir, resourcesDir } from '@/const/dir';
import { isDev, isMac, isWindows } from '@/const/env';
import { isDev, isWindows } from '@/const/env';
import {
BACKGROUND_DARK,
BACKGROUND_LIGHT,
@@ -269,20 +269,7 @@ export default class Browser {
hide() {
logger.debug(`Hiding window: ${this.identifier}`);
// Fix for macOS fullscreen black screen issue
// See: https://github.com/electron/electron/issues/20263
if (isMac && this.browserWindow.isFullScreen()) {
logger.debug(
`[${this.identifier}] Window is in fullscreen mode, exiting fullscreen before hiding.`,
);
this.browserWindow.once('leave-full-screen', () => {
this.browserWindow.hide();
});
this.browserWindow.setFullScreen(false);
} else {
this.browserWindow.hide();
}
this.browserWindow.hide();
}
close() {
@@ -426,7 +413,7 @@ export default class Browser {
// logger.error(`[${this.identifier}] Failed to save window state on hide:`, error);
// }
e.preventDefault();
this.hide();
browserWindow.hide();
} else {
// Window is actually closing (not keepAlive)
logger.debug(
@@ -478,7 +465,7 @@ export default class Browser {
toggleVisible() {
logger.debug(`Toggling visibility for window: ${this.identifier}`);
if (this._browserWindow.isVisible() && this._browserWindow.isFocused()) {
this.hide(); // Use the hide() method which handles fullscreen
this._browserWindow.hide();
} else {
this._browserWindow.show();
this._browserWindow.focus();
@@ -8,9 +8,5 @@ export class IoCContainer {
> = new WeakMap();
static shortcuts: WeakMap<any, { methodName: string; name: string }[]> = new WeakMap();
static protocolHandlers: WeakMap<any, { action: string; methodName: string; urlType: string }[]> =
new WeakMap();
init() {}
}
@@ -1,256 +0,0 @@
import { app } from 'electron';
import { isDev } from '@/const/env';
import { createLogger } from '@/utils/logger';
import { getProtocolScheme, parseProtocolUrl } from '@/utils/protocol';
import { App } from '../App';
// Create logger
const logger = createLogger('core:ProtocolManager');
/**
* Protocol handler manager for custom URI schemes
*/
export class ProtocolManager {
private app: App;
private protocolScheme: string;
private pendingUrls: string[] = [];
constructor(app: App) {
logger.debug('Initializing ProtocolManager');
this.app = app;
this.protocolScheme = getProtocolScheme();
logger.info(`ProtocolManager initialized for scheme: ${this.protocolScheme}://`);
}
/**
* Register protocol handlers and set up event listeners
*/
public initialize(): void {
logger.debug('Setting up protocol handlers');
this.registerProtocolHandlers();
this.setupEventListeners();
logger.debug('Protocol initialization completed');
}
/**
* Register the application as default protocol client
*/
private registerProtocolHandlers(): void {
logger.debug(`🔗 [Protocol] Registering protocol handlers for ${this.protocolScheme}://`);
// Debug info about current app
logger.debug(`🔗 [Protocol] App name: ${app.name}`);
logger.debug(`🔗 [Protocol] App path: ${app.getPath('exe')}`);
logger.debug(`🔗 [Protocol] Is development: ${isDev}`);
logger.debug(`🔗 [Protocol] Process argv[0]: ${process.argv[0]}`);
// Check if already registered
const isCurrentlyRegistered = app.isDefaultProtocolClient(this.protocolScheme);
logger.debug(`🔗 [Protocol] Is currently default protocol client: ${isCurrentlyRegistered}`);
// Register as default protocol client
let registrationResult: boolean;
if (isDev) {
// In development, use explicit parameters to ensure proper registration
const appPath = process.cwd(); // Current working directory (our app)
logger.debug(`🔗 [Protocol] Development mode: using explicit registration parameters`);
logger.debug(`🔗 [Protocol] Executable path: ${process.execPath}`);
logger.debug(`🔗 [Protocol] App path: ${appPath}`);
logger.debug(`🔗 [Protocol] Arguments: ${JSON.stringify([appPath])}`);
registrationResult = app.setAsDefaultProtocolClient(this.protocolScheme, process.execPath, [
appPath,
]);
} else {
// In production, use simple registration
registrationResult = app.setAsDefaultProtocolClient(this.protocolScheme);
}
logger.debug(`🔗 [Protocol] Registration result: ${registrationResult}`);
if (!registrationResult) {
logger.error(
`🔗 [Protocol] Failed to register as default protocol client for ${this.protocolScheme}://`,
);
} else {
logger.debug(`🔗 [Protocol] Successfully registered ${this.protocolScheme}:// protocol`);
}
// Verify registration
const isRegisteredAfter = app.isDefaultProtocolClient(this.protocolScheme);
logger.debug(`🔗 [Protocol] Final registration status: ${isRegisteredAfter}`);
}
/**
* Set up protocol event listeners
*/
private setupEventListeners(): void {
// Handle protocol URL from cold start (Windows/Linux)
const protocolUrl = this.getProtocolUrlFromArgs(process.argv);
if (protocolUrl) {
logger.debug(`🔗 [Protocol] Found protocol URL from cold start: ${protocolUrl}`);
this.pendingUrls.push(protocolUrl);
}
// Handle protocol URL from macOS open-url event
app.on('open-url', (event, url) => {
event.preventDefault();
logger.debug(`🔗 [Protocol] Received URL from open-url event: ${url}`);
logger.debug(`🔗 [Protocol] App ready state: ${app.isReady()}`);
logger.debug(`🔗 [Protocol] Event prevented, processing URL...`);
this.handleProtocolUrl(url);
});
// Handle protocol URL from second instance (Windows/Linux)
app.on('second-instance', (event, commandLine) => {
const url = this.getProtocolUrlFromArgs(commandLine);
if (url) {
logger.debug(`🔗 [Protocol] Received protocol URL from second instance: ${url}`);
this.handleProtocolUrl(url);
}
// Show main window when second instance is triggered
this.app.browserManager.showMainWindow();
});
}
/**
* Extract protocol URL from command line arguments
*/
private getProtocolUrlFromArgs(args: string[]): string | null {
const protocolPrefix = `${this.protocolScheme}://`;
logger.debug(`🔗 [Protocol] Searching for protocol URLs in args: ${JSON.stringify(args)}`);
logger.debug(`🔗 [Protocol] Looking for prefix: ${protocolPrefix}`);
for (const arg of args) {
if (arg.startsWith(protocolPrefix)) {
logger.debug(`🔗 [Protocol] Found protocol URL in args: ${arg}`);
return arg;
}
}
logger.debug(`🔗 [Protocol] No protocol URL found in args`);
return null;
}
/**
* Handle protocol URL - either immediately or store for later processing
*/
private handleProtocolUrl(url: string): void {
try {
logger.debug(`🔗 [Protocol] handleProtocolUrl called with: ${url}`);
logger.debug(`🔗 [Protocol] App ready state: ${app.isReady()}`);
logger.debug(`🔗 [Protocol] Current pending URLs count: ${this.pendingUrls.length}`);
if (!app.isReady()) {
// App not ready yet, store for later processing
logger.debug('🔗 [Protocol] App not ready, storing protocol URL for later processing');
this.pendingUrls.push(url);
logger.debug(`🔗 [Protocol] Pending URLs after push: ${this.pendingUrls.length}`);
return;
}
// App is ready, process immediately
logger.debug('🔗 [Protocol] App is ready, processing URL immediately');
this.processProtocolUrl(url);
} catch (error) {
logger.error('🔗 [Protocol] Failed to handle protocol URL:', error);
}
}
/**
* Process protocol URL by showing main window and sending to renderer
*/
private async processProtocolUrl(url: string): Promise<void> {
try {
logger.debug(`🔗 [Protocol] processProtocolUrl called with: ${url}`);
// Basic URL validation - just check if it's our protocol
if (!url.startsWith(`${this.protocolScheme}://`)) {
logger.warn(`🔗 [Protocol] Invalid protocol scheme in URL: ${url}`);
return;
}
// Show main window
logger.debug('🔗 [Protocol] Showing main window...');
this.app.browserManager.showMainWindow();
// Parse protocol URL to extract urlType and action
const parsed = parseProtocolUrl(url);
if (!parsed) {
logger.warn(`🔗 [Protocol] Failed to parse protocol URL: ${url}`);
return;
}
logger.debug(
`🔗 [Protocol] Parsed URL - type: ${parsed.urlType}, action: ${parsed.action}, data: %s`,
parsed.params,
);
// Dispatch to registered protocol handlers via App with parsed data
logger.debug('🔗 [Protocol] Dispatching to protocol handlers...');
const handled = await this.app.handleProtocolRequest(
parsed.urlType,
parsed.action,
parsed.params,
);
if (handled) {
logger.debug('🔗 [Protocol] Protocol URL processed successfully by handler');
} else {
logger.warn(
`🔗 [Protocol] No handler found for protocol: ${parsed.urlType}:${parsed.action}`,
);
}
} catch (error) {
logger.error('🔗 [Protocol] Failed to process protocol URL:', error);
logger.error('🔗 [Protocol] Error details:', error);
}
}
/**
* Process any pending protocol URLs after app is ready
*/
public async processPendingUrls(): Promise<void> {
logger.debug(`🔗 [Protocol] processPendingUrls called`);
logger.debug(`🔗 [Protocol] Pending URLs count: ${this.pendingUrls.length}`);
if (this.pendingUrls.length === 0) {
logger.debug(`🔗 [Protocol] No pending URLs to process`);
return;
}
logger.debug(
`🔗 [Protocol] Processing ${this.pendingUrls.length} pending protocol URLs:`,
this.pendingUrls,
);
for (const url of this.pendingUrls) {
logger.debug(`🔗 [Protocol] Processing pending URL: ${url}`);
await this.processProtocolUrl(url);
}
// Clear pending URLs
this.pendingUrls = [];
logger.debug(`🔗 [Protocol] All pending URLs processed and cleared`);
}
/**
* Get current protocol scheme
*/
public getScheme(): string {
return this.protocolScheme;
}
/**
* Check if protocol is registered
*/
public isRegistered(): boolean {
return app.isDefaultProtocolClient(this.protocolScheme);
}
}
@@ -8,17 +8,6 @@ import type { App } from '../App';
// Create logger
const logger = createLogger('core:ShortcutManager');
export interface ShortcutUpdateResult {
errorType?:
| 'INVALID_ID'
| 'INVALID_FORMAT'
| 'NO_MODIFIER'
| 'CONFLICT'
| 'SYSTEM_OCCUPIED'
| 'UNKNOWN';
success: boolean;
}
export class ShortcutManager {
private app: App;
private shortcuts: Map<string, () => void> = new Map();
@@ -33,28 +22,6 @@ export class ShortcutManager {
});
}
/**
* Convert react-hotkey format to Electron accelerator format
* @param accelerator The accelerator string from frontend
* @returns Converted accelerator string for Electron
*/
private convertAcceleratorFormat(accelerator: string): string {
return accelerator
.split('+')
.map((key) => {
const trimmedKey = key.trim().toLowerCase();
// Convert react-hotkey 'mod' to Electron 'CommandOrControl'
if (trimmedKey === 'mod') {
return 'CommandOrControl';
}
// Keep other keys as is, but preserve proper casing
return key.trim().length === 1 ? key.trim().toUpperCase() : key.trim();
})
.join('+');
}
initialize() {
logger.info('Initializing global shortcuts');
// Load shortcuts configuration from storage
@@ -73,79 +40,18 @@ export class ShortcutManager {
/**
* Update a single shortcut configuration
*/
updateShortcutConfig(id: string, accelerator: string): ShortcutUpdateResult {
updateShortcutConfig(id: string, accelerator: string): boolean {
try {
logger.debug(`Updating shortcut ${id} to ${accelerator}`);
// 1. 检查 ID 是否有效
if (!DEFAULT_SHORTCUTS_CONFIG[id]) {
logger.error(`Invalid shortcut ID: ${id}`);
return { errorType: 'INVALID_ID', success: false };
}
// 2. 基本格式校验
if (!accelerator || typeof accelerator !== 'string' || accelerator.trim() === '') {
logger.error(`Invalid accelerator format: ${accelerator}`);
return { errorType: 'INVALID_FORMAT', success: false };
}
// 转换前端格式到 Electron 格式
const convertedAccelerator = this.convertAcceleratorFormat(accelerator.trim());
const cleanAccelerator = convertedAccelerator.toLowerCase();
logger.debug(`Converted accelerator from ${accelerator} to ${convertedAccelerator}`);
// 3. 检查是否包含 + 号(修饰键格式)
if (!cleanAccelerator.includes('+')) {
logger.error(
`Invalid accelerator format: ${cleanAccelerator}. Must contain modifier keys like 'CommandOrControl+E'`,
);
return { errorType: 'INVALID_FORMAT', success: false };
}
// 4. 检查是否有基本的修饰键
const hasModifier = ['CommandOrControl', 'Command', 'Ctrl', 'Alt', 'Shift'].some((modifier) =>
cleanAccelerator.includes(modifier.toLowerCase()),
);
if (!hasModifier) {
logger.error(`Invalid accelerator format: ${cleanAccelerator}. Must contain modifier keys`);
return { errorType: 'NO_MODIFIER', success: false };
}
// 5. 检查冲突
for (const [existingId, existingAccelerator] of Object.entries(this.shortcutsConfig)) {
if (
existingId !== id &&
typeof existingAccelerator === 'string' &&
existingAccelerator.toLowerCase() === cleanAccelerator
) {
logger.error(`Shortcut conflict: ${cleanAccelerator} already used by ${existingId}`);
return { errorType: 'CONFLICT', success: false };
}
}
// 6. 尝试注册测试(检查是否被系统占用)
const testSuccess = globalShortcut.register(convertedAccelerator, () => {});
if (!testSuccess) {
logger.error(
`Shortcut ${convertedAccelerator} is already registered by system or other app`,
);
return { errorType: 'SYSTEM_OCCUPIED', success: false };
} else {
// 测试成功,立即取消注册
globalShortcut.unregister(convertedAccelerator);
}
// 7. 更新配置
this.shortcutsConfig[id] = convertedAccelerator;
// Update configuration
this.shortcutsConfig[id] = accelerator;
this.saveShortcutsConfig();
this.registerConfiguredShortcuts();
return { success: true };
return true;
} catch (error) {
logger.error(`Error updating shortcut ${id}:`, error);
return { errorType: 'UNKNOWN', success: false };
return false;
}
}
@@ -224,34 +130,7 @@ export class ShortcutManager {
this.shortcutsConfig = DEFAULT_SHORTCUTS_CONFIG;
this.saveShortcutsConfig();
} else {
// Filter out invalid shortcuts that are not in DEFAULT_SHORTCUTS_CONFIG
const filteredConfig: Record<string, string> = {};
let hasInvalidKeys = false;
Object.entries(config).forEach(([id, accelerator]) => {
if (DEFAULT_SHORTCUTS_CONFIG[id]) {
filteredConfig[id] = accelerator;
} else {
hasInvalidKeys = true;
logger.debug(`Filtering out invalid shortcut ID: ${id}`);
}
});
// Ensure all default shortcuts are present
Object.entries(DEFAULT_SHORTCUTS_CONFIG).forEach(([id, defaultAccelerator]) => {
if (!(id in filteredConfig)) {
filteredConfig[id] = defaultAccelerator;
logger.debug(`Adding missing default shortcut: ${id} = ${defaultAccelerator}`);
}
});
this.shortcutsConfig = filteredConfig;
// Save the filtered configuration back to storage if we removed invalid keys
if (hasInvalidKeys) {
logger.debug('Saving filtered shortcuts config to remove invalid keys');
this.saveShortcutsConfig();
}
this.shortcutsConfig = config;
}
logger.debug('Loaded shortcuts config:', this.shortcutsConfig);
@@ -1,539 +0,0 @@
import { globalShortcut } from 'electron';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { DEFAULT_SHORTCUTS_CONFIG } from '@/shortcuts';
import type { App } from '../../App';
import { ShortcutManager } from '../ShortcutManager';
// Mock electron
vi.mock('electron', () => ({
globalShortcut: {
register: vi.fn(),
unregister: vi.fn(),
unregisterAll: vi.fn(),
isRegistered: vi.fn(),
},
}));
// Mock Logger
vi.mock('@/utils/logger', () => ({
createLogger: () => ({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
}));
// Mock DEFAULT_SHORTCUTS_CONFIG
vi.mock('@/shortcuts', () => ({
DEFAULT_SHORTCUTS_CONFIG: {
showApp: 'Control+E',
openSettings: 'CommandOrControl+,',
},
}));
describe('ShortcutManager', () => {
let shortcutManager: ShortcutManager;
let mockApp: App;
let mockStoreManager: any;
let mockShortcutMethodMap: Map<string, () => void>;
beforeEach(() => {
vi.clearAllMocks();
// Reset all mocks to their default behavior
vi.mocked(globalShortcut.register).mockReturnValue(true);
vi.mocked(globalShortcut.unregister).mockReturnValue(undefined);
vi.mocked(globalShortcut.unregisterAll).mockReturnValue(undefined);
vi.mocked(globalShortcut.isRegistered).mockReturnValue(false);
// Mock store manager
mockStoreManager = {
get: vi.fn(),
set: vi.fn(),
};
// Mock shortcut method map
mockShortcutMethodMap = new Map();
const showAppMethod = vi.fn();
const openSettingsMethod = vi.fn();
mockShortcutMethodMap.set('showApp', showAppMethod);
mockShortcutMethodMap.set('openSettings', openSettingsMethod);
// Mock App
mockApp = {
storeManager: mockStoreManager,
shortcutMethodMap: mockShortcutMethodMap,
} as unknown as App;
shortcutManager = new ShortcutManager(mockApp);
});
describe('constructor', () => {
it('should initialize shortcut manager with app', () => {
expect(shortcutManager).toBeDefined();
expect(shortcutManager['app']).toBe(mockApp);
});
it('should populate shortcuts map from app shortcut method map', () => {
expect(shortcutManager['shortcuts'].size).toBe(2);
expect(shortcutManager['shortcuts'].has('showApp')).toBe(true);
expect(shortcutManager['shortcuts'].has('openSettings')).toBe(true);
});
});
describe('convertAcceleratorFormat', () => {
it('should convert mod to CommandOrControl', () => {
const result = shortcutManager['convertAcceleratorFormat']('mod+e');
expect(result).toBe('CommandOrControl+E');
});
it('should preserve other keys as is except single characters', () => {
const result = shortcutManager['convertAcceleratorFormat']('ctrl+alt+f12');
expect(result).toBe('ctrl+alt+f12');
});
it('should handle single character keys with uppercase', () => {
const result = shortcutManager['convertAcceleratorFormat']('ctrl + a');
expect(result).toBe('ctrl+A');
});
it('should handle complex combinations', () => {
const result = shortcutManager['convertAcceleratorFormat']('mod+shift+delete');
expect(result).toBe('CommandOrControl+shift+delete');
});
});
describe('initialize', () => {
it('should load shortcuts config and register shortcuts', () => {
// Mock store to return empty config (will use defaults)
mockStoreManager.get.mockReturnValue({});
shortcutManager.initialize();
expect(mockStoreManager.get).toHaveBeenCalledWith('shortcuts');
expect(globalShortcut.unregisterAll).toHaveBeenCalled();
expect(globalShortcut.register).toHaveBeenCalledWith('Control+E', expect.any(Function));
expect(globalShortcut.register).toHaveBeenCalledWith(
'CommandOrControl+,',
expect.any(Function),
);
});
it('should handle stored config with filtering', () => {
const storedConfig = {
showApp: 'Alt+E',
openSettings: 'Ctrl+Shift+P',
invalidKey: 'Ctrl+I', // Should be filtered out
};
mockStoreManager.get.mockReturnValue(storedConfig);
shortcutManager.initialize();
const config = shortcutManager.getShortcutsConfig();
expect(config.showApp).toBe('Alt+E');
expect(config.openSettings).toBe('Ctrl+Shift+P');
expect(config.invalidKey).toBeUndefined();
});
});
describe('getShortcutsConfig', () => {
it('should return current shortcuts configuration', () => {
mockStoreManager.get.mockReturnValue({});
shortcutManager.initialize();
const config = shortcutManager.getShortcutsConfig();
expect(config).toEqual(DEFAULT_SHORTCUTS_CONFIG);
});
});
describe('updateShortcutConfig', () => {
beforeEach(() => {
mockStoreManager.get.mockReturnValue({});
shortcutManager.initialize();
});
it('should successfully update valid shortcut', () => {
const result = shortcutManager.updateShortcutConfig('showApp', 'Alt+E');
expect(result.success).toBe(true);
expect(result.errorType).toBeUndefined();
expect(mockStoreManager.set).toHaveBeenCalledWith(
'shortcuts',
expect.objectContaining({
showApp: 'Alt+E',
}),
);
});
it('should reject invalid shortcut ID', () => {
const result = shortcutManager.updateShortcutConfig('invalidId', 'Alt+E');
expect(result.success).toBe(false);
expect(result.errorType).toBe('INVALID_ID');
});
it('should reject empty accelerator', () => {
const result = shortcutManager.updateShortcutConfig('showApp', '');
expect(result.success).toBe(false);
expect(result.errorType).toBe('INVALID_FORMAT');
});
it('should reject accelerator without modifier keys', () => {
const result = shortcutManager.updateShortcutConfig('showApp', 'E');
expect(result.success).toBe(false);
expect(result.errorType).toBe('INVALID_FORMAT');
});
it('should reject accelerator without proper modifiers', () => {
const result = shortcutManager.updateShortcutConfig('showApp', 'F1+E');
expect(result.success).toBe(false);
expect(result.errorType).toBe('NO_MODIFIER');
});
it('should detect conflicts with existing shortcuts', () => {
// First set a shortcut
shortcutManager.updateShortcutConfig('showApp', 'Alt+E');
// Try to set the same accelerator for another shortcut
const result = shortcutManager.updateShortcutConfig('openSettings', 'Alt+E');
expect(result.success).toBe(false);
expect(result.errorType).toBe('CONFLICT');
});
it('should detect system occupied shortcuts', () => {
vi.mocked(globalShortcut.register).mockReturnValue(false);
const result = shortcutManager.updateShortcutConfig('showApp', 'Ctrl+Alt+T');
expect(result.success).toBe(false);
expect(result.errorType).toBe('SYSTEM_OCCUPIED');
});
it('should handle registration test cleanup', () => {
vi.mocked(globalShortcut.register).mockReturnValue(true);
shortcutManager.updateShortcutConfig('showApp', 'Ctrl+Alt+T');
// Should unregister the test registration
expect(globalShortcut.unregister).toHaveBeenCalledWith('Ctrl+Alt+T');
});
it('should handle conversion from react-hotkey format', () => {
const result = shortcutManager.updateShortcutConfig('showApp', 'mod+shift+e');
expect(result.success).toBe(true);
const config = shortcutManager.getShortcutsConfig();
expect(config.showApp).toBe('CommandOrControl+shift+E');
});
it('should handle errors gracefully', () => {
// Mock globalShortcut.register to throw an error during testing
vi.mocked(globalShortcut.register).mockImplementation(() => {
throw new Error('Register error');
});
const result = shortcutManager.updateShortcutConfig('showApp', 'Alt+E');
expect(result.success).toBe(false);
expect(result.errorType).toBe('UNKNOWN');
});
});
describe('registerShortcut', () => {
it('should register new shortcut successfully', () => {
const callback = vi.fn();
vi.mocked(globalShortcut.register).mockReturnValue(true);
const result = shortcutManager.registerShortcut('Ctrl+T', callback);
expect(result).toBe(true);
expect(globalShortcut.register).toHaveBeenCalledWith('Ctrl+T', callback);
expect(shortcutManager['shortcuts'].has('Ctrl+T')).toBe(true);
});
it('should unregister existing shortcut before registering new one', () => {
const callback1 = vi.fn();
const callback2 = vi.fn();
// First registration
shortcutManager['shortcuts'].set('Ctrl+T', callback1);
vi.mocked(globalShortcut.register).mockReturnValue(true);
shortcutManager.registerShortcut('Ctrl+T', callback2);
expect(globalShortcut.unregister).toHaveBeenCalledWith('Ctrl+T');
expect(globalShortcut.register).toHaveBeenCalledWith('Ctrl+T', callback2);
});
it('should handle registration failure', () => {
const callback = vi.fn();
vi.mocked(globalShortcut.register).mockReturnValue(false);
const result = shortcutManager.registerShortcut('Ctrl+T', callback);
expect(result).toBe(false);
expect(shortcutManager['shortcuts'].has('Ctrl+T')).toBe(false);
});
it('should handle registration errors', () => {
const callback = vi.fn();
vi.mocked(globalShortcut.register).mockImplementation(() => {
throw new Error('Registration error');
});
const result = shortcutManager.registerShortcut('Ctrl+T', callback);
expect(result).toBe(false);
});
});
describe('unregisterShortcut', () => {
it('should unregister shortcut successfully', () => {
const callback = vi.fn();
shortcutManager['shortcuts'].set('Ctrl+T', callback);
shortcutManager.unregisterShortcut('Ctrl+T');
expect(globalShortcut.unregister).toHaveBeenCalledWith('Ctrl+T');
expect(shortcutManager['shortcuts'].has('Ctrl+T')).toBe(false);
});
it('should handle unregistration errors', () => {
vi.mocked(globalShortcut.unregister).mockImplementation(() => {
throw new Error('Unregister error');
});
// Should not throw
expect(() => shortcutManager.unregisterShortcut('Ctrl+T')).not.toThrow();
});
});
describe('isRegistered', () => {
it('should check if shortcut is registered', () => {
vi.mocked(globalShortcut.isRegistered).mockReturnValue(true);
const result = shortcutManager.isRegistered('Ctrl+T');
expect(result).toBe(true);
expect(globalShortcut.isRegistered).toHaveBeenCalledWith('Ctrl+T');
});
});
describe('unregisterAll', () => {
it('should unregister all shortcuts', () => {
shortcutManager.unregisterAll();
expect(globalShortcut.unregisterAll).toHaveBeenCalled();
});
});
describe('loadShortcutsConfig', () => {
it('should use defaults when no config exists', () => {
mockStoreManager.get.mockReturnValue(null);
shortcutManager['loadShortcutsConfig']();
expect(shortcutManager['shortcutsConfig']).toEqual(DEFAULT_SHORTCUTS_CONFIG);
expect(mockStoreManager.set).toHaveBeenCalledWith('shortcuts', DEFAULT_SHORTCUTS_CONFIG);
});
it('should use defaults when config is empty', () => {
mockStoreManager.get.mockReturnValue({});
shortcutManager['loadShortcutsConfig']();
expect(shortcutManager['shortcutsConfig']).toEqual(DEFAULT_SHORTCUTS_CONFIG);
});
it('should filter invalid keys from stored config', () => {
const storedConfig = {
showApp: 'Alt+E',
openSettings: 'Ctrl+P',
invalidKey1: 'Ctrl+I',
invalidKey2: 'Ctrl+J',
};
mockStoreManager.get.mockReturnValue(storedConfig);
shortcutManager['loadShortcutsConfig']();
const config = shortcutManager['shortcutsConfig'];
expect(config.showApp).toBe('Alt+E');
expect(config.openSettings).toBe('Ctrl+P');
expect(config.invalidKey1).toBeUndefined();
expect(config.invalidKey2).toBeUndefined();
// Should save filtered config
expect(mockStoreManager.set).toHaveBeenCalledWith('shortcuts', config);
});
it('should add missing default shortcuts', () => {
const incompleteConfig = {
showApp: 'Alt+E',
// Missing openSettings
};
mockStoreManager.get.mockReturnValue(incompleteConfig);
shortcutManager['loadShortcutsConfig']();
const config = shortcutManager['shortcutsConfig'];
expect(config.showApp).toBe('Alt+E');
expect(config.openSettings).toBe('CommandOrControl+,'); // Default value
});
it('should not save config if no invalid keys were found', () => {
const validConfig = {
showApp: 'Alt+E',
openSettings: 'Ctrl+P',
};
mockStoreManager.get.mockReturnValue(validConfig);
shortcutManager['loadShortcutsConfig']();
// Should not call set since no changes were made
expect(mockStoreManager.set).not.toHaveBeenCalled();
});
it('should handle store errors gracefully', () => {
mockStoreManager.get.mockImplementation(() => {
throw new Error('Store error');
});
shortcutManager['loadShortcutsConfig']();
expect(shortcutManager['shortcutsConfig']).toEqual(DEFAULT_SHORTCUTS_CONFIG);
expect(mockStoreManager.set).toHaveBeenCalledWith('shortcuts', DEFAULT_SHORTCUTS_CONFIG);
});
});
describe('saveShortcutsConfig', () => {
it('should save shortcuts config to store', () => {
shortcutManager['shortcutsConfig'] = { showApp: 'Alt+E', openSettings: 'Ctrl+P' };
shortcutManager['saveShortcutsConfig']();
expect(mockStoreManager.set).toHaveBeenCalledWith('shortcuts', {
showApp: 'Alt+E',
openSettings: 'Ctrl+P',
});
});
it('should handle save errors gracefully', () => {
mockStoreManager.set.mockImplementation(() => {
throw new Error('Save error');
});
// Should not throw
expect(() => shortcutManager['saveShortcutsConfig']()).not.toThrow();
});
});
describe('registerConfiguredShortcuts', () => {
beforeEach(() => {
shortcutManager['shortcutsConfig'] = {
showApp: 'Alt+E',
openSettings: 'Ctrl+P',
};
});
it('should register all configured shortcuts', () => {
vi.mocked(globalShortcut.register).mockReturnValue(true);
shortcutManager['registerConfiguredShortcuts']();
expect(globalShortcut.unregisterAll).toHaveBeenCalled();
expect(globalShortcut.register).toHaveBeenCalledWith('Alt+E', expect.any(Function));
expect(globalShortcut.register).toHaveBeenCalledWith('Ctrl+P', expect.any(Function));
});
it('should skip shortcuts not in DEFAULT_SHORTCUTS_CONFIG', () => {
shortcutManager['shortcutsConfig'] = {
showApp: 'Alt+E',
invalidKey: 'Ctrl+I',
};
shortcutManager['registerConfiguredShortcuts']();
expect(globalShortcut.register).toHaveBeenCalledWith('Alt+E', expect.any(Function));
expect(globalShortcut.register).not.toHaveBeenCalledWith('Ctrl+I', expect.any(Function));
});
it('should skip shortcuts with empty accelerator', () => {
shortcutManager['shortcutsConfig'] = {
showApp: '',
openSettings: 'Ctrl+P',
};
shortcutManager['registerConfiguredShortcuts']();
expect(globalShortcut.register).not.toHaveBeenCalledWith('', expect.any(Function));
expect(globalShortcut.register).toHaveBeenCalledWith('Ctrl+P', expect.any(Function));
});
it('should skip shortcuts without corresponding methods', () => {
// Remove method from map
mockShortcutMethodMap.delete('openSettings');
shortcutManager = new ShortcutManager(mockApp);
shortcutManager['shortcutsConfig'] = {
showApp: 'Alt+E',
openSettings: 'Ctrl+P',
};
shortcutManager['registerConfiguredShortcuts']();
expect(globalShortcut.register).toHaveBeenCalledWith('Alt+E', expect.any(Function));
expect(globalShortcut.register).not.toHaveBeenCalledWith('Ctrl+P', expect.any(Function));
});
});
describe('integration tests', () => {
it('should complete full initialization flow', () => {
const storedConfig = {
showApp: 'Alt+E',
openSettings: 'Ctrl+Shift+P',
invalidKey: 'Ctrl+I',
};
mockStoreManager.get.mockReturnValue(storedConfig);
vi.mocked(globalShortcut.register).mockReturnValue(true);
shortcutManager.initialize();
// Should filter config and register valid shortcuts
const config = shortcutManager.getShortcutsConfig();
expect(config.showApp).toBe('Alt+E');
expect(config.openSettings).toBe('Ctrl+Shift+P');
expect(config.invalidKey).toBeUndefined();
expect(globalShortcut.register).toHaveBeenCalledTimes(2);
expect(mockStoreManager.set).toHaveBeenCalledWith('shortcuts', config);
});
it('should handle complete update workflow', () => {
mockStoreManager.get.mockReturnValue({});
shortcutManager.initialize();
// Update a shortcut
const result = shortcutManager.updateShortcutConfig('showApp', 'mod+alt+e');
expect(result.success).toBe(true);
// Should convert format and register
const config = shortcutManager.getShortcutsConfig();
expect(config.showApp).toBe('CommandOrControl+alt+E');
// Should have saved and re-registered shortcuts
expect(mockStoreManager.set).toHaveBeenCalled();
expect(globalShortcut.unregisterAll).toHaveBeenCalled();
expect(globalShortcut.register).toHaveBeenCalledWith(
'CommandOrControl+alt+E',
expect.any(Function),
);
});
});
});
@@ -14,7 +14,6 @@ const menu = {
edit: {
copy: '复制',
cut: '剪切',
delete: '删除',
paste: '粘贴',
redo: '重做',
selectAll: '全选',
+24 -9
View File
@@ -181,15 +181,29 @@ export class LinuxMenu extends BaseMenuPlatform implements IMenuPlatform {
}
private getChatContextMenuTemplate(data?: any): MenuItemConstructorOptions[] {
console.log(data);
const t = this.app.i18n.ns('menu');
const commonT = this.app.i18n.ns('common');
return [
{ accelerator: 'Ctrl+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Ctrl+V', label: t('edit.paste'), role: 'paste' },
const items: MenuItemConstructorOptions[] = [
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
if (data?.messageId) {
items.push(
{ type: 'separator' },
{
click: () => {
console.log('尝试删除消息:', data.messageId);
},
label: commonT('actions.delete'),
},
);
}
return items;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -197,13 +211,14 @@ export class LinuxMenu extends BaseMenuPlatform implements IMenuPlatform {
const t = this.app.i18n.ns('menu');
return [
{ accelerator: 'Ctrl+X', label: t('edit.cut'), role: 'cut' },
{ accelerator: 'Ctrl+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Ctrl+V', label: t('edit.paste'), role: 'paste' },
{ label: t('edit.cut'), role: 'cut' },
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ accelerator: 'Ctrl+A', label: t('edit.selectAll'), role: 'selectAll' },
{ label: t('edit.undo'), role: 'undo' },
{ label: t('edit.redo'), role: 'redo' },
{ type: 'separator' },
{ label: t('edit.delete'), role: 'delete' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
}
+28 -9
View File
@@ -301,29 +301,48 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
}
private getChatContextMenuTemplate(data?: any): MenuItemConstructorOptions[] {
console.log(data);
const t = this.app.i18n.ns('menu');
const commonT = this.app.i18n.ns('common');
return [
{ accelerator: 'Command+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Command+V', label: t('edit.paste'), role: 'paste' },
const items: MenuItemConstructorOptions[] = [
{ label: t('edit.cut'), role: 'cut' },
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
if (data?.messageId) {
items.push(
{ type: 'separator' },
{
click: () => {
console.log('尝试删除消息:', data.messageId);
// 调用 MessageService (假设存在)
// const messageService = this.app.getService(MessageService);
// messageService?.deleteMessage(data.messageId);
},
label: commonT('actions.delete'),
},
);
}
return items;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
private getEditorContextMenuTemplate(_data?: any): MenuItemConstructorOptions[] {
const t = this.app.i18n.ns('menu');
// 编辑器特定的上下文菜单
return [
{ accelerator: 'Command+X', label: t('edit.cut'), role: 'cut' },
{ accelerator: 'Command+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Command+V', label: t('edit.paste'), role: 'paste' },
{ label: t('edit.cut'), role: 'cut' },
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ accelerator: 'Command+A', label: t('edit.selectAll'), role: 'selectAll' },
{ label: t('edit.undo'), role: 'undo' },
{ label: t('edit.redo'), role: 'redo' },
{ type: 'separator' },
{ label: t('edit.delete'), role: 'delete' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
}
+27 -9
View File
@@ -161,15 +161,32 @@ export class WindowsMenu extends BaseMenuPlatform implements IMenuPlatform {
}
private getChatContextMenuTemplate(data?: any): MenuItemConstructorOptions[] {
console.log(data);
const t = this.app.i18n.ns('menu');
const commonT = this.app.i18n.ns('common');
return [
{ accelerator: 'Ctrl+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Ctrl+V', label: t('edit.paste'), role: 'paste' },
const items: MenuItemConstructorOptions[] = [
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
if (data?.messageId) {
items.push(
{ type: 'separator' },
{
click: () => {
console.log('尝试删除消息:', data.messageId);
// 调用 MessageService (假设存在)
// const messageService = this.app.getService(MessageService);
// messageService?.deleteMessage(data.messageId);
},
label: commonT('actions.delete'),
},
);
}
return items;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -177,13 +194,14 @@ export class WindowsMenu extends BaseMenuPlatform implements IMenuPlatform {
const t = this.app.i18n.ns('menu');
return [
{ accelerator: 'Ctrl+X', label: t('edit.cut'), role: 'cut' },
{ accelerator: 'Ctrl+C', label: t('edit.copy'), role: 'copy' },
{ accelerator: 'Ctrl+V', label: t('edit.paste'), role: 'paste' },
{ label: t('edit.cut'), role: 'cut' },
{ label: t('edit.copy'), role: 'copy' },
{ label: t('edit.paste'), role: 'paste' },
{ type: 'separator' },
{ accelerator: 'Ctrl+A', label: t('edit.selectAll'), role: 'selectAll' },
{ label: t('edit.undo'), role: 'undo' },
{ label: t('edit.redo'), role: 'redo' },
{ type: 'separator' },
{ label: t('edit.delete'), role: 'delete' },
{ label: t('edit.selectAll'), role: 'selectAll' },
];
}
@@ -1,5 +1,4 @@
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
import { SocksProxies, socksDispatcher } from 'fetch-socks';
import { Agent, ProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';
import { createLogger } from '@/utils/logger';
@@ -92,29 +91,8 @@ export class ProxyDispatcherManager {
*/
static createProxyAgent(proxyType: string, proxyUrl: string) {
try {
if (proxyType === 'socks5') {
// 解析 SOCKS5 代理 URL
const url = new URL(proxyUrl);
const socksProxies: SocksProxies = [
{
host: url.hostname,
port: parseInt(url.port, 10),
type: 5,
...(url.username && url.password
? {
password: url.password,
userId: url.username,
}
: {}),
},
];
// 使用 fetch-socks 处理 SOCKS5 代理
return socksDispatcher(socksProxies);
} else {
// undici 的 ProxyAgent 支持 http, https
return new ProxyAgent({ uri: proxyUrl });
}
// undici 的 ProxyAgent 支持 http, https 和 socks5
return new ProxyAgent({ uri: proxyUrl });
} catch (error) {
logger.error(`Failed to create proxy agent for ${proxyType}:`, error);
throw new Error(
+2 -4
View File
@@ -2,11 +2,10 @@
* 快捷键操作类型枚举
*/
export const ShortcutActionEnum = {
openSettings: 'openSettings',
/**
* 显示/隐藏主窗口
*/
showApp: 'showApp',
showMainWindow: 'showMainWindow',
} as const;
export type ShortcutActionType = (typeof ShortcutActionEnum)[keyof typeof ShortcutActionEnum];
@@ -15,6 +14,5 @@ export type ShortcutActionType = (typeof ShortcutActionEnum)[keyof typeof Shortc
* 默认快捷键配置
*/
export const DEFAULT_SHORTCUTS_CONFIG: Record<ShortcutActionType, string> = {
[ShortcutActionEnum.showApp]: 'Control+E',
[ShortcutActionEnum.openSettings]: 'CommandOrControl+,',
[ShortcutActionEnum.showMainWindow]: 'Control+E',
};
-60
View File
@@ -1,60 +0,0 @@
/**
* MCP Schema - stdio 配置类型
*/
export interface McpStdioConfig {
args?: string[];
command: string;
env?: Record<string, string>;
type: 'stdio';
}
/**
* MCP Schema - http 配置类型
*/
export interface McpHttpConfig {
headers?: Record<string, string>;
type: 'http';
url: string;
}
/**
* MCP Schema 配置类型
*/
export type McpConfig = McpStdioConfig | McpHttpConfig;
/**
* MCP Schema 对象
* 符合 RFC 0001 定义
*/
export interface McpSchema {
/** 插件作者 */
author: string;
/** 插件配置 */
config: McpConfig;
/** 插件描述 */
description: string;
/** 插件主页 */
homepage?: string;
/** 插件图标 */
icon?: string;
/** 插件唯一标识符,必须与URL中的id参数匹配 */
identifier: string;
/** 插件名称 */
name: string;
/** 插件版本 (semver) */
version: string;
}
/**
* 协议URL解析结果
*/
export interface ProtocolUrlParsed {
/** 操作类型 (如: 'install') */
action: string;
/** 原始URL */
originalUrl: string;
/** 解析后的所有查询参数 */
params: Record<string, string>;
/** URL类型 (如: 'plugin') */
urlType: string;
}
-1
View File
@@ -1,7 +1,6 @@
import { DataSyncConfig, NetworkProxySettings } from '@lobechat/electron-client-ipc';
export interface ElectronMainStore {
autoUpdateNotificationEnabled: boolean;
dataSyncConfig: DataSyncConfig;
encryptedTokens: {
accessToken?: string;
@@ -1,203 +0,0 @@
import { describe, expect, it } from 'vitest';
import { McpSchema } from '../../types/protocol';
import { generateRFCProtocolUrl, parseProtocolUrl } from '../protocol';
describe('Protocol', () => {
describe('generateRFCProtocolUrl', () => {
it('should generate valid RFC protocol URL for stdio type', () => {
const schema: McpSchema = {
identifier: 'edgeone-mcp',
name: 'EdgeOne MCP',
author: 'Higress Team',
description: 'EdgeOne API integration for LobeChat',
version: '1.0.0',
homepage: 'https://github.com/higress/edgeone-mcp',
config: {
type: 'stdio',
command: 'npx',
args: ['-y', '@higress/edgeone-mcp'],
env: { NODE_ENV: 'production' },
},
};
const url = generateRFCProtocolUrl({
id: 'edgeone-mcp',
schema,
marketId: 'higress',
});
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
expect(url).toContain('id=edgeone-mcp');
expect(url).toContain('marketId=higress');
// Verify schema is URL encoded
const urlObj = new URL(url);
const schemaParam = urlObj.searchParams.get('schema');
expect(schemaParam).toBeTruthy();
// URLSearchParams.get() 自动解码,所以这里得到的是解码后的JSON
expect(schemaParam).toContain('"'); // 解码后的引号
});
it('should generate valid RFC protocol URL for http type', () => {
const schema: McpSchema = {
identifier: 'awesome-api',
name: 'Awesome API',
author: 'Smithery',
description: 'Awesome API integration',
version: '2.0.0',
config: {
type: 'http',
url: 'https://api.smithery.ai/v1/mcp',
headers: {
'Authorization': 'Bearer token123',
'X-Custom-Header': 'value',
},
},
};
const url = generateRFCProtocolUrl({
id: 'awesome-api',
schema,
marketId: 'smithery',
});
expect(url).toMatch(/^lobehub:\/\/plugin\/install\?/);
expect(url).toContain('id=awesome-api');
expect(url).toContain('marketId=smithery');
});
it('should throw error if schema identifier does not match id', () => {
const schema: McpSchema = {
identifier: 'wrong-id',
name: 'Test',
author: 'Test',
description: 'Test',
version: '1.0.0',
config: { type: 'stdio', command: 'test' },
};
expect(() => generateRFCProtocolUrl({ id: 'different-id', schema })).toThrowError(
'Schema identifier must match the id parameter',
);
});
});
describe('parseProtocolUrl', () => {
it('should parse RFC protocol URL correctly', () => {
const schema: McpSchema = {
identifier: 'test-mcp',
name: 'Test MCP',
author: 'Test Author',
description: 'Test Description',
version: '1.0.0',
config: {
type: 'stdio',
command: 'test',
args: ['arg1', 'arg2'],
},
};
const url = generateRFCProtocolUrl({
id: 'test-mcp',
schema,
marketId: 'lobehub',
});
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.urlType).toBe('plugin');
expect(parsed?.action).toBe('install');
expect(parsed?.params.type).toBe('mcp');
expect(parsed?.params.id).toBe('test-mcp');
expect(parsed?.params.marketId).toBe('lobehub');
expect(parsed?.originalUrl).toBe(url);
// 验证 schema 可以被解析
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
expect(parsedSchema).toEqual(schema);
});
it('should return null for invalid protocol', () => {
const result = parseProtocolUrl('http://example.com');
expect(result).toBeNull();
});
it('should parse URLs with any action', () => {
const result = parseProtocolUrl('lobehub://plugin/configure?id=test');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('configure');
expect(result?.params.id).toBe('test');
});
it('should parse URLs with any query parameters', () => {
const result = parseProtocolUrl('lobehub://plugin/install?custom=value&another=param');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('install');
expect(result?.params.custom).toBe('value');
expect(result?.params.another).toBe('param');
});
it('should handle URLs without query parameters', () => {
const result = parseProtocolUrl('lobehub://plugin/install');
expect(result).toBeTruthy();
expect(result?.urlType).toBe('plugin');
expect(result?.action).toBe('install');
expect(Object.keys(result?.params || {})).toHaveLength(0);
});
it('should return null for URLs without action', () => {
const result = parseProtocolUrl('lobehub://plugin/');
expect(result).toBeNull();
});
});
describe('URL encoding/decoding', () => {
it('should handle special characters correctly', () => {
const schema: McpSchema = {
identifier: 'special-chars',
name: '特殊字符 ñ 🚀',
author: 'Test <test@example.com>',
description: 'Description with "quotes" and \'apostrophes\'',
version: '1.0.0',
config: {
type: 'stdio',
command: 'cmd',
args: ['arg with spaces', 'arg/with/slashes'],
},
};
const url = generateRFCProtocolUrl({ id: 'special-chars', schema });
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.params.id).toBe('special-chars');
expect(parsed?.params.type).toBe('mcp');
// 验证 schema 可以正确解析
const parsedSchema = JSON.parse(parsed?.params.schema || '{}');
expect(parsedSchema).toEqual(schema);
});
it('should handle different protocol schemes', () => {
const testCases = [
'lobehub://plugin/install?test=value',
'lobehub-dev://plugin/install?test=value',
'lobehub-beta://plugin/install?test=value',
'lobehub-nightly://plugin/install?test=value',
];
testCases.forEach((url) => {
const parsed = parseProtocolUrl(url);
expect(parsed).toBeTruthy();
expect(parsed?.urlType).toBe('plugin');
expect(parsed?.action).toBe('install');
expect(parsed?.params.test).toBe('value');
expect(parsed?.originalUrl).toBe(url);
});
});
});
});
-210
View File
@@ -1,210 +0,0 @@
import { app } from 'electron';
import { McpSchema, ProtocolUrlParsed } from '../types/protocol';
export type AppChannel = 'stable' | 'beta' | 'nightly';
export const getProtocolScheme = (): string => {
// 在 Electron 环境中可以通过多种方式判断版本
const bundleId = app.name;
const appPath = app.getPath('exe');
// 通过 bundle identifier 判断
if (bundleId?.toLowerCase().includes('nightly')) return 'lobehub-nightly';
if (bundleId?.toLowerCase().includes('beta')) return 'lobehub-beta';
if (bundleId?.includes('dev')) return 'lobehub-dev';
// 通过可执行文件路径判断
if (appPath?.toLowerCase().includes('nightly')) return 'lobehub-nightly';
if (appPath?.toLowerCase().includes('beta')) return 'lobehub-beta';
if (appPath?.includes('dev')) return 'lobehub-dev';
return 'lobehub';
};
export const getVersionInfo = (): { channel: AppChannel; protocolScheme: string } => {
const protocolScheme = getProtocolScheme();
let appChannel: AppChannel = 'stable';
if (protocolScheme.includes('nightly')) {
appChannel = 'nightly';
} else if (protocolScheme.includes('beta')) {
appChannel = 'beta';
}
return {
channel: appChannel,
protocolScheme,
};
};
/**
* 验证 MCP Schema 对象结构
* @param schema 待验证的对象
* @returns 是否为有效的 MCP Schema
*/
function validateMcpSchema(schema: any): schema is McpSchema {
if (!schema || typeof schema !== 'object') return false;
// 必填字段验证
if (typeof schema.identifier !== 'string' || !schema.identifier) return false;
if (typeof schema.name !== 'string' || !schema.name) return false;
if (typeof schema.author !== 'string' || !schema.author) return false;
if (typeof schema.description !== 'string' || !schema.description) return false;
if (typeof schema.version !== 'string' || !schema.version) return false;
// 可选字段验证
if (schema.homepage !== undefined && typeof schema.homepage !== 'string') return false;
if (schema.icon !== undefined && typeof schema.icon !== 'string') return false;
// config 字段验证
if (!schema.config || typeof schema.config !== 'object') return false;
const config = schema.config;
if (config.type === 'stdio') {
if (typeof config.command !== 'string' || !config.command) return false;
if (config.args !== undefined && !Array.isArray(config.args)) return false;
if (config.env !== undefined && typeof config.env !== 'object') return false;
} else if (config.type === 'http') {
if (typeof config.url !== 'string' || !config.url) return false;
try {
new URL(config.url); // 验证URL格式
} catch {
return false;
}
if (config.headers !== undefined && typeof config.headers !== 'object') return false;
} else {
return false; // 未知的 config type
}
return true;
}
/**
* 解析 lobehub:// 协议 URL (支持多版本协议)
*
* 支持的URL格式:
* - lobehub://plugin/install?id=figma&schema=xxx&marketId=lobehub
* - lobehub://plugin/configure?id=xxx&...
* - lobehub-bet://plugin/install?id=figma&schema=xxx&marketId=lobehub
* - lobehub-nightly://plugin/install?id=figma&schema=xxx&marketId=lobehub
* - lobehub-dev://plugin/install?id=figma&schema=xxx&marketId=lobehub
*
* @param url 协议 URL
* @returns 解析结果,包含基本结构和所有查询参数
*/
export const parseProtocolUrl = (url: string): ProtocolUrlParsed | null => {
try {
const parsedUrl = new URL(url);
// 支持多种协议 scheme
const validProtocols = ['lobehub:', 'lobehub-dev:', 'lobehub-nightly:', 'lobehub-beta:'];
if (!validProtocols.includes(parsedUrl.protocol)) {
return null;
}
// 对于自定义协议,URL 解析后:
// lobehub://plugin/install -> hostname: "plugin", pathname: "/install"
const urlType = parsedUrl.hostname; // "plugin"
const pathParts = parsedUrl.pathname.split('/').filter(Boolean); // ["install"]
if (pathParts.length < 1) {
return null;
}
const action = pathParts[0]; // "install"
// 解析所有查询参数
const params: Record<string, string> = {};
const searchParams = new URLSearchParams(parsedUrl.search);
for (const [key, value] of searchParams.entries()) {
params[key] = value;
}
return {
action,
originalUrl: url,
params,
urlType,
};
} catch (error) {
console.error('Failed to parse protocol URL:', error);
return null;
}
};
/**
* 生成符合 RFC 0001 的协议 URL
*
* @param params 协议参数
* @returns 生成的协议URL
*/
export function generateRFCProtocolUrl(params: {
/** 插件唯一标识符 */
id: string;
/** Marketplace ID */
marketId?: string;
/** MCP Schema 对象 */
schema: McpSchema;
/** 协议 scheme (默认: lobehub) */
scheme?: string;
}): string {
const { id, schema, marketId, scheme = 'lobehub' } = params;
// 验证 schema.identifier 与 id 匹配
if (schema.identifier !== id) {
throw new Error('Schema identifier must match the id parameter');
}
// 验证 schema 结构
if (!validateMcpSchema(schema)) {
throw new Error('Invalid MCP Schema structure');
}
// 构建基础 URL
const baseUrl = `${scheme}://plugin/install`;
// 构建查询参数
const searchParams = new URLSearchParams();
// 必需参数
searchParams.set('type', 'mcp');
searchParams.set('id', id);
// 编码 schema - 直接传 JSON 字符串,让 URLSearchParams 自动编码
const schemaJson = JSON.stringify(schema);
searchParams.set('schema', schemaJson);
// 可选参数
if (marketId) {
searchParams.set('marketId', marketId);
}
return `${baseUrl}?${searchParams.toString()}`;
}
/**
* 生成协议 URL 示例
*
* @example
* ```typescript
* const url = generateRFCProtocolUrl({
* id: 'edgeone-mcp',
* schema: {
* identifier: 'edgeone-mcp',
* name: 'EdgeOne MCP',
* author: 'Higress Team',
* description: 'EdgeOne API integration for LobeChat',
* version: '1.0.0',
* config: {
* type: 'stdio',
* command: 'npx',
* args: ['-y', '@higress/edgeone-mcp']
* }
* },
* marketId: 'higress'
* });
* // Result: lobehub://plugin/install?id=edgeone-mcp&schema=%7B%22identifier%22%3A...&marketId=higress
* ```
*/
@@ -25,34 +25,6 @@ export const setupRouteInterceptors = function () {
// 存储被阻止的路径,避免pushState重复触发
const preventedPaths = new Set<string>();
// 重写 window.open 方法来拦截 JavaScript 调用
const originalWindowOpen = window.open;
window.open = function (url?: string | URL, target?: string, features?: string) {
if (url) {
try {
const urlString = typeof url === 'string' ? url : url.toString();
const urlObj = new URL(urlString, window.location.href);
// 检查是否为外部链接
if (urlObj.origin !== window.location.origin) {
console.log(`[preload] Intercepted window.open for external URL:`, urlString);
// 调用主进程处理外部链接
invoke('openExternalLink', urlString);
return null; // 返回 null 表示没有打开新窗口
}
} catch (error) {
// 处理无效 URL 或特殊协议
console.error(`[preload] Intercepted window.open for special protocol:`, url);
console.error(error);
invoke('openExternalLink', typeof url === 'string' ? url : url.toString());
return null;
}
}
// 对于内部链接,调用原始的 window.open
return originalWindowOpen.call(window, url, target, features);
};
// 拦截所有a标签的点击事件 - 针对Next.js的Link组件
document.addEventListener(
'click',
+1917 -639
View File
File diff suppressed because it is too large Load Diff
+2779 -1821
View File
File diff suppressed because it is too large Load Diff
+2 -30
View File
@@ -1,39 +1,11 @@
component_management:
individual_components:
# App architecture layers
- component_id: app_store
name: "Store"
paths:
- src/store/**
- component_id: app_services
name: "Services"
paths:
- src/services/**
- component_id: app_server
name: "Server"
paths:
- src/server/**
- component_id: app_libs
name: "Libs"
paths:
- src/libs/**
- component_id: app_utils
name: "Utils"
paths:
- src/utils/**
coverage:
status:
project:
default: off
database:
server:
flags:
- database
- server
app:
flags:
- app
patch: off
comment:
layout: "header, diff, flags, components" # show component info in the PR comment
-40
View File
@@ -1,40 +0,0 @@
name: lobe-chat-development
services:
network-service:
image: alpine
container_name: lobe-network
restart: always
ports:
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
- '9001:9001' # MinIO Console
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
command: tail -f /dev/null
networks:
- lobe-network
postgresql:
extends:
file: docker-compose/local/docker-compose.yml
service: postgresql
minio:
extends:
file: docker-compose/local/docker-compose.yml
service: minio
casdoor:
extends:
file: docker-compose/local/docker-compose.yml
service: casdoor
searxng:
extends:
file: docker-compose/local/docker-compose.yml
service: searxng
volumes:
data:
driver: local
s3_data:
driver: local
networks:
lobe-network:
driver: bridge

Some files were not shown because too many files have changed in this diff Show More