SilverStarShadow hf4all commited on
Commit
15bfa8d
·
0 Parent(s):

Duplicate from hf4all/bingo

Browse files

Co-authored-by: hf4all <[email protected]>

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .dockerignore +35 -0
  2. .editorconfig +36 -0
  3. .env.example +4 -0
  4. .eslintrc.json +3 -0
  5. .gitattributes +35 -0
  6. .github/workflows/docker.yml +28 -0
  7. .github/workflows/huggingface.yml +24 -0
  8. .gitignore +35 -0
  9. Dockerfile +36 -0
  10. LICENSE +21 -0
  11. README.md +195 -0
  12. cloudflare/worker.js +18 -0
  13. docs/images/curl.png +0 -0
  14. docs/images/demo.png +0 -0
  15. docs/images/wechat.png +0 -0
  16. next.config.js +38 -0
  17. package-lock.json +0 -0
  18. package.json +92 -0
  19. postcss.config.js +6 -0
  20. render.yaml +12 -0
  21. src/app/favicon.ico +0 -0
  22. src/app/globals.scss +1669 -0
  23. src/app/layout.tsx +47 -0
  24. src/app/loading.css +68 -0
  25. src/app/page.tsx +15 -0
  26. src/assets/images/brush.svg +5 -0
  27. src/assets/images/camera.svg +3 -0
  28. src/assets/images/chat.svg +3 -0
  29. src/assets/images/check-mark.svg +3 -0
  30. src/assets/images/clear.svg +3 -0
  31. src/assets/images/help.svg +3 -0
  32. src/assets/images/logo.svg +71 -0
  33. src/assets/images/paste.svg +3 -0
  34. src/assets/images/pin-fill.svg +3 -0
  35. src/assets/images/pin.svg +3 -0
  36. src/assets/images/refresh.svg +3 -0
  37. src/assets/images/send.svg +3 -0
  38. src/assets/images/settings.svg +1 -0
  39. src/assets/images/speech.svg +18 -0
  40. src/assets/images/stop.svg +3 -0
  41. src/assets/images/upload.svg +3 -0
  42. src/assets/images/visual-search.svg +3 -0
  43. src/assets/images/voice.svg +3 -0
  44. src/assets/images/warning.svg +3 -0
  45. src/components/button-scroll-to-bottom.tsx +34 -0
  46. src/components/chat-attachments.tsx +37 -0
  47. src/components/chat-header.tsx +12 -0
  48. src/components/chat-history.tsx +48 -0
  49. src/components/chat-image.tsx +170 -0
  50. src/components/chat-list.tsx +28 -0
