derina hf4all commited on
Commit
7e0c4ad
·
0 Parent(s):

Duplicate from hf4all/bingo

Browse files

Co-authored-by: hf4all <hf4all@users.noreply.huggingface.co>

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 +192 -0
  12. cloudflare/worker.js +9 -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 +1470 -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-image.tsx +170 -0
  49. src/components/chat-list.tsx +28 -0
  50. src/components/chat-message.tsx +93 -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:$HF_TOKEN@huggingface.co/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,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ 你也可以一键部署自己的 New Bing AI 到 🤗 HuggingFace 。
60
+
61
+ ### 部署到 Huggingface
62
+ 1. 点击此图标
63
+ [![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),配置可以不改。
64
+
65
+ 2. 部署署完成后,点击“设置” 》“站点域名”,点一下,复制一下 HF 域名信息,然后分享给别人即可。
66
+
67
+ > Huggingface 不支持绑定自己的域名,不过我们可以使用曲线救国的方式来达到这个目的
68
+ > 1. 方式二,借助 Cloudflare Workers [部署Cloudflare Workers](#使用Cloudflare-Workers自定义域名)
69
+ > 2. 方式一,借助 Github Pages 及 iframe [如何绑定域名](https://github.com/weaigc/bingo/issues/4)
70
+
71
+ ### 使用Cloudflare Workers自定义域名
72
+
73
+ > 核心代码 [worker.js](./cloudflare/worker.js)
74
+
75
+ - [注册 Cloudflare 账号](https://dash.cloudflare.com/sign-up)
76
+
77
+ - 添加一个新的网站,需要你有自己的域名并且将域名`Name Server`托管给 Cloudflare 才行(更多信息可自行 Google)
78
+
79
+ - 通过左侧菜单进入「Workers」,并点击「Create a Worker」。
80
+
81
+ - 创建 Worker 服务,复制 [worker.js](./cloudflare/worker.js) 全部代码,粘贴至创建的服务中,根据注释进行改动,保存并部署。
82
+
83
+ - 触发器 中自定义访问域名。
84
+
85
+ ### 部署其它平台
86
+ <details>
87
+ <summary>
88
+ 由于其他平台目前遭到 New Bing 封杀,会遇到很多问题,不再做推荐,有需要的可以自行查看
89
+ </summary>
90
+
91
+ #### 部署到 Netlify
92
+ [![Deploy to Netlify Button](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/weaigc/bingo)
93
+
94
+ #### 部署到 Vercel
95
+ 如果你是 Vercel 付费用户,可以点以下链接一键部署到 Vercel。免费版本有[接口超时限制](https://vercel.com/docs/concepts/limits/overview),不推荐使用
96
+
97
+ [![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)
98
+
99
+ #### 部署到 Render
100
+
101
+ [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/weaigc/bingo)
102
+ </details>
103
+
104
+ ## 环境和依赖
105
+
106
+ - Node.js >= 18
107
+ - Bing AI 的[身份信息](#如何获取-BING_HEADER))
108
+
109
+ ## 安装和使用
110
+
111
+ * 使用 Node 启动
112
+
113
+ ```bash
114
+ git clone https://github.com/weaigc/bingo.git
115
+ npm i # 推荐使用 pnpm i
116
+ npm run build
117
+ npm run start
118
+ ```
119
+
120
+ * 使用 Docker 启动
121
+ ```bash
122
+ git clone https://github.com/weaigc/bingo.git
123
+ docker build . -t bingo
124
+ docker run --rm -it -e BING_HEADER=xxxx -p 7860:7860 bingo
125
+ ```
126
+
127
+ ## 如何获取 BING_HEADER
128
+ 打开 https://www.bing.com 并登录,然后访问 https://www.bing.com/turing/captcha/challenge,通过人机��验,然后
129
+
130
+ ![BING HEADER](./docs/images/curl.png)
131
+
132
+
133
+ > 复制出来的内容应该如下所示。确认格式无误后,打开 https://effulgent-bubblegum-e2f5df.netlify.app/#dialog=%22settings%22 ,粘贴进去,点击“转成 BING_HEADER 并复制”,然后从剪切板粘贴即可得到。(你也可以先在网页上进行验证)
134
+
135
+ 以下是格式参考,需要注意的是,网页端保存的格式是以`curl`开头, 而服务端配置的 `BING_HEADER` 是 `base64` 格式,两者不能互通。
136
+ <details>
137
+ <summary>正常格式/网页端保存的格式(格式仅供参考)</summary>
138
+
139
+ ```
140
+ curl 'https://www.bing.com/turing/captcha/challenge' \
141
+ -H 'authority: www.bing.com' \
142
+ -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' \
143
+ -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
144
+ -H 'cache-control: max-age=0' \
145
+ -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' \
146
+ -H 'dnt: 1' \
147
+ -H 'sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"' \
148
+ -H 'sec-ch-ua-arch: "x86"' \
149
+ -H 'sec-ch-ua-bitness: "64"' \
150
+ -H 'sec-ch-ua-full-version: "116.0.1938.29"' \
151
+ -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"' \
152
+ -H 'sec-ch-ua-mobile: ?0' \
153
+ -H 'sec-ch-ua-model: ""' \
154
+ -H 'sec-ch-ua-platform: "Windows"' \
155
+ -H 'sec-ch-ua-platform-version: "15.0.0"' \
156
+ -H 'sec-fetch-dest: document' \
157
+ -H 'sec-fetch-mode: navigate' \
158
+ -H 'sec-fetch-site: none' \
159
+ -H 'sec-fetch-user: ?1' \
160
+ -H 'sec-ms-gec: B3F47AD4A283CAB374C0451C46AAFD147C6A4DACAFF6A1C13F34B2C72B024494' \
161
+ -H 'sec-ms-gec-version: 1-116.0.1938.29' \
162
+ -H 'upgrade-insecure-requests: 1' \
163
+ -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' \
164
+ -H 'x-client-data: eyIxIjoiMiIsIjEwIjoiXCJTMGg3R05HOTF2aDQ1TUZSUnZ5NHN2akRmMWdlaVJKenNxNlA3aU1WbnF3PVwiIiwiMiI6IjEiLCIzIjoiMSIsIjQiOiIyMTU4ODQ5NTM4MjY4OTM5NTA3IiwiNSI6IlwiSm9GUWpPTDk3OS9MbkRRZnlCd2N1M2FsOUN3eTZTQmdaMGNYMXBtOWVMZz1cIiIsIjYiOiJiZXRhIiwiNyI6IjE4MDM4ODYyNjQzNSIsIjkiOiJkZXNrdG9wIn0=' \
165
+ -H 'x-edge-shopping-flag: 1' \
166
+ --compressed
167
+ ```
168
+ </details>
169
+
170
+ <details>
171
+ <summary>转成base64之后的格式(BING_HEADER只能使用 base64 之后的格式)</summary>
172
+
173
+ ```
174
+ Y3VybCAnaHR0cHM6Ly93d3cuYmluZy5jb20vdHVyaW5nL2NvbnZlcnNhdGlvbi9jcmVhdGUnIFwgICAtSCAnYXV0aG9yaXR5OiB3d3cuYmluZy5jb20nIFwgICAtSCAnYWNjZXB0OiB0ZXh0L2h0bWwsYXBwbGljYXRpb24veGh0bWwreG1sLGFwcGxpY2F0aW9uL3htbDtxPTAuOSxpbWFnZS93ZWJwLGltYWdlL2FwbmcsKi8qO3E9MC44LGFwcGxpY2F0aW9uL3NpZ25lZC1leGNoYW5nZTt2PWIzO3E9MC43JyBcICAgLUggJ2FjY2VwdC1sYW5ndWFnZTogemgtQ04semg7cT0wLjksZW47cT0wLjgsZW4tR0I7cT0wLjcsZW4tVVM7cT0wLjYnIFwgICAtSCAnY2FjaGUtY29udHJvbDogbWF4LWFnZT0wJyBcICAgLUggJ2Nvb2tpZTogTWljcm9zb2Z0QXBwbGljYXRpb25zVGVsZW1ldHJ5RGV2aWNlSWQ9MzM5OWMwMDQtZmQwZS00OGVjLWJiOTItZDgyYTI3YjJiYmQ0OyBfRURHRV9WPTE7IFNSQ0hEPUFGPU5PRk9STTsgU1JDSFVJRD1WPTImR1VJRD0yOUVCRERBNEU2Njc0MzI5QUNDRjFBMEE0MjNDM0U5OCZkbW5jaGc9MTsgX1VSPVFTPTAmVFFTPTA7IF9IUFZOPUNTPWV5SlFiaUk2ZXlKRGJpSTZNU3dpVTNRaU9qQXNJbEZ6SWpvd0xDSlFjbTlrSWpvaVVDSjlMQ0pUWXlJNmV5SkRiaUk2TVN3aVUzUWlPakFzSWxGeklqb3dMQ0pRY205a0lqb2lTQ0o5TENKUmVpSTZleUpEYmlJNk1Td2lVM1FpT2pBc0lsRnpJam93TENKUWNtOWtJam9pVkNKOUxDSkJjQ0k2ZEhKMVpTd2lUWFYwWlNJNmRISjFaU3dpVEdGa0lqb2lNakF5TXkwd055MHlOVlF3TURvd01Eb3dNRm9pTENKSmIzUmtJam93TENKSGQySWlPakFzSWtSbWRDSTZiblZzYkN3aVRYWnpJam93TENKR2JIUWlPakFzSWtsdGNDSTZNbjA9OyBfUndCZj1pbHQ9MSZpaHBkPTEmaXNwZD0wJnJjPTAmcmI9MCZnYj0wJnJnPTIwMCZwYz0wJm10dT0wJnJiYj0wJmc9MCZjaWQ9JmNsbz0wJnY9MSZsPTIwMjMtMDctMjVUMDc6MDA6MDAuMDAwMDAwMFombGZ0PTAwMDEtMDEtMDFUMDA6MDA6MDAuMDAwMDAwMCZhb2Y9MCZvPTImcD0mYz0mdD0wJnM9MDAwMS0wMS0wMVQwMDowMDowMC4wMDAwMDAwKzAwOjAwJnRzPTIwMjMtMDctMjVUMTE6MDA6MzEuNzExMTU0OCswMDowMCZyd3JlZD0wJndscz0mbGthPTAmbGt0PTAmVEg9JmRjaT0wOyBBTk9OPUE9MDA0M0M2NTkwRUE4MDhFRDZFMzk1MDU5RkZGRkZGRkYmRT0xYzhiJlc9MTsgTkFQPVY9MS45JkU9MWMzMSZDPURuYU1TYkROXzRlZlpfeFhxQkYzRGFvcmpyNTNrWXFZb2FQOFlIc3Vwam1pWG55c1g3YTM3QSZXPTE7IFBQTFN0YXRlPTE7IEtpZXZSUFNTZWNBdXRoPUZBQlNCQlJhVE9KSUx0RnNNa3BMVldTRzZBTjZDL3N2UndObUFBQUVnQUFBQ01HVUE3RUdWU2pHRUFRQkdIdE5zYzVzTkw3dW5tSnNmUEoydDZpbWZvNEJlVUpsQWlhM0lwTVR0TVV5NFBVL0M1UUF6Ukk1cE9EdHNJZWUwK2JsZ2xsWHQvNUlpV3dHandtZGhpdnNGTTU5N3BSUGtqQVJQZndzUGhOTFBOYkpyQ1BOUEhkamU0SXM3OE1uQ0FEWHc2L05CcTJGTDhWMi9ieXcyZkg2SXVBTUQyTXZOL1Z2cXBFYTlaeGlEalp0RU5qNEhFajBtTzJTZ3pqZnlFaFZBa2p2em5KcVUycncvUTJ0SG1YOTROQU0ya3psektGL2hXUGhDQ1VtdThJSEx2Q25IRFM2bVNwdHZKRERQL3NwM292dHpPWGtQMW1sTS9YanU1ZnRlc1V2Y2NWRVFHZmZYT1JhMWRFNWhFTWJLSWlLWHoxdERkZHVTWEUxOWc5LyttUk1BamFRaHB3aEk4WG1pbENUeDFhZGIxTGw1cUsrVmpDOUdOZkVaemNic0dCUFZhT2wrYW5HOHJFTXErWG5oam83SitOcVROb2xhdkhnY3VWOGtKc0NlSlpJZ2VkMzNVQThlT1plRm8rd0FFQ01ndXhNb1NxZ3BHSCtzdGhxeW52RC9GSkQ2ci90aVUyTjN1cVZxOE5FOFYzN2Fzck42VDE0WjBGR0JKT2U2RVQxK1BHQXBtM3MxMU9ZOS94aEZFQjlUNUJFUFVHRWJ2UmNMY1cybmNGUVgwRVUreHdlaVBxbzFRMWhOVWcvZEN0U0krbFo3YzJIOFhoZWVQWmF2WjBUSlE4b05DU0F1S2lUcUptSTBmVkdwd2JYd2ZhQURrRWlwdWF3ejNmSXVNSkJOZ01VME90QTdIbTU5djJmR0xJQnV2aTZZZUtTNkdnVmszQklQZitQL2VLYWh3b3pyeFFaYUZub0hUU3FNa3ZjdDd4Q1A0YXRCUk9mWEtmNVd3MENjRktwKzJXWDlCSXNrVE9vMmpqazZiQXl5WUorRWxVQjFmZ0xLTms1bS9ZU01jOWlZQ0xJQk1JR044RjBZdnkzdFo3Y3ZoN1VlNUtsbzk4VVMvSStuVzFHN1pKTUhSZ1VPOGg4bHBuZUhxRU1lZ0tkOGd5bk80VkY3UnBDakprdW5EbVcwVGErUmtYQVA2MTlwZzBkcUhNRmtvT2drbk43OG9CYkdUVjZmSlVLb3R2K3ZpNjFrTGhBZVhaR1dvSEdDUlhoMndVQzZZZ2ZQZ0tBNkVTUk5IdEZuN0U1QjNISHBMYzVyVk1EU05oS1pZZmRodXBWNEV6ZjYrNURoTWNaTFpoaTBraytpdkRpTjFnZEhsVnRTTjU1eHB2ZitjK1haRHpSMHVoZ2N2Z3kwTEFibXpnazZ5NFdiWUgrTFFzTXB6Tk5qK2FDNzJ2TWlXb3ZXcktoOWpZNE1ZQ21kZ3hzUy9za1B0TGRwMThtdWlFSVJYVGJaUUdVbWh4RnBKQUliQklzQ3NjTXB6TDBCZ2V1anhVd001d3I3OVNkOXI0eHdiZ1NNd21CbEJmVUhSVkJkTnlnOGZlZXBlSmJDUzYzbkQ2ZUhPdUxxTVJzUElpbzN3L2tpL0VBYTkyVVVFaVplYXZMc01VRC95L3FBdldVZHpkUDVZK0MvVE0rQ01HUy9rR0w0TEVkWS8yOE1RZVR2VTFxdjFYMjFrUXQyYWlhajNwUFZMMzZoQXp4YmNMZ3FjTW85b3ltRFJ5ODdrZENYVy8rZzRvS0x0TWg2Zm0vRzZXNlkvQjAxSmx4b2h5eXZ1ZUhRSUc1NTd1emtFa1RKM0ZuT1ZPRFNLQktwYjNXWjY1ckV4ZlY3MXpTWmEyNUYzR21wYUlHNkhpWXJYMllZaFFBa0lFOXBLRVFCSGJud0h1d05ER290dFpUWFp3PTsgV0xTPUM9OWRmM2Y5ZDg1MThmYWUxOSZOPXdlbjsgV0xJRD1wR1k4SGdXQ3U0cDVYWUNPazJvYTArREJkZnRrTVVmbU5JbjhYdFNqU1RLc2d2L0lsN0dVbFlzMEpwamYvRTEyalpNZ1Y3eDQ0RHkzZlhPZ2pqVW9KeDdZL0NsTHJMaHNrMjBUSGtzSkpvST07IF9FREdFX1M9Rj0xJlNJRD0xN0NGNkVFMDA2NDI2NDQ4MjEzQzdEQjkwNzQzNjU4OCZta3Q9emgtQ047IE1VSUQ9MjI1NjIxMDkzRDhBNkMyNzMwMTYzMjQxM0MwRTZEMDg7IE1VSURCPTIyNTYyMTA5M0Q4QTZDMjczMDE2MzI0MTNDMEU2RDA4OyBTVUlEPUE7IFNOUkhPUD1JPSZUUz07IF9VPW5HeXpLUXJ1RXNEd0xpdTY1ZlpGSUc2ZTEyaGYybHdUSm1yb1dfX2s4am9VSklLbUczT0lqYXlYS0dXOWRDVlIzc05oRjc2bUVWeHlXNnlqVUdQb2RPZmp0U2EzczNKX0R4TU9yRUsxQnFYQ09CSTliQzY2c3BBSUFTVjdwcnNZRmxWQUp6NzNqVk5FTnBfdEJ1YkxISnk2RWJUMEJLUmU0QWpyWWtILTl1TW5tQ0tCOFpteWc7IF9TUz1TSUQ9MTdDRjZFRTAwNjQyNjQ0ODIxM0M3REI5MDc0MzY1ODgmUj0wJlJCPTAmR0I9MCZSRz0yMDAmUlA9MCZQQz1VNTMxOyBTUkNIUz1QQz1VNTMxOyBVU1JMT0M9SFM9MSZFTE9DPUxBVD0yMi41MDE1Mjk2OTM2MDM1MTZ8TE9OPTExMy45MjYzNjg3MTMzNzg5fE49JUU1JThEJTk3JUU1JUIxJUIxJUU1JThDJUJBJUVGJUJDJThDJUU1JUI5JUJGJUU0JUI4JTlDJUU3JTlDJTgxfEVMVD0yfCZDTE9DPUxBVD0yMi41MDE1MzAyOTA0NjQ2MXxMT049MTEzLjkyNjM3MDcwNjMyOTI4fEE9NzMzLjQ0NjQ1ODYxMjA4MzJ8VFM9MjMwNzI2MTUxMDM0fFNSQz1XOyBTUkNIVVNSPURPQj0yMDIzMDcyNSZUPTE2OTAzODQ5MDgwMDAmUE9FWD1XOyBpcHY2PWhpdD0xNjkwMzg4NTA5OTc0JnQ9NjsgU1JDSEhQR1VTUj1IVj0xNjkwMzg0OTQ1JlNSQ0hMQU5HPXpoLUhhbnMmUFY9MTUuMC4wJkJSVz1NVyZCUkg9TVQmQ1c9NDEwJkNIPTc5NCZTQ1c9NDEwJlNDSD03OTQmRFBSPTEuNSZVVEM9NDgwJkRNPTAmV1RTPTYzODI1ODc5NjI3JlBSVkNXPTQxMCZQUlZDSD03OTQmUFI9MS41OyBjY3Q9QWpXSUJZT29WUC1BZnE2Z1d3dHg4MElmNnlIbjZpQnVFVkhBMVhIZEFLcG55NllfQ1Z5aV9NU3lNOTRWeU1XbmpkWWtrY2NWdG0zY3pvSUF0WFVHUUE7IEdDPUFqV0lCWU9vVlAtQWZxNmdXd3R4ODBJZjZ5SG42aUJ1RVZIQTFYSGRBS3BSM1lfRDlZdGNrczRIdDZYaGFkWGs3NWR2aHpQNFlPVVMwVW1vRXlxeXh3JyBcICAgLUggJ2RudDogMScgXCAgIC1IICdzZWMtY2gtdWE6ICJDaHJvbWl1bSI7dj0iMTE2IiwgIk5vdClBO0JyYW5kIjt2PSIyNCIsICJNaWNyb3NvZnQgRWRnZSI7dj0iMTE2IicgXCAgIC1IICdzZWMtY2gtdWEtYXJjaDogIng4NiInIFwgICAtSCAnc2VjLWNoLXVhLWJpdG5lc3M6ICI2NCInIFwgICAtSCAnc2VjLWNoLXVhLWZ1bGwtdmVyc2lvbjogIjExNi4wLjE5MzguMjkiJyBcICAgLUggJ3NlYy1jaC11YS1mdWxsLXZlcnNpb24tbGlzdDogIkNocm9taXVtIjt2PSIxMTYuMC41ODQ1LjQyIiwgIk5vdClBO0JyYW5kIjt2PSIyNC4wLjAuMCIsICJNaWNyb3NvZnQgRWRnZSI7dj0iMTE2LjAuMTkzOC4yOSInIFwgICAtSCAnc2VjLWNoLXVhLW1vYmlsZTogPzAnIFwgICAtSCAnc2VjLWNoLXVhLW1vZGVsOiAiIicgXCAgIC1IICdzZWMtY2gtdWEtcGxhdGZvcm06ICJXaW5kb3dzIicgXCAgIC1IICdzZWMtY2gtdWEtcGxhdGZvcm0tdmVyc2lvbjogIjE1LjAuMCInIFwgICAtSCAnc2VjLWZldGNoLWRlc3Q6IGRvY3VtZW50JyBcICAgLUggJ3NlYy1mZXRjaC1tb2RlOiBuYXZpZ2F0ZScgXCAgIC1IICdzZWMtZmV0Y2gtc2l0ZTogbm9uZScgXCAgIC1IICdzZWMtZmV0Y2gtdXNlcjogPzEnIFwgICAtSCAnc2VjLW1zLWdlYzogQjNGNDdBRDRBMjgzQ0FCMzc0QzA0NTFDNDZBQUZEMTQ3QzZBNERBQ0FGRjZBMUMxM0YzNEIyQzcyQjAyNDQ5NCcgXCAgIC1IICdzZWMtbXMtZ2VjLXZlcnNpb246IDEtMTE2LjAuMTkzOC4yOScgXCAgIC1IICd1cGdyYWRlLWluc2VjdXJlLXJlcXVlc3RzOiAxJyBcICAgLUggJ3VzZXItYWdlbnQ6IE1vemlsbGEvNS4wIChXaW5kb3dzIE5UIDEwLjA7IFdpbjY0OyB4NjQpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMTYuMC4wLjAgU2FmYXJpLzUzNy4zNiBFZGcvMTE2LjAuMC4wJyBcICAgLUggJ3gtY2xpZW50LWRhdGE6IGV5SXhJam9pTWlJc0lqRXdJam9pWENKVE1HZzNSMDVIT1RGMmFEUTFUVVpTVW5aNU5ITjJha1JtTVdkbGFWSktlbk54TmxBM2FVMVdibkYzUFZ3aUlpd2lNaUk2SWpFaUxDSXpJam9pTVNJc0lqUWlPaUl5TVRVNE9EUTVOVE00TWpZNE9UTTVOVEEzSWl3aU5TSTZJbHdpU205R1VXcFBURGszT1M5TWJrUlJabmxDZDJOMU0yRnNPVU4zZVRaVFFtZGFNR05ZTVhCdE9XVk1aejFjSWlJc0lqWWlPaUppWlhSaElpd2lOeUk2SWpFNE1ETTRPRFl5TmpRek5TSXNJamtpT2lKa1pYTnJkRzl3SW4wPScgXCAgIC1IICd4LWVkZ2Utc2hvcHBpbmctZmxhZzogMScgXCAgIC0tY29tcHJlc3NlZA==
175
+ ```
176
+ </details>
177
+
178
+
179
+ ## 鸣谢
180
+ - 感谢 [EdgeGPT](https://github.com/acheong08/EdgeGPT) 提供的代理 API 的方法。
181
+ - 感谢 [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) 提供的部分代码。
182
+
183
+
184
+ ## 答疑及交流
185
+
186
+ <image src="./docs/images/wechat.png" width=240 />
187
+
188
+ ## License
189
+
190
+ MIT © [LICENSE](https://github.com/weaigc/bingo/blob/main/LICENSE).
191
+
192
+
cloudflare/worker.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ const TRAGET_HOST='hf4all-bingo.hf.space' // 请将此域名改成你自己的,域名信息在设置》站点域名查看。
2
+
3
+ export default {
4
+ async fetch(request) {
5
+ const uri = new URL(request.url);
6
+ uri.host = TRAGET_HOST
7
+ return fetch(new Request(uri.toString(), request));
8
+ },
9
+ };
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.0",
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,1470 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ &.user {
533
+ img {
534
+ max-width: 300px;
535
+ max-height: 300px;
536
+ object-fit: contain;
537
+ }
538
+ }
539
+
540
+ a {
541
+ position: relative;
542
+ text-decoration: none;
543
+ }
544
+ }
545
+
546
+
547
+ .text-message-content {
548
+ display: flex;
549
+ flex-direction: column;
550
+ padding: 10px 16px 4px 16px;
551
+ user-select: text;
552
+ word-break: break-word;
553
+ min-height: var(--cib-type-body2-line-height);
554
+ font-size: var(--cib-type-body2-font-size);
555
+ line-height: var(--cib-type-body2-line-height);
556
+ font-weight: var(--cib-type-body2-font-weight);
557
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
558
+ overflow: hidden;
559
+
560
+ h1 {
561
+ font-size: var(--cib-type-title2-font-size);
562
+ line-height: var(--cib-type-title2-line-height);
563
+ font-weight: var(--cib-type-title2-font-weight);
564
+ font-variation-settings: var(--cib-type-title2-font-variation-settings);
565
+ }
566
+
567
+ p,
568
+ h1,
569
+ h2,
570
+ h3,
571
+ h4 {
572
+ padding: 0px;
573
+ user-select: text;
574
+ word-break: break-word;
575
+ display: block;
576
+ }
577
+
578
+ pre {
579
+ display: block;
580
+ }
581
+
582
+ ol,
583
+ menu {
584
+ list-style: decimal;
585
+ margin: 0;
586
+ padding: 0;
587
+ padding-inline-start: 24px;
588
+ }
589
+
590
+ ul,
591
+ ol {
592
+ display: flex;
593
+ flex-direction: column;
594
+ gap: 10px;
595
+ padding-inline-start: 24px;
596
+ }
597
+
598
+ ul {
599
+ list-style: disc;
600
+ }
601
+
602
+ >*:nth-child(n+2) {
603
+ margin-top: 12px;
604
+ }
605
+
606
+ .codeblock {
607
+ border-radius: var(--cib-border-radius-large);
608
+ overflow: hidden;
609
+ }
610
+
611
+ blockquote>p>img {
612
+ max-width: 50%;
613
+ float: left;
614
+ }
615
+ }
616
+
617
+ table,
618
+ ul,
619
+ ol,
620
+ p {
621
+ padding-bottom: 12px;
622
+ }
623
+
624
+ .text-message-footer {
625
+ display: grid;
626
+ grid-template-columns: 1fr auto;
627
+ justify-content: space-between;
628
+
629
+ border-top: 1px solid var(--cib-color-stroke-neutral-primary);
630
+ padding: 0px;
631
+ align-items: self-start;
632
+ }
633
+
634
+ .learn-more-root {
635
+ display: flex;
636
+ flex-direction: row;
637
+ row-gap: 8px;
638
+ padding: 0px 16px;
639
+ margin: 9px 0px;
640
+ overflow: hidden;
641
+ }
642
+
643
+ @media (max-width: 600px) {
644
+ .learn-more-root {
645
+ flex-wrap: wrap;
646
+ }
647
+ }
648
+
649
+ .learn-more {
650
+ position: relative;
651
+ align-self: flex-start;
652
+ min-width: fit-content;
653
+ top: 2px;
654
+ inset-inline-start: 1px;
655
+ margin-inline-end: 8px;
656
+ font-size: var(--cib-type-body1-stronger-font-size);
657
+ line-height: var(--cib-type-body1-stronger-line-height);
658
+ font-weight: var(--cib-type-body1-stronger-font-weight);
659
+ font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings);
660
+ }
661
+
662
+ .attribution-container {
663
+ display: flex;
664
+ flex-direction: row;
665
+ row-gap: 6px;
666
+ }
667
+
668
+ .attribution-items {
669
+ display: flex;
670
+ flex-flow: wrap;
671
+ row-gap: 6px;
672
+ }
673
+
674
+ .attribution-item {
675
+ cursor: pointer;
676
+ text-decoration: none;
677
+ display: flex;
678
+ align-items: center;
679
+ justify-content: center;
680
+ min-width: max-content;
681
+ height: 24px;
682
+ border-radius: var(--cib-border-radius-medium);
683
+ box-sizing: border-box;
684
+ padding: 0px 8px;
685
+ margin-inline-end: 6px;
686
+ color: var(--cib-color-foreground-accent-primary);
687
+ background: var(--cib-color-fill-accent-alt-primary);
688
+ font-family: var(--cib-font-text);
689
+ font-size: var(--cib-type-body1-strong-font-size);
690
+ line-height: var(--cib-type-body1-strong-line-height);
691
+ font-weight: var(--cib-type-body1-strong-font-weight);
692
+ font-variation-settings: var(--cib-type-body1-strong-font-variation-settings);
693
+ }
694
+
695
+ .turn-counter {
696
+ display: flex;
697
+ flex-shrink: 0;
698
+ flex-direction: row;
699
+ align-items: center;
700
+ gap: 6px;
701
+ margin-inline-start: 12px;
702
+ grid-area: 1 / 2 / 2 / 3;
703
+ margin: 9px 14px;
704
+
705
+ .text {
706
+ display: flex;
707
+ gap: 3px;
708
+ font-size: var(--cib-type-body1-stronger-font-size);
709
+ line-height: var(--cib-type-body1-stronger-line-height);
710
+ font-weight: var(--cib-type-body1-stronger-font-weight);
711
+ font-variation-settings: var(--cib-type-body1-stronger-font-variation-settings);
712
+ }
713
+
714
+ .indicator {
715
+ width: 12px;
716
+ height: 12px;
717
+ border-radius: var(--cib-border-radius-circular);
718
+ background: rgb(44, 130, 71);
719
+ }
720
+ }
721
+
722
+ button {
723
+ &:focus {
724
+ outline: none !important;
725
+ }
726
+ }
727
+
728
+ @media (max-width: 600px) {
729
+ .turn-counter {
730
+ inset-inline-end: 0px;
731
+ }
732
+ }
733
+
734
+ @media (max-width: 767px) {
735
+ .suggestion-items {
736
+ display: contents;
737
+ }
738
+ }
739
+
740
+ .suggestion-items {
741
+ display: flex;
742
+ align-items: center;
743
+ justify-content: flex-end;
744
+ flex-flow: wrap;
745
+ gap: 8px 8px;
746
+ order: 1;
747
+ padding-inline-end: 2px;
748
+ overflow: hidden;
749
+ }
750
+
751
+ .suggestion-container {
752
+ height: 30px;
753
+ min-width: max-content;
754
+ overflow: hidden;
755
+ box-sizing: border-box;
756
+ padding: 0px 12px;
757
+ margin: 1px;
758
+ cursor: pointer;
759
+ border: 1px solid var(--cib-color-stroke-accent-primary);
760
+ color: var(--cib-color-foreground-accent-primary);
761
+ background: var(--cib-color-fill-accent-primary);
762
+ border-radius: var(--cib-border-radius-large);
763
+ font-family: var(--cib-font-text);
764
+ font-size: var(--cib-type-body1-strong-font-size);
765
+ line-height: var(--cib-type-body1-strong-line-height);
766
+ font-weight: var(--cib-type-body1-strong-font-weight);
767
+ font-variation-settings: var(--cib-type-body1-strong-font-variation-settings);
768
+
769
+ &:hover,
770
+ &:focus {
771
+ background: var(--cib-color-fill-accent-secondary);
772
+ border-color: var(--cib-color-stroke-accent-secondary);
773
+ color: var(--cib-color-foreground-accent-secondary);
774
+ }
775
+ }
776
+
777
+ .typing-control-item {
778
+ position: relative;
779
+ display: flex;
780
+ flex-direction: row;
781
+ align-items: center;
782
+ cursor: pointer;
783
+ justify-content: center;
784
+ background: var(--cib-color-fill-accent-secondary);
785
+ border-radius: var(--cib-border-radius-large);
786
+ height: 40px;
787
+ box-sizing: border-box;
788
+ padding: 0px 8px;
789
+ color: var(--cib-color-foreground-accent-primary);
790
+ fill: var(--cib-color-foreground-accent-primary);
791
+ border: 1px solid var(--cib-color-stroke-accent-primary);
792
+ font-family: var(--cib-font-text);
793
+ font-size: var(--cib-type-subtitle2-font-size);
794
+ line-height: var(--cib-type-subtitle2-line-height);
795
+ font-weight: var(--cib-type-subtitle2-font-weight);
796
+ font-variation-settings: var(--cib-type-subtitle2-font-variation-settings);
797
+
798
+ &>.stop {
799
+ gap: 2px;
800
+ padding: 0px 12px;
801
+ }
802
+ }
803
+
804
+ .notification-container {
805
+ align-items: flex-end;
806
+ justify-content: center;
807
+ width: 100%;
808
+ transition-property: transform, max-width, min-width;
809
+ transition-duration: var(--cib-motion-duration-slowest);
810
+ transition-timing-function: var(--cib-motion-easing-transition);
811
+
812
+ .bottom-notifications {
813
+ display: flex;
814
+ align-items: center;
815
+ justify-content: center;
816
+ width: 100%;
817
+ margin: 60px 0px 0px;
818
+ }
819
+
820
+ .inline-type {
821
+ display: flex;
822
+ justify-content: center;
823
+ align-items: center;
824
+ text-align: center;
825
+ width: 100%;
826
+ max-width: 1120px;
827
+ color: var(--cib-color-foreground-neutral-primary);
828
+ font-size: var(--cib-type-body2-font-size);
829
+ line-height: var(--cib-type-body2-line-height);
830
+ font-weight: var(--cib-type-body2-font-weight);
831
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
832
+
833
+ &.with-decorative-line {
834
+ &::before {
835
+ margin-inline-end: 1vw;
836
+ }
837
+
838
+ &::before,
839
+ &::after {
840
+ content: "";
841
+ flex: 1 1 0%;
842
+ border-bottom: 1px solid var(--cib-color-stroke-neutral-primary);
843
+ }
844
+ }
845
+
846
+ .text-container {
847
+ max-width: 80%;
848
+ padding: 0px 10px;
849
+ align-items: center;
850
+ }
851
+
852
+ .title {
853
+ position: relative;
854
+ color: var(--cib-color-foreground-neutral-primary);
855
+
856
+ a {
857
+ color: var(--cib-color-foreground-system-link-primary);
858
+ }
859
+ }
860
+ }
861
+ }
862
+
863
+ @media (max-width: 767px) {
864
+ .inline-type {
865
+ margin-bottom: unset;
866
+ }
867
+ }
868
+
869
+ .action-bar {
870
+ position: fixed;
871
+ display: flex;
872
+ align-items: flex-end;
873
+ justify-content: center;
874
+ min-height: 90px;
875
+ bottom: 0;
876
+ box-sizing: border-box;
877
+ z-index: 100;
878
+ width: 100%;
879
+ left: 0;
880
+ transition-property: transform, max-width, min-width;
881
+ transition-duration: var(--cib-motion-duration-slowest);
882
+ transition-timing-function: var(--cib-motion-easing-transition);
883
+ }
884
+
885
+ .action-root {
886
+ position: relative;
887
+ display: flex;
888
+ align-items: flex-start;
889
+ gap: 12px;
890
+ width: calc(100% - var(--side-panel-width));
891
+ height: auto;
892
+ max-width: 1120px;
893
+ min-height: 90px;
894
+ transition-property: width, max-width;
895
+ transition-duration: var(--cib-motion-duration-slowest);
896
+ transition-timing-function: var(--cib-motion-easing-transition);
897
+ }
898
+
899
+ .root[visual-search] .main-container {
900
+ padding-inline-end: 84px;
901
+ }
902
+
903
+ .main-container {
904
+ display: flex;
905
+ flex-direction: column;
906
+ gap: 4px;
907
+ justify-content: space-between;
908
+ align-items: flex-start;
909
+ position: relative;
910
+ width: 100%;
911
+ height: 100%;
912
+ min-height: 48px;
913
+ box-sizing: border-box;
914
+ padding-block: 13px 11px;
915
+ padding-inline: 16px;
916
+ z-index: 1;
917
+ background: var(--cib-color-background-surface-solid-quaternary);
918
+ border-radius: var(--cib-action-bar-search-border-radius);
919
+ outline: transparent solid 1px;
920
+ cursor: text;
921
+ transition-property: min-height, height, width, transform, border-radius, box-shadow;
922
+ transition-duration: var(--cib-motion-duration-fast);
923
+ transition-timing-function: var(--cib-motion-easing-in);
924
+ transition-delay: var(--cib-motion-duration-faster);
925
+ box-shadow: var(--cib-shadow-card);
926
+
927
+ img {
928
+ cursor: pointer;
929
+ user-select: none;
930
+ }
931
+
932
+ textarea {
933
+ white-space: nowrap;
934
+ text-overflow: ellipsis;
935
+ overflow-x: hidden;
936
+ }
937
+
938
+ &:hover,
939
+ &.active {
940
+ min-height: 90px;
941
+ border-radius: var(--cib-border-radius-extra-large);
942
+
943
+ .bottom-bar {
944
+ opacity: 1;
945
+ }
946
+
947
+ textarea {
948
+ white-space: pre-wrap;
949
+ }
950
+ }
951
+
952
+ .main-bar {
953
+ display: flex;
954
+ flex-direction: row;
955
+ width: 100%;
956
+ gap: 16px;
957
+ justify-content: space-between;
958
+ align-items: flex-start;
959
+
960
+ &>*:nth-child(n+5) {
961
+ display: none;
962
+ }
963
+ }
964
+
965
+ .message-input {
966
+ max-height: 50vh;
967
+ overflow-y: auto;
968
+ }
969
+ }
970
+
971
+ .body-1 {
972
+ font-size: var(--cib-type-body1-font-size);
973
+ line-height: var(--cib-type-body1-line-height);
974
+ font-weight: var(--cib-type-body1-font-weight);
975
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
976
+ }
977
+
978
+ .body-2 {
979
+ font-size: var(--cib-type-body2-font-size);
980
+ line-height: var(--cib-type-body2-line-height);
981
+ font-weight: var(--cib-type-body2-font-weight);
982
+ font-variation-settings: var(--cib-type-body2-font-variation-settings);
983
+ }
984
+
985
+ .outside-left-container {
986
+ position: relative;
987
+ align-self: flex-end;
988
+ height: 48px;
989
+ bottom: 42px;
990
+ margin: 0px;
991
+ padding: 0px;
992
+ transition-property: opacity;
993
+ transition-duration: var(--cib-motion-duration-slow);
994
+ transition-delay: var(--cib-motion-duration-normal);
995
+ transition-timing-function: var(--cib-motion-easing-transition);
996
+
997
+ .button-compose-wrapper {
998
+ transition-property: opacity, transform;
999
+ transition-duration: var(--cib-motion-duration-fast);
1000
+ transition-timing-function: var(--cib-motion-easing-in);
1001
+ }
1002
+
1003
+ .button-compose {
1004
+ display: flex;
1005
+ flex-direction: row;
1006
+ position: relative;
1007
+ height: 48px;
1008
+ width: var(--button-compose-expanded-width);
1009
+ font-family: var(--cib-font-text);
1010
+ border-radius: var(--cib-border-radius-circular);
1011
+ color: var(--cib-color-foreground-on-accent-primary);
1012
+ fill: var(--cib-color-foreground-on-accent-primary);
1013
+ background: transparent;
1014
+ border: none;
1015
+ outline: transparent solid 1px;
1016
+ margin: 0px;
1017
+ padding: 0px;
1018
+ overflow: hidden;
1019
+ transition-property: width, opacity;
1020
+ transition-duration: var(--cib-motion-duration-normal);
1021
+ transition-timing-function: var(--cib-motion-easing-in);
1022
+
1023
+ &:not([disabled]) {
1024
+ pointer-events: auto;
1025
+ cursor: pointer;
1026
+ }
1027
+
1028
+ &::before {
1029
+ content: "";
1030
+ position: absolute;
1031
+ width: 100%;
1032
+ height: 100%;
1033
+ border-radius: var(--cib-border-radius-circular);
1034
+ background: var(--cib-color-fill-accent-gradient-primary);
1035
+ box-shadow: var(--cib-shadow-elevation-4);
1036
+ transition-property: transform;
1037
+ transition-duration: var(--cib-motion-duration-fast);
1038
+ transition-timing-function: var(--cib-motion-easing-in);
1039
+ }
1040
+ }
1041
+
1042
+ &.collapsed .button-compose {
1043
+ width: var(--button-compose-collapsed-width);
1044
+ }
1045
+
1046
+ &:hover .button-compose {
1047
+ width: var(--button-compose-expanded-width);
1048
+ }
1049
+
1050
+ @media (max-width: 600px) {
1051
+ .button-compose {
1052
+ width: var(--button-compose-collapsed-width) !important;
1053
+ }
1054
+ }
1055
+
1056
+ .button-compose-content {
1057
+ position: relative;
1058
+ display: grid;
1059
+ grid-template-columns: 48px auto;
1060
+ align-items: center;
1061
+ height: 48px;
1062
+ }
1063
+
1064
+ .button-compose-text {
1065
+ min-width: max-content;
1066
+ margin-inline-end: 20px;
1067
+ transition-property: opacity;
1068
+ transition-duration: var(--cib-motion-duration-fast);
1069
+ transition-timing-function: var(--cib-motion-easing-in);
1070
+ }
1071
+
1072
+ }
1073
+
1074
+ .visual-search-container {
1075
+ position: relative;
1076
+
1077
+ .visual-search {
1078
+ position: absolute;
1079
+ bottom: 42px;
1080
+ width: 380px;
1081
+ inset-inline-end: -50px;
1082
+ display: flex;
1083
+ flex-direction: column;
1084
+ align-items: flex-start;
1085
+ padding: 4px 4px 12px;
1086
+ padding-block-end: 4px;
1087
+
1088
+
1089
+ will-change: transform;
1090
+ border-radius: var(--cib-flyout-host-border-radius);
1091
+ box-shadow: var(--cib-shadow-elevation-28);
1092
+ background-color: var(--cib-color-background-surface-solid-tertiary);
1093
+ transition-duration: var(--cib-motion-duration-fast);
1094
+ transition-delay: var(--cib-motion-duration-normal);
1095
+ transition-timing-function: var(--cib-motion-easing-in);
1096
+ font-size: var(--cib-type-body1-font-size);
1097
+ line-height: var(--cib-type-body1-line-height);
1098
+ font-weight: var(--cib-type-body1-font-weight);
1099
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1100
+
1101
+ .header {
1102
+ width: 100%;
1103
+ box-sizing: border-box;
1104
+ padding: 10px 12px 6px;
1105
+ }
1106
+
1107
+ &.none {
1108
+ display: none;
1109
+ }
1110
+
1111
+
1112
+ h4 {
1113
+ margin: 0px;
1114
+ color: var(--cib-color-foreground-neutral-primary);
1115
+ font-size: var(--cib-type-subtitle2-stronger-font-size);
1116
+ line-height: var(--cib-type-subtitle2-stronger-line-height);
1117
+ font-weight: var(--cib-type-subtitle2-stronger-font-weight);
1118
+ font-variation-settings: var(--cib-type-subtitle2-stronger-font-variation-settings);
1119
+ }
1120
+
1121
+ .paste {
1122
+ position: relative;
1123
+ width: 100%;
1124
+ box-sizing: border-box;
1125
+ padding: 10px 12px;
1126
+
1127
+ img {
1128
+ position: absolute;
1129
+ top: 20px;
1130
+ inset-inline-start: 24px;
1131
+ }
1132
+
1133
+ }
1134
+
1135
+ .paste-input {
1136
+ display: flex;
1137
+ flex-direction: column;
1138
+ justify-content: center;
1139
+ height: 40px;
1140
+ width: 100%;
1141
+ box-sizing: border-box;
1142
+ padding: 10px 16px;
1143
+ padding-inline-start: 44px;
1144
+ margin: 0px;
1145
+ border: 1px solid var(--cib-color-stroke-surface-card-primary);
1146
+ border-radius: var(--cib-border-radius-extra-large);
1147
+ box-shadow: var(--cib-shadow-elevation-4);
1148
+ color: var(--cib-color-foreground-neutral-primary);
1149
+ background: var(--cib-color-fill-neutral-primary);
1150
+ font-size: var(--cib-type-body1-font-size);
1151
+ line-height: var(--cib-type-body1-line-height);
1152
+ font-weight: var(--cib-type-body1-font-weight);
1153
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1154
+
1155
+ &:focus {
1156
+ outline: 1px solid var(--cib-color-stroke-accent-primary);
1157
+ }
1158
+ }
1159
+
1160
+ .buttons {
1161
+ width: 100%;
1162
+ box-sizing: border-box;
1163
+ padding: 0px 12px;
1164
+
1165
+ button {
1166
+ display: flex;
1167
+ flex-direction: row;
1168
+ align-items: center;
1169
+ justify-content: flex-start;
1170
+ position: relative;
1171
+ cursor: pointer;
1172
+ gap: 12px;
1173
+ height: 40px;
1174
+ width: 100%;
1175
+ box-sizing: border-box;
1176
+ padding: 10px 12px;
1177
+ margin: 0px;
1178
+ border: none;
1179
+ border-radius: var(--cib-border-radius-medium);
1180
+ cursor: pointer;
1181
+ background: transparent;
1182
+ color: var(--cib-color-foreground-neutral-primary);
1183
+ font-size: var(--cib-type-body1-font-size);
1184
+ line-height: var(--cib-type-body1-line-height);
1185
+ font-weight: var(--cib-type-body1-font-weight);
1186
+ font-variation-settings: var(--cib-type-body1-font-variation-settings);
1187
+ &:hover {
1188
+ background: var(--cib-color-fill-subtle-secondary);
1189
+ }
1190
+ }
1191
+ }
1192
+ .fileinput {
1193
+ opacity: 0;
1194
+ position: absolute;
1195
+ width: 100%;
1196
+ height: 100%;
1197
+ }
1198
+
1199
+ &::after {
1200
+ content: "";
1201
+ position: absolute;
1202
+ inset-inline-end: 50px;
1203
+ top: 100%;
1204
+ inset-inline-start: var(--arrow-start-offset);
1205
+ border-top-width: 10px;
1206
+ border-right: 10px solid transparent;
1207
+ border-left: 10px solid transparent;
1208
+ border-top-style: solid;
1209
+ border-image: initial;
1210
+ border-bottom: none;
1211
+ border-top-color: var(--cib-color-background-surface-solid-tertiary);
1212
+ filter: drop-shadow(0 1px 0 var(--cib-color-stroke-neutral-primary));
1213
+ transition-property: inset-inline-start;
1214
+ transition-duration: var(--cib-motion-duration-fast);
1215
+ transition-timing-function: var(--cib-motion-easing-in);
1216
+ }
1217
+ }
1218
+
1219
+ .webvideo-container {
1220
+ position: relative;
1221
+ }
1222
+
1223
+ .webvideo {
1224
+ display: block;
1225
+ background-color: rgb(0, 0, 0);
1226
+ width: 100%;
1227
+ height: auto;
1228
+ }
1229
+ .webcanvas {
1230
+ display: none
1231
+ }
1232
+ .cambtn {
1233
+ cursor: pointer;
1234
+ width: min-content;
1235
+ height: 46px;
1236
+ padding: 8px;
1237
+ margin: auto;
1238
+ }
1239
+ .cam-btn-circle-large {
1240
+ width: 30px;
1241
+ height: 30px;
1242
+ border-radius: var(--cib-border-radius-circular);
1243
+ background: var(--cib-color-fill-accent-gradient-primary);
1244
+ opacity: 0.4;
1245
+ }
1246
+ .cam-btn-circle-small {
1247
+ width: 20px;
1248
+ height: 20px;
1249
+ border-radius: var(--cib-border-radius-circular);
1250
+ background: var(--cib-color-fill-accent-gradient-primary);
1251
+ opacity: 1;
1252
+ position: relative;
1253
+ top: -25px;
1254
+ inset-inline-start: 5px;
1255
+ }
1256
+
1257
+ .normal-content, .cam-content {
1258
+ width: 100%;
1259
+ display: none;
1260
+ }
1261
+ .normal .normal-content {
1262
+ display: block;
1263
+ }
1264
+ .camera-mode .cam-content {
1265
+ display: block;
1266
+ }
1267
+ }
1268
+
1269
+ .spinner:before {
1270
+ content: "";
1271
+ box-sizing: border-box;
1272
+ position: absolute;
1273
+ top: 50%;
1274
+ inset-inline-start: 50%;
1275
+ width: 20px;
1276
+ height: 20px;
1277
+ margin-top: -10px;
1278
+ margin-inline-start: -10px;
1279
+ border-radius: 50%;
1280
+ border-top: 2px solid var(--cib-color-fill-accent-gradient-primary);
1281
+ border-inline-end: 2px solid transparent;
1282
+ animation: spinner 0.6s linear infinite;
1283
+ }
1284
+
1285
+ @keyframes spinner {
1286
+ to {
1287
+ transform: rotate(360deg);
1288
+ }
1289
+ }
1290
+
1291
+ @keyframes borealisBar {
1292
+ 0% {
1293
+ inset-inline-start: 0%;
1294
+ inset-inline-end: 100%;
1295
+ width: 0%;
1296
+ }
1297
+ 35% {
1298
+ inset-inline-start: 0%;
1299
+ inset-inline-end: 50%;
1300
+ width: 50%;
1301
+ }
1302
+ 65% {
1303
+ inset-inline-end: 0%;
1304
+ inset-inline-start: 50%;
1305
+ width: 50%;
1306
+ }
1307
+ 100% {
1308
+ inset-inline-start: 100%;
1309
+ inset-inline-end: 0%;
1310
+ width: 0%;
1311
+ }
1312
+ }
1313
+
1314
+ .attachment-list {
1315
+ display: flex;
1316
+ flex-wrap: wrap;
1317
+ gap: 8px;
1318
+ margin-block: 16px;
1319
+ overflow: hidden;
1320
+ .file-item {
1321
+ display: flex;
1322
+ flex-direction: row;
1323
+ height: 48px;
1324
+ border-radius: var(--cib-border-radius-medium);
1325
+ overflow: hidden;
1326
+ cursor: default;
1327
+ opacity: 1;
1328
+ animation-name: file-item-enter;
1329
+ animation-fill-mode: both;
1330
+ animation-delay: var(--cib-motion-duration-fast);
1331
+ animation-duration: var(--cib-motion-duration-normal);
1332
+ animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
1333
+ background: var(--cib-color-background-surface-app-primary);
1334
+ border: 1px solid var(--cib-color-stroke-neutral-primary);
1335
+ position: relative;
1336
+ }
1337
+ .thumbnail {
1338
+ display: flex;
1339
+ align-items: center;
1340
+ justify-content: center;
1341
+ height: 48px;
1342
+ width: 48px;
1343
+ img {
1344
+ height: 48px;
1345
+ width: 48px;
1346
+ object-fit: cover;
1347
+ object-position: center center;
1348
+ -webkit-user-drag: none;
1349
+ overflow-clip-margin: content-box;
1350
+ overflow: clip;
1351
+ }
1352
+ }
1353
+ .dismiss {
1354
+ display: flex;
1355
+ align-items: center;
1356
+ justify-content: center;
1357
+ width: 32px;
1358
+ border: none;
1359
+ margin: 0px;
1360
+ padding: 0px;
1361
+ background-color: transparent;
1362
+ cursor: pointer;
1363
+ fill: var(--cib-color-foreground-neutral-primary);
1364
+ &.no-file {
1365
+ background-color: var(--cib-color-background-surface-solid-tertiary);
1366
+ }
1367
+ }
1368
+ .error {
1369
+ height: 48px;
1370
+ width: 48px;
1371
+ background-color: var(--cib-color-fill-accent-secondary);
1372
+ display: flex;
1373
+ justify-content: center;
1374
+ align-items: center;
1375
+ }
1376
+ .loading {
1377
+ width: 100%;
1378
+ position: absolute;
1379
+ height: 3px;
1380
+ bottom: -1px;
1381
+ }
1382
+ .bar {
1383
+ top: 0;
1384
+ inset-inline-end: 100%;
1385
+ bottom: 0;
1386
+ inset-inline-start: 0;
1387
+ width: 0;
1388
+ position: absolute;
1389
+ background: var(--cib-color-fill-accent-gradient-primary);
1390
+ animation: borealisBar 2s linear infinite;
1391
+ }
1392
+ }
1393
+
1394
+ .bottom-bar {
1395
+ position: absolute;
1396
+ display: flex;
1397
+ flex-direction: row;
1398
+ align-items: center;
1399
+ justify-content: space-between;
1400
+ height: 36px;
1401
+ bottom: 4px;
1402
+ inset-inline: 0px;
1403
+ box-sizing: border-box;
1404
+ padding-block: 0px;
1405
+ padding-inline: 16px 8px;
1406
+ opacity: 0;
1407
+ transition-property: opacity;
1408
+ transition-duration: var(--cib-motion-duration-faster);
1409
+ transition-delay: var(--cib-motion-duration-faster);
1410
+ transition-timing-function: var(--cib-motion-easing-transition);
1411
+
1412
+ .letter-counter {
1413
+ color: var(--cib-color-foreground-neutral-secondary);
1414
+ }
1415
+ }
1416
+
1417
+ .fade {
1418
+ position: fixed;
1419
+ left: 0;
1420
+ height: 104px;
1421
+ width: 100%;
1422
+ z-index: -1;
1423
+ overflow: hidden;
1424
+ clip-path: inset(0px);
1425
+ pointer-events: none;
1426
+
1427
+ &.bottom {
1428
+ display: block;
1429
+ bottom: 0px;
1430
+ height: 140px;
1431
+ -webkit-mask-image: linear-gradient(transparent calc(100% - 140px), black calc(100% - 118px));
1432
+ mask-image: linear-gradient(transparent calc(100% - 140px), black calc(100% - 118px));
1433
+ }
1434
+
1435
+ .background {
1436
+ height: 100%;
1437
+ transition-property: transform;
1438
+ transition-duration: var(--cib-motion-duration-slowest);
1439
+ transition-timing-function: var(--cib-motion-easing-transition);
1440
+ background: var(--cib-color-fill-accent-gradient-quaternary);
1441
+ }
1442
+ }
1443
+
1444
+ @media (max-width: 600px) {
1445
+ .action-root {
1446
+ align-items: flex-end;
1447
+ justify-content: flex-end;
1448
+ min-height: unset;
1449
+ }
1450
+
1451
+ .main-container {
1452
+ width: calc(100% - 60px);
1453
+
1454
+ button[type="submit"] {
1455
+ display: none;
1456
+ }
1457
+
1458
+ &:hover,
1459
+ &.active {
1460
+ width: 100%;
1461
+ transition-delay: 167ms;
1462
+ }
1463
+ }
1464
+
1465
+ .outside-left-container {
1466
+ position: absolute;
1467
+ bottom: 0px;
1468
+ inset-inline-start: 0px;
1469
+ }
1470
+ }
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-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
+ }
src/components/chat-message.tsx ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import remarkGfm from 'remark-gfm'
2
+ import remarkMath from 'remark-math'
3
+ import supersub from 'remark-supersub'
4
+ import remarkBreaks from 'remark-breaks'
5
+ import { cn } from '@/lib/utils'
6
+ import { CodeBlock } from '@/components/ui/codeblock'
7
+ import { MemoizedReactMarkdown } from '@/components/markdown'
8
+ import { LearnMore } from './learn-more'
9
+ import { ChatMessageModel } from '@/lib/bots/bing/types'
10
+ import { useEffect } from 'react'
11
+ import { TurnCounter } from './turn-counter'
12
+
13
+ export interface ChatMessageProps {
14
+ message: ChatMessageModel
15
+ }
16
+
17
+ export function ChatMessage({ message, ...props }: ChatMessageProps) {
18
+ useEffect(() => {
19
+ if (document.body.scrollHeight - window.innerHeight - window.scrollY - 200 < 0) {
20
+ window.scrollBy(0, 200)
21
+ }
22
+ }, [message.text])
23
+
24
+ return message.text ? (
25
+ <div
26
+ className={cn('text-message', message.author)}
27
+ {...props}
28
+ >
29
+ <div className="text-message-content">
30
+ <MemoizedReactMarkdown
31
+ linkTarget="_blank"
32
+ className="prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0"
33
+ remarkPlugins={[remarkGfm, remarkMath, supersub, remarkBreaks]}
34
+ components={{
35
+ img(obj) {
36
+ try {
37
+ const uri = new URL(obj.src!)
38
+ const w = uri.searchParams.get('w')
39
+ const h = uri.searchParams.get('h')
40
+ if (w && h) {
41
+ uri.searchParams.delete('w')
42
+ uri.searchParams.delete('h')
43
+ return <a style={{ float: 'left', maxWidth: '50%' }} href={uri.toString()} target="_blank" rel="noopener noreferrer"><img src={obj.src} alt={obj.alt} width={w!} height={h!}/></a>
44
+ }
45
+ } catch (e) {
46
+ }
47
+ return <img src={obj.src} alt={obj.alt} title={obj.title} />
48
+ },
49
+ p({ children }) {
50
+ return <p className="mb-2">{children}</p>
51
+ },
52
+ code({ node, inline, className, children, ...props }) {
53
+ if (children.length) {
54
+ if (children[0] == '▍') {
55
+ return (
56
+ <span className="mt-1 animate-pulse cursor-default">▍</span>
57
+ )
58
+ }
59
+
60
+ children[0] = (children[0] as string).replace('`▍`', '▍')
61
+ }
62
+
63
+ const match = /language-(\w+)/.exec(className || '')
64
+
65
+ if (inline) {
66
+ return (
67
+ <code className={className} {...props}>
68
+ {children}
69
+ </code>
70
+ )
71
+ }
72
+
73
+ return (
74
+ <CodeBlock
75
+ key={Math.random()}
76
+ language={(match && match[1]) || ''}
77
+ value={String(children).replace(/\n$/, '')}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+ }}
83
+ >
84
+ {message.text}
85
+ </MemoizedReactMarkdown>
86
+ </div>
87
+ <div className="text-message-footer">
88
+ {message.author === 'bot' && <LearnMore sourceAttributions={message.sourceAttributions} />}
89
+ {message.author === 'bot' && <TurnCounter throttling={message.throttling} />}
90
+ </div>
91
+ </div>
92
+ ) : null
93
+ }