Compare commits

...

10 Commits

Author SHA1 Message Date
sz.kgt
191266bacb
Merge 520c2850b4 into 0031544e14 2025-07-12 20:33:38 -03:00
RiverRay
0031544e14
Merge pull request #6552 from hyiip/main
Some checks failed
Run Tests / test (push) Has been cancelled
Migrate to claude 4
2025-07-08 23:35:22 +08:00
RiverRay
1f33ceee8f
Merge pull request #6557 from JI4JUN/feat/support-302ai-provider
Feat/support 302ai provider
2025-07-08 23:34:38 +08:00
JI4JUN
666ca734ec docs: update README 2025-07-07 18:20:04 +08:00
JI4JUN
fda2eb1fb5 docs: update README 2025-07-07 18:18:46 +08:00
JI4JUN
93f8340744 Merge branch 'main' into feat/support-302ai-provider 2025-07-07 18:17:57 +08:00
hyiip
21d39b8dd6 Migrate to claude 4 2025-07-02 22:14:32 +08:00
gq97a6
520c2850b4 Refactor auto scroll functionality. 2025-04-25 03:58:01 +02:00
gq97a6
98e323af4c Add auto scroll configuration to settings. 2025-04-25 03:50:45 +02:00
gq97a6
edd83c0908 Add auto scroll feature locales. 2025-04-25 03:50:04 +02:00
28 changed files with 223 additions and 136 deletions

View File

