xiao commited on
Commit
e2d7d0b
·
1 Parent(s): eeb257a
This view is limited to 50 files because it contains too many changes.   See raw diff
.env.local.config DELETED
@@ -1,3 +0,0 @@
1
- OPENAI_API_KEY=""
2
- WANJUAN_TOKEN=""
3
- COOKIES=""
 
 
 
 
.eslintignore DELETED
@@ -1 +0,0 @@
1
- public/serviceWorker.js
 
 
.eslintrc.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "next/core-web-vitals",
3
- "plugins": ["prettier"]
4
- }
 
 
 
 
 
.gitattributes DELETED
@@ -1,34 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tflite filter=lfs diff=lfs merge=lfs -text
29
- *.tgz filter=lfs diff=lfs merge=lfs -text
30
- *.wasm filter=lfs diff=lfs merge=lfs -text
31
- *.xz filter=lfs diff=lfs merge=lfs -text
32
- *.zip filter=lfs diff=lfs merge=lfs -text
33
- *.zst filter=lfs diff=lfs merge=lfs -text
34
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore DELETED
@@ -1,39 +0,0 @@
1
- # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
-
3
- # dependencies
4
- /node_modules
5
- /.pnp
6
- .pnp.js
7
-
8
- # testing
9
- /coverage
10
-
11
- # next.js
12
- /.next/
13
- /out/
14
-
15
- # production
16
- /build
17
-
18
- # misc
19
- .DS_Store
20
- *.pem
21
-
22
- # debug
23
- npm-debug.log*
24
- yarn-debug.log*
25
- yarn-error.log*
26
- .pnpm-debug.log*
27
-
28
- # local env files
29
- .env*.local
30
-
31
- # vercel
32
- .vercel
33
-
34
- # typescript
35
- *.tsbuildinfo
36
- next-env.d.ts
37
- dev
38
-
39
- public/prompts.json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitpod.yml DELETED
@@ -1,11 +0,0 @@
1
- # This configuration file was automatically generated by Gitpod.
2
- # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
3
- # and commit this file to your remote git repository to share the goodness with others.
4
-
5
- # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
6
-
7
- tasks:
8
- - init: yarn install && yarn run dev
9
- command: yarn run dev
10
-
11
-
 
 
 
 
 
 
 
 
 
 
 
 
.husky/pre-commit DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env sh
2
- . "$(dirname -- "$0")/_/husky.sh"
3
-
4
- npx lint-staged
 
 
 
 
 
.lintstagedrc.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "./app/**/*.{js,ts,jsx,tsx,json,html,css,scss,md}": [
3
- "eslint --fix",
4
- "prettier --write"
5
- ]
6
- }
 
 
 
 
 
 
 
.prettierrc.js DELETED
@@ -1,10 +0,0 @@
1
- module.exports = {
2
- printWidth: 80,
3
- tabWidth: 2,
4
- useTabs: false,
5
- semi: true,
6
- singleQuote: false,
7
- trailingComma: 'all',
8
- bracketSpacing: true,
9
- arrowParens: 'always',
10
- };
 
 
 
 
 
 
 
 
 
 
 
.vscode/settings.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "typescript.tsdk": "node_modules\\typescript\\lib",
3
- "typescript.enablePromptUseWorkspaceTsdk": true
4
- }
 
 
 
 
 