.dockerignore ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
27
+ # local env files
28
+ .env*.local
29
+
30
+ # vercel
31
+ .vercel
32
+
33
+ # typescript
34
+ *.tsbuildinfo
35
+ next-env.d.ts
.editorconfig ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # http://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ indent_style = space
6
+ indent_size = 2
7
+ end_of_line = lf
8
+ charset = utf-8
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
11
+
12
+ # Use 4 spaces for the Python files
13
+ [*.py]
14
+ indent_size = 4
15
+ max_line_length = 80
16
+
17
+ # The JSON files contain newlines inconsistently
18
+ [*.json]
19
+ insert_final_newline = ignore
20
+
21
+ # Minified JavaScript files shouldn't be changed
22
+ [**.min.js]
23
+ indent_style = ignore
24
+ insert_final_newline = ignore
25
+
26
+ # Makefiles always use tabs for indentation
27
+ [Makefile]
28
+ indent_style = tab
29
+
30
+ # Batch files use tabs for indentation
31
+ [*.bat]
32
+ indent_style = tab
33
+
34
+ [*.md]
35
+ trim_trailing_whitespace = false
36
+
.env.example ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # 此配置全局生效,当用户没有配置用户信息时,此配置会做为默认配置,以下为示例配置,请根据实际情况修改。更多详情请参考 README.md
2
+
3
+ # 文档地址 https://github.com/weaigc/bingo/blob/main/README.md#%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96-bing_header
4
+ BING_HEADER=
.eslintrc.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "extends": "next/core-web-vitals"
3
+ }
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.github/workflows/docker.yml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build Docker Image
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ github:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v1
13
+
14
+ - name: Login to github registry
15
+ uses: actions-hub/docker/login@master
16
+ env:
17
+ DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
18
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
19
+
20
+ - name: Build :latest
21
+ if: success()
22
+ run: docker build -t weaigc/bingo:latest .
23
+
24
+ - name: Push to docker hub :latest
25
+ if: success()
26
+ uses: actions-hub/docker@master
27
+ with:
28
+ args: push weaigc/bingo:latest
.github/workflows/huggingface.yml ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Sync to Hugging Face hub
2
+ on:
3
+ workflow_run:
4
+ workflows: ["Build Docker Image"]
5
+ types:
6
+ - completed
7
+
8
+ # to run this workflow manually from the Actions tab
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ sync-to-hub:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+ with:
17
+ fetch-depth: 0
18
+ lfs: true
19
+ - name: Push to hub
20
+ env:
21
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
22
+ HF_USERNAME: hf4all
23
+ SPACE_NAME: bingo
24
+ run: git push https://$HF_USERNAME:[email protected]/spaces/$HF_USERNAME/$SPACE_NAME main -f
.gitignore ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
27
+ # local env files
28
+ .env*.local
29
+
30
+ # vercel
31
+ .vercel
32
+
33
+ # typescript
34
+ *.tsbuildinfo
35
+ next-env.d.ts
Dockerfile ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18
2
+
3
+
4
+ ARG DEBIAN_FRONTEND=noninteractive
5
+
6
+ ENV BING_HEADER ""
7
+
8
+ # Set home to the user's home directory
9
+ ENV HOME=/home/user \
10
+ PATH=/home/user/.local/bin:$PATH
11
+
12
+ # Set up a new user named "user" with user ID 1000
13
+ RUN useradd -o -u 1000 user && mkdir -p $HOME/app && chown -R user $HOME
14
+
15
+ # Switch to the "user" user
16
+ USER user
17
+
18
+ # Set the working directory to the user's home directory
19
+ WORKDIR $HOME/app
20
+
21
+ # Install app dependencies
22
+ # A wildcard is used to ensure both package.json AND package-lock.json are copied
23
+ # where available (npm@5+)
24
+ COPY --chown=user package*.json $HOME/app/
25
+
26
+ RUN npm install
27
+
28
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
29
+ COPY --chown=user . $HOME/app/
30
+
31
+ RUN npm run build
32
+
33
+ ENV PORT 7860
34
+ EXPOSE 7860
35
+
36
+ CMD npm start
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License Copyright (c) 2023 weaigc
2
+
3
+ Permission is hereby granted, free
4
+ of charge, to any person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use, copy, modify, merge,
7
+ publish, distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to the
9
+ following conditions:
10
+
11
+ The above copyright notice and this permission notice
12
+ (including the next paragraph) shall be included in all copies or substantial
13
+ portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
18
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
README.md ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: bingo
3
+ emoji: 📉
4
+ colorFrom: red
5
+ colorTo: red
6
+ sdk: docker
7
+ pinned: true
8
+ license: mit
9
+ duplicated_from: hf4all/bingo
10
+ ---
11
+
12
+ <div align="center">
13
+
14
+ # Bingo
15
+
16
+ Bingo,一个让你呼吸顺畅 New Bing。
17
+
18
+ 高度还原 New Bing 网页版的主要操作,国内可用,兼容绝大多数微软 Bing AI 的功能,可自行部署使用。
19
+
20
+ ![Github stars](https://badgen.net/github/stars/weaigc/bingo?icon=github&label=stars)
21
+ ![Gthub issues](https://img.shields.io/github/issues/weaigc/bingo)
22
+ [![docker build](https://github.com/weaigc/bingo/actions/workflows/docker.yml/badge.svg)](https://hub.docker.com/repository/docker/weaigc/bingo/)
23
+ [![docker hub](https://badgen.net/docker/size/weaigc/bingo?icon=docker&label=image%20size)](https://hub.docker.com/repository/docker/weaigc/bingo/)
24
+ [![MIT License](https://img.shields.io/badge/license-MIT-97c50f)](https://github.com/weaigc/bingo/blob/main/license)
25
+
26
+ </div>
27
+
28
+ ## 演示站点
29
+
30
+ https://bing.github1s.tk
31
+
32
+
33
+
34
+ [![img](./docs/images/demo.png)](https://bing.github1s.tk)
35
+
36
+ ## 功能和特点
37
+
38
+ - 完全基于 Next.js 重写,高度还原 New Bing Web 版 UI,使用体验和 Bing AI 基本一致。
39
+ - 支持 Docker 构建,方便快捷地部署和访问。
40
+ - Cookie 可全局配置,全局共享。
41
+ - 支持持续语音对话
42
+
43
+ ## RoadMap
44
+
45
+ - [x] 支持 wss 转发
46
+ - [x] 支持一键部署
47
+ - [x] 优化移动端展示
48
+ - [x] 支持画图
49
+ - [x] 支持语音输入(支持语音指令,目前仅支持 PC 版 Edge 及 Chrome 浏览器)
50
+ - [x] 支持语音输出(需要手动开启)
51
+ - [x] 支持图片输入
52
+ - [x] 支持自定义域名
53
+ - [ ] 支持历史记录
54
+ - [ ] 适配深色模式
55
+ - [ ] 支持内置提示词
56
+ - [ ] 支持离线访问
57
+ - [ ] 国际化翻译
58
+
59
+ ## 一键部署
60
+ 你也可以一键部署自己的 New Bing AI 到 🤗 HuggingFace 。
61
+
62
+ ### 部署到 Huggingface
63
+ 1. 点击此图标
64
+ [![Deploy to HuggingFace](https://img.shields.io/badge/%E7%82%B9%E5%87%BB%E9%83%A8%E7%BD%B2-%F0%9F%A4%97-fff)](https://huggingface.co/login?next=%2Fspaces%2Fhf4all%2Fbingo%3Fduplicate%3Dtrue%26visibility%3Dpublic),配置可以不改。
65
+
66
+ 2. 部署署完成后,点击“设置” 》“站点域名”,点一下,复制一下 HF 域名信息,然后分享给别人即可。
67
+
68
+ > Huggingface 不支持绑定自己的域名,不过我们可以使用曲线救国的方式来达到这个目的
69
+ > 1. 方式二,借助 Cloudflare Workers [部署Cloudflare Workers](#使用Cloudflare-Workers自定义域名)
70
+ > 2. 方式一,借助 Github Pages 及 iframe [如何绑定域名](https://github.com/weaigc/bingo/issues/4)
71
+
72
+ ### 使用Cloudflare Workers自定义域名
73
+
74
+ > 核心代码 [worker.js](./cloudflare/worker.js)
75
+
76
+ - [注册 Cloudflare 账号](https://dash.cloudflare.com/sign-up)
77
+
78
+ - 添加一个新的网站,需要你有自己的域名并且将域名`Name Server`托管给 Cloudflare 才行(更多信息可自行 Google)
79
+
80
+ - 通过左侧菜单进入「Workers」,并点击「Create a Worker」。
81
+
82
+ - 创建 Worker 服务,复制 [worker.js](./cloudflare/worker.js) 全部代码,粘贴至创建的服务中,根据注释进行改动,保存并部署。
83
+
84
+ - 触发器 中自定义访问域名。
85
+
86
+ ### 部署其它平台
87
+ <details>
88
+ <summary>
89
+ 由于其他平台目前遭到 New Bing 封杀,会遇到很多问题,不再做推荐,有需要的可以自行查看
90
+ </summary>
91
+
92
+ #### 部署到 Netlify
93
+ [![Deploy to Netlify Button](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/weaigc/bingo)
94
+
95
+ #### 部署到 Vercel
96
+ 如果你是 Vercel 付费用户,可以点以下链接一键部署到 Vercel。免费版本有[接口超时限制](https://vercel.com/docs/concepts/limits/overview),不推荐使用
97
+
98
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?demo-title=bingo&demo-description=bingo&demo-url=https%3A%2F%2Fbing.github1s.tk%2F&project-name=bingo&repository-name=bingo&repository-url=https%3A%2F%2Fgithub.com%2Fweaigc%2Fbingo&from=templates&skippable-integrations=1&env=BING_HEADER&envDescription=%E5%A6%82%E6%9E%9C%E4%B8%8D%E7%9F%A5%E9%81%93%E6%80%8E%E4%B9%88%E9%85%8D%E7%BD%AE%E8%AF%B7%E7%82%B9%E5%8F%B3%E4%BE%A7Learn+More&envLink=https%3A%2F%2Fgithub.com%2Fweaigc%2Fbingo%2Fblob%2Fmain%2F.env.example)
99
+
100
+ #### 部署到 Render
101
+
102
+ [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/weaigc/bingo)
103
+ </details>
104
+
105
+ ## 环境和依赖
106
+
107
+ - Node.js >= 18
108
+ - Bing AI 的[身份信息](#如何获取-BING_HEADER))
109
+
110
+ ## 安装和使用
111
+
112
+ * 使用 Node 启动
113
+
114
+ ```bash
115
+ git clone https://github.com/weaigc/bingo.git
116
+ npm i # 推荐使用 pnpm i
117
+ npm run build
118
+ npm run start
119
+ ```
120
+
121
+ * 使用 Docker 启动
122
+ ```bash
123
+ docker pull weaigc/bingo
124
+ docker run --rm -it -p 7860:7860 weaigc/bingo
125
+ # 或者
126
+ docker run --rm -it -e BING_HEADER=xxxx -p 7860:7860 weaigc/bingo
127
+ ```
128
+
129
+ ## 如何获取 BING_HEADER
130
+ > 配置了 BING_HEADER 意味着你将自己的账号共享给所有���用此服务的人,如果不需要免登录画图的功能,不建议设置此变量
131
+
132
+ 打开 https://www.bing.com 并登录,然后访问 https://www.bing.com/turing/captcha/challenge,通过人机校验,然后
133
+
134
+ ![BING HEADER](./docs/images/curl.png)
135
+
136
+ > 复制出来的内容应该如下所示。确认格式无误后,打开 https://effulgent-bubblegum-e2f5df.netlify.app/#dialog=%22settings%22 ,粘贴进去,点击“转成 BING_HEADER 并复制”,然后从剪切板粘贴即可得到。(你也可以先在网页上进行验证)
137
+
138
+ 以下是格式参考,需要注意的是,网页端保存的格式是以`curl`开头, 而服务端配置的 `BING_HEADER` 是 `base64` 格式,两者不能互通。
139
+ <details>
140
+ <summary>正常格式/网页端保存的格式(格式仅供参考)</summary>
141
+
142
+ ```
143
+ curl 'https://www.bing.com/turing/captcha/challenge' \
144
+ -H 'authority: www.bing.com' \
145
+ -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' \
146
+ -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
147
+ -H 'cache-control: max-age=0' \
148
+ -H 'cookie: MicrosoftApplicationsTelemetryDeviceId=3399c004-fd0e-48ec-bb92-d82a27b2bbd4; _EDGE_V=1; SRCHD=AF=NOFORM; SRCHUID=V=2&GUID=29EBDDA4E6674329ACCF1A0A423C3E98&dmnchg=1; _UR=QS=0&TQS=0; _HPVN=CS=eyJQbiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiUCJ9LCJTYyI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiSCJ9LCJReiI6eyJDbiI6MSwiU3QiOjAsIlFzIjowLCJQcm9kIjoiVCJ9LCJBcCI6dHJ1ZSwiTXV0ZSI6dHJ1ZSwiTGFkIjoiMjAyMy0wNy0yNVQwMDowMDowMFoiLCJJb3RkIjowLCJHd2IiOjAsIkRmdCI6bnVsbCwiTXZzIjowLCJGbHQiOjAsIkltcCI6Mn0=; _RwBf=ilt=1&ihpd=1&ispd=0&rc=0&rb=0&gb=0&rg=200&pc=0&mtu=0&rbb=0&g=0&cid=&clo=0&v=1&l=2023-07-25T07:00:00.0000000Z&lft=0001-01-01T00:00:00.0000000&aof=0&o=2&p=&c=&t=0&s=0001-01-01T00:00:00.0000000+00:00&ts=2023-07-25T11:00:31.7111548+00:00&rwred=0&wls=&lka=0&lkt=0&TH=&dci=0; ANON=A=0043C6590EA808ED6E395059FFFFFFFF&E=1c8b&W=1; NAP=V=1.9&E=1c31&C=DnaMSbDN_4efZ_xXqBF3Daorjr53kYqYoaP8YHsupjmiXnysX7a37A&W=1; PPLState=1; KievRPSSecAuth=FABSBBRaTOJILtFsMkpLVWSG6AN6C/svRwNmAAAEgAAACMGUA7EGVSjGEAQBGHtNsc5sNL7unmJsfPJ2t6imfo4BeUJlAia3IpMTtMUy4PU/C5QAzRI5pODtsIee0+blgllXt/5IiWwGjwmdhivsFM597pRPkjARPfwsPhNLPNbJrCPNPHdje4Is78MnCADXw6/NBq2FL8V2/byw2fH6IuAMD2MvN/VvqpEa9ZxiDjZtENj4HEj0mO2SgzjfyEhVAkjvznJqU2rw/Q2tHmX94NAM2kzlzKF/hWPhCCUmu8IHLvCnHDS6mSptvJDDP/sp3ovtzOXkP1mlM/Xju5ftesUvccVEQGffXORa1dE5hEMbKIiKXz1tDdduSXE19g9/+mRMAjaQhpwhI8XmilCTx1adb1Ll5qK+VjC9GNfEZzcbsGBPVaOl+anG8rEMq+Xnhjo7J+NqTNolavHgcuV8kJsCeJZIged33UA8eOZeFo+wAECMguxMoSqgpGH+sthqynvD/FJD6r/tiU2N3uqVq8NE8V37asrN6T14Z0FGBJOe6ET1+PGApm3s11OY9/xhFEB9T5BEPUGEbvRcLcW2ncFQX0EU+xweiPqo1Q1hNUg/dCtSI+lZ7c2H8XheePZavZ0TJQ8oNCSAuKiTqJmI0fVGpwbXwfaADkEipuawz3fIuMJBNgMU0OtA7Hm59v2fGLIBuvi6YeKS6GgVk3BIPf+P/eKahwozrxQZaFnoHTSqMkvct7xCP4atBROfXKf5Ww0CcFKp+2WX9BIskTOo2jjk6bAyyYJ+ElUB1fgLKNk5m/YSMc9iYCLIBMIGN8F0Yvy3tZ7cvh7Ue5Klo98US/I+nW1G7ZJMHRgUO8h8lpneHqEMegKd8gynO4VF7RpCjJkunDmW0Ta+RkXAP619pg0dqHMFkoOgknN78oBbGTV6fJUKotv+vi61kLhAeXZGWoHGCRXh2wUC6YgfPgKA6ESRNHtFn7E5B3HHpLc5rVMDSNhKZYfdhupV4Ezf6+5DhMcZLZhi0kk+ivDiN1gdHlVtSN55xpvf+c+XZDzR0uhgcvgy0LAbmzgk6y4WbYH+LQsMpzNNj+aC72vMiWovWrKh9jY4MYCmdgxsS/skPtLdp18muiEIRXTbZQGUmhxFpJAIbBIsCscMpzL0BgeujxUwM5wr79Sd9r4xwbgSMwmBlBfUHRVBdNyg8feepeJbCS63nD6eHOuLqMRsPIio3w/ki/EAa92UUEiZeavLsMUD/y/qAvWUdzdP5Y+C/TM+CMGS/kGL4LEdY/28MQeTvU1qv1X21kQt2aiaj3pPVL36hAzxbcLgqcMo9oymDRy87kdCXW/+g4oKLtMh6fm/G6W6Y/B01JlxohyyvueHQIG557uzkEkTJ3FnOVODSKBKpb3WZ65rExfV71zSZa25F3GmpaIG6HiYrX2YYhQAkIE9pKEQBHbnwHuwNDGottZTXZw=; WLS=C=9df3f9d8518fae19&N=wen; WLID=pGY8HgWCu4p5XYCOk2oa0+DBdftkMUfmNIn8XtSjSTKsgv/Il7GUlYs0Jpjf/E12jZMgV7x44Dy3fXOgjjUoJx7Y/ClLrLhsk20THksJJoI=; _EDGE_S=F=1&SID=17CF6EE006426448213C7DB907436588&mkt=zh-CN; MUID=225621093D8A6C27301632413C0E6D08; MUIDB=225621093D8A6C27301632413C0E6D08; SUID=A; SNRHOP=I=&TS=; _U=nGyzKQruEsDwLiu65fZFIG6e12hf2lwTJmroW__k8joUJIKmG3OIjayXKGW9dCVR3sNhF76mEVxyW6yjUGPodOfjtSa3s3J_DxMOrEK1BqXCOBI9bC66spAIASV7prsYFlVAJz73jVNENp_tBubLHJy6EbT0BKRe4AjrYkH-9uMnmCKB8Zmyg; _SS=SID=17CF6EE006426448213C7DB907436588&R=0&RB=0&GB=0&RG=200&RP=0&PC=U531; SRCHS=PC=U531; USRLOC=HS=1&ELOC=LAT=22.501529693603516|LON=113.9263687133789|N=%E5%8D%97%E5%B1%B1%E5%8C%BA%EF%BC%8C%E5%B9%BF%E4%B8%9C%E7%9C%81|ELT=2|&CLOC=LAT=22.50153029046461|LON=113.92637070632928|A=733.4464586120832|TS=230726151034|SRC=W; SRCHUSR=DOB=20230725&T=1690384908000&POEX=W; ipv6=hit=1690388509974&t=6; SRCHHPGUSR=HV=1690384945&SRCHLANG=zh-Hans&PV=15.0.0&BRW=MW&BRH=MT&CW=410&CH=794&SCW=410&SCH=794&DPR=1.5&UTC=480&DM=0&WTS=63825879627&PRVCW=410&PRVCH=794&PR=1.5; cct=AjWIBYOoVP-Afq6gWwtx80If6yHn6iBuEVHA1XHdAKpny6Y_CVyi_MSyM94VyMWnjdYkkccVtm3czoIAtXUGQA; GC=AjWIBYOoVP-Afq6gWwtx80If6yHn6iBuEVHA1XHdAKpR3Y_D9Ytcks4Ht6XhadXk75dvhzP4YOUS0UmoEyqyxw' \
149
+ -H 'dnt: 1' \
150
+ -H 'sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"' \
151
+ -H 'sec-ch-ua-arch: "x86"' \
152
+ -H 'sec-ch-ua-bitness: "64"' \
153
+ -H 'sec-ch-ua-full-version: "116.0.1938.29"' \
154
+ -H 'sec-ch-ua-full-version-list: "Chromium";v="116.0.5845.42", "Not)A;Brand";v="24.0.0.0", "Microsoft Edge";v="116.0.1938.29"' \
155
+ -H 'sec-ch-ua-mobile: ?0' \
156
+ -H 'sec-ch-ua-model: ""' \
157
+ -H 'sec-ch-ua-platform: "Windows"' \
158
+ -H 'sec-ch-ua-platform-version: "15.0.0"' \
159
+ -H 'sec-fetch-dest: document' \
160
+ -H 'sec-fetch-mode: navigate' \
161
+ -H 'sec-fetch-site: none' \
162
+ -H 'sec-fetch-user: ?1' \
163
+ -H 'sec-ms-gec: B3F47AD4A283CAB374C0451C46AAFD147C6A4DACAFF6A1C13F34B2C72B024494' \
164
+ -H 'sec-ms-gec-version: 1-116.0.1938.29' \
165
+ -H 'upgrade-insecure-requests: 1' \
166
+ -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.0.0' \
167
+ -H 'x-client-data: eyIxIjoiMiIsIjEwIjoiXCJTMGg3R05HOTF2aDQ1TUZSUnZ5NHN2akRmMWdlaVJKenNxNlA3aU1WbnF3PVwiIiwiMiI6IjEiLCIzIjoiMSIsIjQiOiIyMTU4ODQ5NTM4MjY4OTM5NTA3IiwiNSI6IlwiSm9GUWpPTDk3OS9MbkRRZnlCd2N1M2FsOUN3eTZTQmdaMGNYMXBtOWVMZz1cIiIsIjYiOiJiZXRhIiwiNyI6IjE4MDM4ODYyNjQzNSIsIjkiOiJkZXNrdG9wIn0=' \
168
+ -H 'x-edge-shopping-flag: 1' \
169
+ --compressed
170
+ ```
171
+ </details>
172
+
173
+ <details>
174
+ <summary>转成base64之后的格式(BING_HEADER只能使用 base64 之后的格式)</summary>
175
+
176
+ ```
177
+ Y3VybCAnaHR0cHM6Ly93d3cuYmluZy5jb20vdHVyaW5nL2NvbnZlcnNhdGlvbi9jcmVhdGUnIFwgICAtSCAnYXV0aG9yaXR5OiB3d3cuYmluZy5jb20nIFwgICAtSCAnYWNjZXB0OiB0ZXh0L2h0bWwsYXBwbGljYXRpb24veGh0bWwreG1sLGFwcGxpY2F0aW9uL3htbDtxPTAuOSxpbWFnZS93ZWJwLGltYWdlL2FwbmcsKi8qO3E9MC44LGFwcGxpY2F0aW9uL3NpZ25lZC1leGNoYW5nZTt2PWIzO3E9MC43JyBcICAgLUggJ2FjY2VwdC1sYW5ndWFnZTogemgtQ04semg7cT0wLjksZW47cT0wLjgsZW4tR0I7cT0wLjcsZW4tVVM7cT0wLjYnIFwgICAtSCAnY2FjaGUtY29udHJvbDogbWF4LWFnZT0wJyBcICAgLUggJ2Nvb2tpZTogTWljcm9zb2Z0QXBwbGljYXRpb25zVGVsZW1ldHJ5RGV2aWNlSWQ9MzM5OWMwMDQtZmQwZS00OGVjLWJiOTItZDgyYTI3YjJiYmQ0OyBfRURHRV9WPTE7IFNSQ0hEPUFGPU5PRk9STTsgU1JDSFVJRD1WPTImR1VJRD0yOUVCRERBNEU2Njc0MzI5QUNDRjFBMEE0MjNDM0U5OCZkbW5jaGc9MTsgX1VSPVFTPTAmVFFTPTA7IF9IUFZOPUNTPWV5SlFiaUk2ZXlKRGJpSTZNU3dpVTNRaU9qQXNJbEZ6SWpvd0xDSlFjbTlrSWpvaVVDSjlMQ0pUWXlJNmV5SkRiaUk2TVN3aVUzUWlPakFzSWxGeklqb3dMQ0pRY205a0lqb2lTQ0o5TENKUmVpSTZleUpEYmlJNk1Td2lVM1FpT2pBc0lsRnpJam93TENKUWNtOWtJam9pVkNKOUxDSkJjQ0k2ZEhKMVpTd2lUWFYwWlNJNmRISjFaU3dpVEdGa0lqb2lNakF5TXkwd055MHlOVlF3TURvd01Eb3dNRm9pTENKSmIzUmtJam93TENKSGQySWlPakFzSWtSbWRDSTZiblZzYkN3aVRYWnpJam93TENKR2JIUWlPakFzSWtsdGNDSTZNbjA9OyBfUndCZj1pbHQ9MSZpaHBkPTEmaXNwZD0wJnJjPTAmcmI9MCZnYj0wJnJnPTIwMCZwYz0wJm10dT0wJnJiYj0wJmc9MCZjaWQ9JmNsbz0wJnY9MSZsPTIwMjMtMDctMjVUMDc6MDA6MDAuMDAwMDAwMFombGZ0PTAwMDEtMDEtMDFUMDA6MDA6MDAuMDAwMDAwMCZhb2Y9MCZvPTImcD0mYz0mdD0wJnM9MDAwMS0wMS0wMVQwMDowMDowMC4wMDAwMDAwKzAwOjAwJnRzPTIwMjMtMDctMjVUMTE6MDA6MzEuNzExMTU0OCswMDowMCZyd3JlZD0wJndscz0mbGthPTAmbGt0PTAmVEg9JmRjaT0wOyBBTk9OPUE9MDA0M0M2NTkwRUE4MDhFRDZFMzk1MDU5RkZGRkZGRkYmRT0xYzhiJlc9MTsgTkFQPVY9MS45JkU9MWMzMSZDPURuYU1TYkROXzRlZlpfeFhxQkYzRGFvcmpyNTNrWXFZb2FQOFlIc3Vwam1pWG55c1g3YTM3QSZXPTE7IFBQTFN0YXRlPTE7IEtpZXZSUFNTZWNBdXRoPUZBQlNCQlJhVE9KSUx0RnNNa3BMVldTRzZBTjZDL3N2UndObUFBQUVnQUFBQ01HVUE3RUdWU2pHRUFRQkdIdE5zYzVzTkw3dW5tSnNmUEoydDZpbWZvNEJlVUpsQWlhM0lwTVR0TVV5NFBVL0M1UUF6Ukk1cE9EdHNJZWUwK2JsZ2xsWHQvNUlpV3dHandtZGhpdnNGTTU5N3BSUGtqQVJQZndzUGhOTFBOYkpyQ1BOUEhkamU0SXM3OE1uQ0FEWHc2L05CcTJGTDhWMi9ieXcyZkg2SXVBTUQyTXZOL1Z2cXBFYTlaeGlEalp0RU5qNEhFajBtTzJTZ3pqZnlFaFZBa2p2em5KcVUycncvUTJ0SG1YOTROQU0ya3psektGL2hXUGhDQ1VtdThJSEx2Q25IRFM2bVNwdHZKRERQL3NwM292dHpPWGtQMW1sTS9YanU1ZnRlc1V2Y2NWRVFHZmZYT1JhMWRFNWhFTWJLSWlLWHoxdERkZHVTWEUxOWc5LyttUk1BamFRaHB3aEk4WG1pbENUeDFhZGIxTGw1cUsrVmpDOUdOZkVaemNic0dCUFZhT2wrYW5HOHJFTXErWG5oam83SitOcVROb2xhdkhnY3VWOGtKc0NlSlpJZ2VkMzNVQThlT1plRm8rd0FFQ01ndXhNb1NxZ3BHSCtzdGhxeW52RC9GSkQ2ci90aVUyTjN1cVZxOE5FOFYzN2Fzck42VDE0WjBGR0JKT2U2RVQxK1BHQXBtM3MxMU9ZOS94aEZFQjlUNUJFUFVHRWJ2UmNMY1cybmNGUVgwRVUreHdlaVBxbzFRMWhOVWcvZEN0U0krbFo3YzJIOFhoZWVQWmF2WjBUSlE4b05DU0F1S2lUcUptSTBmVkdwd2JYd2ZhQURrRWlwdWF3ejNmSXVNSkJOZ01VME90QTdIbTU5djJmR0xJQnV2aTZZZUtTNkdnVmszQklQZitQL2VLYWh3b3pyeFFaYUZub0hUU3FNa3ZjdDd4Q1A0YXRCUk9mWEtmNVd3MENjRktwKzJXWDlCSXNrVE9vMmpqazZiQXl5WUorRWxVQjFmZ0xLTms1bS9ZU01jOWlZQ0xJQk1JR044RjBZdnkzdFo3Y3ZoN1VlNUtsbzk4VVMvSStuVzFHN1pKTUhSZ1VPOGg4bHBuZUhxRU1lZ0tkOGd5bk80VkY3UnBDakprdW5EbVcwVGErUmtYQVA2MTlwZzBkcUhNRmtvT2drbk43OG9CYkdUVjZmSlVLb3R2K3ZpNjFrTGhBZVhaR1dvSEdDUlhoMndVQzZZZ2ZQZ0tBNkVTUk5IdEZuN0U1QjNISHBMYzVyVk1EU05oS1pZZmRodXBWNEV6ZjYrNURoTWNaTFpoaTBraytpdkRpTjFnZEhsVnRTTjU1eHB2ZitjK1haRHpSMHVoZ2N2Z3kwTEFibXpnazZ5NFdiWUgrTFFzTXB6Tk5qK2FDNzJ2TWlXb3ZXcktoOWpZNE1ZQ21kZ3hzUy9za1B0TGRwMThtdWlFSVJYVGJaUUdVbWh4RnBKQUliQklzQ3NjTXB6TDBCZ2V1anhVd001d3I3OVNkOXI0eHdiZ1NNd21CbEJmVUhSVkJkTnlnOGZlZXBlSmJDUzYzbkQ2ZUhPdUxxTVJzUElpbzN3L2tpL0VBYTkyVVVFaVplYXZMc01VRC95L3FBdldVZHpkUDVZK0MvVE0rQ01HUy9rR0w0TEVkWS8yOE1RZVR2VTFxdjFYMjFrUXQyYWlhajNwUFZMMzZoQXp4YmNMZ3FjTW85b3ltRFJ5ODdrZENYVy8rZzRvS0x0TWg2Zm0vRzZXNlkvQjAxSmx4b2h5eXZ1ZUhRSUc1NTd1emtFa1RKM0ZuT1ZPRFNLQktwYjNXWjY1ckV4ZlY3MXpTWmEyNUYzR21wYUlHNkhpWXJYMllZaFFBa0lFOXBLRVFCSGJud0h1d05ER290dFpUWFp3PTsgV0xTPUM9OWRmM2Y5ZDg1MThmYWUxOSZOPXdlbjsgV0xJRD1wR1k4SGdXQ3U0cDVYWUNPazJvYTArREJkZnRrTVVmbU5JbjhYdFNqU1RLc2d2L0lsN0dVbFlzMEpwamYvRTEyalpNZ1Y3eDQ0RHkzZlhPZ2pqVW9KeDdZL0NsTHJMaHNrMjBUSGtzSkpvST07IF9FREdFX1M9Rj0xJlNJRD0xN0NGNkVFMDA2NDI2NDQ4MjEzQzdEQjkwNzQzNjU4OCZta3Q9emgtQ047IE1VSUQ9MjI1NjIxMDkzRDhBNkMyNzMwMTYzMjQxM0MwRTZEMDg7IE1VSURCPTIyNTYyMTA5M0Q4QTZDMjczMDE2MzI0MTNDMEU2RDA4OyBTVUlEPUE7IFNOUkhPUD1JPSZUUz07IF9VPW5HeXpLUXJ1RXNEd0xpdTY1ZlpGSUc2ZTEyaGYybHdUSm1yb1dfX2s4am9VSklLbUczT0lqYXlYS0dXOWRDVlIzc05oRjc2bUVWeHlXNnlqVUdQb2RPZmp0U2EzczNKX0R4TU9yRUsxQnFYQ09CSTliQzY2c3BBSUFTVjdwcnNZRmxWQUp6NzNqVk5FTnBfdEJ1YkxISnk2RWJUMEJLUmU0QWpyWWtILTl1TW5tQ0tCOFpteWc7IF9TUz1TSUQ9MTdDRjZFRTAwNjQyNjQ0ODIxM0M3REI5MDc0MzY1ODgmUj0wJlJCPTAmR0I9MCZSRz0yMDAmUlA9MCZQQz1VNTMxOyBTUkNIUz1QQz1VNTMxOyBVU1JMT0M9SFM9MSZFTE9DPUxBVD0yMi41MDE1Mjk2OTM2MDM1MTZ8TE9OPTExMy45MjYzNjg3MTMzNzg5fE49JUU1JThEJTk3JUU1JUIxJUIxJUU1JThDJUJBJUVGJUJDJThDJUU1JUI5JUJGJUU0JUI4JTlDJUU3JTlDJTgxfEVMVD0yfCZDTE9DPUxBVD0yMi41MDE1MzAyOTA0NjQ2MXxMT049MTEzLjkyNjM3MDcwNjMyOTI4fEE9NzMzLjQ0NjQ1ODYxMjA4MzJ8VFM9MjMwNzI2MTUxMDM0fFNSQz1XOyBTUkNIVVNSPURPQj0yMDIzMDcyNSZUPTE2OTAzODQ5MDgwMDAmUE9FWD1XOyBpcHY2PWhpdD0xNjkwMzg4NTA5OTc0JnQ9NjsgU1JDSEhQR1VTUj1IVj0xNjkwMzg0OTQ1JlNSQ0hMQU5HPXpoLUhhbnMmUFY9MTUuMC4wJkJSVz1NVyZCUkg9TVQmQ1c9NDEwJkNIPTc5NCZTQ1c9NDEwJlNDSD03OTQmRFBSPTEuNSZVVEM9NDgwJkRNPTAmV1RTPTYzODI1ODc5NjI3JlBSVkNXPTQxMCZQUlZDSD03OTQmUFI9MS41OyBjY3Q9QWpXSUJZT29WUC1BZnE2Z1d3dHg4MElmNnlIbjZpQnVFVkhBMVhIZEFLcG55NllfQ1Z5aV9NU3lNOTRWeU1XbmpkWWtrY2NWdG0zY3pvSUF0WFVHUUE7IEdDPUFqV0lCWU9vVlAtQWZxNmdXd3R4ODBJZjZ5SG42aUJ1RVZIQTFYSGRBS3BSM1lfRDlZdGNrczRIdDZYaGFkWGs3NWR2aHpQNFlPVVMwVW1vRXlxeXh3JyBcICAgLUggJ2RudDogMScgXCAgIC1IICdzZWMtY2gtdWE6ICJDaHJvbWl1bSI7dj0iMTE2IiwgIk5vdClBO0JyYW5kIjt2PSIyNCIsICJNaWNyb3NvZnQgRWRnZSI7dj0iMTE2IicgXCAgIC1IICdzZWMtY2gtdWEtYXJjaDogIng4NiInIFwgICAtSCAnc2VjLWNoLXVhLWJpdG5lc3M6ICI2NCInIFwgICAtSCAnc2VjLWNoLXVhLWZ1bGwtdmVyc2lvbjogIjExNi4wLjE5MzguMjkiJyBcICAgLUggJ3NlYy1jaC11YS1mdWxsLXZlcnNpb24tbGlzdDogIkNocm9taXVtIjt2PSIxMTYuMC41ODQ1LjQyIiwgIk5vdClBO0JyYW5kIjt2PSIyNC4wLjAuMCIsICJNaWNyb3NvZnQgRWRnZSI7dj0iMTE2LjAuMTkzOC4yOSInIFwgICAtSCAnc2VjLWNoLXVhLW1vYmlsZTogPzAnIFwgICAtSCAnc2VjLWNoLXVhLW1vZGVsOiAiIicgXCAgIC1IICdzZWMtY2gtdWEtcGxhdGZvcm06ICJXaW5kb3dzIicgXCAgIC1IICdzZWMtY2gtdWEtcGxhdGZvcm0tdmVyc2lvbjogIjE1LjAuMCInIFwgICAtSCAnc2VjLWZldGNoLWRlc3Q6IGRvY3VtZW50JyBcICAgLUggJ3NlYy1mZXRjaC1tb2RlOiBuYXZpZ2F0ZScgXCAgIC1IICdzZWMtZmV0Y2gtc2l0ZTogbm9uZScgXCAgIC1IICdzZWMtZmV0Y2gtdXNlcjogPzEnIFwgICAtSCAnc2VjLW1zLWdlYzogQjNGNDdBRDRBMjgzQ0FCMzc0QzA0NTFDNDZBQUZEMTQ3QzZBNERBQ0FGRjZBMUMxM0YzNEIyQzcyQjAyNDQ5NCcgXCAgIC1IICdzZWMtbXMtZ2VjLXZlcnNpb246IDEtMTE2LjAuMTkzOC4yOScgXCAgIC1IICd1cGdyYWRlLWluc2VjdXJlLXJlcXVlc3RzOiAxJyBcICAgLUggJ3VzZXItYWdlbnQ6IE1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTYuMC4wLjAgU2FmYXJpLzUzNy4zNiBFZGcvMTE2LjAuMC4wJyBcICAgLUggJ3gtY2xpZW50LWRhdGE6IGV5SXhJam9pTWlJc0lqRXdJam9pWENKVE1HZzNSMDVIT1RGMmFEUTFUVVpTVW5aNU5ITjJha1JtTVdkbGFWSktlbk54TmxBM2FVMVdibkYzUFZ3aUlpd2lNaUk2SWpFaUxDSXpJam9pTVNJc0lqUWlPaUl5TVRVNE9EUTVOVE00TWpZNE9UTTVOVEEzSWl3aU5TSTZJbHdpU205R1VXcFBURGszT1M5TWJrUlJabmxDZDJOMU0yRnNPVU4zZVRaVFFtZGFNR05ZTVhCdE9XVk1aejFjSWlJc0lqWWlPaUppWlhSaElpd2lOeUk2SWpFNE1ETTRPRFl5TmpRek5TSXNJamtpT2lKa1pYTnJkRzl3SW4wPScgXCAgIC1IICd4LWVkZ2Utc2hvcHBpbmctZmxhZzogMScgXCAgIC0tY29tcHJlc3NlZA==
178
+ ```
179
+ </details>
180
+
181
+
182
+ ## 鸣谢
183
+ - 感谢 [EdgeGPT](https://github.com/acheong08/EdgeGPT) 提供的代理 API 的方法。
184
+ - 感谢 [Vercel AI](https://github.com/vercel-labs/ai-chatbot) 提供的基础脚手架和 [ChatHub](https://github.com/chathub-dev/chathub) [go-proxy-bingai](https://github.com/adams549659584/go-proxy-bingai) 提供的部分代码。
185
+
186
+
187
+ ## 答疑及交流
188
+
189
+ <image src="./docs/images/wechat.png" width=240 />
190
+
191
+ ## License
192
+
193
+ MIT © [LICENSE](https://github.com/weaigc/bingo/blob/main/LICENSE).
194
+
195
+
cloudflare/worker.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const TRAGET_HOST='hf4all-bingo.hf.space' // 请将此域名改成你自己的,域名信息在设置》站点域名查看。
2
+
3
+ export default {
4
+ async fetch(request) {
5
+ const uri = new URL(request.url);
6
+ if (uri.protocol === 'http:') {
7
+ uri.protocol = 'https:';
8
+ return new Response('', {
9
+ status: 301,
10
+ headers: {
11
+ location: uri.toString(),
12
+ },
13
+ })
14
+ }
15
+ uri.host = TRAGET_HOST
16
+ return fetch(new Request(uri.toString(), request));
17
+ },
18
+ };
docs/images/curl.png ADDED
docs/images/demo.png ADDED
docs/images/wechat.png ADDED
next.config.js ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ // output: 'export',
4
+ // assetPrefix: '.',
5
+ webpack: (config, { isServer }) => {
6
+ if (!isServer) {
7
+ config.resolve = {
8
+ ...config.resolve,
9
+ fallback: {
10
+ 'bufferutil': false,
11
+ 'utf-8-validate': false,
12
+ http: false,
13
+ https: false,
14
+ stream: false,
15
+ // fixes proxy-agent dependencies
16
+ net: false,
17
+ dns: false,
18
+ tls: false,
19
+ assert: false,
20
+ // fixes next-i18next dependencies
21
+ path: false,
22
+ fs: false,
23
+ // fixes mapbox dependencies
24
+ events: false,
25
+ // fixes sentry dependencies
26
+ process: false
27
+ }
28
+ };
29
+ }
30
+ config.module.exprContextCritical = false;
31
+
32
+ return config;
33
+ },
34
+ }
35
+
36
+ module.exports = (...args) => {
37
+ return nextConfig
38
+ }
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "bingo",
3
+ "version": "0.2.1",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "cross-env DEBUG=bingo next dev --hostname 0.0.0.0",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@headlessui/react": "^1.7.15",
13
+ "@radix-ui/react-alert-dialog": "^1.0.4",
14
+ "@radix-ui/react-dialog": "^1.0.4",
15
+ "@radix-ui/react-dropdown-menu": "^2.0.5",
16
+ "@radix-ui/react-label": "^2.0.2",
17
+ "@radix-ui/react-select": "^1.2.2",
18
+ "@radix-ui/react-separator": "^1.0.3",
19
+ "@radix-ui/react-slot": "^1.0.2",
20
+ "@radix-ui/react-tooltip": "^1.0.6",
21
+ "autoprefixer": "10.4.14",
22
+ "cheerio": "^1.0.0-rc.12",
23
+ "class-variance-authority": "^0.7.0",
24
+ "clsx": "^2.0.0",
25
+ "debug": "^4.3.4",
26
+ "dotenv": "^16.3.1",
27
+ "eslint": "8.44.0",
28
+ "eslint-config-next": "13.4.9",
29
+ "form-data": "^4.0.0",
30
+ "http-proxy-middleware": "^2.0.6",
31
+ "https-proxy-agent": "^7.0.1",
32
+ "i18next": "^22.5.0",
33
+ "i18next-browser-languagedetector": "^7.0.2",
34
+ "idb-keyval": "^6.2.1",
35
+ "immer": "^9.0.19",
36
+ "inter-ui": "^3.19.3",
37
+ "jotai": "^2.2.1",
38
+ "jotai-immer": "^0.2.0",
39
+ "jotai-location": "^0.5.1",
40
+ "js-base64": "^3.7.5",
41
+ "lodash": "^4.17.21",
42
+ "lodash-es": "^4.17.21",
43
+ "nanoid": "^4.0.2",
44
+ "next": "13.4.9",
45
+ "next-auth": "^4.22.3",
46
+ "next-themes": "^0.2.1",
47
+ "postcss": "8.4.25",
48
+ "react": "18.2.0",
49
+ "react-dom": "18.2.0",
50
+ "react-hot-toast": "^2.4.1",
51
+ "react-intersection-observer": "^9.5.2",
52
+ "react-markdown": "^8.0.7",
53
+ "react-syntax-highlighter": "^15.5.0",
54
+ "react-textarea-autosize": "^8.5.0",
55
+ "react-viewport-list": "^7.1.1",
56
+ "rehype-highlight": "^6.0.0",
57
+ "rehype-stringify": "^9.0.3",
58
+ "remark": "^14.0.3",
59
+ "remark-breaks": "^3.0.3",
60
+ "remark-gfm": "^3.0.1",
61
+ "remark-math": "^5.1.1",
62
+ "remark-parse": "^10.0.2",
63
+ "remark-rehype": "^10.1.0",
64
+ "remark-supersub": "^1.0.0",
65
+ "tailwind-merge": "^1.14.0",
66
+ "tailwind-scrollbar": "^3.0.4",
67
+ "tailwindcss": "3.3.2",
68
+ "typescript": "5.1.6",
69
+ "undici": "^5.22.1",
70
+ "websocket-as-promised": "^2.0.1",
71
+ "ws": "^8.13.0"
72
+ },
73
+ "devDependencies": {
74
+ "@headlessui/tailwindcss": "^0.1.3",
75
+ "@types/debug": "^4.1.8",
76
+ "@types/dom-speech-recognition": "^0.0.1",
77
+ "@types/lodash-es": "^4.17.7",
78
+ "@types/md5": "^2.3.2",
79
+ "@types/node": "20.4.2",
80
+ "@types/react": "18.2.14",
81
+ "@types/react-color": "^3.0.6",
82
+ "@types/react-copy-to-clipboard": "^5.0.4",
83
+ "@types/react-dom": "18.2.7",
84
+ "@types/react-scroll-to-bottom": "^4.2.0",
85
+ "@types/react-syntax-highlighter": "^15.5.6",
86
+ "@types/ws": "^8.5.5",
87
+ "@typescript-eslint/eslint-plugin": "^5.60.1",
88
+ "@typescript-eslint/parser": "^5.60.1",
89
+ "cross-env": "^7.0.3",
90
+ "sass": "^1.62.1"
91
+ }
92
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
render.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ - type: web
3
+ name: bingo
4
+ env: docker
5
+ autoDeploy: true
6
+ healthCheckPath: /api/healthz
7
+ plan: free
8
+ envVars:
9
+ - key: BING_HEADER
10
+ value:
11
+ - key: PORT
12
+ value: 10000
src/app/favicon.ico ADDED
src/app/globals.scss ADDED
@@ -0,0 +1,1669 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --cib-color-foreground-accent-primary: #75306C;
7
+ --cib-color-foreground-accent-secondary: #692B61;
8
+ --cib-color-foreground-accent-tertiary: #5E2656;
9
+ --cib-color-foreground-accent-disabled: rgba(117, 48, 108, 0.3);
10
+ --cib-color-foreground-on-accent-primary: #FFFFFF;
11
+ --cib-color-foreground-on-accent-secondary: #FFF4F4;
12
+ --cib-color-foreground-on-accent-tertiary: #FFF4F4;
13
+ --cib-color-foreground-on-accent-disabled: rgba(255, 244, 244, 0.3);
14
+ --cib-color-foreground-neutral-primary: #111111;
15
+ --cib-color-foreground-neutral-secondary: #666666;
16
+ --cib-color-foreground-neutral-tertiary: #919191;
17
+ --cib-color-foreground-neutral-disabled: rgba(17, 17, 17, 0.4);
18
+ --cib-color-foreground-on-accent-strong-primary: #FFFFFF;
19
+ --cib-color-foreground-on-accent-strong-secondary: #FFFFFF;
20
+ --cib-color-foreground-on-accent-strong-disabled: rgba(255, 255, 255, 0.3);
21
+ --cib-color-foreground-system-attention-primary: #106EBE;
22
+ --cib-color-foreground-system-attribution-primary: #006621;
23
+ --cib-color-foreground-system-caution-primary: #9D5D00;
24
+ --cib-color-foreground-system-critical-primary: #C42B1C;
25
+ --cib-color-foreground-system-link-primary: #4007A2;
26
+ --cib-color-foreground-system-neutral-primary: rgba(0, 0, 0, 0.45);
27
+ --cib-color-foreground-system-success-primary: #0F7B0F;
28
+ --cib-color-fill-accent-primary: rgba(255, 255, 255, 0.7);
29
+ --cib-color-fill-accent-secondary: #FFF4F4;
30
+ --cib-color-fill-accent-tertiary: #FBE2E2;
31
+ --cib-color-fill-accent-disabled: rgba(255, 255, 255, 0.3);
32
+ --cib-color-fill-accent-alt-primary: #F6D0D0;
33
+ --cib-color-fill-accent-alt-secondary: #FFF4F4;
34
+ --cib-color-fill-accent-alt-tertiary: #FFF4F4;
35
+ --cib-color-fill-accent-alt-disabled: rgba(246, 208, 208, 0.3);
36
+ --cib-color-fill-accent-gradient-primary: linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
37
+ --cib-color-fill-accent-gradient-secondary: linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)),
38
+ linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
39
+ --cib-color-fill-accent-gradient-tertiary: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)),
40
+ linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
41
+ --cib-color-fill-accent-gradient-quaternary: linear-gradient(90deg, rgb(238, 237, 243) 0%, 0.77381%, rgb(239, 238, 244) 1.54762%, 6.72619%, rgb(239, 236, 243) 11.9048%, 12.381%, rgb(240, 237, 244) 12.8571%, 27.9167%, rgb(242, 236, 244) 42.9762%, 51.9048%, rgb(239, 236, 243) 60.8333%, 61.9643%, rgb(238, 235, 246) 63.0952%, 66.7262%, rgb(235, 234, 249) 70.3571%, 73.2738%, rgb(232, 232, 248) 76.1905%, 77.1429%, rgb(230, 231, 248) 78.0952%, 79.9405%, rgb(228, 229, 249) 81.7857%, 84.1667%, rgb(227, 228, 248) 86.5476%, 87.0238%, rgb(226, 227, 248) 87.5%, 89.3452%, rgb(224, 224, 252) 91.1905%, 95.5952%, rgb(220, 223, 252) 100%);
42
+ --cib-color-fill-accent-strong-primary: #742F6B;
43
+ --cib-color-fill-accent-strong-secondary: #692B61;
44
+ --cib-color-fill-accent-strong-tertiary: #5E2656;
45
+ --cib-color-fill-accent-strong-disabled: rgba(116, 47, 107, 0.3);
46
+ --cib-color-fill-neutral-primary: #FFFFFF;
47
+ --cib-color-fill-neutral-secondary: #F9F9F9;
48
+ --cib-color-fill-neutral-tertiary: #F3F3F3;
49
+ --cib-color-fill-neutral-quaternary: transparent;
50
+ --cib-color-fill-neutral-disabled: rgba(255, 255, 255, 0.3);
51
+ --cib-color-fill-neutral-transparent: transparent;
52
+ --cib-color-fill-neutral-alt-primary: transparent;
53
+ --cib-color-fill-neutral-alt-secondary: rgba(0, 0, 0, 0.06);
54
+ --cib-color-fill-neutral-alt-tertiary: rgba(0, 0, 0, 0.09);
55
+ --cib-color-fill-neutral-alt-quaternary: rgba(0, 0, 0, 0.12);
56
+ --cib-color-fill-neutral-alt-disabled: transparent;
57
+ --cib-color-fill-neutral-alt-transparent: transparent;
58
+ --cib-color-fill-neutral-strong-primary: #444444;
59
+ --cib-color-fill-neutral-strong-secondary: #666666;
60
+ --cib-color-fill-neutral-strong-tertriary: #767676;
61
+ --cib-color-fill-neutral-strong-disabled: rgba(68, 68, 68, 0.3);
62
+ --cib-color-fill-subtle-primary: transparent;
63
+ --cib-color-fill-subtle-secondary: rgba(0, 0, 0, 0.06);
64
+ --cib-color-fill-subtle-tertiary: rgba(0, 0, 0, 0.1);
65
+ --cib-color-fill-subtle-quaternary: rgba(0, 0, 0, 0.2);
66
+ --cib-color-fill-subtle-disabled: transparent;
67
+ --cib-color-fill-subtle-transparent: transparent;
68
+ --cib-color-fill-subtle-alt-primary: rgba(0, 0, 0, 0.06);
69
+ --cib-color-fill-subtle-alt-secondary: rgba(0, 0, 0, 0.1);
70
+ --cib-color-fill-subtle-alt-tertiary: rgba(0, 0, 0, 0.2);
71
+ --cib-color-fill-accent-gradient-balanced-primary: linear-gradient(130deg, #2870EA 20%, #1B4AEF 77.5%);
72
+ --cib-color-fill-accent-gradient-balanced-secondary: linear-gradient(130deg, #2870EA 20%, #1B4AEF 77.5%),
73
+ linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1));
74
+ --cib-color-fill-accent-gradient-balanced-tertiary: linear-gradient(130deg, #2870EA 20%, #1B4AEF 77.5%),
75
+ linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2));
76
+ --cib-color-fill-accent-gradient-balanced-quaternary: linear-gradient(90deg, rgb(239, 242, 247) 0%, 7.60286%, rgb(237, 240, 249) 15.2057%, 20.7513%, rgb(235, 239, 248) 26.297%, 27.6386%, rgb(235, 239, 248) 28.9803%, 38.2826%, rgb(231, 237, 249) 47.585%, 48.1216%, rgb(230, 236, 250) 48.6583%, 53.1306%, rgb(228, 236, 249) 57.6029%, 61.5385%, rgb(227, 234, 250) 65.4741%, 68.7835%, rgb(222, 234, 250) 72.093%, 75.7603%, rgb(219, 230, 248) 79.4275%, 82.8265%, rgb(216, 229, 248) 86.2254%, 87.8354%, rgb(213, 228, 249) 89.4454%, 91.8605%, rgb(210, 226, 249) 94.2755%, 95.4383%, rgb(209, 225, 248) 96.6011%, 98.3005%, rgb(208, 224, 247) 100%);
77
+ --cib-color-fill-accent-gradient-creative-primary: linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
78
+ --cib-color-fill-accent-gradient-creative-secondary: linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)),
79
+ linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
80
+ --cib-color-fill-accent-gradient-creative-tertiary: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)),
81
+ linear-gradient(130deg, #914887 20%, #8B257E 77.5%);
82
+ --cib-color-fill-accent-gradient-creative-quaternary: linear-gradient(90deg, rgb(238, 237, 243) 0%, 0.77381%, rgb(239, 238, 244) 1.54762%, 6.72619%, rgb(239, 236, 243) 11.9048%, 12.381%, rgb(240, 237, 244) 12.8571%, 27.9167%, rgb(242, 236, 244) 42.9762%, 51.9048%, rgb(239, 236, 243) 60.8333%, 61.9643%, rgb(238, 235, 246) 63.0952%, 66.7262%, rgb(235, 234, 249) 70.3571%, 73.2738%, rgb(232, 232, 248) 76.1905%, 77.1429%, rgb(230, 231, 248) 78.0952%, 79.9405%, rgb(228, 229, 249) 81.7857%, 84.1667%, rgb(227, 228, 248) 86.5476%, 87.0238%, rgb(226, 227, 248) 87.5%, 89.3452%, rgb(224, 224, 252) 91.1905%, 95.5952%, rgb(220, 223, 252) 100%);
83
+ --cib-color-fill-accent-gradient-precise-primary: linear-gradient(130deg, #006880 20%, #005366 77.5%);
84
+ --cib-color-fill-accent-gradient-precise-secondary: linear-gradient(0deg, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)),
85
+ linear-gradient(130deg, #006880 20%, #005366 77.5%);
86
+ --cib-color-fill-accent-gradient-precise-tertiary: linear-gradient(0deg, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.2)),
87
+ linear-gradient(130deg, #006880 20%, #005366 77.5%);
88
+ --cib-color-fill-accent-gradient-precise-quaternary: linear-gradient(90deg, rgb(236, 242, 245) 0%, 1.3089%, rgb(234, 243, 245) 2.6178%, 17.4084%, rgb(232, 241, 242) 32.199%, 36.2565%, rgb(229, 241, 242) 40.3141%, 45.0262%, rgb(227, 240, 242) 49.7382%, 51.8325%, rgb(226, 239, 245) 53.9267%, 57.199%, rgb(224, 239, 245) 60.4712%, 62.9581%, rgb(220, 237, 245) 65.445%, 66.2304%, rgb(220, 237, 245) 67.0157%, 68.0628%, rgb(218, 236, 244) 69.1099%, 75.1309%, rgb(214, 233, 240) 81.1518%, 82.5916%, rgb(211, 231, 240) 84.0314%, 84.4241%, rgb(212, 231, 239) 84.8168%, 86.911%, rgb(210, 230, 239) 89.0052%, 94.5026%, rgb(207, 227, 236) 100%);
89
+ --cib-color-background-surface-app-primary: #FFFFFF;
90
+ --cib-color-background-surface-card-primary: rgba(255, 255, 255, 0.7);
91
+ --cib-color-background-surface-card-secondary: rgba(255, 255, 255, 0.4);
92
+ --cib-color-background-surface-card-tertiary: #FFFFFF;
93
+ --cib-color-background-surface-card-disabled: rgba(255, 255, 255, 0.4);
94
+ --cib-color-background-surface-smoke-primary: rgba(0, 0, 0, 0.5);
95
+ --cib-color-background-surface-solid-base: #F5F5F5;
96
+ --cib-color-background-surface-solid-secondary: #EEEEEE;
97
+ --cib-color-background-surface-solid-tertiary: #F9F9F9;
98
+ --cib-color-background-surface-solid-quaternary: #FFFFFF;
99
+ --cib-color-background-system-attention-primary: rgba(255, 255, 255, 0.5);
100
+ --cib-color-background-system-attention-strong: #106EBE;
101
+ --cib-color-background-system-success-primary: #DFF6DD;
102
+ --cib-color-background-system-success-strong: #0F7B0F;
103
+ --cib-color-background-system-caution-primary: #FFF4CE;
104
+ --cib-color-background-system-caution-strong: #9D5D00;
105
+ --cib-color-background-system-critical-primary: #FDE7E9;
106
+ --cib-color-background-system-critical-strong: #C42B1C;
107
+ --cib-color-stroke-accent-primary: #742F6B;
108
+ --cib-color-stroke-accent-secondary: #75306C;
109
+ --cib-color-stroke-accent-tertiary: #75306C;
110
+ --cib-color-stroke-accent-disabled: rgba(116, 47, 107, 0.3);
111
+ --cib-color-stroke-neutral-primary: rgba(0, 0, 0, 0.1);
112
+ --cib-color-stroke-neutral-secondary: rgba(0, 0, 0, 0.2);
113
+ --cib-color-stroke-neutral-tertiary: transparent;
114
+ --cib-color-stroke-neutral-alt-primary: rgba(0, 0, 0, 0.3);
115
+ --cib-color-stroke-surface-card-primary: transparent;
116
+ --cib-color-stroke-surface-card-solid: transparent;
117
+ --cib-color-stroke-surface-divider-primary: rgba(0, 0, 0, 0.1);
118
+ --cib-color-stroke-focus-outer: #111111;
119
+ --cib-color-stroke-focus-inner: #111111;
120
+ --cib-color-stroke-system-attention-primary: #106EBE;
121
+ --cib-color-stroke-system-success-primary: #0F7B0F;
122
+ --cib-color-stroke-system-caution-primary: #9D5D00;
123
+ --cib-color-stroke-system-critical-primary: #C42B1C;
124
+ --cib-color-stroke-system-neutral-primary: rgba(0, 0, 0, 0.45);
125
+ --cib-color-syntax-background-surface: rgba(0, 0, 0, 0.03);
126
+ --cib-color-syntax-background-green: #1B4721;
127
+ --cib-color-syntax-background-red: #78191B;
128
+ --cib-color-syntax-blue: #005CC5;
129
+ --cib-color-syntax-blue-strong: #032F62;
130
+ --cib-color-syntax-gold: #735C0F;
131
+ --cib-color-syntax-gray: #6A737D;
132
+ --cib-color-syntax-gray-strong: #24292E;
133
+ --cib-color-syntax-green: #22863A;
134
+ --cib-color-syntax-orange: #E36209;
135
+ --cib-color-syntax-purple: #6F42C1;
136
+ --cib-color-syntax-red: #D73A49;
137
+ --cib-color-syntax-red-strong: #B31D28;
138
+ --cib-action-bar-search-border-radius: 24px;
139
+ --cib-copy-host-border-radius: 8px;
140
+ --cib-copy-button-border-radius: 6px;
141
+ --cib-feedback-host-border-radius: 8px;
142
+ --cib-feedback-menu-border-radius: 8px;
143
+ --cib-feedback-menu-before-border-radius: 9px;
144
+ --cib-feedback-button-border-radius: 6px;
145
+ --cib-flyout-host-border-radius: 6px;
146
+ --cib-message-ac-container-border-radius: 3px;
147
+ --cib-modal-before-border-radius: 13px;
148
+ --cib-side-panel-aad-msa-redirect-border-radius: 9px;
149
+ --cib-thread-host-border-radius: 6px;
150
+ --cib-thread-host-preview-border-radius: 8px;
151
+ --cib-thread-name-border-radius: 3px;
152
+ --cib-tooltip-host-before-border-radius: 5px;
153
+ --cib-welcome-container-preview-button-border-radius: 3px;
154
+ --cib-color-icon-red-cancel: #c80000;
155
+ --cib-color-icon-green-confirm: #13a10e;
156
+ --cib-image-background: url(https://bing.vcanbb.top/cdx/bg.jpg);
157
+ --cib-shadow-card: 0px 0.3px 0.9px rgba(0, 0, 0, 0.12),
158
+ 0px 1.6px 3.6px rgba(0, 0, 0, 0.16);
159
+ --cib-shadow-card-raised: 0px 0.6px 1.8px rgba(0, 0, 0, 0.12),
160
+ 0px 3.2px 7.2px rgba(0, 0, 0, 0.16);
161
+ --cib-shadow-dialog: 0px 4.8px 14.4px rgba(0, 0, 0, 0.18),
162
+ 0px 25.6px 57.6px rgba(0, 0, 0, 0.22);
163
+ --cib-shadow-flyout: 0px 1.2px 3.6px rgba(0, 0, 0, 0.16),
164
+ 0px 6.4px 14.4px rgba(0, 0, 0, 0.2);
165
+ --cib-shadow-layer: 0px 0.15px 0.45px rgba(0, 0, 0, 0.12),
166
+ 0px 0.8px 1.8px rgba(0, 0, 0, 0.16);
167
+ --cib-shadow-panel: 0px 14px 28px rgba(0, 0, 0, 0.24),
168
+ 0px 0px 8px rgba(0, 0, 0, 0.2);
169
+ --cib-shadow-tooltip: 0px 1.2px 3.6px rgba(0, 0, 0, 0.16),
170
+ 0px 6.4px 14.4px rgba(0, 0, 0, 0.2);
171
+ --cib-shadow-elevation-1: 0px 0.075px 0.225px rgba(0, 0, 0, 0.12),
172
+ 0px 0.4px 0.9px rgba(0, 0, 0, 0.16);
173
+ --cib-shadow-elevation-2: 0px 0.15px 0.45px rgba(0, 0, 0, 0.12),
174
+ 0px 0.8px 1.8px rgba(0, 0, 0, 0.16);
175
+ --cib-shadow-elevation-4: 0px 0.3px 0.9px rgba(0, 0, 0, 0.12),
176
+ 0px 1.6px 3.6px rgba(0, 0, 0, 0.16);
177
+ --cib-shadow-elevation-8: 0px 0.6px 1.8px rgba(0, 0, 0, 0.12),
178
+ 0px 3.2px 7.2px rgba(0, 0, 0, 0.16);
179
+ --cib-shadow-elevation-16: 0px 1.2px 3.6px rgba(0, 0, 0, 0.16),
180
+ 0px 6.4px 14.4px rgba(0, 0, 0, 0.2);
181
+ --cib-shadow-elevation-28: 0px 14px 28px rgba(0, 0, 0, 0.24),
182
+ 0px 0px 8px rgba(0, 0, 0, 0.2);
183
+ --cib-shadow-elevation-64: 0px 4.8px 14.4px rgba(0, 0, 0, 0.18),
184
+ 0px 25.6px 57.6px rgba(0, 0, 0, 0.22);
185
+ --cib-border-radius-none: 0;
186
+ --cib-border-radius-small: 2px;
187
+ --cib-border-radius-medium: 4px;
188
+ --cib-border-radius-large: 8px;
189
+ --cib-border-radius-extra-large: 12px;
190
+ --cib-border-radius-circular: 10000px;
191
+ --cib-font-text: -apple-system,
192
+ Roboto,
193
+ SegoeUI,
194
+ 'Segoe UI',
195
+ 'Helvetica Neue',
196
+ Helvetica,
197
+ 'Microsoft YaHei',
198
+ 'Meiryo UI',
199
+ Meiryo,
200
+ Arial Unicode MS,
201
+ sans-serif;
202
+ --cib-font-icons: 'Fluent Icons';
203
+ --cib-type-caption2-font-size: 10px;
204
+ --cib-type-caption2-line-height: 14px;
205
+ --cib-type-caption2-font-weight: 400;
206
+ --cib-type-caption2-font-variation-settings: unset;
207
+ --cib-type-caption2-strong-font-size: 10px;
208
+ --cib-type-caption2-strong-line-height: 14px;
209
+ --cib-type-caption2-strong-font-weight: 600;
210
+ --cib-type-caption2-strong-font-variation-settings: unset;
211
+ --cib-type-caption1-font-size: 12px;
212
+ --cib-type-caption1-line-height: 16px;
213
+ --cib-type-caption1-font-weight: 400;
214
+ --cib-type-caption1-font-variation-settings: unset;
215
+ --cib-type-caption1-strong-font-size: 12px;
216
+ --cib-type-caption1-strong-line-height: 16px;
217
+ --cib-type-caption1-strong-font-weight: 600;
218
+ --cib-type-caption1-strong-font-variation-settings: unset;
219
+ --cib-type-caption1-stronger-font-size: 12px;
220
+ --cib-type-caption1-stronger-line-height: 16px;
221
+ --cib-type-caption1-stronger-font-weight: 700;
222
+ --cib-type-caption1-stronger-font-variation-settings: unset;
223
+ --cib-type-body1-font-size: 14px;
224
+ --cib-type-body1-line-height: 20px;
225
+ --cib-type-body1-font-weight: 400;
226
+ --cib-type-body1-font-variation-settings: unset;
227
+ --cib-type-body1-strong-font-size: 14px;
228
+ --cib-type-body1-strong-line-height: 20px;
229
+ --cib-type-body1-strong-font-weight: 500;
230
+ --cib-type-body1-strong-font-variation-settings: unset;
231
+ --cib-type-body1-stronger-font-size: 14px;
232
+ --cib-type-body1-stronger-line-height: 20px;
233
+ --cib-type-body1-stronger-font-weight: 600;
234
+ --cib-type-body1-stronger-font-variation-settings: unset;
235
+ --cib-type-body2-font-size: 16px;
236
+ --cib-type-body2-line-height: 24px;
237
+ --cib-type-body2-font-weight: 400;
238
+ --cib-type-body2-font-variation-settings: unset;
239
+ --cib-type-subtitle2-font-size: 16px;
240
+ --cib-type-subtitle2-line-height: 24px;
241
+ --cib-type-subtitle2-font-weight: 500;
242
+ --cib-type-subtitle2-font-variation-settings: unset;
243
+ --cib-type-subtitle2-stronger-font-size: 16px;
244
+ --cib-type-subtitle2-stronger-line-height: 24px;
245
+ --cib-type-subtitle2-stronger-font-weight: 600;
246
+ --cib-type-subtitle2-stronger-font-variation-settings: unset;
247
+ --cib-type-subtitle1-font-size: 20px;
248
+ --cib-type-subtitle1-line-height: 26px;
249
+ --cib-type-subtitle1-font-weight: 500;
250
+ --cib-type-subtitle1-font-variation-settings: unset;
251
+ --cib-type-subtitle1-stronger-font-size: 20px;
252
+ --cib-type-subtitle1-stronger-line-height: 26px;
253
+ --cib-type-subtitle1-stronger-font-weight: 600;
254
+ --cib-type-subtitle1-stronger-font-variation-settings: unset;
255
+ --cib-type-message-font-size: 18px;
256
+ --cib-type-message-line-height: 24px;
257
+ --cib-type-message-font-weight: 400;
258
+ --cib-type-message-font-variation-settings: unset;
259
+ --cib-type-message-strong-font-size: 18px;
260
+ --cib-type-message-strong-line-height: 24px;
261
+ --cib-type-message-strong-font-weight: 600;
262
+ --cib-type-message-strong-font-variation-settings: unset;
263
+ --cib-type-title3-font-size: 24px;
264
+ --cib-type-title3-line-height: 32px;
265
+ --cib-type-title3-font-weight: 600;
266
+ --cib-type-title3-font-variation-settings: unset;
267
+ --cib-type-title2-font-size: 28px;
268
+ --cib-type-title2-line-height: 36px;
269
+ --cib-type-title2-font-weight: 600;
270
+ --cib-type-title2-font-variation-settings: unset;
271
+ --cib-type-title1-font-size: 32px;
272
+ --cib-type-title1-line-height: 40px;
273
+ --cib-type-title1-font-weight: 600;
274
+ --cib-type-title1-font-variation-settings: unset;
275
+ --cib-type-large-title-font-size: 40px;
276
+ --cib-type-large-title-line-height: 52px;
277
+ --cib-type-large-title-font-weight: 600;
278
+ --cib-type-large-title-font-variation-settings: unset;
279
+ --cib-type-display-font-size: 68px;
280
+ --cib-type-display-line-height: 92px;
281
+ --cib-type-display-font-weight: 600;
282
+ --cib-type-display-font-variation-settings: unset;
283
+ --cib-motion-duration-faster: 83ms;
284
+ --cib-motion-duration-fast: 187ms;
285
+ --cib-motion-duration-normal: 333ms;
286
+ --cib-motion-duration-slow: 500ms;
287
+ --cib-motion-duration-slower: 667ms;
288
+ --cib-motion-duration-slowest: 1000ms;
289
+ --cib-motion-duration-faster-number: 83;
290
+ --cib-motion-duration-fast-number: 187;
291
+ --cib-motion-duration-normal-number: 333;
292
+ --cib-motion-duration-slow-number: 500;
293
+ --cib-motion-duration-slower-number: 667;
294
+ --cib-motion-duration-slowest-number: 1000;
295
+ --cib-motion-easing-linear: cubic-bezier(0, 0, 1, 1);
296
+ --cib-motion-easing-in: cubic-bezier(0, 0, 0, 1);
297
+ --cib-motion-easing-out: cubic-bezier(1, 0, 1, 1);
298
+ --cib-motion-easing-strong: cubic-bezier(0.13, 1.62, 0, 0.92);
299
+ --cib-motion-easing-direct: cubic-bezier(0.55, 0.55, 0, 1);
300
+ --cib-motion-easing-transition: cubic-bezier(0.75, 0, 0.25, 1);
301
+ --button-compose-collapsed-width: 48px;
302
+ --button-compose-expanded-width: 116px;
303
+ font-family: var(--cib-font-text);
304
+ }
305
+
306
+ @media (prefers-color-scheme: dark) {
307
+ html {
308
+ color-scheme: light !important;
309
+ }
310
+ }
311
+
312
+ body {
313
+ background: var(--cib-color-fill-accent-gradient-creative-quaternary);
314
+ }
315
+
316
+ .bg-background {
317
+ background: var(--cib-color-background-surface-app-primary);
318
+ }
319
+
320
+ main {
321
+ margin: 0 auto;
322
+ position: relative;
323
+ width: calc(100% - var(--side-panel-width));
324
+ }
325
+
326
+ :root {
327
+ --side-panel-width: 280px;
328
+ }
329
+
330
+ @media (max-width: 767px) {
331
+ :root {
332
+ --side-panel-width: 16px;
333
+ }
334
+ }
335
+
336
+ .chat-container,
337
+ .suggestion-items {
338
+ max-width: 1120px;
339
+ margin: 0 auto;
340
+ }
341
+
342
+ .welcome-container {
343
+ display: flex;
344
+ flex-direction: row;
345
+ flex-wrap: wrap;
346
+ align-items: center;
347
+ height: 100%;
348
+ gap: 24px;
349
+ justify-content: center;
350
+ }
351
+
352
+ .welcome-item {
353
+ display: flex;
354
+ flex-direction: column;
355
+ align-items: center;
356
+ gap: 8px;
357
+ background: transparent;
358
+ border: none;
359
+ font-family: var(--cib-font-text);
360
+ }
361
+
362
+ .item-title {
363
+ display: flex;
364
+ align-items: center;
365
+ justify-content: center;
366
+ text-align: center;
367
+ min-height: 52px;
368
+ color: var(--cib-color-foreground-neutral-primary);
369
+ font-family: var(--cib-font-text);
370
+ font-size: var(--cib-type-message-strong-font-size);
371
+ line-height: var(--cib-type-message-strong-line-height);
372
+ font-weight: var(--cib-type-message-strong-font-weight);
373
+ font-variation-settings: var(--cib-type-message-strong-font-variation-settings);
374
+ }
375
+
376
+ .item-content {
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 4px;
380
+ position: relative;
381
+ height: 100%;
382
+ background: var(--cib-color-background-surface-card-primary);
383
+ border-radius: var(--cib-border-radius-medium);
384
+ text-align: start;
385
+ outline: transparent solid 1px;
386
+ box-sizing: border-box;
387
+ padding: 20px;
388
+ cursor: pointer;
389
+ }
390
+
391
+ .item-content::before {
392
+ content: "";
393
+ position: absolute;
394
+ width: 100%;
395
+ height: 100%;
396
+ top: 0px;
397
+ left: 0px;
398
+ z-index: -1;
399
+ opacity: 0;
400
+ background: var(--cib-color-background-surface-card-primary);
401
+ border-radius: var(--cib-border-radius-medium);
402
+ transition-property: opacity;
403
+ transition-duration: var(--cib-motion-duration-fast);
404
+ transition-timing-function: var(--cib-motion-easing-transition);
405
+ box-shadow: var(--cib-shadow-card);
406
+ }
407
+
408
+ .item-content:hover::before {
409
+ opacity: 1;
410
+ }
411
+
412
+ .item-body {
413
+ color: var(--cib-color-foreground-neutral-primary);
414
+ align-items: center;
415
+ display: flex;
416
+ flex-direction: column;
417
+ font-family: var(--cib-font-text);
418
+ font-size: var(--cib-type-body2-font-size);
419
+ line-height: var(--cib-type-body2-line-height);
420
+ font-weight: var(--cib-type-body2-font-weight);
421
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
422
+ }
423
+
424
+ .fieldset {
425
+ margin: 48px auto;
426
+ padding: 0px;
427
+ border: none;
428
+ width: 310px;
429
+ transition-property: opacity;
430
+ transition-duration: var(--cib-motion-duration-fast);
431
+ transition-timing-function: var(--cib-motion-easing-transition);
432
+ }
433
+
434
+ .legend {
435
+ width: 100%;
436
+ display: flex;
437
+ justify-content: center;
438
+ align-items: center;
439
+ }
440
+
441
+ .caption-2-strong {
442
+ font-size: var(--cib-type-caption2-strong-font-size);
443
+ line-height: var(--cib-type-caption2-strong-line-height);
444
+ font-weight: var(--cib-type-caption2-strong-font-weight);
445
+ font-variation-settings: var(--cib-type-caption2-strong-font-variation-settings);
446
+ }
447
+
448
+ .label-modifier {
449
+ display: block;
450
+ margin-bottom: -2px;
451
+ }
452
+
453
+ .options-list-container {
454
+ padding: 3px;
455
+ margin: 16px 0px;
456
+ border-radius: var(--cib-border-radius-large);
457
+ background: var(--cib-color-background-surface-card-primary);
458
+ box-shadow: var(--cib-shadow-card);
459
+ }
460
+
461
+ .options {
462
+ display: grid;
463
+ grid-auto-columns: 1fr;
464
+ grid-auto-flow: column;
465
+ padding: 0px;
466
+ margin: 0px;
467
+ list-style: none;
468
+ }
469
+
470
+ .option {
471
+ display: inline-block;
472
+ min-width: 96px;
473
+ height: 42px;
474
+ padding: 0px;
475
+ outline: transparent solid 1px;
476
+ border-radius: var(--cib-border-radius-medium);
477
+ }
478
+
479
+ .option button {
480
+ position: relative;
481
+ display: flex;
482
+ flex-direction: column;
483
+ align-items: center;
484
+ justify-content: center;
485
+ width: 100%;
486
+ height: 100%;
487
+ padding: 0px 8px;
488
+ border: none;
489
+ border-radius: var(--cib-border-radius-medium);
490
+ background: transparent;
491
+ cursor: pointer;
492
+ font-family: var(--cib-font-text);
493
+ }
494
+
495
+ .option button.selected {
496
+ color: var(--cib-color-foreground-on-accent-primary);
497
+ background: var(--cib-color-fill-accent-gradient-primary);
498
+ }
499
+
500
+ .text-message {
501
+ position: relative;
502
+ display: flex;
503
+ flex-direction: column;
504
+ max-width: min(768px, 100%);
505
+ margin-inline-end: 80px;
506
+ width: fit-content;
507
+ opacity: 1;
508
+ z-index: 10;
509
+ outline: transparent solid 1px;
510
+ box-shadow: var(--cib-shadow-card);
511
+ border-radius: var(--cib-border-radius-extra-large);
512
+ background: var(--cib-color-background-surface-card-primary);
513
+ }
514
+
515
+ .text-message {
516
+ &.user {
517
+ align-items: flex-end;
518
+ align-self: flex-end;
519
+ margin-inline-end: unset;
520
+ margin-inline-start: 80px;
521
+ z-index: 10;
522
+ background: var(--cib-color-fill-accent-gradient-primary);
523
+ box-shadow: var(--cib-shadow-elevation-4);
524
+ color: var(--cib-color-foreground-on-accent-primary);
525
+ }
526
+
527
+ &.bot {
528
+ a {
529
+ color: var(--cib-color-foreground-system-link-primary);
530
+ }
531
+ }
532
+
533
+ &.user {
534
+ img {
535
+ max-width: 300px;
536
+ max-height: 300px;
537
+ object-fit: contain;
538
+ }
539
+ }
540
+
541
+ a {
542
+ position: relative;
543
+ text-decoration: none;
544
+ }
545
+ }
546
+
547
+
548
+ .text-message-content {
549
+ display: flex;
550
+ flex-direction: column;
551
+ padding: 10px 16px 4px 16px;
552
+ user-select: text;
553
+ word-break: break-word;
554
+ min-height: var(--cib-type-body2-line-height);
555
+ font-size: var(--cib-type-body2-font-size);
556
+ line-height: var(--cib-type-body2-line-height);
557
+ font-weight: var(--cib-type-body2-font-weight);
558
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
559
+ overflow: hidden;
560
+
561
+ h1 {
562
+ font-size: var(--cib-type-title2-font-size);
563
+ line-height: var(--cib-type-title2-line-height);
564
+ font-weight: var(--cib-type-title2-font-weight);
565
+ font-variation-settings: var(--cib-type-title2-font-variation-settings);
566
+ }
567
+
568
+ p,
569
+ h1,
570
+ h2,
571
+ h3,
572
+ h4 {
573
+ padding: 0px;
574
+ user-select: text;
575
+ word-break: break-word;
576
+ display: block;
577
+ }
578
+
579
+ pre {
580
+ display: block;
581
+ }
582
+
583
+ ol,
584
+ menu {
585
+ list-style: decimal;
586
+ margin: 0;
587
+ padding: 0;
588
+ padding-inline-start: 24px;
589
+ }
590
+
591
+ ul,
592
+ ol {
593
+ display: flex;
594
+ flex-direction: column;
595
+ gap: 10px;
596
+ padding-inline-start: 24px;
597
+ }
598
+
599
+ ul {
600
+ list-style: disc;
601
+ }
602
+
603
+ >*:nth-child(n+2) {
604
+ margin-top: 12px;
605
+ }
606
+
607
+ .codeblock {
608
+ border-radius: var(--cib-border-radius-large);
609
+ overflow: hidden;
610
+ }
611
+
612
+ blockquote>p>img {
613
+ max-width: 50%;
614
+ float: left;
615
+ }
616
+ }
617
+
618
+ table,
619
+ ul,
620
+ ol,
621
+ p {
622
+ padding-bottom: 12px;
623
+ }
624
+
625
+ .text-message-footer {
626
+ display: grid;
627
+ grid-template-columns: 1fr auto;
628
+ justify-content: space-between;
629
+
630
+ border-top: 1px solid var(--cib-color-stroke-neutral-primary);
631
+ padding: 0px;
632
+ align-items: self-start;
633
+ }
634
+
635
+ .learn-more-root {
636
+ display: flex;
637
+ flex-direction: row;
638
+ row-gap: 8px;
639
+ padding: 0px 16px;
640
+ margin: 9px 0px;
641
+ overflow: hidden;
642
+ }
643
+
644
+ @media (max-width: 600px) {
645
+ .learn-more-root {
646
+ flex-wrap: wrap;
647
+ }
648
+ }
649
+
650
+ .learn-more {
651
+ position: relative;
652
+ align-self: flex-start;
653
+ min-width: fit-content;
654
+ top: 2px;
655
+ inset-inline-start: 1px;
656
+ margin-inline-end: 8px;
657
+ font-size: var(--cib-type-body1-stronger-font-size);
658
+ line-height: var(--cib-type-body1-stronger-line-height);
659
+ font-weight: var(--cib-type-body1-stronger-font-weight);
660
+ font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings);
661
+ }
662
+
663
+ .attribution-container {
664
+ display: flex;
665
+ flex-direction: row;
666
+ row-gap: 6px;
667
+ }
668
+
669
+ .attribution-items {
670
+ display: flex;
671
+ flex-flow: wrap;
672
+ row-gap: 6px;
673
+ }
674
+
675
+ .attribution-item {
676
+ cursor: pointer;
677
+ text-decoration: none;
678
+ display: flex;
679
+ align-items: center;
680
+ justify-content: center;
681
+ min-width: max-content;
682
+ height: 24px;
683
+ border-radius: var(--cib-border-radius-medium);
684
+ box-sizing: border-box;
685
+ padding: 0px 8px;
686
+ margin-inline-end: 6px;
687
+ color: var(--cib-color-foreground-accent-primary);
688
+ background: var(--cib-color-fill-accent-alt-primary);
689
+ font-family: var(--cib-font-text);
690
+ font-size: var(--cib-type-body1-strong-font-size);
691
+ line-height: var(--cib-type-body1-strong-line-height);
692
+ font-weight: var(--cib-type-body1-strong-font-weight);
693
+ font-variation-settings: var(--cib-type-body1-strong-font-variation-settings);
694
+ }
695
+
696
+ .turn-counter {
697
+ display: flex;
698
+ flex-shrink: 0;
699
+ flex-direction: row;
700
+ align-items: center;
701
+ gap: 6px;
702
+ margin-inline-start: 12px;
703
+ grid-area: 1 / 2 / 2 / 3;
704
+ margin: 9px 14px;
705
+
706
+ .text {
707
+ display: flex;
708
+ gap: 3px;
709
+ font-size: var(--cib-type-body1-stronger-font-size);
710
+ line-height: var(--cib-type-body1-stronger-line-height);
711
+ font-weight: var(--cib-type-body1-stronger-font-weight);
712
+ font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings);
713
+ }
714
+
715
+ .indicator {
716
+ width: 12px;
717
+ height: 12px;
718
+ border-radius: var(--cib-border-radius-circular);
719
+ background: rgb(44, 130, 71);
720
+ }
721
+ }
722
+
723
+ button {
724
+ &:focus {
725
+ outline: none !important;
726
+ }
727
+ }
728
+
729
+ @media (max-width: 600px) {
730
+ .turn-counter {
731
+ inset-inline-end: 0px;
732
+ }
733
+ }
734
+
735
+ @media (max-width: 767px) {
736
+ .suggestion-items {
737
+ display: contents;
738
+ }
739
+ }
740
+
741
+ .suggestion-items {
742
+ display: flex;
743
+ align-items: center;
744
+ justify-content: flex-end;
745
+ flex-flow: wrap;
746
+ gap: 8px 8px;
747
+ order: 1;
748
+ padding-inline-end: 2px;
749
+ overflow: hidden;
750
+ }
751
+
752
+ .suggestion-container {
753
+ height: 30px;
754
+ min-width: max-content;
755
+ overflow: hidden;
756
+ box-sizing: border-box;
757
+ padding: 0px 12px;
758
+ margin: 1px;
759
+ cursor: pointer;
760
+ border: 1px solid var(--cib-color-stroke-accent-primary);
761
+ color: var(--cib-color-foreground-accent-primary);
762
+ background: var(--cib-color-fill-accent-primary);
763
+ border-radius: var(--cib-border-radius-large);
764
+ font-family: var(--cib-font-text);
765
+ font-size: var(--cib-type-body1-strong-font-size);
766
+ line-height: var(--cib-type-body1-strong-line-height);
767
+ font-weight: var(--cib-type-body1-strong-font-weight);
768
+ font-variation-settings: var(--cib-type-body1-strong-font-variation-settings);
769
+
770
+ &:hover,
771
+ &:focus {
772
+ background: var(--cib-color-fill-accent-secondary);
773
+ border-color: var(--cib-color-stroke-accent-secondary);
774
+ color: var(--cib-color-foreground-accent-secondary);
775
+ }
776
+ }
777
+
778
+ .typing-control-item {
779
+ position: relative;
780
+ display: flex;
781
+ flex-direction: row;
782
+ align-items: center;
783
+ cursor: pointer;
784
+ justify-content: center;
785
+ background: var(--cib-color-fill-accent-secondary);
786
+ border-radius: var(--cib-border-radius-large);
787
+ height: 40px;
788
+ box-sizing: border-box;
789
+ padding: 0px 8px;
790
+ color: var(--cib-color-foreground-accent-primary);
791
+ fill: var(--cib-color-foreground-accent-primary);
792
+ border: 1px solid var(--cib-color-stroke-accent-primary);
793
+ font-family: var(--cib-font-text);
794
+ font-size: var(--cib-type-subtitle2-font-size);
795
+ line-height: var(--cib-type-subtitle2-line-height);
796
+ font-weight: var(--cib-type-subtitle2-font-weight);
797
+ font-variation-settings: var(--cib-type-subtitle2-font-variation-settings);
798
+
799
+ &>.stop {
800
+ gap: 2px;
801
+ padding: 0px 12px;
802
+ }
803
+ }
804
+
805
+ .notification-container {
806
+ align-items: flex-end;
807
+ justify-content: center;
808
+ width: 100%;
809
+ transition-property: transform, max-width, min-width;
810
+ transition-duration: var(--cib-motion-duration-slowest);
811
+ transition-timing-function: var(--cib-motion-easing-transition);
812
+
813
+ .bottom-notifications {
814
+ display: flex;
815
+ align-items: center;
816
+ justify-content: center;
817
+ width: 100%;
818
+ margin: 60px 0px 0px;
819
+ }
820
+
821
+ .inline-type {
822
+ display: flex;
823
+ justify-content: center;
824
+ align-items: center;
825
+ text-align: center;
826
+ width: 100%;
827
+ max-width: 1120px;
828
+ color: var(--cib-color-foreground-neutral-primary);
829
+ font-size: var(--cib-type-body2-font-size);
830
+ line-height: var(--cib-type-body2-line-height);
831
+ font-weight: var(--cib-type-body2-font-weight);
832
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
833
+
834
+ &.with-decorative-line {
835
+ &::before {
836
+ margin-inline-end: 1vw;
837
+ }
838
+
839
+ &::before,
840
+ &::after {
841
+ content: "";
842
+ flex: 1 1 0%;
843
+ border-bottom: 1px solid var(--cib-color-stroke-neutral-primary);
844
+ }
845
+ }
846
+
847
+ .text-container {
848
+ max-width: 80%;
849
+ padding: 0px 10px;
850
+ align-items: center;
851
+ }
852
+
853
+ .title {
854
+ position: relative;
855
+ color: var(--cib-color-foreground-neutral-primary);
856
+
857
+ a {
858
+ color: var(--cib-color-foreground-system-link-primary);
859
+ }
860
+ }
861
+ }
862
+ }
863
+
864
+ @media (max-width: 767px) {
865
+ .inline-type {
866
+ margin-bottom: unset;
867
+ }
868
+ }
869
+
870
+ .action-bar {
871
+ position: fixed;
872
+ display: flex;
873
+ align-items: flex-end;
874
+ justify-content: center;
875
+ min-height: 90px;
876
+ bottom: 0;
877
+ box-sizing: border-box;
878
+ z-index: 100;
879
+ width: 100%;
880
+ left: 0;
881
+ transition-property: transform, max-width, min-width;
882
+ transition-duration: var(--cib-motion-duration-slowest);
883
+ transition-timing-function: var(--cib-motion-easing-transition);
884
+ }
885
+
886
+ .action-root {
887
+ position: relative;
888
+ display: flex;
889
+ align-items: flex-start;
890
+ gap: 12px;
891
+ width: calc(100% - var(--side-panel-width));
892
+ height: auto;
893
+ max-width: 1120px;
894
+ min-height: 90px;
895
+ transition-property: width, max-width;
896
+ transition-duration: var(--cib-motion-duration-slowest);
897
+ transition-timing-function: var(--cib-motion-easing-transition);
898
+ }
899
+
900
+ .root[visual-search] .main-container {
901
+ padding-inline-end: 84px;
902
+ }
903
+
904
+ .main-container {
905
+ display: flex;
906
+ flex-direction: column;
907
+ gap: 4px;
908
+ justify-content: space-between;
909
+ align-items: flex-start;
910
+ position: relative;
911
+ width: 100%;
912
+ height: 100%;
913
+ min-height: 48px;
914
+ box-sizing: border-box;
915
+ padding-block: 13px 11px;
916
+ padding-inline: 16px;
917
+ z-index: 1;
918
+ background: var(--cib-color-background-surface-solid-quaternary);
919
+ border-radius: var(--cib-action-bar-search-border-radius);
920
+ outline: transparent solid 1px;
921
+ cursor: text;
922
+ transition-property: min-height, height, width, transform, border-radius, box-shadow;
923
+ transition-duration: var(--cib-motion-duration-fast);
924
+ transition-timing-function: var(--cib-motion-easing-in);
925
+ transition-delay: var(--cib-motion-duration-faster);
926
+ box-shadow: var(--cib-shadow-card);
927
+
928
+ img {
929
+ cursor: pointer;
930
+ user-select: none;
931
+ }
932
+
933
+ textarea {
934
+ white-space: nowrap;
935
+ text-overflow: ellipsis;
936
+ overflow-x: hidden;
937
+ }
938
+
939
+ &:hover,
940
+ &.active {
941
+ min-height: 90px;
942
+ border-radius: var(--cib-border-radius-extra-large);
943
+
944
+ .bottom-bar {
945
+ opacity: 1;
946
+ }
947
+
948
+ textarea {
949
+ white-space: pre-wrap;
950
+ }
951
+ }
952
+
953
+ .main-bar {
954
+ display: flex;
955
+ flex-direction: row;
956
+ width: 100%;
957
+ gap: 16px;
958
+ justify-content: space-between;
959
+ align-items: flex-start;
960
+
961
+ &>*:nth-child(n+5) {
962
+ display: none;
963
+ }
964
+ }
965
+
966
+ .message-input {
967
+ max-height: 50vh;
968
+ overflow-y: auto;
969
+ }
970
+ }
971
+
972
+ .body-1 {
973
+ font-size: var(--cib-type-body1-font-size);
974
+ line-height: var(--cib-type-body1-line-height);
975
+ font-weight: var(--cib-type-body1-font-weight);
976
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
977
+ }
978
+
979
+ .body-2 {
980
+ font-size: var(--cib-type-body2-font-size);
981
+ line-height: var(--cib-type-body2-line-height);
982
+ font-weight: var(--cib-type-body2-font-weight);
983
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
984
+ }
985
+
986
+ .outside-left-container {
987
+ position: relative;
988
+ align-self: flex-end;
989
+ height: 48px;
990
+ bottom: 42px;
991
+ margin: 0px;
992
+ padding: 0px;
993
+ transition-property: opacity;
994
+ transition-duration: var(--cib-motion-duration-slow);
995
+ transition-delay: var(--cib-motion-duration-normal);
996
+ transition-timing-function: var(--cib-motion-easing-transition);
997
+
998
+ .button-compose-wrapper {
999
+ transition-property: opacity, transform;
1000
+ transition-duration: var(--cib-motion-duration-fast);
1001
+ transition-timing-function: var(--cib-motion-easing-in);
1002
+ }
1003
+
1004
+ .button-compose {
1005
+ display: flex;
1006
+ flex-direction: row;
1007
+ position: relative;
1008
+ height: 48px;
1009
+ width: var(--button-compose-expanded-width);
1010
+ font-family: var(--cib-font-text);
1011
+ border-radius: var(--cib-border-radius-circular);
1012
+ color: var(--cib-color-foreground-on-accent-primary);
1013
+ fill: var(--cib-color-foreground-on-accent-primary);
1014
+ background: transparent;
1015
+ border: none;
1016
+ outline: transparent solid 1px;
1017
+ margin: 0px;
1018
+ padding: 0px;
1019
+ overflow: hidden;
1020
+ transition-property: width, opacity;
1021
+ transition-duration: var(--cib-motion-duration-normal);
1022
+ transition-timing-function: var(--cib-motion-easing-in);
1023
+
1024
+ &:not([disabled]) {
1025
+ pointer-events: auto;
1026
+ cursor: pointer;
1027
+ }
1028
+
1029
+ &::before {
1030
+ content: "";
1031
+ position: absolute;
1032
+ width: 100%;
1033
+ height: 100%;
1034
+ border-radius: var(--cib-border-radius-circular);
1035
+ background: var(--cib-color-fill-accent-gradient-primary);
1036
+ box-shadow: var(--cib-shadow-elevation-4);
1037
+ transition-property: transform;
1038
+ transition-duration: var(--cib-motion-duration-fast);
1039
+ transition-timing-function: var(--cib-motion-easing-in);
1040
+ }
1041
+ }
1042
+
1043
+ &.collapsed .button-compose {
1044
+ width: var(--button-compose-collapsed-width);
1045
+ }
1046
+
1047
+ &:hover .button-compose {
1048
+ width: var(--button-compose-expanded-width);
1049
+ }
1050
+
1051
+ @media (max-width: 600px) {
1052
+ .button-compose {
1053
+ width: var(--button-compose-collapsed-width) !important;
1054
+ }
1055
+ }
1056
+
1057
+ .button-compose-content {
1058
+ position: relative;
1059
+ display: grid;
1060
+ grid-template-columns: 48px auto;
1061
+ align-items: center;
1062
+ height: 48px;
1063
+ }
1064
+
1065
+ .button-compose-text {
1066
+ min-width: max-content;
1067
+ margin-inline-end: 20px;
1068
+ transition-property: opacity;
1069
+ transition-duration: var(--cib-motion-duration-fast);
1070
+ transition-timing-function: var(--cib-motion-easing-in);
1071
+ }
1072
+
1073
+ }
1074
+
1075
+ .visual-search-container {
1076
+ position: relative;
1077
+
1078
+ .visual-search {
1079
+ position: absolute;
1080
+ bottom: 42px;
1081
+ width: 380px;
1082
+ inset-inline-end: -50px;
1083
+ display: flex;
1084
+ flex-direction: column;
1085
+ align-items: flex-start;
1086
+ padding: 4px 4px 12px;
1087
+ padding-block-end: 4px;
1088
+
1089
+
1090
+ will-change: transform;
1091
+ border-radius: var(--cib-flyout-host-border-radius);
1092
+ box-shadow: var(--cib-shadow-elevation-28);
1093
+ background-color: var(--cib-color-background-surface-solid-tertiary);
1094
+ transition-duration: var(--cib-motion-duration-fast);
1095
+ transition-delay: var(--cib-motion-duration-normal);
1096
+ transition-timing-function: var(--cib-motion-easing-in);
1097
+ font-size: var(--cib-type-body1-font-size);
1098
+ line-height: var(--cib-type-body1-line-height);
1099
+ font-weight: var(--cib-type-body1-font-weight);
1100
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1101
+
1102
+ .header {
1103
+ width: 100%;
1104
+ box-sizing: border-box;
1105
+ padding: 10px 12px 6px;
1106
+ }
1107
+
1108
+ &.none {
1109
+ display: none;
1110
+ }
1111
+
1112
+
1113
+ h4 {
1114
+ margin: 0px;
1115
+ color: var(--cib-color-foreground-neutral-primary);
1116
+ font-size: var(--cib-type-subtitle2-stronger-font-size);
1117
+ line-height: var(--cib-type-subtitle2-stronger-line-height);
1118
+ font-weight: var(--cib-type-subtitle2-stronger-font-weight);
1119
+ font-variation-settings: var(--cib-type-subtitle2-stronger-font-variation-settings);
1120
+ }
1121
+
1122
+ .paste {
1123
+ position: relative;
1124
+ width: 100%;
1125
+ box-sizing: border-box;
1126
+ padding: 10px 12px;
1127
+
1128
+ img {
1129
+ position: absolute;
1130
+ top: 20px;
1131
+ inset-inline-start: 24px;
1132
+ }
1133
+
1134
+ }
1135
+
1136
+ .paste-input {
1137
+ display: flex;
1138
+ flex-direction: column;
1139
+ justify-content: center;
1140
+ height: 40px;
1141
+ width: 100%;
1142
+ box-sizing: border-box;
1143
+ padding: 10px 16px;
1144
+ padding-inline-start: 44px;
1145
+ margin: 0px;
1146
+ border: 1px solid var(--cib-color-stroke-surface-card-primary);
1147
+ border-radius: var(--cib-border-radius-extra-large);
1148
+ box-shadow: var(--cib-shadow-elevation-4);
1149
+ color: var(--cib-color-foreground-neutral-primary);
1150
+ background: var(--cib-color-fill-neutral-primary);
1151
+ font-size: var(--cib-type-body1-font-size);
1152
+ line-height: var(--cib-type-body1-line-height);
1153
+ font-weight: var(--cib-type-body1-font-weight);
1154
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1155
+
1156
+ &:focus {
1157
+ outline: 1px solid var(--cib-color-stroke-accent-primary);
1158
+ }
1159
+ }
1160
+
1161
+ .buttons {
1162
+ width: 100%;
1163
+ box-sizing: border-box;
1164
+ padding: 0px 12px;
1165
+
1166
+ button {
1167
+ display: flex;
1168
+ flex-direction: row;
1169
+ align-items: center;
1170
+ justify-content: flex-start;
1171
+ position: relative;
1172
+ cursor: pointer;
1173
+ gap: 12px;
1174
+ height: 40px;
1175
+ width: 100%;
1176
+ box-sizing: border-box;
1177
+ padding: 10px 12px;
1178
+ margin: 0px;
1179
+ border: none;
1180
+ border-radius: var(--cib-border-radius-medium);
1181
+ cursor: pointer;
1182
+ background: transparent;
1183
+ color: var(--cib-color-foreground-neutral-primary);
1184
+ font-size: var(--cib-type-body1-font-size);
1185
+ line-height: var(--cib-type-body1-line-height);
1186
+ font-weight: var(--cib-type-body1-font-weight);
1187
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1188
+
1189
+ &:hover {
1190
+ background: var(--cib-color-fill-subtle-secondary);
1191
+ }
1192
+ }
1193
+ }
1194
+
1195
+ .fileinput {
1196
+ opacity: 0;
1197
+ position: absolute;
1198
+ width: 100%;
1199
+ height: 100%;
1200
+ }
1201
+
1202
+ &::after {
1203
+ content: "";
1204
+ position: absolute;
1205
+ inset-inline-end: 50px;
1206
+ top: 100%;
1207
+ inset-inline-start: var(--arrow-start-offset);
1208
+ border-top-width: 10px;
1209
+ border-right: 10px solid transparent;
1210
+ border-left: 10px solid transparent;
1211
+ border-top-style: solid;
1212
+ border-image: initial;
1213
+ border-bottom: none;
1214
+ border-top-color: var(--cib-color-background-surface-solid-tertiary);
1215
+ filter: drop-shadow(0 1px 0 var(--cib-color-stroke-neutral-primary));
1216
+ transition-property: inset-inline-start;
1217
+ transition-duration: var(--cib-motion-duration-fast);
1218
+ transition-timing-function: var(--cib-motion-easing-in);
1219
+ }
1220
+ }
1221
+
1222
+ .webvideo-container {
1223
+ position: relative;
1224
+ }
1225
+
1226
+ .webvideo {
1227
+ display: block;
1228
+ background-color: rgb(0, 0, 0);
1229
+ width: 100%;
1230
+ height: auto;
1231
+ }
1232
+
1233
+ .webcanvas {
1234
+ display: none
1235
+ }
1236
+
1237
+ .cambtn {
1238
+ cursor: pointer;
1239
+ width: min-content;
1240
+ height: 46px;
1241
+ padding: 8px;
1242
+ margin: auto;
1243
+ }
1244
+
1245
+ .cam-btn-circle-large {
1246
+ width: 30px;
1247
+ height: 30px;
1248
+ border-radius: var(--cib-border-radius-circular);
1249
+ background: var(--cib-color-fill-accent-gradient-primary);
1250
+ opacity: 0.4;
1251
+ }
1252
+
1253
+ .cam-btn-circle-small {
1254
+ width: 20px;
1255
+ height: 20px;
1256
+ border-radius: var(--cib-border-radius-circular);
1257
+ background: var(--cib-color-fill-accent-gradient-primary);
1258
+ opacity: 1;
1259
+ position: relative;
1260
+ top: -25px;
1261
+ inset-inline-start: 5px;
1262
+ }
1263
+
1264
+ .normal-content,
1265
+ .cam-content {
1266
+ width: 100%;
1267
+ display: none;
1268
+ }
1269
+
1270
+ .normal .normal-content {
1271
+ display: block;
1272
+ }
1273
+
1274
+ .camera-mode .cam-content {
1275
+ display: block;
1276
+ }
1277
+ }
1278
+
1279
+ .spinner:before {
1280
+ content: "";
1281
+ box-sizing: border-box;
1282
+ position: absolute;
1283
+ top: 50%;
1284
+ inset-inline-start: 50%;
1285
+ width: 20px;
1286
+ height: 20px;
1287
+ margin-top: -10px;
1288
+ margin-inline-start: -10px;
1289
+ border-radius: 50%;
1290
+ border-top: 2px solid var(--cib-color-fill-accent-gradient-primary);
1291
+ border-inline-end: 2px solid transparent;
1292
+ animation: spinner 0.6s linear infinite;
1293
+ }
1294
+
1295
+ @keyframes spinner {
1296
+ to {
1297
+ transform: rotate(360deg);
1298
+ }
1299
+ }
1300
+
1301
+ @keyframes borealisBar {
1302
+ 0% {
1303
+ inset-inline-start: 0%;
1304
+ inset-inline-end: 100%;
1305
+ width: 0%;
1306
+ }
1307
+
1308
+ 35% {
1309
+ inset-inline-start: 0%;
1310
+ inset-inline-end: 50%;
1311
+ width: 50%;
1312
+ }
1313
+
1314
+ 65% {
1315
+ inset-inline-end: 0%;
1316
+ inset-inline-start: 50%;
1317
+ width: 50%;
1318
+ }
1319
+
1320
+ 100% {
1321
+ inset-inline-start: 100%;
1322
+ inset-inline-end: 0%;
1323
+ width: 0%;
1324
+ }
1325
+ }
1326
+
1327
+ .attachment-list {
1328
+ display: flex;
1329
+ flex-wrap: wrap;
1330
+ gap: 8px;
1331
+ margin-block: 16px;
1332
+ overflow: hidden;
1333
+
1334
+ .file-item {
1335
+ display: flex;
1336
+ flex-direction: row;
1337
+ height: 48px;
1338
+ border-radius: var(--cib-border-radius-medium);
1339
+ overflow: hidden;
1340
+ cursor: default;
1341
+ opacity: 1;
1342
+ animation-name: file-item-enter;
1343
+ animation-fill-mode: both;
1344
+ animation-delay: var(--cib-motion-duration-fast);
1345
+ animation-duration: var(--cib-motion-duration-normal);
1346
+ animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
1347
+ background: var(--cib-color-background-surface-app-primary);
1348
+ border: 1px solid var(--cib-color-stroke-neutral-primary);
1349
+ position: relative;
1350
+ }
1351
+
1352
+ .thumbnail {
1353
+ display: flex;
1354
+ align-items: center;
1355
+ justify-content: center;
1356
+ height: 48px;
1357
+ width: 48px;
1358
+
1359
+ img {
1360
+ height: 48px;
1361
+ width: 48px;
1362
+ object-fit: cover;
1363
+ object-position: center center;
1364
+ -webkit-user-drag: none;
1365
+ overflow-clip-margin: content-box;
1366
+ overflow: clip;
1367
+ }
1368
+ }
1369
+
1370
+ .dismiss {
1371
+ display: flex;
1372
+ align-items: center;
1373
+ justify-content: center;
1374
+ width: 32px;
1375
+ border: none;
1376
+ margin: 0px;
1377
+ padding: 0px;
1378
+ background-color: transparent;
1379
+ cursor: pointer;
1380
+ fill: var(--cib-color-foreground-neutral-primary);
1381
+
1382
+ &.no-file {
1383
+ background-color: var(--cib-color-background-surface-solid-tertiary);
1384
+ }
1385
+ }
1386
+
1387
+ .error {
1388
+ height: 48px;
1389
+ width: 48px;
1390
+ background-color: var(--cib-color-fill-accent-secondary);
1391
+ display: flex;
1392
+ justify-content: center;
1393
+ align-items: center;
1394
+ }
1395
+
1396
+ .loading {
1397
+ width: 100%;
1398
+ position: absolute;
1399
+ height: 3px;
1400
+ bottom: -1px;
1401
+ }
1402
+
1403
+ .bar {
1404
+ top: 0;
1405
+ inset-inline-end: 100%;
1406
+ bottom: 0;
1407
+ inset-inline-start: 0;
1408
+ width: 0;
1409
+ position: absolute;
1410
+ background: var(--cib-color-fill-accent-gradient-primary);
1411
+ animation: borealisBar 2s linear infinite;
1412
+ }
1413
+ }
1414
+
1415
+ .bottom-bar {
1416
+ position: absolute;
1417
+ display: flex;
1418
+ flex-direction: row;
1419
+ align-items: center;
1420
+ justify-content: space-between;
1421
+ height: 36px;
1422
+ bottom: 4px;
1423
+ inset-inline: 0px;
1424
+ box-sizing: border-box;
1425
+ padding-block: 0px;
1426
+ padding-inline: 16px 8px;
1427
+ opacity: 0;
1428
+ transition-property: opacity;
1429
+ transition-duration: var(--cib-motion-duration-faster);
1430
+ transition-delay: var(--cib-motion-duration-faster);
1431
+ transition-timing-function: var(--cib-motion-easing-transition);
1432
+
1433
+ .letter-counter {
1434
+ color: var(--cib-color-foreground-neutral-secondary);
1435
+ }
1436
+ }
1437
+
1438
+ .fade {
1439
+ position: fixed;
1440
+ left: 0;
1441
+ height: 104px;
1442
+ width: 100%;
1443
+ z-index: -1;
1444
+ overflow: hidden;
1445
+ clip-path: inset(0px);
1446
+ pointer-events: none;
1447
+
1448
+ &.bottom {
1449
+ display: block;
1450
+ bottom: 0px;
1451
+ height: 140px;
1452
+ -webkit-mask-image: linear-gradient(transparent calc(100% - 140px), black calc(100% - 118px));
1453
+ mask-image: linear-gradient(transparent calc(100% - 140px), black calc(100% - 118px));
1454
+ }
1455
+
1456
+ .background {
1457
+ height: 100%;
1458
+ transition-property: transform;
1459
+ transition-duration: var(--cib-motion-duration-slowest);
1460
+ transition-timing-function: var(--cib-motion-easing-transition);
1461
+ background: var(--cib-color-fill-accent-gradient-quaternary);
1462
+ }
1463
+ }
1464
+
1465
+ @media (max-width: 600px) {
1466
+ .action-root {
1467
+ align-items: flex-end;
1468
+ justify-content: flex-end;
1469
+ min-height: unset;
1470
+ }
1471
+
1472
+ .main-container {
1473
+ width: calc(100% - 60px);
1474
+
1475
+ button[type="submit"] {
1476
+ display: none;
1477
+ }
1478
+
1479
+ &:hover,
1480
+ &.active {
1481
+ width: 100%;
1482
+ transition-delay: 167ms;
1483
+ }
1484
+ }
1485
+
1486
+ .outside-left-container {
1487
+ position: absolute;
1488
+ bottom: 0px;
1489
+ inset-inline-start: 0px;
1490
+ }
1491
+ }
1492
+
1493
+ .chat-history {
1494
+ &-header {
1495
+ position: relative;
1496
+
1497
+ &::after {
1498
+ position: absolute;
1499
+ content: "";
1500
+ inset-block-end: 0px;
1501
+ inset-inline: 16px;
1502
+ border-block-end: 1px solid var(--cib-color-stroke-neutral-primary);
1503
+ }
1504
+ }
1505
+
1506
+ &-main {
1507
+ display: flex;
1508
+ flex-direction: column;
1509
+ flex-grow: 1;
1510
+ overflow: hidden;
1511
+ }
1512
+
1513
+ .scroller {
1514
+ position: relative;
1515
+ box-sizing: border-box;
1516
+ padding: 16px;
1517
+ max-block-size: max(324px, 60%);
1518
+ }
1519
+
1520
+ .surface {
1521
+ display: flex;
1522
+ flex-direction: column;
1523
+ max-block-size: 100%;
1524
+ border-radius: var(--cib-border-radius-large);
1525
+ background: var(--cib-color-background-surface-card-secondary);
1526
+ box-shadow: var(--cib-shadow-card);
1527
+ }
1528
+
1529
+ .threads {
1530
+ display: flex;
1531
+ flex-direction: column;
1532
+ padding: 4px;
1533
+ overflow: hidden;
1534
+ max-block-size: calc(100% - 44px);
1535
+ box-sizing: border-box;
1536
+ }
1537
+
1538
+ .thread {
1539
+ display: flex;
1540
+ justify-content: space-between;
1541
+ flex-direction: row;
1542
+ inline-size: 100%;
1543
+ align-items: center;
1544
+ position: relative;
1545
+ border-radius: var(--cib-comp-thread-host-border-radius);
1546
+ cursor: pointer;
1547
+ padding-block: 9px;
1548
+ padding-inline: 12px;
1549
+ display: flex;
1550
+ flex-direction: column;
1551
+ gap: 5px;
1552
+
1553
+ &:hover {
1554
+ background: var(--cib-color-background-surface-card-primary);
1555
+ box-shadow: var(--cib-shadow-elevation-1);
1556
+
1557
+ &::after {
1558
+ content: "";
1559
+ position: absolute;
1560
+ top: 0;
1561
+ inline-size: 3px;
1562
+ block-size: 100%;
1563
+ inset-inline-start: 0px;
1564
+ border-start-start-radius: var(--cib-comp-thread-host-border-radius);
1565
+ border-end-start-radius: var(--cib-comp-thread-host-border-radius);
1566
+ background: var(--cib-color-fill-accent-gradient-creative-primary);
1567
+ }
1568
+ .time {
1569
+ display: none;
1570
+ }
1571
+ }
1572
+ &:not(:hover) {
1573
+ .controls {
1574
+ display: none;
1575
+ }
1576
+ }
1577
+
1578
+ &>* {
1579
+ width: 100%;
1580
+ }
1581
+
1582
+ &::nth-child(n+1)::before {
1583
+ position: absolute;
1584
+ content: "";
1585
+ inset-block-start: 0px;
1586
+ inset-inline: 16px;
1587
+ border-block-start: 1px solid var(--cib-color-fill-subtle-secondary);
1588
+ }
1589
+ }
1590
+
1591
+ .primary-row {
1592
+ display: flex;
1593
+ align-items: center;
1594
+ justify-content: space-between;
1595
+ gap: 8px;
1596
+
1597
+ &>button {
1598
+ position: absolute;
1599
+ inset: 0px;
1600
+ }
1601
+
1602
+ button {
1603
+ display: flex;
1604
+ align-items: center;
1605
+ justify-content: center;
1606
+ background: transparent;
1607
+ border: none;
1608
+ margin: 0px;
1609
+ padding: 0px;
1610
+ }
1611
+
1612
+ &>* {
1613
+ flex-grow: 0;
1614
+ flex-shrink: 0;
1615
+ }
1616
+ }
1617
+
1618
+ .controls {
1619
+ display: flex;
1620
+ align-items: center;
1621
+ }
1622
+
1623
+ .description {
1624
+ -webkit-mask-image: linear-gradient(to right, var(--cib-color-background-surface-app-primary) 90%, transparent);
1625
+ }
1626
+
1627
+ .name {
1628
+ display: flex;
1629
+ align-items: center;
1630
+ max-inline-size: 100%;
1631
+ padding-block: calc((26px - var(--cib-type-body1-line-height)) / 2);
1632
+ margin: 0px;
1633
+ white-space: nowrap;
1634
+ overflow: hidden;
1635
+ border-radius: var(--cib-comp-thread-name-border-radius);
1636
+ font-family: var(--cib-font-text);
1637
+ font-size: var(--cib-type-body1-font-size);
1638
+ line-height: var(--cib-type-body1-line-height);
1639
+ font-weight: var(--cib-type-body1-font-weight);
1640
+ }
1641
+
1642
+ .time {
1643
+ display: flex;
1644
+ align-items: center;
1645
+ text-align: right;
1646
+ margin: 0px;
1647
+ flex-shrink: 0;
1648
+ padding-inline-end: 6px;
1649
+ color: var(--cib-color-foreground-neutral-secondary);
1650
+ font-size: var(--cib-type-caption2-font-size);
1651
+ line-height: var(--cib-type-caption2-line-height);
1652
+ font-weight: var(--cib-type-caption2-font-weight);
1653
+ font-variation-settings: var(--cib-type-caption2-font-variation-settings);
1654
+ }
1655
+
1656
+ .icon-button {
1657
+ display: none;
1658
+ position: relative;
1659
+ flex-shrink: 0;
1660
+ inline-size: 26px;
1661
+ aspect-ratio: 1 / 1;
1662
+ border-radius: var(--cib-border-radius-medium);
1663
+ fill: var(--cib-color-foreground-neutral-primary);
1664
+ &:hover {
1665
+ background: var(--cib-color-fill-subtle-secondary);
1666
+ cursor: pointer;
1667
+ }
1668
+ }
1669
+ }
src/app/layout.tsx ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Metadata } from 'next'
2
+ import { Toaster } from 'react-hot-toast'
3
+ import { TailwindIndicator } from '@/components/tailwind-indicator'
4
+ import { Providers } from '@/components/providers'
5
+ import { Header } from '@/components/header'
6
+
7
+ import '@/app/globals.scss'
8
+
9
+
10
+ export const metadata: Metadata = {
11
+ title: {
12
+ default: 'Bing AI Chatbot',
13
+ template: `%s - Bing AI Chatbot`
14
+ },
15
+ description: 'Bing AI Chatbot Web App.',
16
+ themeColor: [
17
+ { media: '(prefers-color-scheme: light)', color: 'white' },
18
+ { media: '(prefers-color-scheme: dark)', color: 'dark' }
19
+ ],
20
+ icons: {
21
+ icon: '/favicon.ico',
22
+ shortcut: '../assets/images/logo.svg',
23
+ apple: '../assets/images/logo.svg'
24
+ }
25
+ }
26
+
27
+ interface RootLayoutProps {
28
+ children: React.ReactNode
29
+ }
30
+
31
+ export default function RootLayout({ children }: RootLayoutProps) {
32
+ return (
33
+ <html lang="zh-CN" suppressHydrationWarning>
34
+ <body>
35
+ <Toaster />
36
+ <Providers attribute="class" defaultTheme="system" enableSystem>
37
+ <div className="flex flex-col min-h-screen">
38
+ {/* @ts-ignore */}
39
+ <Header />
40
+ <main className="flex flex-col flex-1">{children}</main>
41
+ </div>
42
+ <TailwindIndicator />
43
+ </Providers>
44
+ </body>
45
+ </html>
46
+ )
47
+ }
src/app/loading.css ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ::-webkit-scrollbar {
2
+ width: 10px;
3
+ height: 10px;
4
+ display: none;
5
+ }
6
+
7
+ ::-webkit-scrollbar-button:start:decrement,
8
+ ::-webkit-scrollbar-button:end:increment {
9
+ height: 30px;
10
+ background-color: transparent;
11
+ }
12
+
13
+ ::-webkit-scrollbar-track-piece {
14
+ background-color: #3b3b3b;
15
+ -webkit-border-radius: 16px;
16
+ }
17
+
18
+ ::-webkit-scrollbar-thumb:vertical {
19
+ height: 50px;
20
+ background-color: #666;
21
+ border: 1px solid #eee;
22
+ -webkit-border-radius: 6px;
23
+ }
24
+
25
+ /* loading start */
26
+ .loading-spinner {
27
+ display: flex;
28
+ justify-content: center;
29
+ align-items: center;
30
+ height: 100vh;
31
+ opacity: 1;
32
+ transition: opacity .8s ease-out;
33
+ }
34
+
35
+ .loading-spinner.hidden {
36
+ opacity: 0;
37
+ }
38
+
39
+ .loading-spinner>div {
40
+ width: 30px;
41
+ height: 30px;
42
+ background: linear-gradient(90deg, #2870EA 10.79%, #1B4AEF 87.08%);
43
+
44
+ border-radius: 100%;
45
+ display: inline-block;
46
+ animation: sk-bouncedelay 1.4s infinite ease-in-out both;
47
+ }
48
+
49
+ .loading-spinner .bounce1 {
50
+ animation-delay: -0.32s;
51
+ }
52
+
53
+ .loading-spinner .bounce2 {
54
+ animation-delay: -0.16s;
55
+ }
56
+
57
+ @keyframes sk-bouncedelay {
58
+
59
+ 0%,
60
+ 80%,
61
+ 100% {
62
+ transform: scale(0);
63
+ }
64
+
65
+ 40% {
66
+ transform: scale(1.0);
67
+ }
68
+ }
src/app/page.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dynamic from 'next/dynamic'
2
+
3
+ const DynamicComponentWithNoSSR = dynamic(
4
+ () => import('../components/chat'),
5
+ { ssr: false }
6
+ )
7
+
8
+ export default function IndexPage() {
9
+ return (
10
+ <>
11
+ <div className="loading-spinner" />
12
+ <DynamicComponentWithNoSSR />
13
+ </>
14
+ )
15
+ }
src/assets/images/brush.svg ADDED
src/assets/images/camera.svg ADDED
src/assets/images/chat.svg ADDED
src/assets/images/check-mark.svg ADDED
src/assets/images/clear.svg ADDED
src/assets/images/help.svg ADDED
src/assets/images/logo.svg ADDED
src/assets/images/paste.svg ADDED
src/assets/images/pin-fill.svg ADDED
src/assets/images/pin.svg ADDED
src/assets/images/refresh.svg ADDED
src/assets/images/send.svg ADDED
src/assets/images/settings.svg ADDED
src/assets/images/speech.svg ADDED
src/assets/images/stop.svg ADDED
src/assets/images/upload.svg ADDED
src/assets/images/visual-search.svg ADDED
src/assets/images/voice.svg ADDED
src/assets/images/warning.svg ADDED
src/components/button-scroll-to-bottom.tsx ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+
5
+ import { cn } from '@/lib/utils'
6
+ import { useAtBottom } from '@/lib/hooks/use-at-bottom'
7
+ import { Button, type ButtonProps } from '@/components/ui/button'
8
+ import { IconArrowDown } from '@/components/ui/icons'
9
+
10
+ export function ButtonScrollToBottom({ className, ...props }: ButtonProps) {
11
+ const isAtBottom = useAtBottom()
12
+
13
+ return (
14
+ <Button
15
+ variant="outline"
16
+ size="icon"
17
+ className={cn(
18
+ 'fixed right-4 bottom-24 z-50 bg-background transition-opacity duration-300 sm:right-20',
19
+ isAtBottom ? 'opacity-0' : 'opacity-100',
20
+ className
21
+ )}
22
+ onClick={() =>
23
+ window.scrollTo({
24
+ top: document.body.offsetHeight,
25
+ behavior: 'smooth'
26
+ })
27
+ }
28
+ {...props}
29
+ >
30
+ <IconArrowDown />
31
+ <span className="sr-only">Scroll to bottom</span>
32
+ </Button>
33
+ )
34
+ }
src/components/chat-attachments.tsx ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Image from 'next/image'
2
+ import ClearIcon from '@/assets/images/clear.svg'
3
+ import RefreshIcon from '@/assets/images/refresh.svg'
4
+ import { FileItem } from '@/lib/bots/bing/types'
5
+ import { cn } from '@/lib/utils'
6
+ import { useBing } from '@/lib/hooks/use-bing'
7
+
8
+ type ChatAttachmentsProps = Pick<ReturnType<typeof useBing>, 'attachmentList' | 'setAttachmentList' | 'uploadImage'>
9
+
10
+ export function ChatAttachments({ attachmentList = [], setAttachmentList, uploadImage }: ChatAttachmentsProps) {
11
+ return attachmentList.length ? (
12
+ <div className="attachment-list">
13
+ {attachmentList.map(file => (
14
+ <div className="file-item" key={file.url}>
15
+ {file.status === 'loading' && (
16
+ <div className="loading">
17
+ <div className="bar" />
18
+ </div>)
19
+ }
20
+ {file.status !== 'error' && (
21
+ <div className="thumbnail">
22
+ <img draggable="false" src={file.url} />
23
+ </div>)
24
+ }
25
+ {file.status === 'error' && (
26
+ <div className="error">
27
+ <Image alt="refresh" src={RefreshIcon} width={18} onClick={() => uploadImage(file.url)} />
28
+ </div>
29
+ )}
30
+ <button className={cn('dismiss', { 'no-file': file.status === 'error' })} type="button">
31
+ <Image alt="clear" src={ClearIcon} width={16} onClick={() => setAttachmentList([])} />
32
+ </button>
33
+ </div>
34
+ ))}
35
+ </div>
36
+ ) : null
37
+ }
src/components/chat-header.tsx ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import LogoIcon from '@/assets/images/logo.svg'
2
+ import Image from 'next/image'
3
+
4
+ export function ChatHeader() {
5
+ return (
6
+ <div className="flex flex-col items-center justify-center">
7
+ <Image alt="logo" src={LogoIcon} width={60}/>
8
+ <div className="mt-8 text-4xl font-bold">欢迎使用新必应</div>
9
+ <div className="mt-4 mb-8 text-lg">由 AI 支持的网页版 Copilot</div>
10
+ </div>
11
+ )
12
+ }
src/components/chat-history.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IconEdit, IconTrash, IconMore, IconDownload } from "./ui/icons"
2
+
3
+ export function ChatHistory() {
4
+ return (
5
+ <div className="chat-history fixed top-18 right-4">
6
+ <div className="chat-history-header text-sm font-semibold text-left w-[280px] px-4 py-6">
7
+ 历史记录
8
+ </div>
9
+ <div className="chat-history-main">
10
+ <div className="scroller">
11
+ <div className="surface">
12
+ <div className="threads">
13
+ <div className="thread">
14
+ <div className="primary-row">
15
+ <button type="button" aria-label="加载聊天">
16
+
17
+ </button>
18
+ <div className="description">
19
+ <h3 className="name">无标题的聊天</h3>
20
+ </div>
21
+ <h4 className="time">上午1:42</h4>
22
+ <div className="controls">
23
+
24
+ <button className="edit icon-button" type="button" aria-label="重命名">
25
+ <IconEdit />
26
+ </button>
27
+
28
+ <button className="delete icon-button" type="button" aria-label="删除">
29
+ <IconTrash />
30
+ </button>
31
+
32
+ <button className="more icon-button" type="button" aria-haspopup="true" aria-expanded="false" aria-label="更多">
33
+ <IconMore />
34
+ </button>
35
+
36
+ <button className="export icon-button" type="button" aria-label="导出">
37
+ <IconDownload />
38
+ </button>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ )
48
+ }
src/components/chat-image.tsx ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {
2
+ useEffect,
3
+ useState,
4
+ useCallback,
5
+ ChangeEvent,
6
+ ClipboardEvent,
7
+ MouseEventHandler,
8
+ FormEvent,
9
+ useRef
10
+ } from "react"
11
+ import Image from 'next/image'
12
+ import PasteIcon from '@/assets/images/paste.svg'
13
+ import UploadIcon from '@/assets/images/upload.svg'
14
+ import CameraIcon from '@/assets/images/camera.svg'
15
+ import { useBing } from '@/lib/hooks/use-bing'
16
+ import { cn } from '@/lib/utils'
17
+
18
+ interface ChatImageProps extends Pick<ReturnType<typeof useBing>, 'uploadImage'> {}
19
+
20
+ const preventDefault: MouseEventHandler<HTMLDivElement> = (event) => {
21
+ event.nativeEvent.stopImmediatePropagation()
22
+ }
23
+
24
+ const toBase64 = (file: File): Promise<string> => new Promise((resolve, reject) => {
25
+ const reader = new FileReader()
26
+ reader.readAsDataURL(file)
27
+ reader.onload = () => resolve(reader.result as string)
28
+ reader.onerror = reject
29
+ })
30
+
31
+ export function ChatImage({ children, uploadImage }: React.PropsWithChildren<ChatImageProps>) {
32
+ const videoRef = useRef<HTMLVideoElement>(null)
33
+ const canvasRef = useRef<HTMLCanvasElement>(null)
34
+ const mediaStream = useRef<MediaStream>()
35
+ const [panel, setPanel] = useState('none')
36
+
37
+ const upload = useCallback((url: string) => {
38
+ if (url) {
39
+ uploadImage(url)
40
+ }
41
+ setPanel('none')
42
+ }, [panel])
43
+
44
+ const onUpload = useCallback(async (event: ChangeEvent<HTMLInputElement>) => {
45
+ const file = event.target.files?.[0]
46
+ if (file) {
47
+ const fileDataUrl = await toBase64(file)
48
+ if (fileDataUrl) {
49
+ upload(fileDataUrl)
50
+ }
51
+ }
52
+ }, [])
53
+
54
+ const onPaste = useCallback((event: ClipboardEvent<HTMLInputElement>) => {
55
+ const pasteUrl = event.clipboardData.getData('text') ?? ''
56
+ upload(pasteUrl)
57
+ }, [])
58
+
59
+ const onEnter = useCallback((event: FormEvent<HTMLFormElement>) => {
60
+ event.preventDefault()
61
+ event.stopPropagation()
62
+ // @ts-ignore
63
+ const inputUrl = event.target.elements.image.value
64
+ if (inputUrl) {
65
+ upload(inputUrl)
66
+ }
67
+ }, [])
68
+
69
+ const openVideo: MouseEventHandler<HTMLButtonElement> = async (event) => {
70
+ event.stopPropagation()
71
+ setPanel('camera-mode')
72
+ }
73
+
74
+ const onCapture = () => {
75
+ if (canvasRef.current && videoRef.current) {
76
+ const canvas = canvasRef.current
77
+ canvas.width = videoRef.current!.videoWidth
78
+ canvas.height = videoRef.current!.videoHeight
79
+ canvas.getContext('2d')?.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height)
80
+ const cameraUrl = canvas.toDataURL('image/jpeg')
81
+ upload(cameraUrl)
82
+ }
83
+ }
84
+
85
+ useEffect(() => {
86
+ const handleBlur = () => {
87
+ if (panel !== 'none') {
88
+ setPanel('none')
89
+ }
90
+ }
91
+ document.addEventListener('click', handleBlur)
92
+ return () => {
93
+ document.removeEventListener('click', handleBlur)
94
+ }
95
+ }, [panel])
96
+
97
+ useEffect(() => {
98
+ if (panel === 'camera-mode') {
99
+ navigator.mediaDevices.getUserMedia({ video: true, audio: false })
100
+ .then(videoStream => {
101
+ mediaStream.current = videoStream
102
+ if (videoRef.current) {
103
+ videoRef.current.srcObject = videoStream
104
+ }
105
+ })
106
+ } else {
107
+ if (mediaStream.current) {
108
+ mediaStream.current.getTracks().forEach(function(track) {
109
+ track.stop()
110
+ })
111
+ mediaStream.current = undefined
112
+ }
113
+ }
114
+ }, [panel])
115
+
116
+ return (
117
+ <div className="visual-search-container">
118
+ <div onClick={() => panel === 'none' ? setPanel('normal') : setPanel('none')}>{children}</div>
119
+ <div className={cn('visual-search', panel)} onClick={preventDefault}>
120
+ <div className="normal-content">
121
+ <div className="header">
122
+ <h4>添加图像</h4>
123
+ </div>
124
+ <div className="paste">
125
+ <Image alt="paste" src={PasteIcon} width={24} />
126
+ <form onSubmitCapture={onEnter}>
127
+ <input
128
+ className="paste-input"
129
+ id="sb_imgpst"
130
+ type="text"
131
+ name="image"
132
+ placeholder="粘贴图像 URL"
133
+ aria-label="粘贴图像 URL"
134
+ onPaste={onPaste}
135
+ onClickCapture={(e) => e.stopPropagation()}
136
+ />
137
+ </form>
138
+ </div>
139
+ <div className="buttons">
140
+ <button type="button" aria-label="从此设备上传">
141
+ <input
142
+ id="vs_fileinput"
143
+ className="fileinput"
144
+ type="file"
145
+ accept="image/gif, image/jpeg, image/png, image/webp"
146
+ onChange={onUpload}
147
+ />
148
+ <Image alt="uplaod" src={UploadIcon} width={20} />
149
+ 从此设备上传
150
+ </button>
151
+ <button type="button" aria-label="拍照" onClick={openVideo}>
152
+ <Image alt="camera" src={CameraIcon} width={20} />
153
+ 拍照
154
+ </button>
155
+ </div>
156
+ </div>
157
+ {panel === 'camera-mode' && <div className="cam-content">
158
+ <div className="webvideo-container">
159
+ <video className="webvideo" autoPlay muted playsInline ref={videoRef} />
160
+ <canvas className="webcanvas" ref={canvasRef} />
161
+ </div>
162
+ <div className="cambtn" role="button" aria-label="拍照" onClick={onCapture}>
163
+ <div className="cam-btn-circle-large"></div>
164
+ <div className="cam-btn-circle-small"></div>
165
+ </div>
166
+ </div>}
167
+ </div>
168
+ </div>
169
+ )
170
+ }
src/components/chat-list.tsx ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+
3
+ import { Separator } from '@/components/ui/separator'
4
+ import { ChatMessage } from '@/components/chat-message'
5
+ import { ChatMessageModel } from '@/lib/bots/bing/types'
6
+
7
+ export interface ChatList {
8
+ messages: ChatMessageModel[]
9
+ }
10
+
11
+ export function ChatList({ messages }: ChatList) {
12
+ if (!messages.length) {
13
+ return null
14
+ }
15
+
16
+ return (
17
+ <div className="chat-container relative flex flex-col">
18
+ {messages.map((message, index) => (
19
+ <React.Fragment key={index}>
20
+ <ChatMessage message={message} />
21
+ {index < messages.length - 1 && (
22
+ <Separator className="my-2" />
23
+ )}
24
+ </React.Fragment>
25
+ ))}
26
+ </div>
27
+ )
28
+ }