@ -4,15 +4,12 @@
<img src="https://github.com/user-attachments/assets/83bdcc07-ae5e-4954-a53a-ac151ba6ccf3" width="1000" alt="icon"/>
</a>
<h1 align="center">NextChat</h1>
English / [简体中文](./README_CN.md)
<a href="https://trendshift.io/repositories/5973" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5973" alt="ChatGPTNextWeb%2FChatGPT-Next-Web | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
✨ Light and Fast AI Assistant,with Claude, DeepSeek, GPT4 & Gemini Pro support.
[![Saas][Saas-image]][saas-url]
@ -23,7 +20,6 @@ English / [简体中文](./README_CN.md)
[NextChatAI](https://nextchat.club?utm_source=readme) / [iOS APP](https://apps.apple.com/us/app/nextchat-ai/id6743085599) / [Web App Demo](https://app.nextchat.club) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Enterprise Edition](#enterprise-edition)
[saas-url]: https://nextchat.club?utm_source=readme
[saas-image]: https://img.shields.io/badge/NextChat-Saas-green?logo=microsoftedge
[web-url]: https://app.nextchat.club/
@ -33,38 +29,38 @@ English / [简体中文](./README_CN.md)
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
[<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/ChatGPTNextWeb/NextChat)
[<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://vercel.com/button" alt="Deploy on Vercel" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/ChatGPTNextWeb/NextChat)
[<img src="https://github.com/user-attachments/assets/903482d4-3e87-4134-9af1-f2588fa90659" height="50" width="" >](https://monica.im/?utm=nxcrp)
</div>
## ❤️ Sponsor AI API
<a href='https://302.ai/'>
<img src="https://github.com/user-attachments/assets/a03edf82-2031-4f23-bdb8-bfc0bfd168a4" width="100%" alt="icon"/>
</a>
[302.AI](https://302.ai/) is a pay-as-you-go AI application platform that offers the most comprehensive AI APIs and online applications available.
## 🥳 Cheer for NextChat iOS Version Online!
> [👉 Click Here to Install Now](https://apps.apple.com/us/app/nextchat-ai/id6743085599)
> [❤️ Source Code Coming Soon](https://github.com/ChatGPTNextWeb/NextChat-iOS)
![Github iOS Image](https://github.com/user-attachments/assets/e0aa334f-4c13-4dc9-8310-e3b09fa4b9f3)
## 🫣 NextChat Support MCP !
## 🫣 NextChat Support MCP !
> Before build, please set env ENABLE_MCP=true
<img src="https://github.com/user-attachments/assets/d8851f40-4e36-4335-b1a4-ec1e11488c7e"/>
## Enterprise Edition
Meeting Your Company's Privatization and Customization Deployment Requirements:
- **Brand Customization**: Tailored VI/UI to seamlessly align with your corporate brand image.
- **Resource Integration**: Unified configuration and management of dozens of AI resources by company administrators, ready for use by team members.
- **Permission Control**: Clearly defined member permissions, resource permissions, and knowledge base permissions, all controlled via a corporate-grade Admin Panel.
@ -81,7 +77,6 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
![More](./docs/images/more.png)
## Features
- **Deploy for free with one-click** on Vercel in under 1 minute
@ -117,10 +112,11 @@ For enterprise inquiries, please contact: **business@nextchat.dev**
- [ ] local knowledge base
## What's New
- 🚀 v2.15.8 Now supports Realtime Chat [#5672](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5672)
- 🚀 v2.15.4 The Application supports using Tauri fetch LLM API, MORE SECURITY! [#5379](https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/5379)
- 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins)
- 🚀 v2.14.0 Now supports Artifacts & SD
- 🚀 v2.14.0 Now supports Artifacts & SD
- 🚀 v2.10.1 support Google Gemini Pro model.
- 🚀 v2.9.11 you can use azure endpoint now.
- 🚀 v2.8 now we have a client that runs across all platforms!
@ -320,10 +316,12 @@ To control custom models, use `+` to add a custom model, use `-` to hide a model
User `-all` to disable all default models, `+all` to enable all default models.
For Azure: use `modelName@Azure=deploymentName` to customize model name and deployment name.
> Example: `+gpt-3.5-turbo@Azure=gpt35` will show option `gpt35(Azure)` in model list.
> If you only can use Azure model, `-all,+gpt-3.5-turbo@Azure=gpt35` will `gpt35(Azure)` the only option in model list.
For ByteDance: use `modelName@bytedance=deploymentName` to customize model name and deployment name.
> Example: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` will show option `Doubao-lite-4k(ByteDance)` in model list.
### `DEFAULT_MODEL` optional
@ -340,8 +338,9 @@ Add additional models to have vision capabilities, beyond the default pattern ma
### `WHITE_WEBDAV_ENDPOINTS` (optional)
You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format
- Each address must be a complete endpoint
> `https://xxxx/yyy`
> `https://xxxx/yyy`
- Multiple addresses are connected by ', '
### `DEFAULT_INPUT_TEMPLATE` (optional)
@ -356,7 +355,6 @@ Stability API key.
Customize Stability API url.
### `ENABLE_MCP` (optional)
Enable MCPModel Context ProtocolFeature
@ -369,13 +367,20 @@ SiliconFlow API Key.
SiliconFlow API URL.
### `AI302_API_KEY` (optional)
302.AI API Key.
### `AI302_URL` (optional)
302.AI API URL.
## Requirements
NodeJS >= 18, Docker >= 20
## Development
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
Before starting development, you must create a new `.env.local` file at project root, and place your api key into it:
@ -399,7 +404,6 @@ yarn dev
## Deployment
### Docker (Recommended)
```shell
@ -457,8 +461,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
- [How to use Vercel (No English)](./docs/vercel-cn.md)
- [User Manual (Only Chinese, WIP)](./docs/user-manual-cn.md)
## Translation
If you want to add a new translation, read this [document](./docs/translation.md).
@ -469,8 +471,6 @@ If you want to add a new translation, read this [document](./docs/translation.md
## Special Thanks
### Contributors
<a href="https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/graphs/contributors">

View File

@ -4,28 +4,28 @@
<img src="./docs/images/ent.svg" alt="icon"/>
</a>
<h1 align="center">NextChat</h1>
一键免费部署你的私人 ChatGPT 网页应用,支持 Claude, GPT4 & Gemini Pro 模型。
[NextChatAI](https://nextchat.club?utm_source=readme) / [企业版](#%E4%BC%81%E4%B8%9A%E7%89%88) / [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
[<img src="https://vercel.com/button" alt="Deploy on Zeabur" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Deploy on Zeabur" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Open in Gitpod" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
</div>
## Sponsor AI API
<a href='https://302.ai/'>
<img src="https://github.com/user-attachments/assets/d8c0c513-1e18-4d3b-a2a9-ff3696aec0d4" width="100%" alt="icon"/>
</a>
[302.AI](https://302.ai/) 是一个按需付费的AI应用平台提供市面上最全的AI API和AI在线应用。
## 企业版
满足您公司私有化部署和定制需求
- **品牌定制**:企业量身定制 VI/UI与企业品牌形象无缝契合
- **资源集成**:由企业管理人员统一配置和管理数十种 AI 资源,团队成员开箱即用
- **权限管理**:成员权限、资源权限、知识库权限层级分明,企业级 Admin Panel 统一控制
@ -38,7 +38,6 @@
<img width="300" src="https://github.com/user-attachments/assets/bb29a11d-ff75-48a8-b1f8-d2d7238cf987">
## 开始使用
1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys);
@ -210,7 +209,6 @@ DeepSeek Api Key.
DeepSeek Api Url.
### `HIDE_USER_API_KEY` (可选)
如果你不想让用户自行填入 API Key将此环境变量设置为 1 即可。
@ -230,8 +228,9 @@ DeepSeek Api Url.
### `WHITE_WEBDAV_ENDPOINTS` (可选)
如果你想增加允许访问的webdav服务地址可以使用该选项格式要求
- 每一个地址必须是一个完整的 endpoint
> `https://xxxx/xxx`
> `https://xxxx/xxx`
- 多个地址以`,`相连
### `CUSTOM_MODELS` (可选)
@ -242,12 +241,13 @@ DeepSeek Api Url.
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
在Azure的模式下支持使用`modelName@Azure=deploymentName`的方式配置模型名称和部署名称(deploy-name)
> 示例:`+gpt-3.5-turbo@Azure=gpt35`这个配置会在模型列表显示一个`gpt35(Azure)`的选项。
> 如果你只能使用Azure模式那么设置 `-all,+gpt-3.5-turbo@Azure=gpt35` 则可以让对话的默认使用 `gpt35(Azure)`
在ByteDance的模式下支持使用`modelName@bytedance=deploymentName`的方式配置模型名称和部署名称(deploy-name)
> 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项
> 示例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx`这个配置会在模型列表显示一个`Doubao-lite-4k(ByteDance)`的选项
### `DEFAULT_MODEL` (可选)
@ -284,6 +284,14 @@ SiliconFlow API Key.
SiliconFlow API URL.
### `AI302_API_KEY` (optional)
302.AI API Key.
### `AI302_URL` (optional)
302.AI API URL.
## 开发
点击下方按钮,开始二次开发:
@ -308,6 +316,7 @@ BASE_URL=https://b.nextweb.fun/api/proxy
## 部署
### 宝塔面板部署
> [简体中文 > 如何通过宝塔一键部署](./docs/bt-cn.md)
### 容器部署 (推荐)

View File

@ -1,29 +1,28 @@
<div align="center">
<img src="./docs/images/ent.svg" alt="プレビュー"/>
<h1 align="center">NextChat</h1>
ワンクリックで無料であなた専用の ChatGPT ウェブアプリをデプロイ。GPT3、GPT4 & Gemini Pro モデルをサポート。
[NextChatAI](https://nextchat.club?utm_source=readme) / [企業版](#企業版) / [デモ](https://chat-gpt-next-web.vercel.app/) / [フィードバック](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discordに参加](https://discord.gg/zrhvHCr79N)
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
[<img src="https://vercel.com/button" alt="Zeaburでデプロイ" height="30">](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat) [<img src="https://zeabur.com/button.svg" alt="Zeaburでデプロイ" height="30">](https://zeabur.com/templates/ZBUEFA) [<img src="https://gitpod.io/button/open-in-gitpod.svg" alt="Gitpodで開く" height="30">](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
</div>
## Sponsor AI API
<a href='https://302.ai/'>
<img src="https://github.com/user-attachments/assets/6cf24233-1010-43e0-9a83-a11159866175" width="100%" alt="icon"/>
</a>
[302.AI](https://302.ai/) は、オンデマンドで支払うAIアプリケーションプラットフォームで、最も安全なAI APIとAIオンラインアプリケーションを提供します。
## 企業版
あなたの会社のプライベートデプロイとカスタマイズのニーズに応える
- **ブランドカスタマイズ**:企業向けに特別に設計された VI/UI、企業ブランドイメージとシームレスにマッチ
- **リソース統合**企業管理者が数十種類のAIリソースを統一管理、チームメンバーはすぐに使用可能
- **権限管理**メンバーの権限、リソースの権限、ナレッジベースの権限を明確にし、企業レベルのAdmin Panelで統一管理
@ -34,7 +33,6 @@
企業版のお問い合わせ: **business@nextchat.dev**
## 始めに
1. [OpenAI API Key](https://platform.openai.com/account/api-keys)を準備する;
@ -49,7 +47,6 @@
</div>
## 更新を維持する
もし上記の手順に従ってワンクリックでプロジェクトをデプロイした場合、「更新があります」というメッセージが常に表示されることがあります。これは、Vercel がデフォルトで新しいプロジェクトを作成するためで、本プロジェクトを fork していないことが原因です。そのため、正しく更新を検出できません。
@ -60,7 +57,6 @@
- ページ右上の fork ボタンを使って、本プロジェクトを fork する
- Vercel で再度選択してデプロイする、[詳細な手順はこちらを参照してください](./docs/vercel-ja.md)。
### 自動更新を開く
> Upstream Sync の実行エラーが発生した場合は、[手動で Sync Fork](./README_JA.md#手動でコードを更新する) してください!
@ -71,15 +67,12 @@
![自動更新を有効にする](./docs/images/enable-actions-sync.jpg)
### 手動でコードを更新する
手動で即座に更新したい場合は、[GitHub のドキュメント](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)を参照して、fork したプロジェクトを上流のコードと同期する方法を確認してください。
このプロジェクトをスターまたはウォッチしたり、作者をフォローすることで、新機能の更新通知をすぐに受け取ることができます。
## ページアクセスパスワードを設定する
> パスワードを設定すると、ユーザーは設定ページでアクセスコードを手動で入力しない限り、通常のチャットができず、未承認の状態であることを示すメッセージが表示されます。
@ -94,7 +87,6 @@ code1,code2,code3
この環境変数を追加または変更した後、**プロジェクトを再デプロイ**して変更を有効にしてください。
## 環境変数
> 本プロジェクトのほとんどの設定は環境変数で行います。チュートリアル:[Vercel の環境変数を変更する方法](./docs/vercel-ja.md)。
@ -205,8 +197,9 @@ ByteDance API の URL。
### `WHITE_WEBDAV_ENDPOINTS` (オプション)
アクセス許可を与える WebDAV サービスのアドレスを追加したい場合、このオプションを使用します。フォーマット要件:
- 各アドレスは完全なエンドポイントでなければなりません。
> `https://xxxx/xxx`
> `https://xxxx/xxx`
- 複数のアドレスは `,` で接続します。
### `CUSTOM_MODELS` (オプション)
@ -217,9 +210,11 @@ ByteDance API の URL。
モデルリストを管理します。`+` でモデルを追加し、`-` でモデルを非表示にし、`モデル名=表示名` でモデルの表示名をカスタマイズし、カンマで区切ります。
Azure モードでは、`modelName@Azure=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。
> 例:`+gpt-3.5-turbo@Azure=gpt35` この設定でモデルリストに `gpt35(Azure)` のオプションが表示されます。
ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデル名とデプロイ名deploy-nameを設定できます。
> 例: `+Doubao-lite-4k@bytedance=ep-xxxxx-xxx` この設定でモデルリストに `Doubao-lite-4k(ByteDance)` のオプションが表示されます。
### `DEFAULT_MODEL` (オプション)
@ -237,6 +232,13 @@ ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデ
『設定』の『ユーザー入力前処理』の初期設定に使用するテンプレートをカスタマイズします。
### `AI302_API_KEY` (オプション)
302.AI API キー.
### `AI302_URL` (オプション)
302.AI API の URL.
## 開発
@ -250,14 +252,12 @@ ByteDance モードでは、`modelName@bytedance=deploymentName` 形式でモデ
OPENAI_API_KEY=<your api key here>
```
### ローカル開発
1. Node.js 18 と Yarn をインストールします。具体的な方法は ChatGPT にお尋ねください。
2. `yarn install && yarn dev` を実行します。⚠️ 注意:このコマンドはローカル開発用であり、デプロイには使用しないでください。
3. ローカルでデプロイしたい場合は、`yarn install && yarn build && yarn start` コマンドを使用してください。プロセスを守るために pm2 を使用することもできます。詳細は ChatGPT にお尋ねください。
## デプロイ
### コンテナデプロイ(推奨)
@ -294,7 +294,6 @@ docker run -d -p 3000:3000 \
他の環境変数を指定する必要がある場合は、上記のコマンドに `-e 環境変数=環境変数値` を追加して指定してください。
### ローカルデプロイ
コンソールで以下のコマンドを実行します:
@ -305,7 +304,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
⚠️ 注意インストール中に問題が発生した場合は、Docker を使用してデプロイしてください。
## 謝辞
### 寄付者
@ -320,7 +318,6 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
- [one-api](https://github.com/songquanpeng/one-api): 一つのプラットフォームで大規模モデルのクォータ管理を提供し、市場に出回っているすべての主要な大規模言語モデルをサポートします。
## オープンソースライセンス
[MIT](https://opensource.org/license/mit/)

View File

@ -224,7 +224,7 @@ export class ClaudeApi implements LLMApi {
let chunkJson:
| undefined
| {
type: "content_block_delta" | "content_block_stop";
type: "content_block_delta" | "content_block_stop" | "message_delta" | "message_stop";
content_block?: {
type: "tool_use";
id: string;
@ -234,11 +234,20 @@ export class ClaudeApi implements LLMApi {
type: "text_delta" | "input_json_delta";
text?: string;
partial_json?: string;
stop_reason?: string;
};
index: number;
};
chunkJson = JSON.parse(text);
// Handle refusal stop reason in message_delta
if (chunkJson?.delta?.stop_reason === "refusal") {
// Return a message to display to the user
const refusalMessage = "\n\n[Assistant refused to respond. Please modify your request and try again.]";
options.onError?.(new Error("Content policy violation: " + refusalMessage));
return refusalMessage;
}
if (chunkJson?.content_block?.type == "tool_use") {
index += 1;
const id = chunkJson?.content_block.id;

View File

@ -1,7 +1,6 @@
import { useDebouncedCallback } from "use-debounce";
import React, {
Fragment,
RefObject,
useCallback,
useEffect,
useMemo,
@ -450,53 +449,12 @@ export function ChatAction(props: {
);
}
function useScrollToBottom(
scrollRef: RefObject<HTMLDivElement>,
detach: boolean = false,
messages: ChatMessage[],
) {
// for auto-scroll
const [autoScroll, setAutoScroll] = useState(true);
const scrollDomToBottom = useCallback(() => {
const dom = scrollRef.current;
if (dom) {
requestAnimationFrame(() => {
setAutoScroll(true);
dom.scrollTo(0, dom.scrollHeight);
});
}
}, [scrollRef]);
// auto scroll
useEffect(() => {
if (autoScroll && !detach) {
scrollDomToBottom();
}
});
// auto scroll when messages length changes
const lastMessagesLength = useRef(messages.length);
useEffect(() => {
if (messages.length > lastMessagesLength.current && !detach) {
scrollDomToBottom();
}
lastMessagesLength.current = messages.length;
}, [messages.length, detach, scrollDomToBottom]);
return {
scrollRef,
autoScroll,
setAutoScroll,
scrollDomToBottom,
};
}
export function ChatActions(props: {
uploadImage: () => void;
setAttachImages: (images: string[]) => void;
setUploading: (uploading: boolean) => void;
showPromptModal: () => void;
scrollToBottom: () => void;
scrollChatToBottom: () => void;
showPromptHints: () => void;
hitBottom: boolean;
uploading: boolean;
@ -608,7 +566,7 @@ export function ChatActions(props: {
)}
{!props.hitBottom && (
<ChatAction
onClick={props.scrollToBottom}
onClick={props.scrollChatToBottom}
text={Locale.Chat.InputActions.ToBottom}
icon={<BottomIcon />}
/>
@ -997,37 +955,12 @@ function _Chat() {
const [showExport, setShowExport] = useState(false);
const scrollRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
const [userInput, setUserInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler();
const scrollRef = useRef<HTMLDivElement>(null);
const isScrolledToBottom = scrollRef?.current
? Math.abs(
scrollRef.current.scrollHeight -
(scrollRef.current.scrollTop + scrollRef.current.clientHeight),
) <= 1
: false;
const isAttachWithTop = useMemo(() => {
const lastMessage = scrollRef.current?.lastElementChild as HTMLElement;
// if scrolllRef is not ready or no message, return false
if (!scrollRef?.current || !lastMessage) return false;
const topDistance =
lastMessage!.getBoundingClientRect().top -
scrollRef.current.getBoundingClientRect().top;
// leave some space for user question
return topDistance < 100;
}, [scrollRef?.current?.scrollHeight]);
const isTyping = userInput !== "";
// if user is typing, should auto scroll to bottom
// if user is not typing, should auto scroll to bottom only if already at bottom
const { setAutoScroll, scrollDomToBottom } = useScrollToBottom(
scrollRef,
(isScrolledToBottom || isAttachWithTop) && !isTyping,
session.messages,
);
const [hitBottom, setHitBottom] = useState(true);
const isMobileScreen = useMobileScreen();
const navigate = useNavigate();
@ -1104,6 +1037,7 @@ function _Chat() {
const doSubmit = (userInput: string) => {
if (userInput.trim() === "" && isEmpty(attachImages)) return;
const matchCommand = chatCommands.match(userInput);
if (matchCommand.matched) {
setUserInput("");
@ -1111,16 +1045,19 @@ function _Chat() {
matchCommand.invoke();
return;
}
setIsLoading(true);
chatStore
.onUserInput(userInput, attachImages)
.then(() => setIsLoading(false));
chatStore.onUserInput(userInput, attachImages).then(() => {
setIsLoading(false);
autoScrollChatToBottom();
});
setAttachImages([]);
chatStore.setLastInput(userInput);
setUserInput("");
setPromptHints([]);
if (!isMobileScreen) inputRef.current?.focus();
setAutoScroll(true);
autoScrollChatToBottom();
};
const onPromptSelect = (prompt: RenderPrompt) => {
@ -1420,14 +1357,33 @@ function _Chat() {
}
setHitBottom(isHitBottom);
setAutoScroll(isHitBottom);
};
function scrollToBottom() {
setMsgRenderIndex(renderMessages.length - CHAT_PAGE_SIZE);
scrollDomToBottom();
function scrollChatToBottom() {
const dom = scrollRef.current;
if (dom) {
setMsgRenderIndex(renderMessages.length - CHAT_PAGE_SIZE);
requestAnimationFrame(() => {
dom.scrollTo(0, dom.scrollHeight);
});
}
}
// scroll if auto-scroll is enabled in the settings
function autoScrollChatToBottom() {
if (config.enableAutoScroll) scrollChatToBottom();
}
// scroll to the bottom on mount
useEffect(() => {
scrollChatToBottom();
}, []);
// keep scroll the chat as it gets longer, but only if the chat is already scrolled to the bottom (sticky bottom)
useEffect(() => {
if (hitBottom) scrollChatToBottom();
});
// clear context index = context length + index in messages
const clearContextIndex =
(session.clearContextIndex ?? -1) >= 0
@ -1775,10 +1731,7 @@ function _Chat() {
ref={scrollRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
onMouseDown={() => inputRef.current?.blur()}
onTouchStart={() => {
inputRef.current?.blur();
setAutoScroll(false);
}}
onTouchStart={() => inputRef.current?.blur()}
>
{messages
// TODO
@ -2050,7 +2003,7 @@ function _Chat() {
setAttachImages={setAttachImages}
setUploading={setUploading}
showPromptModal={() => setShowPromptModal(true)}
scrollToBottom={scrollToBottom}
scrollChatToBottom={scrollChatToBottom}
hitBottom={hitBottom}
uploading={uploading}
showPromptHints={() => {
@ -2083,8 +2036,8 @@ function _Chat() {
onInput={(e) => onInput(e.currentTarget.value)}
value={userInput}
onKeyDown={onInputKeyDown}
onFocus={scrollToBottom}
onClick={scrollToBottom}
onFocus={autoScrollChatToBottom}
onClick={autoScrollChatToBottom}
onPaste={handlePaste}
rows={inputRows}
autoFocus={autoFocus}

View File

@ -1740,6 +1740,23 @@ export function Settings() {
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.AutoScroll.Title}
subTitle={Locale.Settings.AutoScroll.SubTitle}
>
<input
aria-label={Locale.Settings.AutoScroll.Title}
type="checkbox"
checked={config.enableAutoScroll}
data-testid="enable-auto-scroll-checkbox"
onChange={(e) =>
updateConfig(
(config) =>
(config.enableAutoScroll = e.currentTarget.checked),
)
}
></input>
</ListItem>
</List>
<SyncItems />

View File

@ -571,6 +571,8 @@ const anthropicModels = [
"claude-3-5-sonnet-latest",
"claude-3-7-sonnet-20250219",
"claude-3-7-sonnet-latest",
"claude-sonnet-4-20250514",
"claude-opus-4-20250514",
];
const baiduModels = [

View File

@ -199,6 +199,11 @@ const ar: PartialLocaleType = {
Title: "توليد العنوان تلقائيًا",
SubTitle: "توليد عنوان مناسب بناءً على محتوى الدردشة",
},
AutoScroll: {
Title: "تفعيل التمرير التلقائي",
SubTitle:
"التمرير التلقائي للدردشة إلى الأسفل عند التركيز على منطقة النص أو إرسال الرسالة",
},
Sync: {
CloudState: "بيانات السحابة",
NotSyncYet: "لم يتم التزامن بعد",

View File

@ -200,6 +200,11 @@ const bn: PartialLocaleType = {
Title: "স্বয়ংক্রিয় শিরোনাম জেনারেশন",
SubTitle: "চ্যাট কনটেন্টের ভিত্তিতে উপযুক্ত শিরোনাম তৈরি করুন",
},
AutoScroll: {
Title: "অটো স্ক্রল সক্ষম করুন",
SubTitle:
"টেক্সট এরিয়া ফোকাস বা মেসেজ সাবমিটে স্বয়ংক্রিয়ভাবে চ্যাট নিচে স্ক্রল করুন",
},
Sync: {
CloudState: "ক্লাউড ডেটা",
NotSyncYet: "এখনো সিঙ্ক করা হয়নি",

View File

@ -220,6 +220,10 @@ const cn = {
Title: "自动生成标题",
SubTitle: "根据对话内容生成合适的标题",
},
AutoScroll: {
Title: "启用自动滚动",
SubTitle: "在文本区域聚焦或提交消息时自动滚动聊天到底部",
},
Sync: {
CloudState: "云端数据",
NotSyncYet: "还没有进行过同步",

View File

@ -200,6 +200,11 @@ const cs: PartialLocaleType = {
Title: "Automatické generování názvu",
SubTitle: "Generovat vhodný název na základě obsahu konverzace",
},
AutoScroll: {
Title: "Povolit automatické posouvání",
SubTitle:
"Automaticky posunout chat dolů při zaměření na textové pole nebo odeslání zprávy",
},
Sync: {
CloudState: "Data na cloudu",
NotSyncYet: "Ještě nebylo synchronizováno",

View File

@ -219,6 +219,11 @@ const da: PartialLocaleType = {
Title: "Lav titel automatisk",
SubTitle: "Foreslå en titel ud fra chatten",
},
AutoScroll: {
Title: "Aktivér automatisk rulning",
SubTitle:
"Rul automatisk chatten til bunden ved fokus på tekstfelt eller afsendelse af besked",
},
Sync: {
CloudState: "Seneste opdatering",
NotSyncYet: "Endnu ikke synkroniseret",

View File

@ -205,6 +205,11 @@ const de: PartialLocaleType = {
SubTitle:
"Basierend auf dem Chat-Inhalt einen passenden Titel generieren",
},
AutoScroll: {
Title: "Automatisches Scrollen aktivieren",
SubTitle:
"Chat automatisch nach unten scrollen bei Fokus auf Texteingabe oder Nachrichtensenden",
},
Sync: {
CloudState: "Cloud-Daten",
NotSyncYet: "Noch nicht synchronisiert",

View File

@ -222,6 +222,11 @@ const en: LocaleType = {
Title: "Auto Generate Title",
SubTitle: "Generate a suitable title based on the conversation content",
},
AutoScroll: {
Title: "Enable Auto Scroll",
SubTitle:
"Automatically scroll chat to bottom on text area focus or message submit",
},
Sync: {
CloudState: "Last Update",
NotSyncYet: "Not sync yet",
@ -767,7 +772,7 @@ const en: LocaleType = {
},
Artifacts: {
Title: "Enable Artifacts",
SubTitle: "Can render HTML page when enable artifacts.",
SubTitle: "Can render HTML page when enable artifacts",
},
CodeFold: {
Title: "Enable CodeFold",

View File

@ -208,6 +208,11 @@ const es: PartialLocaleType = {
Title: "Generar título automáticamente",
SubTitle: "Generar un título adecuado basado en el contenido del chat",
},
AutoScroll: {
Title: "Habilitar desplazamiento automático",
SubTitle:
"Desplazar el chat automáticamente hacia abajo al enfocar el área de texto o enviar un mensaje",
},
Sync: {
CloudState: "Datos en la nube",
NotSyncYet: "Aún no se ha sincronizado",

View File

@ -207,6 +207,11 @@ const fr: PartialLocaleType = {
SubTitle:
"Générer un titre approprié en fonction du contenu de la discussion",
},
AutoScroll: {
Title: "Activer le défilement automatique",
SubTitle:
"Faire défiler automatiquement le chat vers le bas lors du focus sur la zone de texte ou de l'envoi d'un message",
},
Sync: {
CloudState: "Données cloud",
NotSyncYet: "Pas encore synchronisé",

View File

@ -201,6 +201,11 @@ const id: PartialLocaleType = {
Title: "Otomatis Membuat Judul",
SubTitle: "Membuat judul yang sesuai berdasarkan konten obrolan",
},
AutoScroll: {
Title: "Aktifkan Gulir Otomatis",
SubTitle:
"Secara otomatis gulir obrolan ke bawah saat area teks difokuskan atau pesan dikirim",
},
Sync: {
CloudState: "Data Cloud",
NotSyncYet: "Belum disinkronkan",

View File

@ -209,6 +209,11 @@ const it: PartialLocaleType = {
SubTitle:
"Genera un titolo appropriato in base al contenuto della conversazione",
},
AutoScroll: {
Title: "Abilita lo scorrimento automatico",
SubTitle:
"Scorri automaticamente la chat in basso quando si seleziona l'area di testo o si invia un messaggio",
},
Sync: {
CloudState: "Dati cloud",
NotSyncYet: "Non è ancora avvenuta alcuna sincronizzazione",

View File

@ -200,6 +200,11 @@ const jp: PartialLocaleType = {
Title: "自動タイトル生成",
SubTitle: "チャット内容に基づいて適切なタイトルを生成",
},
AutoScroll: {
Title: "自動スクロールを有効にする",
SubTitle:
"テキストエリアにフォーカスするか、メッセージを送信するとチャットが自動で下にスクロールされます",
},
Sync: {
CloudState: "クラウドデータ",
NotSyncYet: "まだ同期されていません",

View File

@ -199,6 +199,11 @@ const ko: PartialLocaleType = {
Title: "제목 자동 생성",
SubTitle: "대화 내용에 따라 적절한 제목 생성",
},
AutoScroll: {
Title: "자동 스크롤 활성화",
SubTitle:
"텍스트 영역에 포커스하거나 메시지를 전송하면 채팅이 자동으로 아래로 스크롤됩니다",
},
Sync: {
CloudState: "클라우드 데이터",
NotSyncYet: "아직 동기화되지 않았습니다.",

View File

@ -206,6 +206,11 @@ const no: PartialLocaleType = {
Title: "Automatisk generere tittel",
SubTitle: "Generer en passende tittel basert på samtaleinnholdet",
},
AutoScroll: {
Title: "Aktiver automatisk rulling",
SubTitle:
"Rull automatisk chatten til bunnen ved fokus på tekstfelt eller sending av melding",
},
Sync: {
CloudState: "Skydatasynkronisering",
NotSyncYet: "Har ikke blitt synkronisert ennå",

View File

@ -199,6 +199,11 @@ const pt: PartialLocaleType = {
Title: "Gerar Título Automaticamente",
SubTitle: "Gerar um título adequado baseado no conteúdo da conversa",
},
AutoScroll: {
Title: "Ativar rolagem automática",
SubTitle:
"Rolar automaticamente o chat para baixo ao focar na área de texto ou enviar uma mensagem",
},
Sync: {
CloudState: "Última Atualização",
NotSyncYet: "Ainda não sincronizado",

View File

@ -202,6 +202,11 @@ const ru: PartialLocaleType = {
Title: "Автоматическое создание заголовка",
SubTitle: "Создание подходящего заголовка на основе содержания беседы",
},
AutoScroll: {
Title: "Включить автопрокрутку",
SubTitle:
"Автоматически прокручивать чат вниз при фокусе на текстовом поле или отправке сообщения",
},
Sync: {
CloudState: "Облачные данные",
NotSyncYet: "Синхронизация еще не проводилась",

View File

@ -200,6 +200,11 @@ const sk: PartialLocaleType = {
Title: "Automaticky generovať názov",
SubTitle: "Generovať vhodný názov na základe obsahu konverzácie",
},
AutoScroll: {
Title: "Povoliť automatické posúvanie",
SubTitle:
"Automaticky posunúť chat nadol pri zameraní na textové pole alebo odoslaní správy",
},
Sync: {
CloudState: "Posledná aktualizácia",
NotSyncYet: "Zatiaľ nesynchronizované",

View File

@ -200,6 +200,11 @@ const tr: PartialLocaleType = {
Title: "Başlığı Otomatik Oluştur",
SubTitle: "Sohbet içeriğine göre uygun başlık oluştur",
},
AutoScroll: {
Title: "Otomatik Kaydırmayı Etkinleştir",
SubTitle:
"Metin alanına odaklanıldığında veya mesaj gönderildiğinde sohbeti otomatik olarak aşağı kaydır",
},
Sync: {
CloudState: "Bulut Verisi",
NotSyncYet: "Henüz senkronize edilmedi",

View File

@ -207,6 +207,10 @@ const tw = {
Title: "自動產生標題",
SubTitle: "根據對話內容產生合適的標題",
},
AutoScroll: {
Title: "啟用自動捲動",
SubTitle: "在文字區域聚焦或送出訊息時,自動將聊天捲動至底部",
},
Sync: {
CloudState: "雲端資料",
NotSyncYet: "還沒有進行過同步",

View File

@ -200,6 +200,11 @@ const vi: PartialLocaleType = {
Title: "Tự động tạo tiêu đề",
SubTitle: "Tạo tiêu đề phù hợp dựa trên nội dung cuộc trò chuyện",
},
AutoScroll: {
Title: "Bật Tự động Cuộn",
SubTitle:
"Tự động cuộn cuộc trò chuyện xuống dưới khi tập trung vào vùng văn bản hoặc gửi tin nhắn",
},
Sync: {
CloudState: "Dữ liệu đám mây",
NotSyncYet: "Chưa thực hiện đồng bộ",

View File

@ -55,6 +55,8 @@ export const DEFAULT_CONFIG = {
enableCodeFold: true, // code fold config
enableAutoScroll: true, // auto scroll config
disablePromptHint: false,
dontShowMaskSplashScreen: false, // dont show splash screen when create chat