Dockerfile DELETED
@@ -1,46 +0,0 @@
1
- FROM node:18-alpine AS base
2
-
3
- FROM base AS deps
4
-
5
- RUN apk add --no-cache libc6-compat
6
-
7
- WORKDIR /app
8
-
9
- COPY package.json yarn.lock* package-lock.json* ./
10
-
11
- RUN \
12
- if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
13
- elif [ -f package-lock.json ]; then npm ci; \
14
- else echo "Lockfile not found." && exit 1; \
15
- fi
16
-
17
- FROM base AS builder
18
-
19
- RUN apk update && apk add --no-cache git
20
-
21
- ENV OPENAI_API_KEY=""
22
- ENV COOKIES=""
23
- ENV CODE=""
24
- ARG DOCKER=true
25
-
26
- WORKDIR /app
27
- COPY --from=deps /app/node_modules ./node_modules
28
- COPY . .
29
-
30
- RUN yarn build
31
-
32
- FROM base AS runner
33
- WORKDIR /app
34
-
35
- ENV OPENAI_API_KEY=""
36
- ENV COOKIES=""
37
- ENV CODE=""
38
-
39
- COPY --from=builder /app/public ./public
40
- COPY --from=builder /app/.next/standalone ./
41
- COPY --from=builder /app/.next/static ./.next/static
42
- COPY --from=builder /app/.next/server ./.next/server
43
-
44
- EXPOSE 3000
45
-
46
- CMD ["node","server.js"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
LICENSE DELETED
@@ -1,75 +0,0 @@
1
- 版权所有(c)<2023><Zhang Yifei>
2
-
3
- 反996许可证版本1.0
4
-
5
- 在符合下列条件的情况下,
6
- 特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以下统称为“授权作品”
7
- )的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括但不限于使
8
- 用、复制,修改,衍生利用、散布,发布和再许可:
9
-
10
-
11
- 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不
12
- 得自行修改。
13
- 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或
14
- 经营地(以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和
15
- 标准。如果该司法管辖区没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可
16
- 执行,则个人或法人实体必须遵守国际劳工标准的核心公约。
17
- 3. 个人或法人不得以任何方式诱导或强迫其全职或兼职员工或其独立承包人以口头或书面形式同
18
- 意直接或间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和
19
- 标准保护的权利或补救措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该
20
- 等个人或法人实体也不得以任何方法限制其雇员或独立承包人向版权持有人或监督许可证合规
21
- 情况的有关当局报告或投诉上述违反许可证的行为的权利。
22
-
23
- 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用
24
- 性和非侵权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均
25
- 不承担因本软件或本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。
26
-
27
-
28
- ------------------------- ENGLISH ------------------------------
29
-
30
-
31
- Copyright (c) <2023> <Zhang Yifei>
32
-
33
- Anti 996 License Version 1.0 (Draft)
34
-
35
- Permission is hereby granted to any individual or legal entity obtaining a copy
36
- of this licensed work (including the source code, documentation and/or related
37
- items, hereinafter collectively referred to as the "licensed work"), free of
38
- charge, to deal with the licensed work for any purpose, including without
39
- limitation, the rights to use, reproduce, modify, prepare derivative works of,
40
- publish, distribute and sublicense the licensed work, subject to the following
41
- conditions:
42
-
43
- 1. The individual or the legal entity must conspicuously display, without
44
- modification, this License on each redistributed or derivative copy of the
45
- Licensed Work.
46
-
47
- 2. The individual or the legal entity must strictly comply with all applicable
48
- laws, regulations, rules and standards of the jurisdiction relating to
49
- labor and employment where the individual is physically located or where
50
- the individual was born or naturalized; or where the legal entity is
51
- registered or is operating (whichever is stricter). In case that the
52
- jurisdiction has no such laws, regulations, rules and standards or its
53
- laws, regulations, rules and standards are unenforceable, the individual
54
- or the legal entity are required to comply with Core International Labor
55
- Standards.
56
-
57
- 3. The individual or the legal entity shall not induce or force its
58
- employee(s), whether full-time or part-time, or its independent
59
- contractor(s), in any methods, to agree in oral or written form,
60
- to directly or indirectly restrict, weaken or relinquish his or
61
- her rights or remedies under such laws, regulations, rules and
62
- standards relating to labor and employment as mentioned above,
63
- no matter whether such written or oral agreement are enforceable
64
- under the laws of the said jurisdiction, nor shall such individual
65
- or the legal entity limit, in any methods, the rights of its employee(s)
66
- or independent contractor(s) from reporting or complaining to the copyright
67
- holder or relevant authorities monitoring the compliance of the license
68
- about its violation(s) of the said license.
69
-
70
- THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
72
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
73
- HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
74
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION
75
- WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md DELETED
@@ -1,183 +0,0 @@
1
- ---
2
- title: ChatGpt Web
3
- emoji: 📊
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- license: openrail
9
- app_port: 3000
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
-
14
- <div align="center">
15
- <img src="./static/icon.svg" alt="预览"/>
16
-
17
- <h1 align="center">ChatGPT Next Web</h1>
18
-
19
- 一键免费部署你的私人 ChatGPT 网页应用。
20
-
21
- One-Click to deploy your own ChatGPT web UI.
22
-
23
- [演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
24
-
25
- [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
26
-
27
- [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
28
-
29
- ![主界面](../ChatGPT-Next-Web/static/cover.png)
30
-
31
- </div>
32
-
33
- ## 主要功能
34
-
35
- - 在 1 分钟内使用 Vercel **免费一键部署**
36
- - 精心设计的 UI,响应式设计,支持深色模式
37
- - 极快的首屏加载速度(~85kb)
38
- - 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
39
- - 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
40
- - 一键导出聊天记录,完整的 Markdown 支持
41
- - 拥有自己的域名?好上加好,绑定后即可在任何地方**无障碍**快速访问
42
-
43
- ## Features
44
-
45
- - **Deploy for free with one-click** on Vercel in under 1 minute
46
- - Responsive design, and dark mode
47
- - Fast first screen loading speed (~85kb)
48
- - Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
49
- - Automatically compresses chat history to support long conversations while also saving your tokens
50
- - One-click export all chat history with full Markdown support
51
-
52
- ## 使用
53
-
54
- 1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys)、NewBingCookie、WanJuanToken;
55
- 2. 点击右侧按钮开始部署:
56
- [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登陆即可,记得在环境变量页填入 API Key;
57
- 3. 部署完毕后,即可开始使用;
58
- 4. (可选)[绑定自定义域名](https://vercel.com/docs/concepts/projects/domains/add-a-domain):Vercel 分配的域名 DNS 在某些区域被污染了,绑定自定义域名即可直连。
59
-
60
- ## Get Started
61
-
62
- 1. Get [OpenAI API Key](https://platform.openai.com/account/api-keys);
63
- 2. Click
64
- [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web);
65
- 3. Enjoy :)
66
-
67
- ## 保持更新 Keep Updated
68
-
69
- 如果你按照上述步骤一键部署了自己的项目,可能会发现总是提示“存在更新”的问题,这是由于 Vercel 会默认为你创建一个新项目而不是 fork 本项目,这会导致无法正确地检测更新。
70
- 推荐你按照下列步骤重新部署:
71
-
72
- - 删除掉原先的 repo;
73
- - fork 本项目;
74
- - 前往 vercel 控制台,删除掉原先的 project,然后新建 project,选择你刚刚 fork 出来的项目重新进行部署即可;
75
- - 在重新部署的过程中,请手动添加名为 `OPENAI_API_KEY` 的环境变量,并填入你的 api key 作为值。
76
-
77
- 本项目会持续更新,如果你想让代码库总是保持更新,可以查看 [Github 的文档](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) 了解如何让 fork 的项目与上游代码同步,建议定期进行同步操作以获得新功能。
78
-
79
- 你可以 star/watch 本项目或者 follow 作者来及时获得新功能更新通知。
80
-
81
- If you have deployed your own project with just one click following the steps above, you may encounter the issue of "Updates Available" constantly showing up. This is because Vercel will create a new project for you by default instead of forking this project, resulting in the inability to detect updates correctly.
82
-
83
- We recommend that you follow the steps below to re-deploy:
84
-
85
- - Delete the original repo;
86
- - Fork this project;
87
- - Go to the Vercel dashboard, delete the original project, then create a new project and select the project you just forked to redeploy;
88
- - Please manually add an environment variable named `OPENAI_API_KEY` and enter your API key as the value during the redeploy process.
89
-
90
- This project will be continuously maintained. If you want to keep the code repository up to date, you can check out the [Github documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code. It is recommended to perform synchronization operations regularly.
91
-
92
- You can star or watch this project or follow author to get release notifictions in time.
93
-
94
- ## 访问控制 Access Control
95
-
96
- 本项目提供有限的权限控制功能,请在环境变量页增加名为 `CODE` 的环境变量,值为用英文逗号分隔的自定义控制码:
97
-
98
- ```
99
- code1,code2,code3
100
- ```
101
-
102
- 增加或修改该环境变量后,请**重新部署**项目使改动生效。
103
-
104
- This project provides limited access control. Please add an environment variable named `CODE` on the environment variables page. The value should be a custom control code separated by comma like this:
105
-
106
- ```
107
- code1,code2,code3
108
- ```
109
-
110
- After adding or modifying this environment variable, please redeploy the project for the changes to take effect.
111
-
112
- ## 开发 Development
113
-
114
- 点击下方按钮,开始二次开发:
115
-
116
- [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
117
-
118
- 在开始写代码之前,需要在项目根目录新建一个 `.env.local` 文件,里面填入环境变量:
119
-
120
- 新必应只需要名为_u的cookie的值
121
-
122
- Before starting development, you must create a new `.env.local` file at project root, and place your api key into it:
123
-
124
- ```
125
- OPENAI_API_KEY=<your api key here>
126
- CODE=<your code>
127
- COOKIES=<your NewBing cookie>
128
- WANJUAN_TOKEN=<your WanJuan Token>
129
- ```
130
-
131
- ### 本地开发 Local Development
132
-
133
- > 如果你是中国大陆用户,不建议在本地进行开发,除非你能够独立解决 OpenAI API 本地代理问题。
134
-
135
- 1. 安装 nodejs 和 yarn,具体细节请询问 ChatGPT;
136
- 2. 执行 `yarn install && yarn dev` 即可。
137
-
138
- ### 本地部署 Local Deployment
139
-
140
- ```shell
141
- bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
142
- ```
143
-
144
- ### 容器部署 Docker Deployment
145
-
146
- ```shell
147
- docker pull yidadaa/chatgpt-next-web
148
-
149
- docker run -d -p 3000:3000 -e OPENAI_API_KEY="" -e CODE="" yidadaa/chatgpt-next-web
150
- ```
151
-
152
- ## 截图 Screenshots
153
-
154
- ![设置 Settings](../ChatGPT-Next-Web/static/settings.png)
155
-
156
- ![更多展示 More](../ChatGPT-Next-Web/static/more.png)
157
-
158
- ## 说明 Attention
159
-
160
- 本项目的演示地址所用的 OpenAI 账户的免费额度将于 2023-04-01 过期,届时将无法通过演示地址在线体验。
161
-
162
- 如果你想贡献出自己的 API Key,可以通过作者主页的邮箱发送给作者,并标注过期时间。
163
-
164
- The free trial of the OpenAI account used by the demo will expire on April 1, 2023, and the demo will not be available at that time.
165
-
166
- If you would like to contribute your API key, you can email it to the author and indicate the expiration date of the API key.
167
-
168
- ## 鸣谢 Special Thanks
169
-
170
- ### 捐赠者 Sponsor
171
-
172
- [@mushan0x0](https://github.com/mushan0x0)
173
- [@ClarenceDan](https://github.com/ClarenceDan)
174
- [@zhangjia](https://github.com/zhangjia)
175
- [@hoochanlon](https://github.com/hoochanlon)
176
-
177
- ### 贡献者 Contributor
178
-
179
- [Contributors](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
180
-
181
- ## LICENSE
182
-
183
- - [Anti 996 License](https://github.com/kattgu7/Anti-996-License/blob/master/LICENSE_CN_EN)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/access.ts DELETED
@@ -1,17 +0,0 @@
1
- import md5 from "spark-md5";
2
-
3
- export function getAccessCodes(): Set<string> {
4
- const code = process.env.CODE;
5
-
6
- try {
7
- const codes = (code?.split(",") ?? [])
8
- .filter((v) => !!v)
9
- .map((v) => md5.hash(v.trim()));
10
- return new Set(codes);
11
- } catch (e) {
12
- return new Set();
13
- }
14
- }
15
-
16
- export const ACCESS_CODES = getAccessCodes();
17
- export const IS_IN_DOCKER = process.env.DOCKER;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/chat-image/route.ts DELETED
@@ -1,30 +0,0 @@
1
- import { OpenAIApi, Configuration } from "openai";
2
- import { ChatImageRequest } from "../chat/typing";
3
-
4
- export async function POST(req: Request) {
5
- try {
6
- let apiKey = process.env.OPENAI_API_KEY;
7
-
8
- const userApiKey = req.headers.get("token");
9
- if (userApiKey) {
10
- apiKey = userApiKey;
11
- console.log("user api key:" + apiKey);
12
- }
13
-
14
- const openai = new OpenAIApi(
15
- new Configuration({
16
- apiKey,
17
- }),
18
- );
19
-
20
- const requestBody = (await req.json()) as ChatImageRequest;
21
- const response = await openai.createImage({
22
- ...requestBody,
23
- });
24
- console.log(response.data.data[0].url);
25
- return new Response(JSON.stringify(response.data));
26
- } catch (e) {
27
- console.error("[Chat] ", e);
28
- return new Response(JSON.stringify(e));
29
- }
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/chat-stream/route.ts DELETED
@@ -1,66 +0,0 @@
1
- import { createParser } from "eventsource-parser";
2
- import { NextRequest } from "next/server";
3
-
4
- async function createStream(req: NextRequest) {
5
- const encoder = new TextEncoder();
6
- const decoder = new TextDecoder();
7
-
8
- let apiKey = process.env.OPENAI_API_KEY;
9
-
10
- const userApiKey = req.headers.get("token");
11
- if (userApiKey) {
12
- apiKey = userApiKey;
13
- console.log("[Stream] using user api key:" + apiKey);
14
- }
15
-
16
- const res = await fetch("https://api.openai.com/v1/chat/completions", {
17
- headers: {
18
- "Content-Type": "application/json",
19
- Authorization: `Bearer ${apiKey}`,
20
- },
21
- method: "POST",
22
- body: req.body,
23
- });
24
-
25
- const stream = new ReadableStream({
26
- async start(controller) {
27
- function onParse(event: any) {
28
- if (event.type === "event") {
29
- const data = event.data;
30
- // https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
31
- if (data === "[DONE]") {
32
- controller.close();
33
- return;
34
- }
35
- try {
36
- const json = JSON.parse(data);
37
- const text = json.choices[0].delta.content;
38
- const queue = encoder.encode(text);
39
- controller.enqueue(queue);
40
- } catch (e) {
41
- controller.error(e);
42
- }
43
- }
44
- }
45
-
46
- const parser = createParser(onParse);
47
- for await (const chunk of res.body as any) {
48
- parser.feed(decoder.decode(chunk));
49
- }
50
- },
51
- });
52
- return stream;
53
- }
54
-
55
- export async function POST(req: NextRequest) {
56
- try {
57
- const stream = await createStream(req);
58
- return new Response(stream);
59
- } catch (error) {
60
- console.error("[Chat Stream]", error);
61
- }
62
- }
63
-
64
- export const config = {
65
- runtime: "edge",
66
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/chat/.gitignore DELETED
@@ -1 +0,0 @@
1
- config.ts
 
 
app/api/chat/route.ts DELETED
@@ -1,29 +0,0 @@
1
- import { OpenAIApi, Configuration } from "openai";
2
- import { ChatRequest } from "./typing";
3
-
4
- export async function POST(req: Request) {
5
- try {
6
- let apiKey = process.env.OPENAI_API_KEY;
7
-
8
- const userApiKey = req.headers.get("token");
9
- if (userApiKey) {
10
- apiKey = userApiKey;
11
- }
12
-
13
- const openai = new OpenAIApi(
14
- new Configuration({
15
- apiKey,
16
- })
17
- );
18
-
19
- const requestBody = (await req.json()) as ChatRequest;
20
- const completion = await openai!.createChatCompletion({
21
- ...requestBody,
22
- });
23
-
24
- return new Response(JSON.stringify(completion.data));
25
- } catch (e) {
26
- console.error("[Chat] ", e);
27
- return new Response(JSON.stringify(e));
28
- }
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/chat/typing.ts DELETED
@@ -1,11 +0,0 @@
1
- import type {
2
- CreateChatCompletionRequest,
3
- CreateChatCompletionResponse,
4
- CreateImageRequest,
5
- ImagesResponse,
6
- } from "openai";
7
-
8
- export type ChatRequest = CreateChatCompletionRequest;
9
- export type ChatReponse = CreateChatCompletionResponse;
10
- export type ChatImageRequest = CreateImageRequest;
11
- export type ChatImagesResponse = ImagesResponse;
 
 
 
 
 
 
 
 
 
 
 
 
app/api/newbing-image/route.ts DELETED
@@ -1,16 +0,0 @@
1
- import { BingChat} from "../../bing-chat/build/index";
2
-
3
- export async function POST(req: Request) {
4
- try {
5
- let cookies = process.env.COOKIES;
6
- const api = new BingChat({
7
- cookie: cookies,
8
- });
9
- const res = await api.createImage(await req.json());
10
- console.log(res)
11
- return new Response();
12
- } catch (e) {
13
- console.error("[NewBing] ", e);
14
- return new Response(JSON.stringify(e));
15
- }
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/newbing/route.ts DELETED
@@ -1,81 +0,0 @@
1
- import { BingChat, ChatMessage } from "../../bing-chat/build/index";
2
-
3
- export async function POST(req: Request) {
4
- try {
5
- let cookies = process.env.COOKIES;
6
- const api = new BingChat({
7
- cookie: cookies,
8
- });
9
- let chat: ChatMessage = {
10
- id: "",
11
- text: "",
12
- author: "bot",
13
- conversationId: "",
14
- clientId: "",
15
- conversationSignature: "",
16
- };
17
- const res: any = await bingAiMessageSendWrapper(
18
- api,
19
- await req.json(),
20
- chat,
21
- );
22
- // const res = await api.sendMessage(await req.json(), {
23
- // // print the partial response as the AI is "typing"
24
- // onProgress: (partialResponse) => {
25
- // console.log(partialResponse.text);
26
- // },
27
- // variant: "Precise",
28
- // });
29
- // console.log(res['text'])
30
- return new Response(res["text"]);
31
- } catch (e) {
32
- console.error("[NewBing] ", e);
33
- return new Response(JSON.stringify(e));
34
- }
35
- }
36
-
37
- /**
38
- * @param { import("bing-chat").BingChat } client
39
- * @param { string } message
40
- * @param { import("bing-chat").ChatMessage } [session]
41
- * @returns { Promise<import("bing-chat").ChatMessage> }
42
- */
43
- function bingAiMessageSendWrapper(
44
- client: BingChat,
45
- message: string,
46
- session: ChatMessage,
47
- ) {
48
- const TIMEOUT_THRESHOLD = 120 * 1000;
49
- return new Promise((resolve, reject) => {
50
- let response = {
51
- text: "",
52
- };
53
- let responseText = "";
54
- let temp = {
55
- time: new Date().valueOf(),
56
- response: response,
57
- };
58
- const verifyIfResponseChangedInterval = setInterval(() => {
59
- if (new Date().valueOf() - temp.time > TIMEOUT_THRESHOLD) {
60
- clearInterval(verifyIfResponseChangedInterval);
61
- temp.response.text = responseText;
62
- resolve(temp.response);
63
- }
64
- }, 500);
65
- client
66
- .sendMessage(message, {
67
- ...session,
68
- onProgress: (partialResponse) => {
69
- temp.response = partialResponse;
70
- responseText += partialResponse.text;
71
- temp.time = new Date().valueOf();
72
- },
73
- })
74
- .then((response) => {
75
- resolve(response);
76
- })
77
- .catch((error) => {
78
- reject(error);
79
- });
80
- });
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/revchat/route.ts DELETED
@@ -1,66 +0,0 @@
1
- import { createParser } from "eventsource-parser";
2
- import { NextRequest } from "next/server";
3
-
4
- async function createStream(req: NextRequest) {
5
- const encoder = new TextEncoder();
6
- const decoder = new TextDecoder();
7
-
8
- const res = await fetch("http://lemurchat.anfans.cn/api/chat/conversation-trial", {
9
- headers: {
10
- "Content-Type": "application/json"
11
- },
12
- method: "POST",
13
- body: req.body,
14
- });
15
-
16
- const stream = new ReadableStream({
17
- async start(controller) {
18
- function onParse(event: any) {
19
- if (event.type === "event") {
20
- const data = event.data;
21
- if(event.id=="1"){
22
- let text1=data.slice(data.indexOf("content"))
23
- const text = text1.slice(12,text1.indexOf("index")-6)
24
- const queue = encoder.encode(text);
25
- controller.enqueue(queue);
26
- return;
27
- }
28
- // https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
29
- try {
30
- const json = JSON.parse(data);
31
- // console.log(data.indexOf("content"))
32
- if (data.indexOf("content")==-1) {
33
- controller.close();
34
- return;
35
- }
36
- // console.log(event.data)
37
- const text = JSON.parse(json.data.slice(5)).choices[0].delta.content;
38
- const queue = encoder.encode(text);
39
- controller.enqueue(queue);
40
- } catch (e) {
41
- controller.error(e);
42
- }
43
- }
44
- }
45
-
46
- const parser = createParser(onParse);
47
- for await (const chunk of res.body as any) {
48
- parser.feed(decoder.decode(chunk));
49
- }
50
- },
51
- });
52
- return stream;
53
- }
54
-
55
- export async function POST(req: NextRequest) {
56
- try {
57
- const stream = await createStream(req);
58
- return new Response(stream);
59
- } catch (error) {
60
- console.error("[Chat Stream]", error);
61
- }
62
- }
63
-
64
- export const config = {
65
- runtime: "edge",
66
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/api/wanjuan/route.ts DELETED
@@ -1,35 +0,0 @@
1
- export async function POST(req: Request) {
2
- try {
3
- let token = process.env.WANJUAN_TOKEN;
4
- let body = { message: await req.json() };
5
-
6
- console.log(JSON.stringify(body));
7
- let res = "";
8
- await fetch("http://47.94.237.159:8080/v1/wanjuan", {
9
- method: "POST",
10
- headers:{
11
- "Authorization":"Bearer "+token
12
- },
13
- body: JSON.stringify(body),
14
- })
15
- .then((response) => response.json())
16
- .then((data) => {
17
- // console.log(data)
18
- if (data["statusInfo"]["code"] == 0) {
19
- // console.log("123123")
20
- res = data["data"]["msgContent"];
21
- } else {
22
- res = data["statusInfo"]["message"];
23
- }
24
- })
25
- .catch((err) => {
26
- console.error("[WanJuan] ", err);
27
- res = "出错了请重试!";
28
- });
29
- // console.log("12312"+res);
30
- return new Response(res);
31
- } catch (e) {
32
- console.error("[WanJuan] ", e);
33
- return new Response(JSON.stringify(e));
34
- }
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/bing-chat/build/index.d.ts DELETED
@@ -1,275 +0,0 @@
1
- type Author = "user" | "bot";
2
- type SendMessageOptions = {
3
- conversationId?: string;
4
- clientId?: string;
5
- conversationSignature?: string;
6
- invocationId?: string;
7
- messageType?: string;
8
- variant?: string;
9
- locale?: string;
10
- market?: string;
11
- region?: string;
12
- location?: {
13
- lat: number | string;
14
- lng: number | string;
15
- re?: string;
16
- };
17
- onProgress?: (partialResponse: ChatMessage) => void;
18
- };
19
- interface ChatMessage {
20
- id: string;
21
- text: string;
22
- author: Author;
23
- conversationId: string;
24
- clientId: string;
25
- conversationSignature: string;
26
- conversationExpiryTime?: string;
27
- invocationId?: string;
28
- messageType?: string;
29
- variant?: string;
30
- detail?: ChatMessageFull | ChatMessagePartial;
31
- }
32
- interface ConversationResult {
33
- conversationId: string;
34
- clientId: string;
35
- conversationSignature: string;
36
- result: APIResult;
37
- }
38
- interface APIResult {
39
- value: string;
40
- message: null;
41
- }
42
- interface ChatUpdate {
43
- type: 1;
44
- target: string;
45
- arguments: ChatUpdateArgument[];
46
- }
47
- interface ChatUpdateArgument {
48
- messages: ChatMessagePartial[];
49
- requestId: string;
50
- result: null;
51
- }
52
- interface ChatMessagePartial {
53
- text: string;
54
- author: Author;
55
- createdAt: string;
56
- timestamp: string;
57
- messageId: string;
58
- offense: string;
59
- adaptiveCards: AdaptiveCard[];
60
- sourceAttributions: any[];
61
- feedback: ChatMessageFeedback;
62
- contentOrigin: string;
63
- privacy?: null;
64
- messageType?: string;
65
- }
66
- interface AdaptiveCard {
67
- type: string;
68
- version: string;
69
- body: AdaptiveCardBody[];
70
- }
71
- interface AdaptiveCardBody {
72
- type: string;
73
- text: string;
74
- wrap: boolean;
75
- }
76
- interface ChatMessageFeedback {
77
- tag: null;
78
- updatedOn: null;
79
- type: string;
80
- }
81
- interface ChatUpdateCompleteResponse {
82
- type: 2;
83
- invocationId: string;
84
- item: ChatResponseItem;
85
- }
86
- interface ChatResponseItem {
87
- messages: ChatMessageFull[];
88
- firstNewMessageIndex: number;
89
- suggestedResponses: null;
90
- conversationId: string;
91
- requestId: string;
92
- conversationExpiryTime: string;
93
- telemetry: Telemetry;
94
- result: ChatRequestResult;
95
- }
96
- interface ChatMessageFull {
97
- text: string;
98
- author: Author;
99
- from?: ChatMessageFrom;
100
- createdAt: string;
101
- timestamp: string;
102
- locale?: string;
103
- market?: string;
104
- region?: string;
105
- location?: string;
106
- locationHints?: LocationHint[];
107
- messageId: string;
108
- requestId: string;
109
- offense: string;
110
- feedback: ChatMessageFeedback;
111
- contentOrigin: string;
112
- privacy?: null;
113
- inputMethod?: string;
114
- adaptiveCards?: AdaptiveCard[];
115
- sourceAttributions?: any[];
116
- suggestedResponses?: SuggestedResponse[];
117
- messageType?: string;
118
- }
119
- interface ChatMessageFrom {
120
- id: string;
121
- name: null;
122
- }
123
- interface LocationHint {
124
- country: string;
125
- countryConfidence: number;
126
- state: string;
127
- city: string;
128
- cityConfidence: number;
129
- zipCode: string;
130
- timeZoneOffset: number;
131
- dma: number;
132
- sourceType: number;
133
- center: Coords;
134
- regionType: number;
135
- }
136
- interface Coords {
137
- latitude: number;
138
- longitude: number;
139
- height: null;
140
- }
141
- interface SuggestedResponse {
142
- text: string;
143
- messageId: string;
144
- messageType: string;
145
- contentOrigin: string;
146
- author?: Author;
147
- createdAt?: string;
148
- timestamp?: string;
149
- offense?: string;
150
- feedback?: ChatMessageFeedback;
151
- privacy?: null;
152
- }
153
- interface ChatRequestResult {
154
- value: string;
155
- serviceVersion: string;
156
- }
157
- interface Telemetry {
158
- metrics?: null;
159
- startTime: string;
160
- }
161
- interface ChatRequest {
162
- arguments: ChatRequestArgument[];
163
- invocationId: string;
164
- target: string;
165
- type: number;
166
- }
167
- interface ChatRequestArgument {
168
- source: string;
169
- optionsSets: string[];
170
- allowedMessageTypes: string[];
171
- sliceIds: any[];
172
- traceId: string;
173
- isStartOfSession: boolean;
174
- message: ChatRequestMessage;
175
- conversationSignature: string;
176
- participant: Participant;
177
- conversationId: string;
178
- previousMessages: PreviousMessage[];
179
- }
180
- interface ChatRequestMessage {
181
- locale: string;
182
- market: string;
183
- region?: string;
184
- location?: string;
185
- locationHints?: LocationHintChatRequestMessage[];
186
- timestamp: string;
187
- author: Author;
188
- inputMethod: string;
189
- text: string;
190
- messageType: string;
191
- }
192
- interface LocationHintChatRequestMessage {
193
- country: string;
194
- state: string;
195
- city: string;
196
- zipcode: string;
197
- timezoneoffset: number;
198
- dma: number;
199
- countryConfidence: number;
200
- cityConfidence: number;
201
- Center: Center;
202
- RegionType: number;
203
- SourceType: number;
204
- }
205
- interface Center {
206
- Latitude: number;
207
- Longitude: number;
208
- }
209
- interface Participant {
210
- id: string;
211
- }
212
- interface PreviousMessage {
213
- text: string;
214
- author: Author;
215
- adaptiveCards: any[];
216
- suggestedResponses: SuggestedResponse[];
217
- messageId: string;
218
- messageType: string;
219
- }
220
-
221
- declare class BingChat {
222
- protected _cookie: string;
223
- protected _debug: boolean;
224
- constructor(opts: {
225
- cookie: string | undefined;
226
- /** @defaultValue `false` **/
227
- debug?: boolean;
228
- });
229
- /**
230
- * Sends a message to Bing Chat, waits for the response to resolve, and returns
231
- * the response.
232
- *
233
- * If you want to receive a stream of partial responses, use `opts.onProgress`.
234
- *
235
- * @param message - The prompt message to send
236
- * @param opts.conversationId - Optional ID of a conversation to continue (defaults to a random UUID)
237
- * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
238
- *
239
- * @returns The response from Bing Chat
240
- */
241
- sendMessage(text: string, opts?: SendMessageOptions): Promise<ChatMessage>;
242
- createConversation(): Promise<ConversationResult>;
243
- createImage(message: string): Array;
244
- }
245
-
246
- export {
247
- APIResult,
248
- AdaptiveCard,
249
- AdaptiveCardBody,
250
- Author,
251
- BingChat,
252
- Center,
253
- ChatMessage,
254
- ChatMessageFeedback,
255
- ChatMessageFrom,
256
- ChatMessageFull,
257
- ChatMessagePartial,
258
- ChatRequest,
259
- ChatRequestArgument,
260
- ChatRequestMessage,
261
- ChatRequestResult,
262
- ChatResponseItem,
263
- ChatUpdate,
264
- ChatUpdateArgument,
265
- ChatUpdateCompleteResponse,
266
- ConversationResult,
267
- Coords,
268
- LocationHint,
269
- LocationHintChatRequestMessage,
270
- Participant,
271
- PreviousMessage,
272
- SendMessageOptions,
273
- SuggestedResponse,
274
- Telemetry,
275
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/bing-chat/build/index.js DELETED
@@ -1,307 +0,0 @@
1
- // src/bing-chat.ts
2
- import { time } from "node:console";
3
- import crypto from "node:crypto";
4
- import WebSocket from "ws";
5
-
6
- // src/fetch.ts
7
- var fetch = globalThis.fetch;
8
- if (typeof fetch !== "function") {
9
- throw new Error("Invalid environment: global fetch not defined");
10
- }
11
-
12
- // src/bing-chat.ts
13
- var terminalChar = "";
14
- var BingChat = class {
15
- constructor(opts) {
16
- const { cookie, debug = false } = opts;
17
- this._cookie = cookie;
18
- this._debug = !!debug;
19
- if (!this._cookie) {
20
- throw new Error("Bing cookie is required");
21
- }
22
- }
23
- /**
24
- * Sends a message to Bing Chat, waits for the response to resolve, and returns
25
- * the response.
26
- *
27
- * If you want to receive a stream of partial responses, use `opts.onProgress`.
28
- *
29
- * @param message - The prompt message to send
30
- * @param opts.conversationId - Optional ID of a conversation to continue (defaults to a random UUID)
31
- * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
32
- *
33
- * @returns The response from Bing Chat
34
- */
35
- async sendMessage(text, opts = {}) {
36
- const {
37
- invocationId = "1",
38
- onProgress,
39
- locale = "en-US",
40
- market = "en-US",
41
- region = "US",
42
- location,
43
- messageType = "Chat",
44
- variant = "Balanced"
45
- } = opts;
46
- let { conversationId, clientId, conversationSignature } = opts;
47
- const isStartOfSession = !(conversationId && clientId && conversationSignature);
48
- if (isStartOfSession) {
49
- const conversation = await this.createConversation();
50
- conversationId = conversation.conversationId;
51
- clientId = conversation.clientId;
52
- conversationSignature = conversation.conversationSignature;
53
- }
54
- const result = {
55
- author: "bot",
56
- id: crypto.randomUUID(),
57
- conversationId,
58
- clientId,
59
- conversationSignature,
60
- invocationId: `${parseInt(invocationId, 10) + 1}`,
61
- text: ""
62
- };
63
- const responseP = new Promise(
64
- async (resolve, reject) => {
65
- const chatWebsocketUrl = "wss://sydney.bing.com/sydney/ChatHub";
66
- const ws = new WebSocket(chatWebsocketUrl, {
67
- perMessageDeflate: false,
68
- headers: {
69
- "accept-language": "en-US,en;q=0.9",
70
- "cache-control": "no-cache",
71
- pragma: "no-cache"
72
- }
73
- });
74
- let isFulfilled = false;
75
- function cleanup() {
76
- ws.close();
77
- ws.removeAllListeners();
78
- }
79
- ws.on("error", (error) => {
80
- console.warn("WebSocket error:", error);
81
- cleanup();
82
- if (!isFulfilled) {
83
- isFulfilled = true;
84
- reject(new Error(`WebSocket error: ${error.toString()}`));
85
- }
86
- });
87
- ws.on("close", () => {
88
- });
89
- ws.on("open", () => {
90
- ws.send(`{"protocol":"json","version":1}${terminalChar}`);
91
- });
92
- let stage = 0;
93
- ws.on("message", (data) => {
94
- var _a, _b;
95
- const objects = data.toString().split(terminalChar);
96
- const messages = objects.map((object) => {
97
- try {
98
- return JSON.parse(object);
99
- } catch (error) {
100
- return object;
101
- }
102
- }).filter(Boolean);
103
- if (!messages.length) {
104
- return;
105
- }
106
- if (stage === 0) {
107
- ws.send(`{"type":6}${terminalChar}`);
108
- const traceId = crypto.randomBytes(16).toString("hex");
109
- const locationStr = location ? `lat:${location.lat};long:${location.lng};re=${location.re || "1000m"};` : void 0;
110
- const optionsSets = [
111
- "nlu_direct_response_filter",
112
- "deepleo",
113
- "enable_debug_commands",
114
- "disable_emoji_spoken_text",
115
- "responsible_ai_policy_235",
116
- 'enablemm',
117
- 'trffovrd',
118
- 'h3toppfp3',
119
- 'forcerep',
120
- 'cpcttl1d',
121
- 'dv3sugg'
122
- ];
123
- if (variant == "Balanced") {
124
- optionsSets.push("galileo");
125
- optionsSets.push('glprompt')
126
- } else if (variant == "Creative") {
127
- optionsSets.push("h3imaginative");
128
- optionsSets.push('gencontentv3')
129
- } else if (variant == "Precise") {
130
- optionsSets.push("h3precise");
131
- }
132
- const params = {
133
- arguments: [
134
- {
135
- source: "cib",
136
- optionsSets,
137
- allowedMessageTypes: [
138
- "Chat",
139
- "InternalSearchQuery",
140
- "InternalSearchResult",
141
- "InternalLoaderMessage",
142
- "RenderCardRequest",
143
- "AdsQuery",
144
- "SemanticSerp"
145
- ],
146
- sliceIds: [],
147
- traceId,
148
- isStartOfSession,
149
- message: {
150
- locale,
151
- market,
152
- region,
153
- location: locationStr,
154
- author: "user",
155
- inputMethod: "Keyboard",
156
- messageType,
157
- text
158
- },
159
- conversationSignature,
160
- participant: { id: clientId },
161
- conversationId
162
- }
163
- ],
164
- invocationId,
165
- target: "chat",
166
- type: 4
167
- };
168
- if (this._debug) {
169
- console.log(chatWebsocketUrl, JSON.stringify(params, null, 2));
170
- }
171
- ws.send(`${JSON.stringify(params)}${terminalChar}`);
172
- ++stage;
173
- return;
174
- }
175
- for (const message of messages) {
176
- if (message.type === 1) {
177
- const update = message;
178
- const msg = (_a = update.arguments[0].messages) == null ? void 0 : _a[0];
179
- if (!msg)
180
- continue;
181
- if (!msg.messageType) {
182
- result.author = msg.author;
183
- result.text = msg.text;
184
- result.detail = msg;
185
- onProgress == null ? void 0 : onProgress(result);
186
- }
187
- } else if (message.type === 2) {
188
- const response = message;
189
- if (this._debug) {
190
- console.log("RESPONSE", JSON.stringify(response, null, 2));
191
- }
192
- const validMessages = (_b = response.item.messages) == null ? void 0 : _b.filter(
193
- (m) => !m.messageType
194
- );
195
- const lastMessage = validMessages == null ? void 0 : validMessages[(validMessages == null ? void 0 : validMessages.length) - 1];
196
- if (lastMessage) {
197
- result.conversationId = response.item.conversationId;
198
- result.conversationExpiryTime = response.item.conversationExpiryTime;
199
- result.author = lastMessage.author;
200
- result.text = lastMessage.text;
201
- result.detail = lastMessage;
202
- if (!isFulfilled) {
203
- isFulfilled = true;
204
- resolve(result);
205
- }
206
- }
207
- } else if (message.type === 3) {
208
- if (!isFulfilled) {
209
- isFulfilled = true;
210
- resolve(result);
211
- }
212
- cleanup();
213
- return;
214
- } else {
215
- }
216
- }
217
- });
218
- }
219
- );
220
- return responseP;
221
- }
222
- async createConversation() {
223
- const requestId = crypto.randomUUID();
224
- const cookie = this._cookie.includes(";") ? this._cookie : `_U=${this._cookie}`;
225
- return fetch("https://www.bing.com/turing/conversation/create", {
226
- headers: {
227
- accept: "application/json",
228
- "accept-language": "en-US,en;q=0.9",
229
- "content-type": "application/json",
230
- "sec-ch-ua": '"Not_A Brand";v="99", "Microsoft Edge";v="109", "Chromium";v="109"',
231
- "sec-ch-ua-arch": '"x86"',
232
- "sec-ch-ua-bitness": '"64"',
233
- "sec-ch-ua-full-version": '"109.0.1518.78"',
234
- "sec-ch-ua-full-version-list": '"Not_A Brand";v="99.0.0.0", "Microsoft Edge";v="109.0.1518.78", "Chromium";v="109.0.5414.120"',
235
- "sec-ch-ua-mobile": "?0",
236
- "sec-ch-ua-model": "",
237
- "sec-ch-ua-platform": '"macOS"',
238
- "sec-ch-ua-platform-version": '"12.6.0"',
239
- "sec-fetch-dest": "empty",
240
- "sec-fetch-mode": "cors",
241
- "sec-fetch-site": "same-origin",
242
- "x-edge-shopping-flag": "1",
243
- "x-ms-client-request-id": requestId,
244
- "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/MacIntel",
245
- 'x-forwarded-for': '1.1.1.1',
246
- cookie
247
- },
248
- referrer: "https://www.bing.com/search",
249
- referrerPolicy: "origin-when-cross-origin",
250
- body: null,
251
- method: "GET",
252
- mode: "cors",
253
- credentials: "include",
254
- }).then((res) => {
255
- if (res.ok) {
256
- return res.json();
257
- } else {
258
- throw new Error(
259
- `unexpected HTTP error createConversation ${res.status}: ${res.statusText}`
260
- );
261
- }
262
- });
263
- }
264
- async createImage(message) {
265
- // const requestId = crypto.randomUUID();
266
- console.log(1)
267
- const cookie = this._cookie.includes(";") ? this._cookie : `_U=${this._cookie}`;
268
- let res = await fetch(`https://www.bing.com/images/create?q=${message}&rt=3&FORM=GENCRE`, {
269
- headers: {
270
- cookie
271
- },
272
- method: "GET",
273
- mode: "cors"
274
- })
275
- console.log(res.headers)
276
- if (res.status!= 302) {
277
- throw new Error(
278
- `unexpected HTTP error createConversation ${res.status}: ${res.statusText}`
279
- );
280
- }
281
- console.log(2)
282
- redirect_url=res.headers["Location"].replace("&nfy=1", "")
283
- request_id = redirect_url.split("id=")[-1]
284
- await fetch("https://www.bing.com"+redirect_url,{method:"GET"})
285
- polling_url = `https://www.bing.com/images/create/async/results/${request_id}?q=${message}`
286
- while (true){
287
- res = await fetch(polling_url,{method:"GET"})
288
- if(res.json()!=''||res.json().indexOf("errorMessage") != -1){
289
- await setTimeout(()=>{console.log(123)},1000)
290
- continue
291
- }else{
292
- break
293
- }
294
- }
295
- console.log(3)
296
- image_links = res.json().match('src="([^"]+)"')
297
- normal_image_links = []
298
- for(let i=0 ;i<image_links.length;i++){
299
- normal_image_links.push(image_links[i].split("?w=")[0])
300
- }
301
- return normal_image_links
302
- }
303
- };
304
- export {
305
- BingChat
306
- };
307
- //# sourceMappingURL=index.js.map
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/bing-chat/build/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bing-chat.ts","../src/fetch.ts"],"sourcesContent":["import crypto from 'node:crypto'\n\nimport WebSocket from 'ws'\n\nimport * as types from './types'\nimport { fetch } from './fetch'\n\nconst terminalChar = '\u001e'\n\nexport class BingChat {\n protected _cookie: string\n protected _debug: boolean\n\n constructor(opts: {\n cookie: string\n\n /** @defaultValue `false` **/\n debug?: boolean\n }) {\n const { cookie, debug = false } = opts\n\n this._cookie = cookie\n this._debug = !!debug\n\n if (!this._cookie) {\n throw new Error('Bing cookie is required')\n }\n }\n\n /**\n * Sends a message to Bing Chat, waits for the response to resolve, and returns\n * the response.\n *\n * If you want to receive a stream of partial responses, use `opts.onProgress`.\n *\n * @param message - The prompt message to send\n * @param opts.conversationId - Optional ID of a conversation to continue (defaults to a random UUID)\n * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated\n *\n * @returns The response from Bing Chat\n */\n async sendMessage(\n text: string,\n opts: types.SendMessageOptions = {}\n ): Promise<types.ChatMessage> {\n const {\n invocationId = '1',\n onProgress,\n locale = 'en-US',\n market = 'en-US',\n region = 'US',\n location,\n messageType = 'Chat',\n variant = 'Balanced'\n } = opts\n\n let { conversationId, clientId, conversationSignature } = opts\n const isStartOfSession = !(\n conversationId &&\n clientId &&\n conversationSignature\n )\n\n if (isStartOfSession) {\n const conversation = await this.createConversation()\n conversationId = conversation.conversationId\n clientId = conversation.clientId\n conversationSignature = conversation.conversationSignature\n }\n\n const result: types.ChatMessage = {\n author: 'bot',\n id: crypto.randomUUID(),\n conversationId,\n clientId,\n conversationSignature,\n invocationId: `${parseInt(invocationId, 10) + 1}`,\n text: ''\n }\n\n const responseP = new Promise<types.ChatMessage>(\n async (resolve, reject) => {\n const chatWebsocketUrl = 'wss://sydney.bing.com/sydney/ChatHub'\n const ws = new WebSocket(chatWebsocketUrl, {\n perMessageDeflate: false,\n headers: {\n 'accept-language': 'en-US,en;q=0.9',\n 'cache-control': 'no-cache',\n pragma: 'no-cache'\n }\n })\n\n let isFulfilled = false\n\n function cleanup() {\n ws.close()\n ws.removeAllListeners()\n }\n\n ws.on('error', (error) => {\n console.warn('WebSocket error:', error)\n cleanup()\n if (!isFulfilled) {\n isFulfilled = true\n reject(new Error(`WebSocket error: ${error.toString()}`))\n }\n })\n ws.on('close', () => {\n // TODO\n })\n\n ws.on('open', () => {\n ws.send(`{\"protocol\":\"json\",\"version\":1}${terminalChar}`)\n })\n let stage = 0\n\n ws.on('message', (data) => {\n const objects = data.toString().split(terminalChar)\n\n const messages = objects\n .map((object) => {\n try {\n return JSON.parse(object)\n } catch (error) {\n return object\n }\n })\n .filter(Boolean)\n\n if (!messages.length) {\n return\n }\n\n if (stage === 0) {\n ws.send(`{\"type\":6}${terminalChar}`)\n\n const traceId = crypto.randomBytes(16).toString('hex')\n\n // example location: 'lat:47.639557;long:-122.128159;re=1000m;'\n const locationStr = location\n ? `lat:${location.lat};long:${location.lng};re=${\n location.re || '1000m'\n };`\n : undefined\n\n // Sets the correct options for the variant of the model\n const optionsSets = [\n 'nlu_direct_response_filter',\n 'deepleo',\n 'enable_debug_commands',\n 'disable_emoji_spoken_text',\n 'responsible_ai_policy_235',\n 'enablemm'\n ]\n if (variant == 'Balanced') {\n optionsSets.push('galileo')\n } else {\n optionsSets.push('clgalileo')\n if (variant == 'Creative') {\n optionsSets.push('h3imaginative')\n } else if (variant == 'Precise') {\n optionsSets.push('h3precise')\n }\n }\n const params = {\n arguments: [\n {\n source: 'cib',\n optionsSets,\n allowedMessageTypes: [\n 'Chat',\n 'InternalSearchQuery',\n 'InternalSearchResult',\n 'InternalLoaderMessage',\n 'RenderCardRequest',\n 'AdsQuery',\n 'SemanticSerp'\n ],\n sliceIds: [],\n traceId,\n isStartOfSession,\n message: {\n locale,\n market,\n region,\n location: locationStr,\n author: 'user',\n inputMethod: 'Keyboard',\n messageType,\n text\n },\n conversationSignature,\n participant: { id: clientId },\n conversationId\n }\n ],\n invocationId,\n target: 'chat',\n type: 4\n }\n\n if (this._debug) {\n console.log(chatWebsocketUrl, JSON.stringify(params, null, 2))\n }\n\n ws.send(`${JSON.stringify(params)}${terminalChar}`)\n\n ++stage\n return\n }\n\n for (const message of messages) {\n // console.log(JSON.stringify(message, null, 2))\n\n if (message.type === 1) {\n const update = message as types.ChatUpdate\n const msg = update.arguments[0].messages?.[0]\n\n if (!msg) continue\n\n // console.log('RESPONSE0', JSON.stringify(update, null, 2))\n\n if (!msg.messageType) {\n result.author = msg.author\n result.text = msg.text\n result.detail = msg\n\n onProgress?.(result)\n }\n } else if (message.type === 2) {\n const response = message as types.ChatUpdateCompleteResponse\n if (this._debug) {\n console.log('RESPONSE', JSON.stringify(response, null, 2))\n }\n const validMessages = response.item.messages?.filter(\n (m) => !m.messageType\n )\n const lastMessage = validMessages?.[validMessages?.length - 1]\n\n if (lastMessage) {\n result.conversationId = response.item.conversationId\n result.conversationExpiryTime =\n response.item.conversationExpiryTime\n\n result.author = lastMessage.author\n result.text = lastMessage.text\n result.detail = lastMessage\n\n if (!isFulfilled) {\n isFulfilled = true\n resolve(result)\n }\n }\n } else if (message.type === 3) {\n if (!isFulfilled) {\n isFulfilled = true\n resolve(result)\n }\n\n cleanup()\n return\n } else {\n // TODO: handle other message types\n // these may be for displaying \"adaptive cards\"\n // console.warn('unexpected message type', message.type, message)\n }\n }\n })\n }\n )\n\n return responseP\n }\n\n async createConversation(): Promise<types.ConversationResult> {\n const requestId = crypto.randomUUID()\n\n const cookie = this._cookie.includes(';')\n ? this._cookie\n : `_U=${this._cookie}`\n\n return fetch('https://www.bing.com/turing/conversation/create', {\n headers: {\n accept: 'application/json',\n 'accept-language': 'en-US,en;q=0.9',\n 'content-type': 'application/json',\n 'sec-ch-ua':\n '\"Not_A Brand\";v=\"99\", \"Microsoft Edge\";v=\"109\", \"Chromium\";v=\"109\"',\n 'sec-ch-ua-arch': '\"x86\"',\n 'sec-ch-ua-bitness': '\"64\"',\n 'sec-ch-ua-full-version': '\"109.0.1518.78\"',\n 'sec-ch-ua-full-version-list':\n '\"Not_A Brand\";v=\"99.0.0.0\", \"Microsoft Edge\";v=\"109.0.1518.78\", \"Chromium\";v=\"109.0.5414.120\"',\n 'sec-ch-ua-mobile': '?0',\n 'sec-ch-ua-model': '',\n 'sec-ch-ua-platform': '\"macOS\"',\n 'sec-ch-ua-platform-version': '\"12.6.0\"',\n 'sec-fetch-dest': 'empty',\n 'sec-fetch-mode': 'cors',\n 'sec-fetch-site': 'same-origin',\n 'x-edge-shopping-flag': '1',\n 'x-ms-client-request-id': requestId,\n 'x-ms-useragent':\n 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.10.0 OS/MacIntel',\n cookie\n },\n referrer: 'https://www.bing.com/search',\n referrerPolicy: 'origin-when-cross-origin',\n body: null,\n method: 'GET',\n mode: 'cors',\n credentials: 'include'\n }).then((res) => {\n if (res.ok) {\n return res.json()\n } else {\n throw new Error(\n `unexpected HTTP error createConversation ${res.status}: ${res.statusText}`\n )\n }\n })\n }\n}\n","/// <reference lib=\"dom\" />\n\nconst fetch = globalThis.fetch\n\nif (typeof fetch !== 'function') {\n throw new Error('Invalid environment: global fetch not defined')\n}\n\nexport { fetch }\n"],"mappings":";AAAA,OAAO,YAAY;AAEnB,OAAO,eAAe;;;ACAtB,IAAM,QAAQ,WAAW;AAEzB,IAAI,OAAO,UAAU,YAAY;AAC/B,QAAM,IAAI,MAAM,+CAA+C;AACjE;;;ADCA,IAAM,eAAe;AAEd,IAAM,WAAN,MAAe;AAAA,EAIpB,YAAY,MAKT;AACD,UAAM,EAAE,QAAQ,QAAQ,MAAM,IAAI;AAElC,SAAK,UAAU;AACf,SAAK,SAAS,CAAC,CAAC;AAEhB,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,YACJ,MACA,OAAiC,CAAC,GACN;AAC5B,UAAM;AAAA,MACJ,eAAe;AAAA,MACf;AAAA,MACA,SAAS;AAAA,MACT,SAAS;AAAA,MACT,SAAS;AAAA,MACT;AAAA,MACA,cAAc;AAAA,MACd,UAAU;AAAA,IACZ,IAAI;AAEJ,QAAI,EAAE,gBAAgB,UAAU,sBAAsB,IAAI;AAC1D,UAAM,mBAAmB,EACvB,kBACA,YACA;AAGF,QAAI,kBAAkB;AACpB,YAAM,eAAe,MAAM,KAAK,mBAAmB;AACnD,uBAAiB,aAAa;AAC9B,iBAAW,aAAa;AACxB,8BAAwB,aAAa;AAAA,IACvC;AAEA,UAAM,SAA4B;AAAA,MAChC,QAAQ;AAAA,MACR,IAAI,OAAO,WAAW;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,GAAG,SAAS,cAAc,EAAE,IAAI;AAAA,MAC9C,MAAM;AAAA,IACR;AAEA,UAAM,YAAY,IAAI;AAAA,MACpB,OAAO,SAAS,WAAW;AACzB,cAAM,mBAAmB;AACzB,cAAM,KAAK,IAAI,UAAU,kBAAkB;AAAA,UACzC,mBAAmB;AAAA,UACnB,SAAS;AAAA,YACP,mBAAmB;AAAA,YACnB,iBAAiB;AAAA,YACjB,QAAQ;AAAA,UACV;AAAA,QACF,CAAC;AAED,YAAI,cAAc;AAElB,iBAAS,UAAU;AACjB,aAAG,MAAM;AACT,aAAG,mBAAmB;AAAA,QACxB;AAEA,WAAG,GAAG,SAAS,CAAC,UAAU;AACxB,kBAAQ,KAAK,oBAAoB,KAAK;AACtC,kBAAQ;AACR,cAAI,CAAC,aAAa;AAChB,0BAAc;AACd,mBAAO,IAAI,MAAM,oBAAoB,MAAM,SAAS,GAAG,CAAC;AAAA,UAC1D;AAAA,QACF,CAAC;AACD,WAAG,GAAG,SAAS,MAAM;AAAA,QAErB,CAAC;AAED,WAAG,GAAG,QAAQ,MAAM;AAClB,aAAG,KAAK,kCAAkC,cAAc;AAAA,QAC1D,CAAC;AACD,YAAI,QAAQ;AAEZ,WAAG,GAAG,WAAW,CAAC,SAAS;AApHnC;AAqHU,gBAAM,UAAU,KAAK,SAAS,EAAE,MAAM,YAAY;AAElD,gBAAM,WAAW,QACd,IAAI,CAAC,WAAW;AACf,gBAAI;AACF,qBAAO,KAAK,MAAM,MAAM;AAAA,YAC1B,SAAS,OAAP;AACA,qBAAO;AAAA,YACT;AAAA,UACF,CAAC,EACA,OAAO,OAAO;AAEjB,cAAI,CAAC,SAAS,QAAQ;AACpB;AAAA,UACF;AAEA,cAAI,UAAU,GAAG;AACf,eAAG,KAAK,aAAa,cAAc;AAEnC,kBAAM,UAAU,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAGrD,kBAAM,cAAc,WAChB,OAAO,SAAS,YAAY,SAAS,UACnC,SAAS,MAAM,aAEjB;AAGJ,kBAAM,cAAc;AAAA,cAClB;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AACA,gBAAI,WAAW,YAAY;AACzB,0BAAY,KAAK,SAAS;AAAA,YAC5B,OAAO;AACL,0BAAY,KAAK,WAAW;AAC5B,kBAAI,WAAW,YAAY;AACzB,4BAAY,KAAK,eAAe;AAAA,cAClC,WAAW,WAAW,WAAW;AAC/B,4BAAY,KAAK,WAAW;AAAA,cAC9B;AAAA,YACF;AACA,kBAAM,SAAS;AAAA,cACb,WAAW;AAAA,gBACT;AAAA,kBACE,QAAQ;AAAA,kBACR;AAAA,kBACA,qBAAqB;AAAA,oBACnB;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF;AAAA,kBACA,UAAU,CAAC;AAAA,kBACX;AAAA,kBACA;AAAA,kBACA,SAAS;AAAA,oBACP;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,UAAU;AAAA,oBACV,QAAQ;AAAA,oBACR,aAAa;AAAA,oBACb;AAAA,oBACA;AAAA,kBACF;AAAA,kBACA;AAAA,kBACA,aAAa,EAAE,IAAI,SAAS;AAAA,kBAC5B;AAAA,gBACF;AAAA,cACF;AAAA,cACA;AAAA,cACA,QAAQ;AAAA,cACR,MAAM;AAAA,YACR;AAEA,gBAAI,KAAK,QAAQ;AACf,sBAAQ,IAAI,kBAAkB,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,YAC/D;AAEA,eAAG,KAAK,GAAG,KAAK,UAAU,MAAM,IAAI,cAAc;AAElD,cAAE;AACF;AAAA,UACF;AAEA,qBAAW,WAAW,UAAU;AAG9B,gBAAI,QAAQ,SAAS,GAAG;AACtB,oBAAM,SAAS;AACf,oBAAM,OAAM,YAAO,UAAU,CAAC,EAAE,aAApB,mBAA+B;AAE3C,kBAAI,CAAC;AAAK;AAIV,kBAAI,CAAC,IAAI,aAAa;AACpB,uBAAO,SAAS,IAAI;AACpB,uBAAO,OAAO,IAAI;AAClB,uBAAO,SAAS;AAEhB,yDAAa;AAAA,cACf;AAAA,YACF,WAAW,QAAQ,SAAS,GAAG;AAC7B,oBAAM,WAAW;AACjB,kBAAI,KAAK,QAAQ;AACf,wBAAQ,IAAI,YAAY,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,cAC3D;AACA,oBAAM,iBAAgB,cAAS,KAAK,aAAd,mBAAwB;AAAA,gBAC5C,CAAC,MAAM,CAAC,EAAE;AAAA;AAEZ,oBAAM,cAAc,gDAAgB,+CAAe,UAAS;AAE5D,kBAAI,aAAa;AACf,uBAAO,iBAAiB,SAAS,KAAK;AACtC,uBAAO,yBACL,SAAS,KAAK;AAEhB,uBAAO,SAAS,YAAY;AAC5B,uBAAO,OAAO,YAAY;AAC1B,uBAAO,SAAS;AAEhB,oBAAI,CAAC,aAAa;AAChB,gCAAc;AACd,0BAAQ,MAAM;AAAA,gBAChB;AAAA,cACF;AAAA,YACF,WAAW,QAAQ,SAAS,GAAG;AAC7B,kBAAI,CAAC,aAAa;AAChB,8BAAc;AACd,wBAAQ,MAAM;AAAA,cAChB;AAEA,sBAAQ;AACR;AAAA,YACF,OAAO;AAAA,YAIP;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,qBAAwD;AAC5D,UAAM,YAAY,OAAO,WAAW;AAEpC,UAAM,SAAS,KAAK,QAAQ,SAAS,GAAG,IACpC,KAAK,UACL,MAAM,KAAK;AAEf,WAAO,MAAM,mDAAmD;AAAA,MAC9D,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,mBAAmB;AAAA,QACnB,gBAAgB;AAAA,QAChB,aACE;AAAA,QACF,kBAAkB;AAAA,QAClB,qBAAqB;AAAA,QACrB,0BAA0B;AAAA,QAC1B,+BACE;AAAA,QACF,oBAAoB;AAAA,QACpB,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,8BAA8B;AAAA,QAC9B,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,QAClB,kBAAkB;AAAA,QAClB,wBAAwB;AAAA,QACxB,0BAA0B;AAAA,QAC1B,kBACE;AAAA,QACF;AAAA,MACF;AAAA,MACA,UAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,IACf,CAAC,EAAE,KAAK,CAAC,QAAQ;AACf,UAAI,IAAI,IAAI;AACV,eAAO,IAAI,KAAK;AAAA,MAClB,OAAO;AACL,cAAM,IAAI;AAAA,UACR,4CAA4C,IAAI,WAAW,IAAI;AAAA,QACjE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
 
 
app/bing-chat/license DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2023 Travis Fischer
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/bing-chat/package.json DELETED
@@ -1,81 +0,0 @@
1
- {
2
- "name": "bing-chat",
3
- "version": "0.2.3",
4
- "description": "Node.js client for the unofficial Bing Chat API.",
5
- "author": "Travis Fischer <[email protected]>",
6
- "repository": "transitive-bullshit/bing-chat",
7
- "license": "MIT",
8
- "type": "module",
9
- "source": "./src/index.ts",
10
- "types": "./build/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "import": "./build/index.js",
14
- "types": "./build/index.d.ts",
15
- "default": "./build/index.js"
16
- }
17
- },
18
- "files": [
19
- "build"
20
- ],
21
- "engines": {
22
- "node": ">=18"
23
- },
24
- "scripts": {
25
- "build": "tsup",
26
- "dev": "tsup --watch",
27
- "clean": "del build",
28
- "prebuild": "run-s clean",
29
- "predev": "run-s clean",
30
- "pretest": "run-s build",
31
- "docs": "typedoc",
32
- "prepare": "husky install",
33
- "pre-commit": "lint-staged",
34
- "test": "run-p test:*",
35
- "test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
36
- },
37
- "dependencies": {
38
- "ws": "^8.13.0"
39
- },
40
- "devDependencies": {
41
- "@trivago/prettier-plugin-sort-imports": "^4.1.1",
42
- "@types/node": "^18.15.3",
43
- "@types/uuid": "^9.0.1",
44
- "@types/ws": "^8.5.4",
45
- "del-cli": "^5.0.0",
46
- "dotenv-safe": "^8.2.0",
47
- "husky": "^8.0.2",
48
- "lint-staged": "^13.2.0",
49
- "npm-run-all": "^4.1.5",
50
- "ora": "^6.1.2",
51
- "prettier": "^2.8.4",
52
- "tsup": "^6.6.3",
53
- "tsx": "^3.12.5",
54
- "typedoc": "^0.23.26",
55
- "typedoc-plugin-markdown": "^3.13.6",
56
- "typescript": "^4.9.3"
57
- },
58
- "lint-staged": {
59
- "*.{ts,tsx}": [
60
- "prettier --write"
61
- ]
62
- },
63
- "keywords": [
64
- "openai",
65
- "bing",
66
- "chat",
67
- "search",
68
- "sydney",
69
- "gpt",
70
- "gpt-3",
71
- "gpt3",
72
- "gpt4",
73
- "chatbot",
74
- "machine learning",
75
- "conversation",
76
- "conversational",
77
- "ai",
78
- "ml",
79
- "bot"
80
- ]
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/bing-chat/readme.md DELETED
@@ -1,109 +0,0 @@
1
- # Bing Chat API <!-- omit in toc -->
2
-
3
- > Node.js client for the unofficial Bing Chat API. It's like ChatGPT on steroids 🔥
4
-
5
- [![NPM](https://img.shields.io/npm/v/bing-chat.svg)](https://www.npmjs.com/package/bing-chat) [![Build Status](https://github.com/transitive-bullshit/bing-chat/actions/workflows/test.yml/badge.svg)](https://github.com/transitive-bullshit/bing-chat/actions/workflows/test.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/transitive-bullshit/bing-chat/blob/main/license) [![Prettier Code Formatting](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io)
6
-
7
- - [Intro](#intro)
8
- - [Demo](#demo)
9
- - [Install](#install)
10
- - [Usage](#usage)
11
- - [Projects](#projects)
12
- - [Compatibility](#compatibility)
13
- - [Credit](#credit)
14
- - [Related](#related)
15
- - [License](#license)
16
-
17
- ## Intro
18
-
19
- This package is a Node.js wrapper around Bing Chat by Microsoft. TS batteries included. ✨
20
-
21
- > **Warning**
22
- > This package is a reverse-engineered hack. I do not expect it to continue working long-term, and it is not meant for use in production. I'm building this in public, and you can follow the progress on Twitter [@transitive_bs](https://twitter.com/transitive_bs).
23
-
24
- ## Demo
25
-
26
- <p align="center">
27
- <img alt="Example conversation" src="/media/demo.gif">
28
- <i>(30s conversation demo)</i>
29
- </p>
30
-
31
- ## Install
32
-
33
- ```bash
34
- npm install bing-chat
35
- ```
36
-
37
- Make sure you're using `node >= 18` so `fetch` is available.
38
-
39
- ## Usage
40
-
41
- **You need access to Bing Chat OR a valid cookie from someone who has access**.
42
-
43
- The cookie you need from Bing is the `_U` cookie (or just all of the cookies concatenated together; both will work).
44
-
45
- ```ts
46
- import { BingChat } from 'bing-chat'
47
-
48
- async function example() {
49
- const api = new BingChat({
50
- cookie: process.env.BING_COOKIE
51
- })
52
-
53
- const res = await api.sendMessage('Hello World!')
54
- console.log(res.text)
55
- }
56
- ```
57
-
58
- You can follow-up messages to continue the conversation. See `demos/demo-conversation.ts` for an example.
59
-
60
- Note that Bing Chat conversations expire after about 20 minutes, so they're not meant to be long-term objects.
61
-
62
- You can add streaming via the `onProgress` handler:
63
-
64
- ```ts
65
- const res = await api.sendMessage('Write a 500 word essay on frogs.', {
66
- // print the partial response as the AI is "typing"
67
- onProgress: (partialResponse) => console.log(partialResponse.text)
68
- })
69
-
70
- // print the full text at the end
71
- console.log(res.text)
72
- ```
73
-
74
- See `demos/demo-on-progress.ts` for a full example of streaming support.
75
-
76
- You can also add the the parameter `variant` to the `sendMessage` function to change the variant of the AI. The default is `Balanced`, but you can also use `Precise` or `Creative`.
77
-
78
- ```ts
79
- const res = await api.sendMessage('Write a 500 word essay on frogs.', {
80
- // change the variant to 'Precise'
81
- variant: 'Creative'
82
- })
83
- ```
84
-
85
- ## Projects
86
-
87
- If you create a cool integration, feel free to open a PR and add it to the list.
88
-
89
- ## Compatibility
90
-
91
- - This package is ESM-only.
92
- - This package supports `node >= 18`.
93
- - This module assumes that `fetch` is installed globally.
94
- - If you want to build a website using `bing-chat`, we recommend using it only from your backend API
95
-
96
- ## Credit
97
-
98
- - Thanks to [waylaidwanderer](https://github.com/waylaidwanderer) and [canfam](https://github.com/canfam) for helping to reverse-engineer the API 💪
99
-
100
- ## Related
101
-
102
- - [chatgpt](https://github.com/transitive-bullshit/chatgpt-api) - Node.js client for the unofficial ChatGPT API. Same author as this package.
103
- - [discord](https://discord.gg/v9gERj825w) - Join our discord server for hackers building on top of ChatGPT / Bing / LLMs.
104
-
105
- ## License
106
-
107
- MIT © [Travis Fischer](https://transitivebullsh.it)
108
-
109
- If you found this project interesting, please consider [sponsoring me](https://github.com/sponsors/transitive-bullshit) or <a href="https://twitter.com/transitive_bs">following me on twitter <img src="https://storage.googleapis.com/saasify-assets/twitter-logo.svg" alt="twitter" height="24px" align="center"></a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/button.module.scss DELETED
@@ -1,60 +0,0 @@
1
- .icon-button {
2
- background-color: var(--white);
3
- border-radius: 10px;
4
- display: flex;
5
- align-items: center;
6
- justify-content: center;
7
- padding: 10px;
8
-
9
- box-shadow: var(--card-shadow);
10
- cursor: pointer;
11
- transition: all 0.3s ease;
12
- overflow: hidden;
13
- user-select: none;
14
- }
15
-
16
- .border {
17
- border: var(--border-in-light);
18
- }
19
-
20
- .icon-button:hover {
21
- filter: brightness(0.9);
22
- border-color: var(--primary);
23
- }
24
-
25
- .icon-button-icon {
26
- width: 16px;
27
- height: 16px;
28
- display: flex;
29
- justify-content: center;
30
- align-items: center;
31
- }
32
-
33
- @media only screen and (max-width: 600px) {
34
- .icon-button {
35
- padding: 16px;
36
- }
37
- }
38
-
39
- @mixin dark-button {
40
- div:not(:global(.no-dark))>.icon-button-icon {
41
- filter: invert(0.5);
42
- }
43
-
44
- .icon-button:hover {
45
- filter: brightness(1.2);
46
- }
47
- }
48
-
49
- :global(.dark) {
50
- @include dark-button;
51
- }
52
-
53
- @media (prefers-color-scheme: dark) {
54
- @include dark-button;
55
- }
56
-
57
- .icon-button-text {
58
- margin-left: 5px;
59
- font-size: 12px;
60
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/button.tsx DELETED
@@ -1,28 +0,0 @@
1
- import * as React from "react";
2
-
3
- import styles from "./button.module.scss";
4
-
5
- export function IconButton(props: {
6
- onClick?: () => void;
7
- icon: JSX.Element;
8
- text?: string;
9
- bordered?: boolean;
10
- className?: string;
11
- title?: string;
12
- }) {
13
- return (
14
- <div
15
- className={
16
- styles["icon-button"] +
17
- ` ${props.bordered && styles.border} ${props.className ?? ""}`
18
- }
19
- onClick={props.onClick}
20
- title={props.title}
21
- >
22
- <div className={styles["icon-button-icon"]}>{props.icon}</div>
23
- {props.text && (
24
- <div className={styles["icon-button-text"]}>{props.text}</div>
25
- )}
26
- </div>
27
- );
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/home.module.scss DELETED
@@ -1,448 +0,0 @@
1
- @import "./window.scss";
2
-
3
- @mixin container {
4
- background-color: var(--white);
5
- border: var(--border-in-light);
6
- border-radius: 20px;
7
- box-shadow: var(--shadow);
8
- color: var(--black);
9
- background-color: var(--white);
10
- min-width: 600px;
11
- min-height: 480px;
12
- max-width: 900px;
13
-
14
- display: flex;
15
- overflow: hidden;
16
- box-sizing: border-box;
17
-
18
- width: var(--window-width);
19
- height: var(--window-height);
20
- }
21
-
22
- .container {
23
- @include container();
24
- }
25
-
26
- @media only screen and (min-width: 600px) {
27
- .tight-container {
28
- --window-width: 100vw;
29
- --window-height: 100vh;
30
- --window-content-width: calc(100% - var(--sidebar-width));
31
-
32
- @include container();
33
-
34
- max-width: 100vw;
35
- max-height: 100vh;
36
-
37
- border-radius: 0;
38
- }
39
- }
40
-
41
- .sidebar {
42
- top: 0;
43
- width: var(--sidebar-width);
44
- box-sizing: border-box;
45
- padding: 20px;
46
- background-color: var(--second);
47
- display: flex;
48
- flex-direction: column;
49
- box-shadow: inset -2px 0px 2px 0px rgb(0, 0, 0, 0.05);
50
- }
51
-
52
- .window-content {
53
- width: var(--window-content-width);
54
- height: 100%;
55
- display: flex;
56
- flex-direction: column;
57
- }
58
-
59
- .mobile {
60
- display: none;
61
- }
62
-
63
- @media only screen and (max-width: 600px) {
64
- .container {
65
- min-height: unset;
66
- min-width: unset;
67
- max-height: unset;
68
- min-width: unset;
69
- border: 0;
70
- border-radius: 0;
71
- }
72
-
73
- .sidebar {
74
- position: absolute;
75
- left: -100%;
76
- z-index: 999;
77
- height: 100vh;
78
- transition: all ease 0.3s;
79
- box-shadow: none;
80
- }
81
-
82
- .sidebar-show {
83
- left: 0;
84
- }
85
-
86
- .mobile {
87
- display: block;
88
- }
89
- }
90
-
91
- .sidebar-header {
92
- position: relative;
93
- padding-top: 20px;
94
- padding-bottom: 20px;
95
- }
96
-
97
- .sidebar-logo {
98
- position: absolute;
99
- right: 0;
100
- bottom: 18px;
101
- }
102
-
103
- .sidebar-title {
104
- font-size: 20px;
105
- font-weight: bold;
106
- }
107
-
108
- .sidebar-sub-title {
109
- font-size: 12px;
110
- font-weight: 400px;
111
- }
112
-
113
- .sidebar-body {
114
- flex: 1;
115
- overflow: auto;
116
- }
117
-
118
- .chat-list {
119
- }
120
-
121
- .chat-item {
122
- padding: 10px 14px;
123
- background-color: var(--white);
124
- border-radius: 10px;
125
- margin-bottom: 10px;
126
- box-shadow: var(--card-shadow);
127
- transition: all 0.3s ease;
128
- cursor: pointer;
129
- user-select: none;
130
- border: 2px solid transparent;
131
- position: relative;
132
- overflow: hidden;
133
- }
134
-
135
- @keyframes slide-in {
136
- from {
137
- opacity: 0;
138
- transform: translateY(20px);
139
- }
140
-
141
- to {
142
- opacity: 1;
143
- transform: translateY(0px);
144
- }
145
- }
146
-
147
- .chat-item:hover {
148
- background-color: var(--hover-color);
149
- }
150
-
151
- .chat-item-selected {
152
- border-color: var(--primary);
153
- }
154
-
155
- .chat-item-title {
156
- font-size: 14px;
157
- font-weight: bolder;
158
- display: block;
159
- width: 200px;
160
- overflow: hidden;
161
- text-overflow: ellipsis;
162
- white-space: nowrap;
163
- }
164
-
165
- .chat-item-delete {
166
- position: absolute;
167
- top: 10px;
168
- right: -20px;
169
- transition: all ease 0.3s;
170
- opacity: 0;
171
- }
172
-
173
- .chat-item:hover > .chat-item-delete {
174
- opacity: 0.5;
175
- right: 10px;
176
- }
177
-
178
- .chat-item:hover > .chat-item-delete:hover {
179
- opacity: 1;
180
- }
181
-
182
- .chat-item-info {
183
- display: flex;
184
- justify-content: space-between;
185
- color: rgb(166, 166, 166);
186
- font-size: 12px;
187
- margin-top: 8px;
188
- }
189
-
190
- .chat-item-count {
191
- }
192
-
193
- .chat-item-date {
194
- }
195
-
196
- .sidebar-tail {
197
- display: flex;
198
- justify-content: space-between;
199
- padding-top: 20px;
200
- }
201
-
202
- .sidebar-actions {
203
- display: inline-flex;
204
- }
205
-
206
- .sidebar-action:not(:last-child) {
207
- margin-right: 15px;
208
- }
209
-
210
- .chat {
211
- display: flex;
212
- flex-direction: column;
213
- position: relative;
214
- height: 100%;
215
- }
216
-
217
- .chat-body {
218
- flex: 1;
219
- overflow: auto;
220
- padding: 20px;
221
- margin-bottom: 100px;
222
- }
223
-
224
- .chat-message {
225
- display: flex;
226
- flex-direction: row;
227
- }
228
-
229
- .chat-message-user {
230
- display: flex;
231
- flex-direction: row-reverse;
232
- }
233
-
234
- .chat-message-container {
235
- max-width: var(--message-max-width);
236
- display: flex;
237
- flex-direction: column;
238
- align-items: flex-start;
239
- animation: slide-in ease 0.3s;
240
-
241
- &:hover {
242
- .chat-message-top-actions {
243
- opacity: 1;
244
- right: 10px;
245
- pointer-events: all;
246
- }
247
- }
248
- }
249
-
250
- .chat-message-user > .chat-message-container {
251
- align-items: flex-end;
252
- }
253
-
254
- .chat-message-avatar {
255
- margin-top: 20px;
256
- }
257
-
258
- .chat-message-status {
259
- font-size: 12px;
260
- color: #aaa;
261
- line-height: 1.5;
262
- margin-top: 5px;
263
- }
264
-
265
- .user-avtar {
266
- height: 30px;
267
- width: 30px;
268
- display: flex;
269
- align-items: center;
270
- justify-content: center;
271
- border: var(--border-in-light);
272
- box-shadow: var(--card-shadow);
273
- border-radius: 10px;
274
- }
275
-
276
- .chat-message-item {
277
- box-sizing: border-box;
278
- max-width: 100%;
279
- margin-top: 10px;
280
- border-radius: 10px;
281
- background-color: rgba(0, 0, 0, 0.05);
282
- padding: 10px;
283
- font-size: 14px;
284
- user-select: text;
285
- word-break: break-word;
286
- border: var(--border-in-light);
287
- position: relative;
288
- }
289
-
290
- .chat-message-top-actions {
291
- font-size: 12px;
292
- position: absolute;
293
- right: 20px;
294
- top: -26px;
295
- left: 100px;
296
- transition: all ease 0.3s;
297
- opacity: 0;
298
- pointer-events: none;
299
-
300
- display: flex;
301
- flex-direction: row-reverse;
302
-
303
- .chat-message-top-action {
304
- opacity: 0.5;
305
- color: var(--black);
306
- white-space: nowrap;
307
- cursor: pointer;
308
-
309
- &:hover {
310
- opacity: 1;
311
- }
312
-
313
- &:not(:first-child) {
314
- margin-right: 10px;
315
- }
316
- }
317
- }
318
-
319
- .chat-message-user > .chat-message-container > .chat-message-item {
320
- background-color: var(--second);
321
- }
322
-
323
- .chat-message-actions {
324
- display: flex;
325
- flex-direction: row-reverse;
326
- width: 100%;
327
- padding-top: 5px;
328
- box-sizing: border-box;
329
- font-size: 12px;
330
- }
331
-
332
- .chat-message-action-date {
333
- color: #aaa;
334
- }
335
-
336
- .chat-input-panel {
337
- position: absolute;
338
- bottom: 0px;
339
- display: flex;
340
- width: 100%;
341
- padding: 20px;
342
- box-sizing: border-box;
343
- flex-direction: column;
344
- }
345
-
346
- @mixin single-line {
347
- white-space: nowrap;
348
- overflow: hidden;
349
- text-overflow: ellipsis;
350
- }
351
-
352
- .prompt-hints {
353
- min-height: 20px;
354
- width: 100%;
355
- max-height: 50vh;
356
- overflow: auto;
357
- display: flex;
358
- flex-direction: column-reverse;
359
-
360
- background-color: var(--white);
361
- border: var(--border-in-light);
362
- border-radius: 10px;
363
- margin-bottom: 10px;
364
- box-shadow: var(--shadow);
365
-
366
- .prompt-hint {
367
- color: var(--black);
368
- padding: 6px 10px;
369
- animation: slide-in ease 0.3s;
370
- cursor: pointer;
371
- transition: all ease 0.3s;
372
- border: transparent 1px solid;
373
- margin: 4px;
374
- border-radius: 8px;
375
-
376
- &:not(:last-child) {
377
- margin-top: 0;
378
- }
379
-
380
- .hint-title {
381
- font-size: 12px;
382
- font-weight: bolder;
383
-
384
- @include single-line();
385
- }
386
- .hint-content {
387
- font-size: 12px;
388
-
389
- @include single-line();
390
- }
391
-
392
- &-selected,
393
- &:hover {
394
- border-color: var(--primary);
395
- }
396
- }
397
- }
398
-
399
- .chat-input-panel-inner {
400
- display: flex;
401
- flex: 1;
402
- }
403
-
404
- .chat-input {
405
- height: 100%;
406
- width: 100%;
407
- border-radius: 10px;
408
- border: var(--border-in-light);
409
- box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03);
410
- background-color: var(--white);
411
- color: var(--black);
412
- font-family: inherit;
413
- padding: 10px 14px;
414
- resize: none;
415
- outline: none;
416
- }
417
-
418
- @media only screen and (max-width: 600px) {
419
- .chat-input {
420
- font-size: 16px;
421
- }
422
- }
423
-
424
- .chat-input:focus {
425
- border: 1px solid var(--primary);
426
- }
427
-
428
- .chat-input-send {
429
- background-color: var(--primary);
430
- color: white;
431
-
432
- position: absolute;
433
- right: 30px;
434
- bottom: 30px;
435
- }
436
-
437
- .export-content {
438
- white-space: break-spaces;
439
- }
440
-
441
- .loading-content {
442
- display: flex;
443
- flex-direction: column;
444
- justify-content: center;
445
- align-items: center;
446
- height: 100%;
447
- width: 100%;
448
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/home.tsx DELETED
@@ -1,674 +0,0 @@
1
- "use client";
2
-
3
- import { useState, useRef, useEffect, useLayoutEffect } from "react";
4
- import { useDebouncedCallback } from "use-debounce";
5
-
6
- import { IconButton } from "./button";
7
- import styles from "./home.module.scss";
8
-
9
- import SettingsIcon from "../icons/settings.svg";
10
- import GithubIcon from "../icons/github.svg";
11
- import ChatGptIcon from "../icons/chatgpt.svg";
12
- import SendWhiteIcon from "../icons/send-white.svg";
13
- import BrainIcon from "../icons/brain.svg";
14
- import ExportIcon from "../icons/export.svg";
15
- import BotIcon from "../icons/bot.svg";
16
- import AddIcon from "../icons/add.svg";
17
- import DeleteIcon from "../icons/delete.svg";
18
- import LoadingIcon from "../icons/three-dots.svg";
19
- import MenuIcon from "../icons/menu.svg";
20
- import CloseIcon from "../icons/close.svg";
21
- import CopyIcon from "../icons/copy.svg";
22
- import DownloadIcon from "../icons/download.svg";
23
-
24
- import { Message, SubmitKey, useChatStore, ChatSession } from "../store";
25
- import { showModal, showToast } from "./ui-lib";
26
- import { copyToClipboard, downloadAs, isIOS, selectOrCopy } from "../utils";
27
- import Locale from "../locales";
28
-
29
- import dynamic from "next/dynamic";
30
- import { REPO_URL } from "../constant";
31
- import { ControllerPool } from "../requests";
32
- import { Prompt, usePromptStore } from "../store/prompt";
33
-
34
- export function Loading(props: { noLogo?: boolean }) {
35
- return (
36
- <div className={styles["loading-content"]}>
37
- {!props.noLogo && <BotIcon />}
38
- <LoadingIcon />
39
- </div>
40
- );
41
- }
42
-
43
- const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
44
- loading: () => <LoadingIcon />,
45
- });
46
-
47
- const Settings = dynamic(async () => (await import("./settings")).Settings, {
48
- loading: () => <Loading noLogo />,
49
- });
50
-
51
- const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, {
52
- loading: () => <LoadingIcon />,
53
- });
54
-
55
- export function Avatar(props: { role: Message["role"] }) {
56
- const config = useChatStore((state) => state.config);
57
-
58
- if (props.role === "assistant") {
59
- return <BotIcon className={styles["user-avtar"]} />;
60
- }
61
-
62
- return (
63
- <div className={styles["user-avtar"]}>
64
- <Emoji unified={config.avatar} size={18} />
65
- </div>
66
- );
67
- }
68
-
69
- export function ChatItem(props: {
70
- onClick?: () => void;
71
- onDelete?: () => void;
72
- title: string;
73
- count: number;
74
- time: string;
75
- selected: boolean;
76
- }) {
77
- return (
78
- <div
79
- className={`${styles["chat-item"]} ${
80
- props.selected && styles["chat-item-selected"]
81
- }`}
82
- onClick={props.onClick}
83
- >
84
- <div className={styles["chat-item-title"]}>{props.title}</div>
85
- <div className={styles["chat-item-info"]}>
86
- <div className={styles["chat-item-count"]}>
87
- {Locale.ChatItem.ChatItemCount(props.count)}
88
- </div>
89
- <div className={styles["chat-item-date"]}>{props.time}</div>
90
- </div>
91
- <div className={styles["chat-item-delete"]} onClick={props.onDelete}>
92
- <DeleteIcon />
93
- </div>
94
- </div>
95
- );
96
- }
97
-
98
- export function ChatList() {
99
- const [sessions, selectedIndex, selectSession, removeSession] = useChatStore(
100
- (state) => [
101
- state.sessions,
102
- state.currentSessionIndex,
103
- state.selectSession,
104
- state.removeSession,
105
- ],
106
- );
107
-
108
- return (
109
- <div className={styles["chat-list"]}>
110
- {sessions.map((item, i) => (
111
- <ChatItem
112
- title={item.topic}
113
- time={item.lastUpdate}
114
- count={item.messages.length}
115
- key={i}
116
- selected={i === selectedIndex}
117
- onClick={() => selectSession(i)}
118
- onDelete={() => removeSession(i)}
119
- />
120
- ))}
121
- </div>
122
- );
123
- }
124
-
125
- function useSubmitHandler() {
126
- const config = useChatStore((state) => state.config);
127
- const submitKey = config.submitKey;
128
-
129
- const shouldSubmit = (e: KeyboardEvent) => {
130
- if (e.key !== "Enter") return false;
131
-
132
- return (
133
- (config.submitKey === SubmitKey.AltEnter && e.altKey) ||
134
- (config.submitKey === SubmitKey.CtrlEnter && e.ctrlKey) ||
135
- (config.submitKey === SubmitKey.ShiftEnter && e.shiftKey) ||
136
- (config.submitKey === SubmitKey.MetaEnter && e.metaKey) ||
137
- (config.submitKey === SubmitKey.Enter &&
138
- !e.altKey &&
139
- !e.ctrlKey &&
140
- !e.shiftKey &&
141
- !e.metaKey)
142
- );
143
- };
144
-
145
- return {
146
- submitKey,
147
- shouldSubmit,
148
- };
149
- }
150
-
151
- export function PromptHints(props: {
152
- prompts: Prompt[];
153
- onPromptSelect: (prompt: Prompt) => void;
154
- }) {
155
- if (props.prompts.length === 0) return null;
156
-
157
- return (
158
- <div className={styles["prompt-hints"]}>
159
- {props.prompts.map((prompt, i) => (
160
- <div
161
- className={styles["prompt-hint"]}
162
- key={prompt.title + i.toString()}
163
- onClick={() => props.onPromptSelect(prompt)}
164
- >
165
- <div className={styles["hint-title"]}>{prompt.title}</div>
166
- <div className={styles["hint-content"]}>{prompt.content}</div>
167
- </div>
168
- ))}
169
- </div>
170
- );
171
- }
172
-
173
- export function Chat(props: {
174
- showSideBar?: () => void;
175
- sideBarShowing?: boolean;
176
- }) {
177
- type RenderMessage = Message & { preview?: boolean };
178
-
179
- const chatStore = useChatStore();
180
- const [session, sessionIndex] = useChatStore((state) => [
181
- state.currentSession(),
182
- state.currentSessionIndex,
183
- ]);
184
- const fontSize = useChatStore((state) => state.config.fontSize);
185
-
186
- const inputRef = useRef<HTMLTextAreaElement>(null);
187
- const [userInput, setUserInput] = useState("");
188
- const [isLoading, setIsLoading] = useState(false);
189
- const { submitKey, shouldSubmit } = useSubmitHandler();
190
-
191
- const model = useChatStore((state) => state.config.model);
192
- // prompt hints
193
- const promptStore = usePromptStore();
194
- const [promptHints, setPromptHints] = useState<Prompt[]>([]);
195
- const onSearch = useDebouncedCallback(
196
- (text: string) => {
197
- if (chatStore.config.disablePromptHint) return;
198
- setPromptHints(promptStore.search(text));
199
- },
200
- 100,
201
- { leading: true, trailing: true },
202
- );
203
-
204
- const onPromptSelect = (prompt: Prompt) => {
205
- setUserInput(prompt.content);
206
- setPromptHints([]);
207
- inputRef.current?.focus();
208
- };
209
-
210
- // only search prompts when user input is short
211
- const SEARCH_TEXT_LIMIT = 30;
212
- const onInput = (text: string) => {
213
- setUserInput(text);
214
- const n = text.trim().length;
215
- if (n === 0 || n > SEARCH_TEXT_LIMIT) {
216
- setPromptHints([]);
217
- } else {
218
- onSearch(text);
219
- }
220
- };
221
-
222
- // submit user input
223
- const onUserSubmit = () => {
224
- if (userInput.length <= 0) return;
225
- setIsLoading(true);
226
- chatStore.onUserInput(userInput, model).then(() => setIsLoading(false));
227
- setUserInput("");
228
- inputRef.current?.focus();
229
- };
230
-
231
- // stop response
232
- const onUserStop = (messageIndex: number) => {
233
- console.log(ControllerPool, sessionIndex, messageIndex);
234
- ControllerPool.stop(sessionIndex, messageIndex);
235
- };
236
-
237
- // check if should send message
238
- const onInputKeyDown = (e: KeyboardEvent) => {
239
- if (shouldSubmit(e)) {
240
- onUserSubmit();
241
- e.preventDefault();
242
- }
243
- };
244
- const onRightClick = (e: any, message: Message) => {
245
- // auto fill user input
246
- if (message.role === "user") {
247
- setUserInput(message.content);
248
- }
249
-
250
- // copy to clipboard
251
- if (selectOrCopy(e.currentTarget, message.content)) {
252
- e.preventDefault();
253
- }
254
- };
255
-
256
- const onResend = (botIndex: number) => {
257
- // find last user input message and resend
258
- for (let i = botIndex; i >= 0; i -= 1) {
259
- if (messages[i].role === "user") {
260
- setIsLoading(true);
261
- chatStore
262
- .onUserInput(messages[i].content, model)
263
- .then(() => setIsLoading(false));
264
- return;
265
- }
266
- }
267
- };
268
-
269
- // for auto-scroll
270
- const latestMessageRef = useRef<HTMLDivElement>(null);
271
-
272
- // wont scroll while hovering messages
273
- const [autoScroll, setAutoScroll] = useState(false);
274
-
275
- // preview messages
276
- const messages = (session.messages as RenderMessage[])
277
- .concat(
278
- isLoading
279
- ? [
280
- {
281
- role: "assistant",
282
- content: "……",
283
- date: new Date().toLocaleString(),
284
- preview: true,
285
- },
286
- ]
287
- : [],
288
- )
289
- .concat(
290
- userInput.length > 0
291
- ? [
292
- {
293
- role: "user",
294
- content: userInput,
295
- date: new Date().toLocaleString(),
296
- preview: true,
297
- },
298
- ]
299
- : [],
300
- );
301
-
302
- // auto scroll
303
- useLayoutEffect(() => {
304
- setTimeout(() => {
305
- const dom = latestMessageRef.current;
306
- if (dom && !isIOS() && autoScroll) {
307
- dom.scrollIntoView({
308
- behavior: "smooth",
309
- block: "end",
310
- });
311
- }
312
- }, 500);
313
- });
314
-
315
- return (
316
- <div className={styles.chat} key={session.id}>
317
- <div className={styles["window-header"]}>
318
- <div
319
- className={styles["window-header-title"]}
320
- onClick={props?.showSideBar}
321
- >
322
- <div className={styles["window-header-main-title"]}>
323
- {session.topic}
324
- </div>
325
- <div className={styles["window-header-sub-title"]}>
326
- {Locale.Chat.SubTitle(session.messages.length)}
327
- </div>
328
- </div>
329
- <div className={styles["window-actions"]}>
330
- <div className={styles["window-action-button"] + " " + styles.mobile}>
331
- <IconButton
332
- icon={<MenuIcon />}
333
- bordered
334
- title={Locale.Chat.Actions.ChatList}
335
- onClick={props?.showSideBar}
336
- />
337
- </div>
338
- <div className={styles["window-action-button"]}>
339
- <IconButton
340
- icon={<BrainIcon />}
341
- bordered
342
- title={Locale.Chat.Actions.CompressedHistory}
343
- onClick={() => {
344
- showMemoryPrompt(session);
345
- }}
346
- />
347
- </div>
348
- <div className={styles["window-action-button"]}>
349
- <IconButton
350
- icon={<ExportIcon />}
351
- bordered
352
- title={Locale.Chat.Actions.Export}
353
- onClick={() => {
354
- exportMessages(session.messages, session.topic);
355
- }}
356
- />
357
- </div>
358
- </div>
359
- </div>
360
-
361
- <div className={styles["chat-body"]}>
362
- {messages.map((message, i) => {
363
- const isUser = message.role === "user";
364
-
365
- return (
366
- <div
367
- key={i}
368
- className={
369
- isUser ? styles["chat-message-user"] : styles["chat-message"]
370
- }
371
- >
372
- <div className={styles["chat-message-container"]}>
373
- <div className={styles["chat-message-avatar"]}>
374
- <Avatar role={message.role} />
375
- </div>
376
- {(message.preview || message.streaming) && (
377
- <div className={styles["chat-message-status"]}>
378
- {Locale.Chat.Typing}
379
- </div>
380
- )}
381
- <div className={styles["chat-message-item"]}>
382
- {!isUser &&
383
- !(message.preview || message.content.length === 0) && (
384
- <div className={styles["chat-message-top-actions"]}>
385
- {message.streaming ? (
386
- <div
387
- className={styles["chat-message-top-action"]}
388
- onClick={() => onUserStop(i)}
389
- >
390
- {Locale.Chat.Actions.Stop}
391
- </div>
392
- ) : (
393
- <div
394
- className={styles["chat-message-top-action"]}
395
- onClick={() => onResend(i)}
396
- >
397
- {Locale.Chat.Actions.Retry}
398
- </div>
399
- )}
400
-
401
- <div
402
- className={styles["chat-message-top-action"]}
403
- onClick={() => copyToClipboard(message.content)}
404
- >
405
- {Locale.Chat.Actions.Copy}
406
- </div>
407
- </div>
408
- )}
409
- {(message.preview || message.content.length === 0) &&
410
- !isUser ? (
411
- <LoadingIcon />
412
- ) : message.content.indexOf("https") != 0 ? (
413
- <div
414
- className="markdown-body"
415
- style={{ fontSize: `${fontSize}px` }}
416
- onContextMenu={(e) => onRightClick(e, message)}
417
- onDoubleClickCapture={() => setUserInput(message.content)}
418
- >
419
- <Markdown content={message.content} />
420
- </div>
421
- ) : (
422
- <div
423
- className="markdown-body"
424
- style={{ fontSize: `${fontSize}px` }}
425
- >
426
- {/* eslint-disable-next-line @next/next/no-img-element */}
427
- <img src={message.content} alt="图片" />
428
- </div>
429
- )}
430
- </div>
431
- {!isUser && !message.preview && (
432
- <div className={styles["chat-message-actions"]}>
433
- <div className={styles["chat-message-action-date"]}>
434
- {message.date.toLocaleString()}
435
- </div>
436
- </div>
437
- )}
438
- </div>
439
- </div>
440
- );
441
- })}
442
- <div ref={latestMessageRef} style={{ opacity: 0, height: "2em" }}>
443
- -
444
- </div>
445
- </div>
446
-
447
- <div className={styles["chat-input-panel"]}>
448
- <PromptHints prompts={promptHints} onPromptSelect={onPromptSelect} />
449
- <div className={styles["chat-input-panel-inner"]}>
450
- <textarea
451
- ref={inputRef}
452
- className={styles["chat-input"]}
453
- placeholder={Locale.Chat.Input(submitKey)}
454
- rows={4}
455
- onInput={(e) => onInput(e.currentTarget.value)}
456
- value={userInput}
457
- onKeyDown={(e) => onInputKeyDown(e as any)}
458
- onFocus={() => setAutoScroll(true)}
459
- onBlur={() => {
460
- setAutoScroll(false);
461
- setTimeout(() => setPromptHints([]), 100);
462
- }}
463
- autoFocus={!props?.sideBarShowing}
464
- />
465
- <IconButton
466
- icon={<SendWhiteIcon />}
467
- text={Locale.Chat.Send}
468
- className={styles["chat-input-send"] + " no-dark"}
469
- onClick={onUserSubmit}
470
- />
471
- </div>
472
- </div>
473
- </div>
474
- );
475
- }
476
-
477
- function useSwitchTheme() {
478
- const config = useChatStore((state) => state.config);
479
-
480
- useEffect(() => {
481
- document.body.classList.remove("light");
482
- document.body.classList.remove("dark");
483
-
484
- if (config.theme === "dark") {
485
- document.body.classList.add("dark");
486
- } else if (config.theme === "light") {
487
- document.body.classList.add("light");
488
- }
489
-
490
- const themeColor = getComputedStyle(document.body)
491
- .getPropertyValue("--theme-color")
492
- .trim();
493
- const metaDescription = document.querySelector('meta[name="theme-color"]');
494
- metaDescription?.setAttribute("content", themeColor);
495
- }, [config.theme]);
496
- }
497
-
498
- function exportMessages(messages: Message[], topic: string) {
499
- const mdText =
500
- `# ${topic}\n\n` +
501
- messages
502
- .map((m) => {
503
- return m.role === "user" ? `## ${m.content}` : m.content.trim();
504
- })
505
- .join("\n\n");
506
- const filename = `${topic}.md`;
507
-
508
- showModal({
509
- title: Locale.Export.Title,
510
- children: (
511
- <div className="markdown-body">
512
- <pre className={styles["export-content"]}>{mdText}</pre>
513
- </div>
514
- ),
515
- actions: [
516
- <IconButton
517
- key="copy"
518
- icon={<CopyIcon />}
519
- bordered
520
- text={Locale.Export.Copy}
521
- onClick={() => copyToClipboard(mdText)}
522
- />,
523
- <IconButton
524
- key="download"
525
- icon={<DownloadIcon />}
526
- bordered
527
- text={Locale.Export.Download}
528
- onClick={() => downloadAs(mdText, filename)}
529
- />,
530
- ],
531
- });
532
- }
533
-
534
- function showMemoryPrompt(session: ChatSession) {
535
- showModal({
536
- title: `${Locale.Memory.Title} (${session.lastSummarizeIndex} of ${session.messages.length})`,
537
- children: (
538
- <div className="markdown-body">
539
- <pre className={styles["export-content"]}>
540
- {session.memoryPrompt || Locale.Memory.EmptyContent}
541
- </pre>
542
- </div>
543
- ),
544
- actions: [
545
- <IconButton
546
- key="copy"
547
- icon={<CopyIcon />}
548
- bordered
549
- text={Locale.Memory.Copy}
550
- onClick={() => copyToClipboard(session.memoryPrompt)}
551
- />,
552
- ],
553
- });
554
- }
555
-
556
- const useHasHydrated = () => {
557
- const [hasHydrated, setHasHydrated] = useState<boolean>(false);
558
-
559
- useEffect(() => {
560
- setHasHydrated(true);
561
- }, []);
562
-
563
- return hasHydrated;
564
- };
565
-
566
- export function Home() {
567
- const [createNewSession, currentIndex, removeSession] = useChatStore(
568
- (state) => [
569
- state.newSession,
570
- state.currentSessionIndex,
571
- state.removeSession,
572
- ],
573
- );
574
- const loading = !useHasHydrated();
575
- const [showSideBar, setShowSideBar] = useState(true);
576
-
577
- // setting
578
- const [openSettings, setOpenSettings] = useState(false);
579
- const config = useChatStore((state) => state.config);
580
-
581
- useSwitchTheme();
582
-
583
- if (loading) {
584
- return <Loading />;
585
- }
586
-
587
- return (
588
- <div
589
- className={`${
590
- config.tightBorder ? styles["tight-container"] : styles.container
591
- }`}
592
- >
593
- <div
594
- className={styles.sidebar + ` ${showSideBar && styles["sidebar-show"]}`}
595
- >
596
- <div className={styles["sidebar-header"]}>
597
- <div className={styles["sidebar-title"]}>
598
- ChatGPT Next 【{config.model}】
599
- </div>
600
- <div className={styles["sidebar-sub-title"]}>
601
- 感谢大家一路以来的支持!
602
- </div>
603
- <div className={styles["sidebar-sub-title"]}>
604
- AI生成的图片将在一小时后自动失效
605
- </div>
606
- <div className={styles["sidebar-logo"]}>
607
- <ChatGptIcon />
608
- </div>
609
- </div>
610
-
611
- <div
612
- className={styles["sidebar-body"]}
613
- onClick={() => {
614
- setOpenSettings(false);
615
- setShowSideBar(false);
616
- }}
617
- >
618
- <ChatList />
619
- </div>
620
-
621
- <div className={styles["sidebar-tail"]}>
622
- <div className={styles["sidebar-actions"]}>
623
- <div className={styles["sidebar-action"] + " " + styles.mobile}>
624
- <IconButton
625
- icon={<CloseIcon />}
626
- onClick={() => {
627
- if (confirm(Locale.Home.DeleteChat)) {
628
- removeSession(currentIndex);
629
- }
630
- }}
631
- />
632
- </div>
633
- <div className={styles["sidebar-action"]}>
634
- <IconButton
635
- icon={<SettingsIcon />}
636
- onClick={() => {
637
- setOpenSettings(true);
638
- setShowSideBar(false);
639
- }}
640
- />
641
- </div>
642
- </div>
643
- <div>
644
- <IconButton
645
- icon={<AddIcon />}
646
- text={Locale.Home.NewChat}
647
- onClick={() => {
648
- createNewSession();
649
- setShowSideBar(false);
650
- }}
651
- />
652
- </div>
653
- </div>
654
- </div>
655
-
656
- <div className={styles["window-content"]}>
657
- {openSettings ? (
658
- <Settings
659
- closeSettings={() => {
660
- setOpenSettings(false);
661
- setShowSideBar(true);
662
- }}
663
- />
664
- ) : (
665
- <Chat
666
- key="chat"
667
- showSideBar={() => setShowSideBar(true)}
668
- sideBarShowing={showSideBar}
669
- />
670
- )}
671
- </div>
672
- </div>
673
- );
674
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/markdown.tsx DELETED
@@ -1,41 +0,0 @@
1
- import ReactMarkdown from "react-markdown";
2
- import "katex/dist/katex.min.css";
3
- import RemarkMath from "remark-math";
4
- import RehypeKatex from "rehype-katex";
5
- import RemarkGfm from "remark-gfm";
6
- import RehypePrsim from "rehype-prism-plus";
7
- import { useRef } from "react";
8
- import { copyToClipboard } from "../utils";
9
-
10
- export function PreCode(props: { children: any }) {
11
- const ref = useRef<HTMLPreElement>(null);
12
-
13
- return (
14
- <pre ref={ref}>
15
- <span
16
- className="copy-code-button"
17
- onClick={() => {
18
- if (ref.current) {
19
- const code = ref.current.innerText;
20
- copyToClipboard(code);
21
- }
22
- }}
23
- ></span>
24
- {props.children}
25
- </pre>
26
- );
27
- }
28
-
29
- export function Markdown(props: { content: string }) {
30
- return (
31
- <ReactMarkdown
32
- remarkPlugins={[RemarkMath, RemarkGfm]}
33
- rehypePlugins={[RehypeKatex, [RehypePrsim, { ignoreMissing: true }]]}
34
- components={{
35
- pre: PreCode,
36
- }}
37
- >
38
- {props.content}
39
- </ReactMarkdown>
40
- );
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/settings.module.scss DELETED
@@ -1,20 +0,0 @@
1
- @import "./window.scss";
2
-
3
- .settings {
4
- padding: 20px;
5
- overflow: auto;
6
- }
7
-
8
- .settings-title {
9
- font-size: 14px;
10
- font-weight: bolder;
11
- }
12
-
13
- .settings-sub-title {
14
- font-size: 12px;
15
- font-weight: normal;
16
- }
17
-
18
- .avatar {
19
- cursor: pointer;
20
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/settings.tsx DELETED
@@ -1,461 +0,0 @@
1
- import { useState, useEffect, useRef, useMemo } from "react";
2
-
3
- import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
4
-
5
- import styles from "./settings.module.scss";
6
-
7
- import ResetIcon from "../icons/reload.svg";
8
- import CloseIcon from "../icons/close.svg";
9
- import ClearIcon from "../icons/clear.svg";
10
- import EditIcon from "../icons/edit.svg";
11
-
12
- import { List, ListItem, Popover, showToast } from "./ui-lib";
13
-
14
- import { IconButton } from "./button";
15
- import {
16
- SubmitKey,
17
- useChatStore,
18
- Theme,
19
- ALL_MODELS,
20
- useUpdateStore,
21
- useAccessStore,
22
- ALL_MODEL,
23
- } from "../store";
24
- import { Avatar, PromptHints } from "./home";
25
-
26
- import Locale, { AllLangs, changeLang, getLang } from "../locales";
27
- import { getCurrentCommitId, getEmojiUrl } from "../utils";
28
- import Link from "next/link";
29
- import { UPDATE_URL } from "../constant";
30
- import { SearchService, usePromptStore } from "../store/prompt";
31
-
32
- function SettingItem(props: {
33
- title: string;
34
- subTitle?: string;
35
- children: JSX.Element;
36
- }) {
37
- return (
38
- <ListItem>
39
- <div className={styles["settings-title"]}>
40
- <div>{props.title}</div>
41
- {props.subTitle && (
42
- <div className={styles["settings-sub-title"]}>{props.subTitle}</div>
43
- )}
44
- </div>
45
- {props.children}
46
- </ListItem>
47
- );
48
- }
49
-
50
- export function Settings(props: { closeSettings: () => void }) {
51
- const [showEmojiPicker, setShowEmojiPicker] = useState(false);
52
- const [config, updateConfig, resetConfig, clearAllData, clearAll] =
53
- useChatStore((state) => [
54
- state.config,
55
- state.updateConfig,
56
- state.resetConfig,
57
- state.clearAllData,
58
- state.clearAll,
59
- ]);
60
-
61
- const updateStore = useUpdateStore();
62
- const [checkingUpdate, setCheckingUpdate] = useState(false);
63
- const currentId = getCurrentCommitId();
64
- const remoteId = updateStore.remoteId;
65
- const hasNewVersion = currentId !== remoteId;
66
-
67
- function checkUpdate(force = false) {
68
- setCheckingUpdate(true);
69
- updateStore.getLatestCommitId(force).then(() => {
70
- setCheckingUpdate(false);
71
- });
72
- }
73
-
74
- useEffect(() => {
75
- checkUpdate();
76
- }, []);
77
-
78
- const accessStore = useAccessStore();
79
- const enabledAccessControl = useMemo(
80
- () => accessStore.enabledAccessControl(),
81
- [],
82
- );
83
-
84
- const promptStore = usePromptStore();
85
- const builtinCount = SearchService.count.builtin;
86
- const customCount = promptStore.prompts.size ?? 0;
87
-
88
- return (
89
- <>
90
- <div className={styles["window-header"]}>
91
- <div className={styles["window-header-title"]}>
92
- <div className={styles["window-header-main-title"]}>
93
- {Locale.Settings.Title}
94
- </div>
95
- <div className={styles["window-header-sub-title"]}>
96
- {Locale.Settings.SubTitle}
97
- </div>
98
- </div>
99
- <div className={styles["window-actions"]}>
100
- <div className={styles["window-action-button"]}>
101
- <IconButton
102
- icon={<ClearIcon />}
103
- onClick={clearAllData}
104
- bordered
105
- title={Locale.Settings.Actions.ClearAll}
106
- />
107
- </div>
108
- <div className={styles["window-action-button"]}>
109
- <IconButton
110
- icon={<ResetIcon />}
111
- onClick={resetConfig}
112
- bordered
113
- title={Locale.Settings.Actions.ResetAll}
114
- />
115
- </div>
116
- <div className={styles["window-action-button"]}>
117
- <IconButton
118
- icon={<CloseIcon />}
119
- onClick={props.closeSettings}
120
- bordered
121
- title={Locale.Settings.Actions.Close}
122
- />
123
- </div>
124
- </div>
125
- </div>
126
- <div className={styles["settings"]}>
127
- <List>
128
- <SettingItem title={Locale.Settings.Avatar}>
129
- <Popover
130
- onClose={() => setShowEmojiPicker(false)}
131
- content={
132
- <EmojiPicker
133
- lazyLoadEmojis
134
- theme={EmojiTheme.AUTO}
135
- getEmojiUrl={getEmojiUrl}
136
- onEmojiClick={(e) => {
137
- updateConfig((config) => (config.avatar = e.unified));
138
- setShowEmojiPicker(false);
139
- }}
140
- />
141
- }
142
- open={showEmojiPicker}
143
- >
144
- <div
145
- className={styles.avatar}
146
- onClick={() => setShowEmojiPicker(true)}
147
- >
148
- <Avatar role="user" />
149
- </div>
150
- </Popover>
151
- </SettingItem>
152
-
153
- <SettingItem
154
- title={Locale.Settings.Update.Version(currentId)}
155
- subTitle={
156
- checkingUpdate
157
- ? Locale.Settings.Update.IsChecking
158
- : hasNewVersion
159
- ? Locale.Settings.Update.FoundUpdate(remoteId ?? "ERROR")
160
- : Locale.Settings.Update.IsLatest
161
- }
162
- >
163
- {checkingUpdate ? (
164
- <div />
165
- ) : (
166
- <IconButton
167
- icon={<ResetIcon></ResetIcon>}
168
- text={Locale.Settings.Update.CheckUpdate}
169
- onClick={() => checkUpdate(true)}
170
- />
171
- )}
172
- </SettingItem>
173
-
174
- <SettingItem title={Locale.Settings.SendKey}>
175
- <select
176
- value={config.submitKey}
177
- onChange={(e) => {
178
- updateConfig(
179
- (config) =>
180
- (config.submitKey = e.target.value as any as SubmitKey),
181
- );
182
- }}
183
- >
184
- {Object.values(SubmitKey).map((v) => (
185
- <option value={v} key={v}>
186
- {v}
187
- </option>
188
- ))}
189
- </select>
190
- </SettingItem>
191
-
192
- <ListItem>
193
- <div className={styles["settings-title"]}>
194
- {Locale.Settings.Theme}
195
- </div>
196
- <select
197
- value={config.theme}
198
- onChange={(e) => {
199
- updateConfig(
200
- (config) => (config.theme = e.target.value as any as Theme),
201
- );
202
- }}
203
- >
204
- {Object.values(Theme).map((v) => (
205
- <option value={v} key={v}>
206
- {v}
207
- </option>
208
- ))}
209
- </select>
210
- </ListItem>
211
-
212
- <SettingItem title={Locale.Settings.Lang.Name}>
213
- <select
214
- value={getLang()}
215
- onChange={(e) => {
216
- changeLang(e.target.value as any);
217
- }}
218
- >
219
- {AllLangs.map((lang) => (
220
- <option value={lang} key={lang}>
221
- {Locale.Settings.Lang.Options[lang]}
222
- </option>
223
- ))}
224
- </select>
225
- </SettingItem>
226
-
227
- <SettingItem
228
- title={Locale.Settings.FontSize.Title}
229
- subTitle={Locale.Settings.FontSize.SubTitle}
230
- >
231
- <input
232
- type="range"
233
- title={`${config.fontSize ?? 14}px`}
234
- value={config.fontSize}
235
- min="12"
236
- max="18"
237
- step="1"
238
- onChange={(e) =>
239
- updateConfig(
240
- (config) =>
241
- (config.fontSize = Number.parseInt(e.currentTarget.value)),
242
- )
243
- }
244
- ></input>
245
- </SettingItem>
246
-
247
- <div className="no-mobile">
248
- <SettingItem title={Locale.Settings.TightBorder}>
249
- <input
250
- type="checkbox"
251
- checked={config.tightBorder}
252
- onChange={(e) =>
253
- updateConfig(
254
- (config) => (config.tightBorder = e.currentTarget.checked),
255
- )
256
- }
257
- ></input>
258
- </SettingItem>
259
- </div>
260
- </List>
261
- <List>
262
- <SettingItem
263
- title={Locale.Settings.Prompt.Disable.Title}
264
- subTitle={Locale.Settings.Prompt.Disable.SubTitle}
265
- >
266
- <input
267
- type="checkbox"
268
- checked={config.disablePromptHint}
269
- onChange={(e) =>
270
- updateConfig(
271
- (config) =>
272
- (config.disablePromptHint = e.currentTarget.checked),
273
- )
274
- }
275
- ></input>
276
- </SettingItem>
277
-
278
- <SettingItem
279
- title={Locale.Settings.Prompt.List}
280
- subTitle={Locale.Settings.Prompt.ListCount(
281
- builtinCount,
282
- customCount,
283
- )}
284
- >
285
- <IconButton
286
- icon={<EditIcon />}
287
- text={Locale.Settings.Prompt.Edit}
288
- onClick={() => showToast(Locale.WIP)}
289
- />
290
- </SettingItem>
291
- </List>
292
- <List>
293
- {enabledAccessControl ? (
294
- <SettingItem
295
- title={Locale.Settings.AccessCode.Title}
296
- subTitle={Locale.Settings.AccessCode.SubTitle}
297
- >
298
- <input
299
- value={accessStore.accessCode}
300
- type="text"
301
- placeholder={Locale.Settings.AccessCode.Placeholder}
302
- onChange={(e) => {
303
- accessStore.updateCode(e.currentTarget.value);
304
- }}
305
- ></input>
306
- </SettingItem>
307
- ) : (
308
- <></>
309
- )}
310
-
311
- <SettingItem
312
- title={Locale.Settings.Token.Title}
313
- subTitle={Locale.Settings.Token.SubTitle}
314
- >
315
- <input
316
- value={accessStore.token}
317
- type="text"
318
- placeholder={Locale.Settings.Token.Placeholder}
319
- onChange={(e) => {
320
- accessStore.updateToken(e.currentTarget.value);
321
- }}
322
- ></input>
323
- </SettingItem>
324
-
325
- <SettingItem
326
- title={Locale.Settings.HistoryCount.Title}
327
- subTitle={Locale.Settings.HistoryCount.SubTitle}
328
- >
329
- <input
330
- type="range"
331
- title={config.historyMessageCount.toString()}
332
- value={config.historyMessageCount}
333
- min="2"
334
- max="25"
335
- step="2"
336
- onChange={(e) =>
337
- updateConfig(
338
- (config) =>
339
- (config.historyMessageCount = e.target.valueAsNumber),
340
- )
341
- }
342
- ></input>
343
- </SettingItem>
344
-
345
- <SettingItem
346
- title={Locale.Settings.CompressThreshold.Title}
347
- subTitle={Locale.Settings.CompressThreshold.SubTitle}
348
- >
349
- <input
350
- type="number"
351
- min={500}
352
- max={4000}
353
- value={config.compressMessageLengthThreshold}
354
- onChange={(e) =>
355
- updateConfig(
356
- (config) =>
357
- (config.compressMessageLengthThreshold =
358
- e.currentTarget.valueAsNumber),
359
- )
360
- }
361
- ></input>
362
- </SettingItem>
363
- </List>
364
-
365
- <List>
366
- <SettingItem title="会话类型">
367
- <select
368
- value={config.model}
369
- onChange={(e) => {
370
- updateConfig(
371
- (config) => (config.model = e.currentTarget.value),
372
- );
373
- clearAll();
374
- }}
375
- >
376
- {ALL_MODEL.map((v) => (
377
- <option value={v.name} key={v.name} disabled={!v.available}>
378
- {v.name}
379
- </option>
380
- ))}
381
- </select>
382
- </SettingItem>
383
- <SettingItem title={Locale.Settings.Model}>
384
- <select
385
- value={config.modelConfig.model}
386
- onChange={(e) => {
387
- updateConfig(
388
- (config) =>
389
- (config.modelConfig.model = e.currentTarget.value),
390
- );
391
- }}
392
- disabled={config.model != "聊天"}
393
- >
394
- {ALL_MODELS.map((v) => (
395
- <option value={v.name} key={v.name} disabled={!v.available}>
396
- {v.name}
397
- </option>
398
- ))}
399
- </select>
400
- </SettingItem>
401
- <SettingItem
402
- title={Locale.Settings.Temperature.Title}
403
- subTitle={Locale.Settings.Temperature.SubTitle}
404
- >
405
- <input
406
- type="range"
407
- value={config.modelConfig.temperature.toFixed(1)}
408
- min="0"
409
- max="1"
410
- step="0.1"
411
- onChange={(e) => {
412
- updateConfig(
413
- (config) =>
414
- (config.modelConfig.temperature =
415
- e.currentTarget.valueAsNumber),
416
- );
417
- }}
418
- ></input>
419
- </SettingItem>
420
- <SettingItem
421
- title={Locale.Settings.MaxTokens.Title}
422
- subTitle={Locale.Settings.MaxTokens.SubTitle}
423
- >
424
- <input
425
- type="number"
426
- min={100}
427
- max={4096}
428
- value={config.modelConfig.max_tokens}
429
- onChange={(e) =>
430
- updateConfig(
431
- (config) =>
432
- (config.modelConfig.max_tokens =
433
- e.currentTarget.valueAsNumber),
434
- )
435
- }
436
- ></input>
437
- </SettingItem>
438
- <SettingItem
439
- title={Locale.Settings.PresencePenlty.Title}
440
- subTitle={Locale.Settings.PresencePenlty.SubTitle}
441
- >
442
- <input
443
- type="range"
444
- value={config.modelConfig.presence_penalty.toFixed(1)}
445
- min="-2"
446
- max="2"
447
- step="0.5"
448
- onChange={(e) => {
449
- updateConfig(
450
- (config) =>
451
- (config.modelConfig.presence_penalty =
452
- e.currentTarget.valueAsNumber),
453
- );
454
- }}
455
- ></input>
456
- </SettingItem>
457
- </List>
458
- </div>
459
- </>
460
- );
461
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/ui-lib.module.scss DELETED
@@ -1,160 +0,0 @@
1
- .card {
2
- background-color: var(--white);
3
- border-radius: 10px;
4
- box-shadow: var(--card-shadow);
5
- padding: 10px;
6
- }
7
-
8
- .popover {
9
- position: relative;
10
- }
11
-
12
- .popover-content {
13
- position: absolute;
14
- animation: slide-in 0.3s ease;
15
- right: 0;
16
- top: calc(100% + 10px);
17
- }
18
-
19
- .popover-mask {
20
- position: fixed;
21
- top: 0;
22
- left: 0;
23
- width: 100vw;
24
- height: 100vh;
25
- }
26
-
27
- @keyframes slide-in {
28
- from {
29
- transform: translateY(10px);
30
- opacity: 0;
31
- }
32
-
33
- to {
34
- transform: translateY(0);
35
- opacity: 1;
36
- }
37
- }
38
-
39
- .list-item {
40
- display: flex;
41
- justify-content: space-between;
42
- align-items: center;
43
- min-height: 40px;
44
- border-bottom: var(--border-in-light);
45
- padding: 10px 20px;
46
- animation: slide-in ease 0.6s;
47
- }
48
-
49
- .list {
50
- border: var(--border-in-light);
51
- border-radius: 10px;
52
- box-shadow: var(--card-shadow);
53
- margin-bottom: 20px;
54
- animation: slide-in ease 0.3s;
55
- }
56
-
57
- .list .list-item:last-child {
58
- border: 0;
59
- }
60
-
61
- .modal-container {
62
- box-shadow: var(--card-shadow);
63
- background-color: var(--white);
64
- border-radius: 12px;
65
- width: 50vw;
66
- animation: slide-in ease 0.3s;
67
-
68
- --modal-padding: 20px;
69
-
70
- .modal-header {
71
- padding: var(--modal-padding);
72
- display: flex;
73
- align-items: center;
74
- justify-content: space-between;
75
- border-bottom: var(--border-in-light);
76
-
77
- .modal-title {
78
- font-weight: bolder;
79
- font-size: 16px;
80
- }
81
-
82
- .modal-close-btn {
83
- cursor: pointer;
84
-
85
- &:hover {
86
- filter: brightness(1.2);
87
- }
88
- }
89
- }
90
-
91
- .modal-content {
92
- max-height: 40vh;
93
- padding: var(--modal-padding);
94
- overflow: auto;
95
- }
96
-
97
- .modal-footer {
98
- padding: var(--modal-padding);
99
- display: flex;
100
- justify-content: flex-end;
101
-
102
- .modal-actions {
103
- display: flex;
104
- align-items: center;
105
-
106
- .modal-action {
107
- &:not(:last-child) {
108
- margin-right: 20px;
109
- }
110
- }
111
- }
112
- }
113
- }
114
-
115
- .show {
116
- opacity: 1;
117
- transition: all ease 0.3s;
118
- transform: translateY(0);
119
- position: fixed;
120
- left: 0;
121
- bottom: 0;
122
- animation: slide-in ease 0.6s;
123
- z-index: 99999;
124
- }
125
-
126
- .hide {
127
- opacity: 0;
128
- transition: all ease 0.3s;
129
- transform: translateY(20px);
130
- }
131
-
132
- .toast-container {
133
- position: fixed;
134
- bottom: 0;
135
- left: 0;
136
- width: 100vw;
137
- display: flex;
138
- justify-content: center;
139
-
140
- .toast-content {
141
- font-size: 14px;
142
- background-color: var(--white);
143
- box-shadow: var(--card-shadow);
144
- border: var(--border-in-light);
145
- color: var(--black);
146
- padding: 10px 30px;
147
- border-radius: 50px;
148
- margin-bottom: 20px;
149
- }
150
- }
151
-
152
- @media only screen and (max-width: 600px) {
153
- .modal-container {
154
- width: 90vw;
155
-
156
- .modal-content {
157
- max-height: 50vh;
158
- }
159
- }
160
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/ui-lib.tsx DELETED
@@ -1,142 +0,0 @@
1
- import styles from "./ui-lib.module.scss";
2
- import LoadingIcon from "../icons/three-dots.svg";
3
- import CloseIcon from "../icons/close.svg";
4
- import { createRoot } from "react-dom/client";
5
-
6
- export function Popover(props: {
7
- children: JSX.Element;
8
- content: JSX.Element;
9
- open?: boolean;
10
- onClose?: () => void;
11
- }) {
12
- return (
13
- <div className={styles.popover}>
14
- {props.children}
15
- {props.open && (
16
- <div className={styles["popover-content"]}>
17
- <div className={styles["popover-mask"]} onClick={props.onClose}></div>
18
- {props.content}
19
- </div>
20
- )}
21
- </div>
22
- );
23
- }
24
-
25
- export function Card(props: { children: JSX.Element[]; className?: string }) {
26
- return (
27
- <div className={styles.card + " " + props.className}>{props.children}</div>
28
- );
29
- }
30
-
31
- export function ListItem(props: { children: JSX.Element[] }) {
32
- if (props.children.length > 2) {
33
- throw Error("Only Support Two Children");
34
- }
35
-
36
- return <div className={styles["list-item"]}>{props.children}</div>;
37
- }
38
-
39
- export function List(props: { children: JSX.Element[] | JSX.Element }) {
40
- return <div className={styles.list}>{props.children}</div>;
41
- }
42
-
43
- export function Loading() {
44
- return (
45
- <div
46
- style={{
47
- height: "100vh",
48
- width: "100vw",
49
- display: "flex",
50
- alignItems: "center",
51
- justifyContent: "center",
52
- }}
53
- >
54
- <LoadingIcon />
55
- </div>
56
- );
57
- }
58
-
59
- interface ModalProps {
60
- title: string;
61
- children?: JSX.Element;
62
- actions?: JSX.Element[];
63
- onClose?: () => void;
64
- }
65
- export function Modal(props: ModalProps) {
66
- return (
67
- <div className={styles["modal-container"]}>
68
- <div className={styles["modal-header"]}>
69
- <div className={styles["modal-title"]}>{props.title}</div>
70
-
71
- <div className={styles["modal-close-btn"]} onClick={props.onClose}>
72
- <CloseIcon />
73
- </div>
74
- </div>
75
-
76
- <div className={styles["modal-content"]}>{props.children}</div>
77
-
78
- <div className={styles["modal-footer"]}>
79
- <div className={styles["modal-actions"]}>
80
- {props.actions?.map((action, i) => (
81
- <div key={i} className={styles["modal-action"]}>
82
- {action}
83
- </div>
84
- ))}
85
- </div>
86
- </div>
87
- </div>
88
- );
89
- }
90
-
91
- export function showModal(props: ModalProps) {
92
- const div = document.createElement("div");
93
- div.className = "modal-mask";
94
- document.body.appendChild(div);
95
-
96
- const root = createRoot(div);
97
- const closeModal = () => {
98
- props.onClose?.();
99
- root.unmount();
100
- div.remove();
101
- };
102
-
103
- div.onclick = (e) => {
104
- if (e.target === div) {
105
- closeModal();
106
- }
107
- };
108
-
109
- root.render(<Modal {...props} onClose={closeModal}></Modal>);
110
- }
111
-
112
- export type ToastProps = { content: string };
113
-
114
- export function Toast(props: ToastProps) {
115
- return (
116
- <div className={styles["toast-container"]}>
117
- <div className={styles["toast-content"]}>{props.content}</div>
118
- </div>
119
- );
120
- }
121
-
122
- export function showToast(content: string, delay = 3000) {
123
- const div = document.createElement("div");
124
- div.className = styles.show;
125
- document.body.appendChild(div);
126
-
127
- const root = createRoot(div);
128
- const close = () => {
129
- div.classList.add(styles.hide);
130
-
131
- setTimeout(() => {
132
- root.unmount();
133
- div.remove();
134
- }, 300);
135
- };
136
-
137
- setTimeout(() => {
138
- close();
139
- }, delay);
140
-
141
- root.render(<Toast content={content} />);
142
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/components/window.scss DELETED
@@ -1,35 +0,0 @@
1
- .window-header {
2
- padding: 14px 20px;
3
- border-bottom: rgba(0, 0, 0, 0.1) 1px solid;
4
-
5
- display: flex;
6
- justify-content: space-between;
7
- align-items: center;
8
- }
9
-
10
- .window-header-title {
11
- max-width: calc(100% - 100px);
12
-
13
- .window-header-main-title {
14
- font-size: 20px;
15
- font-weight: bolder;
16
- overflow: hidden;
17
- text-overflow: ellipsis;
18
- white-space: nowrap;
19
- display: block;
20
- max-width: 50vw;
21
- }
22
-
23
- .window-header-sub-title {
24
- font-size: 14px;
25
- margin-top: 5px;
26
- }
27
- }
28
-
29
- .window-actions {
30
- display: inline-flex;
31
- }
32
-
33
- .window-action-button {
34
- margin-left: 10px;
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/constant.ts DELETED
@@ -1,5 +0,0 @@
1
- export const OWNER = "Yidadaa";
2
- export const REPO = "ChatGPT-Next-Web";
3
- export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
4
- export const UPDATE_URL = `${REPO_URL}#%E4%BF%9D%E6%8C%81%E6%9B%B4%E6%96%B0-keep-updated`;
5
- export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
 
 
 
 
 
 
app/icons/add.svg DELETED
app/icons/bot.svg DELETED
app/icons/brain.svg DELETED
app/icons/chat.svg DELETED
app/icons/chatgpt.svg DELETED
app/icons/clear.svg DELETED
app/icons/close.svg DELETED
app/icons/copy.svg DELETED
app/icons/delete.svg DELETED
app/icons/download.svg DELETED