Compare commits

...

435 Commits

Author SHA1 Message Date
fred-bf
7c04a90d77 Merge pull request #4287 from fred-bf/main
feat: bump version
2024-03-14 02:30:58 +08:00
fred-bf
a8a65ac769 Merge branch 'ChatGPTNextWeb:main' into main 2024-03-14 02:30:22 +08:00
Fred
aec3c5d6cc feat: bump version 2024-03-14 02:29:31 +08:00
fred-bf
a22141c2eb Merge pull request #4285 from fred-bf/fix/cors-ssrf
[Bugfix] Fix CORS SSRF security issue
2024-03-14 02:27:55 +08:00
Fred
99aa064319 fix: fix webdav sync issue 2024-03-14 01:58:25 +08:00
Fred
6aaf83f3c2 fix: fix upstash sync issue 2024-03-14 01:56:36 +08:00
Fred
133ce39a13 chore: update cors default path 2024-03-14 01:33:41 +08:00
Fred
8645214654 fix: change matching pattern 2024-03-14 01:26:13 +08:00
Fred
eebc334e02 fix: remove corsFetch 2024-03-14 00:57:54 +08:00
Fred
038fa3b301 fix: add webdav request filter 2024-03-14 00:33:26 +08:00
Fred
9a8497299d fix: adjust upstash api 2024-03-13 23:58:28 +08:00
fred-bf
61ce3868b5 Merge pull request #4279 from SukkaW/package-json-corepack
chore: specify yarn 1 in package.json
2024-03-13 20:09:57 +08:00
SukkaW
844c2a26bc chore: specify yarn 1 in package.json 2024-03-13 13:30:16 +08:00
fred-bf
a15c4d9c20 Merge pull request #4234 from fengzai6/main
Fix EmojiPicker mobile width adaptation and update avatar clicking behavior
2024-03-11 13:59:09 +08:00
fred-bf
ff9f0e60ac Merge pull request #3972 from greenjerry/fix-export-garbled
fix: 修复导出时字符乱码问题
2024-03-07 17:07:16 +08:00
fred-bf
2bf6111bf5 Merge branch 'main' into fix-export-garbled 2024-03-07 17:07:08 +08:00
fengzai6
ad10a11903 Add z-index to avatar 2024-03-07 15:51:58 +08:00
fengzai6
c22153a4eb Revert "fix: No history message attached when for gemini-pro-vision"
This reverts commit c197962851.
2024-03-07 15:46:13 +08:00
fengzai6
5348d57057 Fix EmojiPicker mobile width adaptation and update avatar clicking behavior 2024-03-07 15:36:19 +08:00
fengzai6
052524dabd Merge remote-tracking branch 'upstream/main' 2024-03-07 15:32:09 +08:00
fred-bf
5529ece220 Merge pull request #4218 from ChatGPTNextWeb/fred-bf-patch-1
chore: update GTM_ID definition
2024-03-05 17:37:22 +08:00
fred-bf
e71094d4a8 chore: update GTM_ID definition, close #4217 2024-03-05 17:36:52 +08:00
fred-bf
98aa023d70 Merge pull request #4195 from aliceric27/main
slightly polishes the tw text.
2024-03-04 19:03:23 +08:00
aliceric27
e1066434d0 fix some text 2024-03-03 00:23:00 +08:00
aliceric27
86ae4b2a75 slightly polishes the tw text. 2024-03-02 23:58:23 +08:00
fred-bf
99fb9dcf11 Merge pull request #4164 from KSnow616/main
feat: Pasting images into the textbox
2024-02-29 22:14:02 +08:00
fred-bf
1294817103 Merge pull request #4089 from H0llyW00dzZ/cherry-pick
[Cherry Pick] Fix [Utils] Regex trimTopic
2024-02-29 16:31:30 +08:00
Snow Kawashiro
9775660da7 Update chat.tsx 2024-02-28 20:45:42 +08:00
Snow Kawashiro
e7051353eb vision_model_only 2024-02-28 20:38:00 +08:00
Snow Kawashiro
bd19e97cf8 add_image_pasting 2024-02-28 20:05:13 +08:00
fred-bf
8b821ac0c9 Merge pull request #4162 from fred-bf/fix/identify-vision-model
fix: fix the method to detect vision model
2024-02-28 11:35:22 +08:00
Fred
43e5dc2292 fix: fix the method to detect vision model 2024-02-28 11:33:43 +08:00
fred-bf
08fa22749a fix: add max_tokens when using vision model (#4157) 2024-02-27 17:28:01 +08:00
fengzai6
c197962851 fix: No history message attached when for gemini-pro-vision 2024-02-27 15:02:58 +08:00
fred-bf
44a51273be Merge pull request #4149 from fred-bf/feat/auto-detach-scrolling
feat: auto detach scrolling
2024-02-27 11:56:37 +08:00
Fred
e3b3ae97bc chore: clear scroll info 2024-02-27 11:49:44 +08:00
Fred
410a22dc63 feat: auto detach scrolling 2024-02-27 11:43:40 +08:00
Algorithm5838
069766d581 Correct cutoff dates (#4118) 2024-02-27 10:28:54 +08:00
DonaldBear
f22e36e52f feat(tw.ts): added new translations (#4142)
* feat(tw.ts): added new translations

I have translated previously untranslated text in response to the latest update.

* feat(tw.ts): added new translations

I have translated previously untranslated text in response to the latest update.
2024-02-27 00:16:56 +08:00
fred-bf
bc1794fb4a feat: bump version (#4133) 2024-02-26 18:15:00 +08:00
Fred
aacd26c7db feat: bump version 2024-02-26 18:14:10 +08:00
fred-bf
ff166f7b4c chore: adjust for ollama support (#4129) 2024-02-26 17:18:46 +08:00
H0llyW00dzZ
22baebaf8c [Cherry Pick] Fix [Utils] Regex trimTopic
- [+] fix(utils.ts): update regular expressions in trimTopic function to handle asterisks
2024-02-21 04:19:12 +07:00
H0llyW00dzZ
e756506c18 [Cherry Pick] Improve Github Issue Template (#4041)
* Improve Github Issue Template

- [+] feat(issue templates): update issue templates from markdown to form schema
- [+] feat(issue templates): translate issue templates to Chinese
- [+] remove(issue templates): delete old markdown issue templates

* chore: remove Chinese template issue temporarily

---------

Co-authored-by: Fred <fred@nextchat.dev>
2024-02-20 18:11:02 +08:00
Qiying Wang
fd67f980a5 Fix temperature range (#4083) 2024-02-20 18:05:17 +08:00
TheRam_
e2da3406d2 Add vision support (#4076) 2024-02-20 18:04:32 +08:00
Ikko Eltociear Ashimine
05b6d989b6 chore: fix typo in next.config.mjs (#4072)
verison -> version
2024-02-20 17:59:59 +08:00
H0llyW00dzZ
1d6ee64e1d [Cherry Pick] Fix [UI/UX] [Front End] Settings Page (#4032)
* Fix [UI/UX] [Locales] Correct Spelling

- [+] fix(locales): correct spelling and improve wording in cn.ts and en.ts locale files

* Fix [UI/UX] [Front End] Settings Page

- [+] fix(settings.tsx): correct typo in ApiVerion to ApiVersion
- [+] refactor(settings.tsx): switch Azure.ApiKey to Google.ApiKey in ListItem title and subTitle

* Fix [UI/UX] [Locales] [SK] Correct Typo

- [+] fix(sk.ts): correct typo in ApiVersion key in Slovak locale file
2024-02-12 20:36:52 +08:00
fred-bf
bfefb99192 chore: update tauri dependencies (#4018)
* feat: bump version

* feat: bump version

* chore: update tauri dependencies
2024-02-07 14:12:04 +08:00
Anivie Michaelis
47ae874e4d fix: add support to http scheme. (#3985)
Co-authored-by: fred-bf <157469842+fred-bf@users.noreply.github.com>
2024-02-07 13:48:28 +08:00
fred-bf
d74f636558 Fix/gemini app endpoint (#4017)
* fix: support custom api endpoint

* fix: attach api key to google gemini
2024-02-07 13:46:52 +08:00
fred-bf
b8f0822214 fix: support custom api endpoint (#4016) 2024-02-07 13:40:30 +08:00
fred-bf
0869455612 feat: bump version (#4015)
* feat: bump version

* feat: bump version
2024-02-07 13:38:02 +08:00
fred-bf
bca74241e6 fix: fix gemini issue when using app (#4013)
* chore: update path

* fix: fix google auth logic

* fix: not using header authorization for google api

* chore: revert to allow stream
2024-02-07 13:17:11 +08:00
fred-bf
9d5801fb5f fix: avoiding not operation for custom models (#4010) 2024-02-07 10:31:49 +08:00
H0llyW00dzZ
462a88ae82 Fix [CI/CD] [Vercel] Deploy Preview (#4005)
- [+] feat(.github/workflows/deploy_preview.yml): add 'reopened' event trigger
2024-02-06 17:20:12 +08:00
fred-bf
887bec019a feat: bump version (#4009) 2024-02-06 15:34:43 +08:00
吴迪
d0463b2089 feat(mac): add sign config, fix arm64 build (#4008)
Co-authored-by: vir <wudi@hudiexiaoying.cn>
2024-02-06 15:11:20 +08:00
Shiroki Satsuki
deea4320ad ci: build universal binary for macos (#3711)
* ci: build universal binary for macos

* ci: add tauri args

* ci: restore rust_target

* ci: fallback value for tauri build args

* ci: cache yarn
2024-02-06 15:07:01 +08:00
H0llyW00dzZ
bb26c03141 Feat [UI/UX] [Constants] [Models] gemini-pro KnowledgeCutOffDate (#3997)
- [+] feat(constant.ts): add 'gemini-pro' to KnowledgeCutOffDate constant
2024-02-05 14:44:46 +08:00
H0llyW00dzZ
a5517a1a51 Improve Default System Template (#3996)
* Feat [UI/UX] [Constant] [DEFAULT System Template] replace hardcoded

- [+] feat(constant.ts): replace hardcoded OpenAI with dynamic ServiceProvider variable in DEFAULT_SYSTEM_TEMPLATE

* Improve [UI/UX] [Chat] "fillTemplateWith"

- [+] feat(chat.ts): add DEFAULT_MODELS to modelConfig
- [+] fix(chat.ts): replace replaceAll with regex in output string replacement
- [+] refactor(chat.ts): use const instead of let for cutoff variable
2024-02-05 13:35:51 +08:00
H0llyW00dzZ
4511aa4d21 Fix [UI/UX] [Emoji] Update CDN (#3966)
- [+] fix(emoji.tsx): update CDN url from 'cdn.jsdelivr.net' to 'fastly.jsdelivr.net'
2024-02-03 01:24:37 +08:00
Duplicate4
b25a0545f5 fix: typo (#3871)
Signed-off-by: Dup4 <lyuzhi.pan@gmail.com>
2024-02-03 01:20:10 +08:00
H0llyW00dzZ
1a97bd55c7 Feat [OpenAI] [Models] gpt-3.5-turbo-0125 (#3971)
- [+] feat(constant.ts): add new model 'gpt-3.5-turbo-0125' to DEFAULT_MODELS array
2024-02-03 01:19:25 +08:00
greenjerry
bf711f2ad7 修复导出json和markdown时中文及其他utf8字符乱码问题 2024-02-02 13:58:06 +08:00
Jerry Zhou
e1b065c74a [ImgBot] Optimize images (#3941)
*Total -- 2,603.15kb -> 2,299.83kb (11.65%)

/docs/images/upstash-7.png -- 83.76kb -> 56.04kb (33.1%)
/public/android-chrome-512x512.png -- 95.26kb -> 65.26kb (31.49%)
/docs/images/upstash-1.png -- 23.29kb -> 15.98kb (31.4%)
/docs/images/upstash-6.png -- 66.20kb -> 46.73kb (29.42%)
/docs/images/upstash-5.png -- 66.88kb -> 47.31kb (29.26%)
/docs/images/upstash-4.png -- 37.04kb -> 26.39kb (28.75%)
/docs/images/upstash-3.png -- 49.25kb -> 35.67kb (27.57%)
/public/apple-touch-icon.png -- 18.71kb -> 14.60kb (21.97%)
/public/android-chrome-192x192.png -- 20.52kb -> 16.06kb (21.74%)
/public/macos.png -- 73.77kb -> 57.84kb (21.59%)
/src-tauri/icons/icon.png -- 82.18kb -> 65.26kb (20.58%)
/docs/images/upstash-2.png -- 64.79kb -> 51.85kb (19.97%)
/app/icons/send-white.svg -- 0.95kb -> 0.82kb (13.83%)
/app/icons/close.svg -- 0.96kb -> 0.83kb (13.76%)
/app/icons/reload.svg -- 1.22kb -> 1.06kb (13.36%)
/app/icons/return.svg -- 0.99kb -> 0.86kb (13.33%)
/app/icons/chat.svg -- 1.50kb -> 1.31kb (12.28%)
/app/icons/settings.svg -- 1.10kb -> 0.96kb (12.02%)
/app/icons/eye.svg -- 1.30kb -> 1.17kb (9.92%)
/public/favicon-32x32.png -- 1.70kb -> 1.56kb (8.12%)
/app/icons/eye-off.svg -- 1.84kb -> 1.71kb (6.99%)
/docs/images/head-cover.png -- 1,724.78kb -> 1,608.59kb (6.74%)
/app/icons/bot.svg -- 4.17kb -> 3.89kb (6.57%)
/src-tauri/icons/Square71x71Logo.png -- 6.38kb -> 6.03kb (5.47%)
/src-tauri/icons/Square89x89Logo.png -- 8.61kb -> 8.16kb (5.25%)
/src-tauri/icons/Square44x44Logo.png -- 3.44kb -> 3.30kb (4.09%)
/app/icons/clear.svg -- 1.24kb -> 1.19kb (3.87%)
/app/icons/edit.svg -- 1.61kb -> 1.55kb (3.58%)
/app/icons/pause.svg -- 1.09kb -> 1.05kb (3.58%)
/src-tauri/icons/Square107x107Logo.png -- 11.03kb -> 10.64kb (3.57%)
/app/icons/rename.svg -- 0.89kb -> 0.86kb (3.29%)
/src-tauri/icons/StoreLogo.png -- 4.03kb -> 3.90kb (3.2%)
/app/icons/auto.svg -- 1.30kb -> 1.26kb (3.01%)
/app/icons/chatgpt.svg -- 3.51kb -> 3.42kb (2.64%)
/app/icons/dark.svg -- 0.83kb -> 0.81kb (2.35%)
/app/icons/light.svg -- 2.98kb -> 2.91kb (2.23%)
/app/icons/drag.svg -- 1.36kb -> 1.33kb (2.16%)
/src-tauri/icons/128x128.png -- 13.99kb -> 13.73kb (1.82%)
/app/icons/pin.svg -- 5.83kb -> 5.75kb (1.39%)
/app/icons/break.svg -- 4.53kb -> 4.47kb (1.21%)
/app/icons/connection.svg -- 8.14kb -> 8.05kb (1.01%)
/app/icons/confirm.svg -- 2.24kb -> 2.22kb (1%)
/app/icons/cancel.svg -- 3.54kb -> 3.50kb (0.97%)
/app/icons/robot.svg -- 11.72kb -> 11.62kb (0.89%)
/app/icons/config.svg -- 14.93kb -> 14.81kb (0.78%)
/app/icons/cloud-fail.svg -- 6.25kb -> 6.20kb (0.75%)
/app/icons/cloud-success.svg -- 4.69kb -> 4.66kb (0.75%)
/app/icons/upload.svg -- 10.70kb -> 10.62kb (0.75%)
/app/icons/lightning.svg -- 3.49kb -> 3.47kb (0.62%)
/app/icons/chat-settings.svg -- 8.99kb -> 8.96kb (0.38%)
/src-tauri/icons/Square142x142Logo.png -- 16.15kb -> 16.09kb (0.34%)
/src-tauri/icons/Square150x150Logo.png -- 17.53kb -> 17.51kb (0.07%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2024-01-29 14:10:07 +08:00
Takanezawa, Kosuke
eacc911cdc Add Japanese translations for ChatGPT messages (#3936) 2024-01-29 10:38:25 +08:00
H0llyW00dzZ
91a5f4af9a Feat Models [GPT-4-turbo] (#3932)
[+] feat(constant.ts): add new models to DEFAULT_MODELS array
2024-01-27 15:58:35 +08:00
NieXi
f6cdda9029 update google api region (#3934)
Co-authored-by: GH Action - Upstream Sync <action@github.com>
2024-01-27 15:55:44 +08:00
Leo Li
981a3629b6 Add gpt-4-0125-preview (#3924) 2024-01-26 15:00:02 +08:00
sungaomeng
118e8f541c fix: cicd, checkout sha Merge pull request #3920 from sungaomeng/main
fix: cicd, checkout sha
2024-01-25 21:25:49 +08:00
孙高猛
3fa55f9022 fix: cicd, checkout sha 2024-01-25 21:17:10 +08:00
fred-bf
1c73c6cc70 Merge pull request #3917 from sungaomeng/main
fix: cicd, remove workflow_dispatch
2024-01-25 20:50:21 +08:00
孙高猛
148c32a383 fix: cicd, remove workflow_dispatch 2024-01-25 20:46:42 +08:00
sungaomeng
ca08062bf3 Merge pull request #3913 from sungaomeng/main
fix: cicd, delete velcel pre
2024-01-25 20:00:11 +08:00
孙高猛
ecbab75a25 fix: cicd, alias domain env name 2024-01-25 19:54:58 +08:00
孙高猛
15d25df245 fix: cicd, delete velcel pre 2024-01-25 19:43:26 +08:00
sungaomeng
b2f7bdca34 Merge branch 'ChatGPTNextWeb:main' into main 2024-01-25 19:38:35 +08:00
sungaomeng
32bcdb8982 fix: cicd, delete vercel pre (#3910) 2024-01-25 19:37:29 +08:00
孙高猛
ba3e7e7974 fix: cicd, vercel alias domain 2024-01-25 19:31:17 +08:00
孙高猛
fae82a30e7 fix: cicd, delete vercel pre 2024-01-25 16:53:03 +08:00
fred-bf
5ce1a55a4f Merge pull request #3909 from sungaomeng/main
fix: ci for vercel deployment
2024-01-25 15:40:11 +08:00
sungaomeng
1132816902 Merge pull request #2 from sungaomeng/sgm/cicd
fix: ci for vercel deployment
2024-01-25 15:28:23 +08:00
sungaomeng
a4c1f1ac47 Merge branch 'main' into sgm/cicd 2024-01-25 15:27:41 +08:00
fred-bf
f619e9df24 chore: change default submit key 2024-01-25 15:22:45 +08:00
孙高猛
43631a3271 fix: cicd, vercel domain suffix secret 2024-01-25 15:17:03 +08:00
fred-bf
04855de495 Merge pull request #3789 from wyxogo/upstream
Fix: Routes `/api/cors/[...path]` was not configured to run with the …
2024-01-25 15:07:40 +08:00
孙高猛
6206ceb49b fix: cicd , add pull_request_target 2024-01-25 15:00:44 +08:00
sungaomeng
628f3ba05f Merge branch 'ChatGPTNextWeb:sgm/cicd' into sgm/cicd 2024-01-25 14:35:32 +08:00
孙高猛
94f534f36f fix vercel alias domains 2024-01-25 14:33:38 +08:00
孙高猛
2f11e7c087 test pr deploy 2024-01-25 11:04:32 +08:00
孙高猛
0739ced407 add workflow_dispatch 2024-01-25 10:57:47 +08:00
孙高猛
8fb34465cd add delete preview scripts 2024-01-25 10:57:47 +08:00
孙高猛
04fe9fadb1 add workflows/deploy preview 2024-01-25 10:57:47 +08:00
孙高猛
998c95da51 test pr deploy 2024-01-24 22:10:02 +08:00
孙高猛
3bc771a0d9 add workflow_dispatch 2024-01-24 22:03:47 +08:00
孙高猛
bdc25adb41 add delete preview scripts 2024-01-24 20:49:30 +08:00
孙高猛
7ec29c2eea add workflows/deploy preview 2024-01-24 20:19:39 +08:00
Fred Liang
b9995e7f70 feat: update dependencies 2024-01-22 15:12:21 +08:00
Fred Liang
31d9d2efcd feat: support setting up GTM 2024-01-22 15:12:21 +08:00
Yasin
a06a2989d2 Merge branch 'ChatGPTNextWeb:main' into upstream 2024-01-11 13:17:56 +08:00
Yifei Zhang
0cf6614186 Merge pull request #3813 from ChatGPTNextWeb/update-cn-qq-info
Update README_CN.md
2024-01-11 01:27:43 +08:00
Yifei Zhang
ddc16cc2be Update README_CN.md 2024-01-11 01:27:31 +08:00
Yifei Zhang
616f0b3e73 Merge pull request #3812 from ChatGPTNextWeb/update-twitter-info
doc: update twitter & QQ group info
2024-01-11 01:26:30 +08:00
Yifei Zhang
bd1056b13f Update README.md 2024-01-11 01:25:20 +08:00
Yasin
5a7b9abe33 Fix: Routes /api/cors/[...path] was not configured to run with the Edge Runtime 2024-01-08 18:43:31 +08:00
Fred Liang
cc86923fd5 fix: import typing error 2024-01-05 21:19:51 +08:00
Fred Liang
916d764477 fix: import language issue 2024-01-05 21:17:26 +08:00
levidius
4a21a8fdae Added Slovak language support
Slovak language was added app/locales/sk.ts and also the language was added to app/locales/index.ts
2024-01-05 21:05:17 +08:00
H0llyW00dzZ
49df5bd590 Chore [Tauri Desktop] Update icons
- [+] chore(icons): update icon files in src-tauri/icons directory
2024-01-05 21:03:44 +08:00
Fred Liang
9eaf492d5b chore: low the google safety setting to avoid unexpected blocking 2023-12-31 19:49:22 +08:00
Fred Liang
a80502f7db fix: fix gemini pro streaming api duplicated issue (#3721)
* fix: streaming duplicated issue

* chore: remove debug logs

* chore: add types defination
2023-12-31 19:08:16 +08:00
Fred Liang
eade013138 fix: update google url description (#3719)
* feat: update new logo & cover image

* fix: update google url description
2023-12-31 18:02:04 +08:00
Fred Liang
912254751a feat: update new logo & cover image (#3716) 2023-12-31 13:24:36 +08:00
Tashvik Srivastava
c3c6f7f7ae update readme (#3715) 2023-12-31 01:43:29 +08:00
Fred Liang
5cf58d9446 feat: support streaming for Gemini Pro (#3688)
* feat: support streaming for Gemini Pro

* feat: display texts smoothly

* chore: remove comments
2023-12-29 03:42:45 +08:00
reece00
3ba598633c Non -GPT model disable system prompt (#3684) 2023-12-28 23:52:45 +08:00
Fred Liang
406530ca69 feat: support vercel speed insight (#3686) 2023-12-28 23:10:19 +08:00
Fred Liang
f8b963df6d chore(docs): update readme for Gemini Pro (#3685)
* chore: update auth value logic

* chore: bump version 2.10.1

* Chore [Package] Downgrade Tauri Builder Version (#3656)

- [+] chore(package.json): update @tauri-apps/cli devDependency to version 1.5.7
- [+] chore(yarn.lock): update @tauri-apps/cli versions to 1.5.7

* chore: update README.md

---------

Co-authored-by: H0llyW00dzZ <priv8@btz.pm>
2023-12-28 22:56:01 +08:00
Fred Liang
d17000975f Release 2.10.1: Support Google Gemini Pro model (#3668)
* chore: update auth value logic

* chore: bump version 2.10.1
2023-12-27 23:45:40 +08:00
H0llyW00dzZ
64a8ba6212 Chore [Package] Downgrade Tauri Builder Version (#3656)
- [+] chore(package.json): update @tauri-apps/cli devDependency to version 1.5.7
- [+] chore(yarn.lock): update @tauri-apps/cli versions to 1.5.7
2023-12-27 23:43:48 +08:00
Yifei Zhang
350ddd2af1 Merge pull request #3215 from akira-cn/main
fix: remove the content-encoding header
2023-12-27 01:26:48 +08:00
Yifei Zhang
c03abdac42 Merge pull request #3570 from Rocky0102/main 2023-12-27 01:24:44 +08:00
ChatGPTNextWeb
bb3cc2c821 Update CN web app link in README.md 2023-12-26 12:49:11 +08:00
ChatGPTNextWeb
0b814eff83 Update Web App link in README.md 2023-12-26 12:47:25 +08:00
H0llyW00dzZ
a91ac91977 Fix [UI/UX] [Emoji] emoji URL (#3647) 2023-12-26 03:44:40 +08:00
Fred Liang
422d70d928 chore: update auth value logic (#3630) 2023-12-25 10:25:43 +08:00
Fred Liang
a4cb859472 Merge pull request #3629 from fredliang44/main
fix: return bearer header when using openai
2023-12-25 10:02:17 +08:00
Fred Liang
19137b79bc fix: return bearer header when using openai 2023-12-25 09:57:40 +08:00
Fred Liang
bd1e311674 Merge pull request #3626 from fredliang44/main
fix: removing `bearer` from auth header when using azure
2023-12-25 05:26:55 +08:00
Fred Liang
c127db335a Merge branch 'ChatGPTNextWeb:main' into main 2023-12-25 05:24:58 +08:00
Fred Liang
f5ed1604aa fix: fix removing bearer header 2023-12-25 05:24:20 +08:00
Fred Liang
771cc9e583 Merge pull request #3625 from fredliang44/main
fix: fix auth key selection when using different model
2023-12-25 05:13:38 +08:00
Fred Liang
7ad1e246a0 Merge branch 'ChatGPTNextWeb:main' into main 2023-12-25 05:12:57 +08:00
Fred Liang
5c638251f8 fix: fix using different model 2023-12-25 05:12:21 +08:00
Fred Liang
0d189dc78e Merge pull request #3622 from fredliang44/main
chore: auto concat messages for gemini-pro
2023-12-25 04:39:03 +08:00
Fred Liang
a9d7253143 Merge branch 'ChatGPTNextWeb:main' into main 2023-12-25 04:38:11 +08:00
Fred Liang
199f29e63c chore: auto concat messages 2023-12-25 04:33:47 +08:00
Fred Liang
c82efceae0 Merge pull request #3620 from fredliang44/main
fix: fix issue cannot using gemini-pro with code auth
2023-12-25 04:09:53 +08:00
Fred Liang
cad461b121 chore: remove console log 2023-12-25 04:09:38 +08:00
Fred Liang
5af68ac545 fix: fix issue https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/issues/3616 2023-12-25 04:07:35 +08:00
Fred Liang
c0ce70ccc7 Merge pull request #3617 from H0llyW00dzZ/fixfixbug
Fix [Bug] Issue ChatGPTNextWeb#3615
2023-12-25 03:47:16 +08:00
Fred Liang
753c518d33 chore: update how to identify google model 2023-12-25 03:46:35 +08:00
Fred Liang
b9ca6694f7 Merge pull request #3619 from H0llyW00dzZ/fix_llm_issue 2023-12-25 03:32:02 +08:00
H0llyW00dzZ
0c116251b1 Revert "Fix [TypesScript] [LLM Api] Chaining Model"
This reverts commit 8ca525dc7a.

Reason: It's suddenly stopped lmao
2023-12-25 01:45:24 +07:00
H0llyW00dzZ
e9def2cdc5 Refactor [Model] [LLM Api] Chaining Model
- [+] refactor(model.ts): change forEach loop to arrow function for readability and consistency
- [+] fix(model.ts): mark 'provider' property as optional in modelTable type
- [+] fix(model.ts): use optional chaining when assigning provider property in modelTable
2023-12-25 01:12:44 +07:00
H0llyW00dzZ
8ca525dc7a Fix [TypesScript] [LLM Api] Chaining Model
- [+] fix(api.ts): make provider property optional in LLMModel interface
2023-12-25 01:12:44 +07:00
H0llyW00dzZ
281fe6927a Feat [Server Side] Google Api Configuration
- [+] feat(server.ts): add support for Google API configuration variables
2023-12-25 00:37:49 +07:00
H0llyW00dzZ
35471a41c8 Fix & Feat [Auth] Api Key Variable
- [+] fix(auth.ts): fix variable name from serverApiKey to systemApiKey
- [+] feat(auth.ts): add support for Google API key in addition to Azure API key
2023-12-25 00:37:48 +07:00
ChatGPTNextWeb
bda3098a31 Merge pull request #3609 from fredliang44/main
[Feature] support Gemini Pro API
2023-12-24 18:42:41 +08:00
Fred Liang
1e05eb1d60 chore: update docs for gemini pro 2023-12-24 17:54:33 +08:00
Fred Liang
6369b902bf chore: update README for google gemini pro model 2023-12-24 17:43:41 +08:00
Fred Liang
aea794e522 chore: remove openai log from google api handler 2023-12-24 17:37:07 +08:00
Fred Liang
7c3dfb7bae feat: support custom gemini pro params 2023-12-24 17:24:04 +08:00
Fred Liang
75057f9a91 chroe: support error reason for gemini pro 2023-12-24 04:32:25 +08:00
Fred Liang
7026bd926a chroe: update prompts 2023-12-24 04:24:38 +08:00
Fred Liang
ae19a0dc5f chroe: update model name 2023-12-24 04:22:12 +08:00
Fred Liang
7d9a2132cb fix: fix server token fetch policy 2023-12-24 03:57:30 +08:00
Fred Liang
20f2f61349 feat: support set api key from client side 2023-12-24 03:44:13 +08:00
Fred Liang
4169431f2c fix: fix add api auth 2023-12-24 03:05:23 +08:00
Fred Liang
45798f993d fix: fix type errors 2023-12-24 02:39:13 +08:00
Fred Liang
ae0d68c27e fix: fix llm models field 2023-12-24 02:39:13 +08:00
Fred Liang
070b191ffe Merge branch 'ChatGPTNextWeb:main' into main 2023-12-24 02:16:51 +08:00
Fred Liang
778e88cb56 chore: resolve conflict 2023-12-24 02:15:30 +08:00
Yifei Zhang
d1fcfa05db Merge pull request #3574 from RoboticsAndCloud/main
Update README.md
2023-12-23 23:56:35 +08:00
ChatGPTNextWeb
f4754dc5fc Merge pull request #3588 from qiuqfang/main
chore: update readme
2023-12-23 23:31:23 +08:00
ChatGPTNextWeb
0f885e73c3 Merge pull request #3607 from fredliang44/main
chore: update product name from`ChatGPT-Next-Web` to `NextChat` for legal concerns of the ChatGPT trademark. The repo name will not be changed for now.
2023-12-23 23:29:18 +08:00
Fred Liang
75acd4c1aa chore: append previous name 2023-12-23 22:21:27 +08:00
Fred Liang
3ef0621eb0 refactor: update product name 2023-12-23 16:48:16 +08:00
GH Action - Upstream Sync
b1db9ead0d Merge branch 'main' of https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web 2023-12-23 00:41:51 +00:00
Yifei Zhang
9cc6000d89 Merge pull request #3601 from zeabur/main
docs: add deploy to Zeabur button in README
2023-12-22 14:59:36 +08:00
Yuhang
42515899d9 docs: Add deploy to Zeabur button in REAME for Chinese 2023-12-22 14:58:30 +08:00
Yuhang
1d096eeb17 docs: Add deploy to Zeabur button in README 2023-12-22 14:57:54 +08:00
pdx
bc5b8f0ff9 Update README.md 2023-12-20 13:55:53 +08:00
pdx
bf412fd309 Update README.md 2023-12-20 13:55:22 +08:00
Fei
6e1f4246b8 Merge branch 'ChatGPTNextWeb:main' into main 2023-12-19 11:27:38 -08:00
Yifei Zhang
ed88619623 Merge pull request #3565 from ChatGPTNextWeb/dependabot/npm_and_yarn/hello-pangea/dnd-16.5.0
chore(deps): bump @hello-pangea/dnd from 16.3.0 to 16.5.0
2023-12-19 15:33:28 +08:00
Yifei Zhang
37b8922348 Merge pull request #3566 from ChatGPTNextWeb/dependabot/npm_and_yarn/tauri-apps/cli-1.5.8
chore(deps-dev): bump @tauri-apps/cli from 1.4.0 to 1.5.8
2023-12-19 15:33:19 +08:00
Yifei Zhang
4a23a0064b Merge pull request #3567 from ChatGPTNextWeb/dependabot/npm_and_yarn/fuse.js-7.0.0
chore(deps): bump fuse.js from 6.6.2 to 7.0.0
2023-12-19 15:33:10 +08:00
Yifei Zhang
e012b1e5d1 Merge pull request #3061 from ChatGPTNextWeb/dependabot/npm_and_yarn/babel/traverse-7.23.2
chore(deps): bump @babel/traverse from 7.21.3 to 7.23.2
2023-12-19 15:32:55 +08:00
Fei
2bc84ec908 Update README.md 2023-12-18 22:43:59 -06:00
Rocky
8efe26ed57 Merge pull request #1 from Rocky0102/Rocky0102-patch-proper-accept-header
Update api.ts to set proper header "Accept"
2023-12-18 20:36:59 +08:00
Rocky
24d7dc9bf9 Update api.ts to set proper header "Accept"
So it can work fine for some GPT forward service.
2023-12-18 20:34:37 +08:00
dependabot[bot]
4b7139d9ae chore(deps): bump fuse.js from 6.6.2 to 7.0.0
Bumps [fuse.js](https://github.com/krisk/Fuse) from 6.6.2 to 7.0.0.
- [Release notes](https://github.com/krisk/Fuse/releases)
- [Changelog](https://github.com/krisk/Fuse/blob/main/CHANGELOG.md)
- [Commits](https://github.com/krisk/Fuse/compare/v6.6.2...v7.0.0)

---
updated-dependencies:
- dependency-name: fuse.js
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 10:30:29 +00:00
dependabot[bot]
1f35624121 chore(deps-dev): bump @tauri-apps/cli from 1.4.0 to 1.5.8
Bumps [@tauri-apps/cli](https://github.com/tauri-apps/tauri) from 1.4.0 to 1.5.8.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/@tauri-apps/cli-v1.4.0...@tauri-apps/cli-v1.5.8)

---
updated-dependencies:
- dependency-name: "@tauri-apps/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 10:30:04 +00:00
dependabot[bot]
860c5b8f20 chore(deps): bump @hello-pangea/dnd from 16.3.0 to 16.5.0
Bumps [@hello-pangea/dnd](https://github.com/hello-pangea/dnd) from 16.3.0 to 16.5.0.
- [Release notes](https://github.com/hello-pangea/dnd/releases)
- [Changelog](https://github.com/hello-pangea/dnd/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hello-pangea/dnd/compare/v16.3.0...v16.5.0)

---
updated-dependencies:
- dependency-name: "@hello-pangea/dnd"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-18 10:29:13 +00:00
Yifei Zhang
a454c39ca0 Merge pull request #3511 from KevenLi8888/add-dockerignore
ci: add .dockerignore to exclude env files to prevent potential key leakages 避免潜在的密钥泄漏
2023-12-18 15:26:32 +08:00
Yifei Zhang
6938910f08 Merge pull request #3559 from RoboticsAndCloud/patch-1
Update README.md
2023-12-18 15:25:49 +08:00
Fei
2c63dde6c5 Update README.md 2023-12-17 21:24:36 -06:00
Yifei Zhang
e69d20a209 Merge pull request #3538 from MCheping8108/main
fix sync.yml
2023-12-18 01:16:54 +08:00
和平peaceful
0b731edd21 fix sync.yml 2023-12-15 21:24:37 +08:00
Yifei Zhang
efdd61595e Merge pull request #3535 from ChatGPTNextWeb/Yidadaa-patch-1
Update README_CN.md
2023-12-15 17:47:47 +08:00
Yifei Zhang
d676f88fed Update README_CN.md 2023-12-15 17:47:36 +08:00
Yifei Zhang
07f2cf7ad0 Merge pull request #3532 from ChatGPTNextWebTeam/update-emoji-url
fix: update emoji url
2023-12-15 15:20:15 +08:00
Yifei Zhang
bcb520ed3b Update emoji.tsx 2023-12-15 15:18:02 +08:00
Qian (Keven) Li
93cee18300 ci: add .dockerignore to exclude env files 2023-12-12 02:26:49 -05:00
Yifei Zhang
cae4655785 Update tauri.conf.json 2023-12-11 15:59:37 +08:00
Yifei Zhang
28c12606a4 Merge pull request #3455 from H0llyW00dzZ/trimtopic 2023-12-06 18:58:38 +08:00
H0llyW00dzZ
54df355014 Fix Trim Topic in Indonesia Language
- [+] fix(utils.ts): fix trimTopic function to remove double quotes from the start and end of the string, and remove specified punctuation from the end of the string
2023-12-05 07:26:11 +07:00
Yifei Zhang
cf50299b14 Update README_CN.md 2023-11-29 11:39:21 +08:00
Yifei Zhang
2c12be62c4 Merge pull request #3383 from H0llyW00dzZ/auth 2023-11-27 15:51:27 +08:00
Yifei Zhang
4636a75b5e Merge pull request #3382 from H0llyW00dzZ/docs 2023-11-27 15:47:39 +08:00
Yifei Zhang
03756e364a Merge pull request #3372 from H0llyW00dzZ/fixserverside 2023-11-27 15:47:24 +08:00
Yifei Zhang
ce1715c79e Merge pull request #3369 from H0llyW00dzZ/chatz 2023-11-27 15:47:01 +08:00
Yifei Zhang
a62ab3c649 Merge pull request #3354 from AnYiEE/fix-type 2023-11-27 15:45:42 +08:00
Yifei Zhang
bfb7b988f4 Merge pull request #3356 from durianice/pr_merge 2023-11-27 15:44:03 +08:00
Yifei Zhang
84f41262f5 Update README.md 2023-11-27 15:28:34 +08:00
H0llyW00dzZ
dda40e29f4 Fix & Refactor UI/UX Page [Settings]
- [+] fix(settings.tsx): fix conditional rendering of ListItem based on clientConfig.isApp
- [+] refactor(settings.tsx): improve readability of conditional rendering code
2023-11-27 08:07:53 +07:00
H0llyW00dzZ
7df868e22a Revert "Fix & Refactor UI/UX Page [Auth]"
This reverts commit d76e744eab.

Reason: Move to commits bf5e7aaa48
2023-11-27 07:12:57 +07:00
H0llyW00dzZ
bf5e7aaa48 Feat UI/UX Page [Settings]
- [+] feat(settings.tsx): set useCustomConfig to true if clientConfig.isApp is truthy
2023-11-27 07:03:41 +07:00
H0llyW00dzZ
d76e744eab Fix & Refactor UI/UX Page [Auth]
- [+] fix(auth.tsx): fix condition to set custom endpoint to true if it's app
- [+] refactor(auth.tsx): update accessStore to set useCustomConfig to true
2023-11-27 05:44:26 +07:00
H0llyW00dzZ
6f5699fe09 Chore Docs (README.md)
- [+] chore(README.md): add support for Korean and Indonesian languages
2023-11-27 04:49:15 +07:00
H0llyW00dzZ
f9d916925e Fixup Api Common [Server Side] [Console Log]
- [+] fix(common.ts): fix condition to check if serverConfig.openaiOrgId is not undefined
2023-11-25 10:23:16 +07:00
H0llyW00dzZ
f9258878db Fix Api Common [Server Side] [Console Log]
- [+] fix(common.ts): fix console.log statement for [Org ID] to handle undefined value
2023-11-25 10:10:06 +07:00
H0llyW00dzZ
ef9e86b50d Fix UI/UX Page Chats [Memory Prompt] [Stored Local Storage]
- [+] fix(chat.ts): update the memory prompt in onFinish callback
- [+] feat(chat.ts): update the current session with lastSummarizeIndex and memoryPrompt
2023-11-25 05:03:34 +07:00
Yifei Zhang
b21931c667 Update README.md 2023-11-24 11:36:14 +08:00
Yifei Zhang
06de3f5e69 Update README.md 2023-11-24 10:33:33 +08:00
durian
261a8fd83b fix(CUSTOM_MODELS):#3349 Dependency chatStore otherwise the session and view will not updated 2023-11-23 15:37:57 +08:00
安忆
6527074cde fix(locales): type error in pt.ts 2023-11-23 11:02:20 +08:00
Yidadaa
f2485931d9 feat: better animation speed 2023-11-19 19:15:11 +08:00
Yifei Zhang
4f8a0b7711 Merge pull request #3308 from yuri-filipe/main 2023-11-19 18:45:31 +08:00
Yifei Zhang
2dde55050e Merge pull request #3311 from Yidadaa/bugfix-1119 2023-11-19 18:44:14 +08:00
Yidadaa
6aade62ce2 feat: close #3300 support multiple api keys 2023-11-19 18:42:30 +08:00
Yidadaa
45b88ebb2a feat: close #3304 use = instead of : to map model name in CUSTOM_MODELS 2023-11-19 18:34:39 +08:00
Yidadaa
dc7159a450 feat: close #3301 enable or diable default models with -all / +all 2023-11-19 18:32:54 +08:00
Yidadaa
536ace8e10 feat: animate streaming response to make more smooth 2023-11-19 18:24:51 +08:00
Yifei Zhang
16b2a3e66e Update vercel.json 2023-11-19 04:23:25 +08:00
Yuri Filipe
6f135a0cce Translation of the application into Brazilian Portuguese 2023-11-18 15:12:15 -03:00
Yifei Zhang
cf220dd2eb Update vercel.json 2023-11-17 15:22:11 +08:00
Yifei Zhang
914f4fb862 Update vercel.json 2023-11-17 15:11:03 +08:00
Yifei Zhang
7bdb68eecf config: disable ip forward in vercel json config 2023-11-17 15:10:51 +08:00
Yifei Zhang
3c510cfaf0 chore: update gh actions nodejs version to 18 2023-11-17 11:56:42 +08:00
Yifei Zhang
401fa198c9 Update tauri.conf.json 2023-11-17 11:29:52 +08:00
Yifei Zhang
600df4f63a Merge pull request #3290 from Eric-2369/main 2023-11-17 11:26:21 +08:00
Eric-2369
74eb42c111 config: exclude hkg1 from the deployment regions of the edge function 2023-11-16 11:33:10 +08:00
Yidadaa
9876a1aeca fix: #3275 refuse on server side if hide user api key 2023-11-16 00:53:11 +08:00
Yifei Zhang
d898ffce23 Merge pull request #3261 from H0llyW00dzZ/clients 2023-11-14 16:07:16 +08:00
H0llyW00dzZ
f1772f4625 Fix UI/UX Page [Settings]
[+] fix(settings.tsx): fix condition to show balance query in settings component
[+] fix(settings.tsx): add condition to hide balance query for app clients
2023-11-14 15:04:57 +07:00
Yifei Zhang
9da455a7ea Merge pull request #3248 from Yidadaa/dependabot/npm_and_yarn/nanoid-5.0.3 2023-11-14 01:24:10 +08:00
Yifei Zhang
2bd6342309 Merge pull request #3249 from Yidadaa/dependabot/npm_and_yarn/emoji-picker-react-4.5.15 2023-11-14 01:23:50 +08:00
Yifei Zhang
5e73577088 Merge pull request #3250 from Yidadaa/dependabot/npm_and_yarn/types/node-20.9.0 2023-11-14 01:23:37 +08:00
Yifei Zhang
5156a80738 Merge pull request #3251 from Yidadaa/dependabot/npm_and_yarn/types/spark-md5-3.0.4 2023-11-14 01:23:17 +08:00
dependabot[bot]
a9d605ed30 chore(deps): bump nanoid from 4.0.2 to 5.0.3
Bumps [nanoid](https://github.com/ai/nanoid) from 4.0.2 to 5.0.3.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/4.0.2...5.0.3)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 17:22:51 +00:00
dependabot[bot]
7d1fae32cd chore(deps): bump emoji-picker-react from 4.5.1 to 4.5.15
Bumps [emoji-picker-react](https://github.com/ealush/emoji-picker-react) from 4.5.1 to 4.5.15.
- [Release notes](https://github.com/ealush/emoji-picker-react/releases)
- [Commits](https://github.com/ealush/emoji-picker-react/commits)

---
updated-dependencies:
- dependency-name: emoji-picker-react
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 17:22:50 +00:00
Yifei Zhang
9f17b45bc4 Merge pull request #3252 from Yidadaa/dependabot/npm_and_yarn/mermaid-10.6.1 2023-11-14 01:21:11 +08:00
dependabot[bot]
a64c9dae42 chore(deps): bump mermaid from 10.3.1 to 10.6.1
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.3.1 to 10.6.1.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Changelog](https://github.com/mermaid-js/mermaid/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/mermaid-js/mermaid/compare/v10.3.1...v10.6.1)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 10:45:59 +00:00
dependabot[bot]
5fbf4c394c chore(deps-dev): bump @types/spark-md5 from 3.0.2 to 3.0.4
Bumps [@types/spark-md5](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/spark-md5) from 3.0.2 to 3.0.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/spark-md5)

---
updated-dependencies:
- dependency-name: "@types/spark-md5"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 10:45:47 +00:00
dependabot[bot]
1e5153173c chore(deps-dev): bump @types/node from 20.3.3 to 20.9.0
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.3.3 to 20.9.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-13 10:45:37 +00:00
Yifei Zhang
011b52d07d Update README.md 2023-11-13 16:53:36 +08:00
Yifei Zhang
d033168d80 fix: #3241 should not ensure openai url non-empty 2023-11-13 10:53:30 +08:00
Yifei Zhang
fdca9e59de Merge pull request #3233 from nanaya-tachibana/nanaya/model-selection 2023-11-13 10:44:03 +08:00
Yifei Zhang
549a2fd206 Merge pull request #3237 from Yidadaa/latex 2023-11-12 19:47:26 +08:00
Yidadaa
a0cd939bfd fix: #2841 dollar sign conflict with latex math 2023-11-12 19:45:58 +08:00
Yifei Zhang
4f52679ec6 Merge pull request #3236 from Yidadaa/latex 2023-11-12 19:35:22 +08:00
Yidadaa
b52e237044 fix: #3189 should correct math eq in exporter 2023-11-12 19:33:19 +08:00
Yifei Zhang
17b329bb63 Merge pull request #3232 from H0llyW00dzZ/language 2023-11-12 18:00:02 +08:00
nanaya
3a654ba199 UI (model selection): hide unavailable model options 2023-11-12 11:18:14 +08:00
H0llyW00dzZ
5ba3fc9321 Revert "Fix UI/UX Page Local Language [Exporter Message]"
This reverts commit a46f08154e.
Reason : better after instead of before
2023-11-12 01:56:31 +07:00
H0llyW00dzZ
a46f08154e Fix UI/UX Page Local Language [Exporter Message]
fix(locales): fix incorrect description title in en.ts and id.ts
 - Change "Only messages after clearing the context will be displayed" to "Only messages before clearing the context will be displayed" in en.ts
 - Change "Hanya pesan setelah menghapus konteks yang akan ditampilkan" to "Hanya pesan sebelum menghapus konteks yang akan ditampilkan" in id.ts
2023-11-12 01:50:35 +07:00
H0llyW00dzZ
0f6ed9c293 Feat UI/UX Page Local Language [Exporter Message]
[+] fix(exporter.tsx): update the text in the ExportMessageModal component to use the localized title from the locale file
[+] feat(cn.ts, en.ts, id.ts): add localized title for the Exporter Description in the respective locale files
2023-11-12 00:53:15 +07:00
Yifei Zhang
295864d36b Merge pull request #3231 from Yidadaa/bugfix-1112 2023-11-12 01:23:56 +08:00
Yidadaa
be6d45e49f feat: close #3222 share message list should start from clear context index 2023-11-12 01:21:39 +08:00
Yifei Zhang
22b6987249 Merge pull request #3230 from Yidadaa/bugfix-1112 2023-11-12 00:52:21 +08:00
Yidadaa
64647b0bb3 chore: update doc for mapped CUSTOM_MODELS 2023-11-12 00:49:58 +08:00
Yidadaa
a5a1f2e8ad feat: CUSTOM_MODELS support mapper 2023-11-12 00:46:21 +08:00
Yifei Zhang
8bd39f33e0 Merge pull request #3229 from Yidadaa/bugfix-1112 2023-11-12 00:31:15 +08:00
Yidadaa
be9774943b feat: #3224 auto switch to first avaliable model 2023-11-12 00:29:36 +08:00
akira-cn
dd6e79922a fix: remove the content-encoding header 2023-11-10 20:42:12 +08:00
akira-cn
bf84269520 fix: remove the content-encoding header 2023-11-10 20:38:53 +08:00
Yifei Zhang
943214c6a7 Update README_CN.md 2023-11-10 18:21:22 +08:00
Yifei Zhang
ca792669fc Update constant.ts 2023-11-10 18:20:51 +08:00
Yifei Zhang
78d68a9949 Merge pull request #3214 from Ensteinjun/main 2023-11-10 15:58:28 +08:00
Ensteinjun
6b2db97347 fixbug 2023-11-10 15:44:07 +08:00
Yifei Zhang
2bfb362832 Update tauri.conf.json 2023-11-10 15:19:31 +08:00
Yidadaa
cb140e482f fix: #3207 ensure corner case 2023-11-10 15:15:43 +08:00
Yifei Zhang
e6b72ac1ff Update README.md 2023-11-10 02:59:47 +08:00
Yifei Zhang
8032e6d68d Update README.md 2023-11-10 02:59:30 +08:00
Yidadaa
c7e0a6f37f doc: update azure env vars 2023-11-10 02:57:46 +08:00
Yifei Zhang
1141cd2e6e Merge pull request #3206 from Yidadaa/azure 2023-11-10 02:55:12 +08:00
Yidadaa
c9dd953817 fixup 2023-11-10 02:50:50 +08:00
Yidadaa
b7ffca031e feat: close #935 add azure support 2023-11-10 02:43:37 +08:00
Yifei Zhang
fd2f441e02 feat: wont send max_tokens 2023-11-09 20:45:25 +08:00
Yifei Zhang
3b3ebda34b fix: #3196 3.5-turbo-1106 should use old cutoff date 2023-11-09 11:01:41 +08:00
Yifei Zhang
87e3d663a2 fix: #3192 use smaller max_tokens as default 2023-11-09 11:00:09 +08:00
Yidadaa
d0a1d910d4 fix: #3186 enable max_tokens in chat payload 2023-11-09 03:19:13 +08:00
Yifei Zhang
33b97082fa Merge pull request #3192 from Yidadaa/bugfix-1109 2023-11-09 03:05:41 +08:00
Yidadaa
d93f05f511 feat: close #3187 use CUSTOM_MODELS to control model list 2023-11-09 03:01:29 +08:00
Yifei Zhang
3dc29197e1 Merge pull request #3191 from Yidadaa/bugfix-1109 2023-11-09 02:23:20 +08:00
Yidadaa
fbc0236748 fix: #3174 should prompt to confirm to delete chat 2023-11-09 02:03:05 +08:00
Yifei Zhang
1304a3943a Merge pull request #3190 from Yidadaa/bugfix-1109 2023-11-09 01:54:09 +08:00
Yidadaa
8c0ba1aee2 feat: close #2954 chat summary should be copyable 2023-11-09 01:51:33 +08:00
Yifei Zhang
47154773f2 Merge pull request #3181 from AnYiEE/docs-typo 2023-11-08 18:37:59 +08:00
Yifei Zhang
deca9bf06d Merge pull request #3183 from hekbobo/patch-1 2023-11-08 18:37:35 +08:00
bob
6ab4c9be2e Update cloudflare-pages-cn.md
cloudflare 构建命令
2023-11-08 17:40:28 +08:00
安忆
3a519612a0 docs: fix typo in #3171 2023-11-08 15:53:45 +08:00
Yifei Zhang
d1ec26ae83 Merge pull request #3171 from AnYiEE/docs-update 2023-11-08 15:31:17 +08:00
安忆
9cb889c34f docs: up to date 2023-11-08 07:09:52 +08:00
Yifei Zhang
77f27b7948 Merge pull request #3166 from Yidadaa/bugfix-1107 2023-11-08 01:08:00 +08:00
Yifei Zhang
dbee5bc515 Update tauri.conf.json 2023-11-08 01:05:07 +08:00
Yidadaa
54a5332834 feat: sort model by name 2023-11-08 01:04:20 +08:00
Yifei Zhang
8af164c99f Merge pull request #3165 from Yidadaa/bugfix-1107 2023-11-08 00:36:09 +08:00
Yidadaa
c7321fddfb fixup 2023-11-08 00:34:31 +08:00
Yidadaa
c5ca278253 feat: close #2908 allow to disable parse settings from link 2023-11-08 00:30:02 +08:00
Yidadaa
638fdd8c3e feat: default disable balance query 2023-11-08 00:20:34 +08:00
Yifei Zhang
3407b57a51 Merge pull request #3163 from Yidadaa/bugfix-1107 2023-11-07 23:56:29 +08:00
Yidadaa
836bf836d3 fix: #3152 system prompt should be injected 2023-11-07 23:54:30 +08:00
Yidadaa
6e52d14180 fix: #3016 disable sidebar transition on ios 2023-11-07 23:30:09 +08:00
Yifei Zhang
1096fe55e1 Merge pull request #3143 from H0llyW00dzZ/newmodels 2023-11-07 10:58:40 +08:00
H0llyW00dzZ
d2d615c84a Refactor KnowledgeCutoff
[+] fix(constant.ts): update DEFAULT_SYSTEM_TEMPLATE to include knowledgeCutoff and time variables
[+] feat(chat.ts): add support for injecting system prompts based on model configuration
2023-11-07 06:42:55 +07:00
H0llyW00dzZ
9f26c8cecb Fix Typo [GPT-4]
[+] fix(constant.ts): fix typo in model name, change "ggpt-4-1106-preview" to "gpt-4-1106-preview"
2023-11-07 06:33:07 +07:00
H0llyW00dzZ
cd1f082c52 Feat Models [GPT-4] [GPT-3.5] [128K context]
[+] feat(constant.ts): add new models to DEFAULT_MODELS array
2023-11-07 06:08:06 +07:00
Yifei Zhang
b90dfb48ee Merge pull request #3095 from Yidadaa/revert-3013-main 2023-10-26 18:34:44 +08:00
Yifei Zhang
70e67a686b Revert "fix serviceWorker cache: auto caching files by workbox" 2023-10-26 00:06:09 +08:00
Yifei Zhang
d1c3d900ab Update README_KO.md 2023-10-23 14:38:26 +08:00
Yifei Zhang
e8a4ad1001 Update README_KO.md 2023-10-23 14:38:04 +08:00
Yifei Zhang
ccac85b079 Update README_CN.md 2023-10-23 14:37:48 +08:00
Yifei Zhang
f92fefb6cd Update synchronise-chat-logs-cn.md 2023-10-23 14:36:42 +08:00
dependabot[bot]
b799213a14 chore(deps): bump @babel/traverse from 7.21.3 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.3 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 23:52:00 +00:00
Yifei Zhang
cc565cf57e Merge pull request #3013 from jessegpt/main 2023-10-16 12:02:26 +08:00
Yifei Zhang
4a56e9c884 Merge pull request #3019 from eltociear/patch-2 2023-10-16 12:02:04 +08:00
Yifei Zhang
d11d906fa2 Merge pull request #3033 from Yidadaa/Yidadaa-patch-1 2023-10-16 11:58:36 +08:00
Yifei Zhang
65c4a0c319 feat: close #3031 user can set larger font size 2023-10-16 11:52:45 +08:00
Yifei Zhang
55bcf78efe Update README.md 2023-10-15 20:08:08 +08:00
Ikko Eltociear Ashimine
f5a2ce52aa Fix typo in README.md
passsword -> password
2023-10-14 15:22:41 +09:00
Jesse
0dcbfd746e fix serviceWorker cache 2023-10-13 15:41:00 +04:00
Yifei Zhang
06e043d3f1 Merge pull request #3003 from suravshrestha/patch1 2023-10-13 00:41:16 +08:00
Surav Shrestha
986d34fb3e docs: fix typo in app/masks/en.ts 2023-10-12 21:22:35 +05:45
Yifei Zhang
5296ab1b09 Merge pull request #2996 from MCheping8108/main 2023-10-12 18:22:52 +08:00
mcheping520
b5d3348d99 已拆分单个文件、翻译多国语言版本 2023-10-12 18:17:38 +08:00
Yifei Zhang
1e77df381a Update constant.ts 2023-10-12 17:31:29 +08:00
mcheping520
ec33281ff5 提供同步聊天记录教程,同时翻译多国语言 2023-10-11 21:29:50 +08:00
Yifei Zhang
ba67f13ad5 Merge pull request #2993 from H0llyW00dzZ/masks 2023-10-11 11:42:45 +08:00
H0llyW00dzZ
1604ed91fb Merge branch 'Yidadaa:main' into masks 2023-10-11 07:35:40 +07:00
H0llyW00dzZ
74fcaab5e9 Refactor Mask UI Page [Masks]
[+] refactor(mask.tsx): refactor simple search to be case-insensitive
2023-10-11 04:36:04 +07:00
Yifei Zhang
adee435ea6 Merge pull request #2992 from ograycode/patch-1 2023-10-11 00:42:52 +08:00
Jason O'Gray
ea59ab5176 Fix type in sync.ts
Simple typo fix.
2023-10-10 11:24:45 -04:00
Yifei Zhang
f78008c1b2 Merge pull request #2983 from Yidadaa/Yidadaa-patch-1 2023-10-10 11:56:24 +08:00
Yifei Zhang
f54db695af fix: #2981 full screen button not works 2023-10-10 11:54:25 +08:00
Yifei Zhang
f21f922160 Update tauri.conf.json 2023-10-09 22:46:36 +08:00
Yifei Zhang
fd413c7b52 Merge pull request #2947 from KeithHello/main 2023-10-09 22:45:56 +08:00
Yifei Zhang
3e2c5af4b5 Merge pull request #2975 from luckykong/patch-1 2023-10-09 22:38:45 +08:00
Kong Gaowen
bdb49b1171 修改界面
似乎这里引入了一个逻辑判断错误,会导致打包之后的界面变宽变高。
2023-10-09 18:50:10 +08:00
GH Action - Upstream Sync
5933b3d7eb Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-10-08 00:19:29 +00:00
Yifei Zhang
4c8d606fae Merge pull request #2950 from PeterDaveHello/locale-tw 2023-10-08 00:20:39 +08:00
Yifei Zhang
13c1d2fd2b Merge pull request #2946 from H0llyW00dzZ/ClientApp 2023-10-07 23:50:49 +08:00
Yifei Zhang
e35c807216 Update tauri.conf.json 2023-10-07 23:48:50 +08:00
Yifei Zhang
5a2cc6f154 Merge pull request #2927 from lifeeric/MACOS 2023-10-07 23:10:43 +08:00
Peter Dave Hello
88f8c43472 Improve tw locale 2023-10-07 23:06:20 +08:00
Yifei Zhang
ef3e8e6fac Merge pull request #2933 from H0llyW00dzZ/authpage 2023-10-07 23:03:56 +08:00
KeithHello
1505372e20 Change log
-   config.ts
    -   line 72: remove type confirmation of x as x always has type 'number'
    -   line 135: remove the redundant local variable
-   chat.ts
    -   delete the unused import
2023-10-04 18:08:29 +09:00
KeithHello
ad5093ce05 Change log
-   config.ts
    -   line 72 remove type confirmation of x as x always has type 'number'
    -   line 135 remove redundant local variable
-   chat.ts
    -   delete unused import
2023-10-04 17:40:23 +09:00
H0llyW00dzZ
b558d1afc6 Feat & Fix "Client App [Notification]"
[+] feat(update.ts): add support for localization in update notifications
[+] fix(update.ts): add missing semicolon in useUpdateStore function
2023-10-04 02:10:26 +07:00
H0llyW00dzZ
ddfd05b008 Fix & Feat Client App [Notification]
[+] fix(update.ts): remove unnecessary notification sending when permission is not granted
[+] feat(update.ts): add notification for already up to date version
2023-10-03 09:12:41 +07:00
H0llyW00dzZ
d2ad01a9ff Client App Fix Issue [Bug] 'export' button does not work #2884
[+] fix(exporter.tsx): add async keyword to download function
[+] feat(exporter.tsx): add support for saving image file using window.__TAURI__ API
[+] feat(global.d.ts): add types for window.__TAURI__ API methods
[+] feat(locales): add translations for download success and failure messages
[+] feat(sync.ts): add support for generating backup file name with date and time
[+] fix(utils.ts): add async keyword to downloadAs function and add support for saving file using window.__TAURI__ API
2023-10-03 08:49:03 +07:00
H0llyW00dzZ
64a17abfe2 Client App [Notification]
[+] feat(global.d.ts): add support for window.__TAURI__.notification methods
[+] feat(update.ts): add notification for new version availability
[+] fix(Cargo.toml): add tauri feature "notification-all" to enable notifications
[+] fix(tauri.conf.json): enable all notification features in tauri configuration
2023-10-03 08:08:11 +07:00
H0llyW00dzZ
04b638aa06 Fix & Refactor UI Page [Auth Page]
[+] fix(auth.tsx): fix conditional rendering of token input field
[+] refactor(auth.tsx): improve code readability by using conditional rendering for token input field
2023-10-01 00:23:19 +07:00
H0llyW00dzZ
31e30906d0 Refactor Locale Indonesia
[+] refactor(id.ts): remove unused import and isApp variable
[+] fix(id.ts): update Unauthorized error message
2023-09-30 22:59:24 +07:00
H0llyW00dzZ
bc00be9065 Feat & Fix UI Page [Auth Page]
[+] feat(auth.tsx): add goChat function to navigate to chat page
[+] fix(auth.tsx): change onClick event from goHome to goChat
2023-09-30 22:33:18 +07:00
H0llyW00dzZ
4a599e986f UI Page [Auth Page]
[+] feat(auth.tsx): add support for resetting access token in resetAccessCode function
[+] fix(auth.tsx): fix formatting issue in resetAccessCode function
[+] feat(locales): add support for sub tips in Auth component for multiple languages :
  - Add sub tips in Arabic locale (ar.ts)
  - Add sub tips in Bengali locale (bn.ts)
  - Add sub tips in Chinese locale (cn.ts)
  - Add sub tips in English locale (en.ts)
  - Add sub tips in Indonesian locale (id.ts)
2023-09-30 22:16:34 +07:00
EricGit
f1ca03e378 [FIXED] now it should detects all macintosh 2023-09-28 13:21:17 -04:00
Eric R
f3d5fc7a84 [FIXED] now the default key should be CMD on MacOS 2023-09-28 06:50:31 -04:00
Eric R
3bfcdf9c41 [ADDED] MacOS detect 2023-09-28 06:10:22 -04:00
Yifei Zhang
144200e315 Merge pull request #2912 from Algorithm5838/Algorithm5838-patch-1 2023-09-27 17:18:15 +08:00
Algorithm5838
398e229c77 Update chat.tsx 2023-09-26 21:49:17 +03:00
Algorithm5838
6a61fe5776 Update chat.tsx 2023-09-26 20:45:09 +03:00
Yifei Zhang
9835206452 Merge pull request #2905 from H0llyW00dzZ/ref#2895 2023-09-26 10:50:49 +08:00
H0llyW00dzZ
70b0580fb7 UI Page [sidebar]
[+] fix(sidebar.tsx): update onClick function to conditionally navigate to different paths based on config.dontShowMaskSplashScreen value

Ref : [Feature] Make the mask selection more streamlined Yidadaa#2895
2023-09-26 04:59:19 +07:00
Yifei Zhang
23eb7732d7 feat: support more http status check for webdav 2023-09-20 17:47:35 +08:00
Yifei Zhang
26e50cefea Update chat.tsx 2023-09-20 02:09:14 +08:00
Yifei Zhang
588e907181 Merge pull request #2873 from Yidadaa/bugfix-0919 2023-09-19 23:42:09 +08:00
Yidadaa
eae7d6260f fix: should not tight border in desktop app 2023-09-19 23:26:52 +08:00
Yifei Zhang
175b4e7f92 Update README_CN.md 2023-09-19 11:04:10 +08:00
Yifei Zhang
b050417ab1 Update tauri.conf.json 2023-09-19 11:03:22 +08:00
Yifei Zhang
37b49400db Update constant.ts 2023-09-19 11:03:03 +08:00
Yifei Zhang
ebcb2e7837 Merge pull request #2867 from Yidadaa/bugfix-0919 2023-09-19 03:34:45 +08:00
Yidadaa
f1e7db6a88 feat: auto fill upstash backup name 2023-09-19 03:33:17 +08:00
Yifei Zhang
2ba0929458 Merge pull request #2866 from Yidadaa/bugfix-0919 2023-09-19 03:20:37 +08:00
Yidadaa
83fed42997 feat: add upstash redis cloud sync 2023-09-19 03:18:34 +08:00
Yifei Zhang
2c4626709c Merge pull request #2865 from Yidadaa/bugfix-0919 2023-09-19 02:25:35 +08:00
Yidadaa
59fbadd9eb fixup 2023-09-19 02:21:31 +08:00
Yidadaa
adb860b464 fix: #2820 try to fix 520 error code 2023-09-19 02:12:43 +08:00
Yifei Zhang
8d8790586d Merge pull request #2864 from Yidadaa/bugfix-0919 2023-09-19 02:02:18 +08:00
Yidadaa
61ca60c550 fix: #2817 min-height for landscape orientation on mobile phone 2023-09-19 01:58:52 +08:00
Yidadaa
d713d01600 feat: close #2848 click drag icon to toggle sidebar width 2023-09-19 01:47:15 +08:00
Yifei Zhang
372ea0f845 Merge pull request #2853 from a6z6/main 2023-09-18 17:33:09 +08:00
Yifei Zhang
61888708f5 Merge pull request #2856 from koho/fix-font 2023-09-18 17:32:56 +08:00
Gerhard Tan
c900459f73 Encode google font url 2023-09-18 10:22:07 +08:00
Amor Zara
2c92f75c86 Update .env.template 2023-09-17 17:32:42 +08:00
Yifei Zhang
3e1514239c Merge pull request #2839 from a6z6/main 2023-09-15 15:17:46 +08:00
Yifei Zhang
0707a1d49a Merge pull request #2840 from Algorithm5838/Algorithm5838-patch-1 2023-09-15 15:17:35 +08:00
Amor Zara
9521f19507 Merge pull request #1 from a6z6/a6z6-patch-1
Update route.ts
2023-09-15 11:23:42 +08:00
Amor Zara
bd69116df2 Update route.ts
Correct typo error and make warning more specific.
2023-09-15 11:21:42 +08:00
Algorithm5838
6535986484 Update markdown.tsx 2023-09-15 06:06:34 +03:00
Yifei Zhang
e03db9c2d5 Merge pull request #2826 from H0llyW00dzZ/language 2023-09-14 10:49:44 +08:00
Yifei Zhang
038790370c Merge pull request #2822 from yhua1998/main 2023-09-13 22:40:03 +08:00
H0llyW00dzZ
261bf0b298 Changes "Nama" -> "User" 2023-09-13 20:49:22 +07:00
H0llyW00dzZ
5a7bdcfe59 [+] Some improvements to the Indonesian language. 2023-09-13 20:17:30 +07:00
H0llyW00dzZ
4f3261b262 [+] Some improvements to the Indonesian language. 2023-09-13 20:05:58 +07:00
yhua1998
48e6087b1b fix: The width of the sidebar changes abruptly by dragging it multiple times over and over again (bouncing) 2023-09-13 16:55:31 +08:00
yhua1998
28103c901d Refactor: sidebar drag 2023-09-13 16:24:21 +08:00
yhua1998
368701610f fix: Width changes abruptly when dragging the sidebar (jumps)
In useRef is non-responsive, we can't get the modified config.sidebarWidth dynamic modification value in handleMouseUp
2023-09-13 13:57:30 +08:00
Yifei Zhang
b589f48aa9 Update tauri.conf.json 2023-09-13 03:01:28 +08:00
Yifei Zhang
74e32efa7d Merge pull request #2819 from Yidadaa/webdav 2023-09-13 02:56:10 +08:00
Yidadaa
dc555b2206 fixup 2023-09-13 02:52:28 +08:00
Yidadaa
859cf6930f fixup 2023-09-13 02:51:57 +08:00
Yidadaa
6f83fbd212 feat: add webdav support 2023-09-13 02:51:02 +08:00
Yifei Zhang
a7be4780ba Merge pull request #2809 from kfear1337/auth-page 2023-09-12 18:14:13 +08:00
B0zal
b5e89d4440 [+] Updated Auth Page
- Made changes to the Auth Page to reset the input field for the access code when the "Later" button is clicked.
This ensures that only expected user is logged by entering access code or entering their OpenAI API Key, mitigating the risk of small bug issue
2023-09-12 07:00:23 +07:00
Yifei Zhang
89bc98d26b Merge pull request #2804 from Yidadaa/dependabot/npm_and_yarn/emoji-picker-react-4.5.1 2023-09-11 20:37:16 +08:00
Yifei Zhang
31a73273ac Merge pull request #2805 from Yidadaa/dependabot/npm_and_yarn/react-router-dom-6.15.0 2023-09-11 20:37:01 +08:00
Yifei Zhang
2a820ea6e7 Merge pull request #2808 from Yidadaa/dependabot/npm_and_yarn/eslint-8.49.0 2023-09-11 20:36:47 +08:00
Yifei Zhang
6484d4f0bd Merge pull request #2807 from Yidadaa/dependabot/npm_and_yarn/typescript-5.2.2 2023-09-11 20:36:33 +08:00
Yifei Zhang
461e48be32 Merge pull request #2799 from kfear1337/CodeQL-Report 2023-09-11 20:35:04 +08:00
dependabot[bot]
ff60ffca3e chore(deps-dev): bump eslint from 8.44.0 to 8.49.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.44.0 to 8.49.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.44.0...v8.49.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 10:32:34 +00:00
dependabot[bot]
1bbf310c46 chore(deps-dev): bump typescript from 4.9.5 to 5.2.2
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.9.5 to 5.2.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.2.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 10:32:25 +00:00
dependabot[bot]
8469f448b5 chore(deps): bump react-router-dom from 6.14.1 to 6.15.0
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.14.1 to 6.15.0.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.15.0/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 10:32:00 +00:00
dependabot[bot]
e36abc3ac6 chore(deps): bump emoji-picker-react from 4.4.8 to 4.5.1
Bumps [emoji-picker-react](https://github.com/ealush/emoji-picker-react) from 4.4.8 to 4.5.1.
- [Release notes](https://github.com/ealush/emoji-picker-react/releases)
- [Commits](https://github.com/ealush/emoji-picker-react/commits)

---
updated-dependencies:
- dependency-name: emoji-picker-react
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 10:31:50 +00:00
B0zal
605dd72354 [+] CodeQL Report Fix log injection vulnerability in useSyncStore
Severity : High
Sanitize the 'res' object before logging it in the 'check' method of useSyncStore to prevent log injection attacks.
The 'res' object is now sanitized by extracting only the necessary properties ('status', 'statusText', and 'headers') and logging the sanitized object instead.
This ensures that only safe and expected data is logged, mitigating the risk of log injection vulnerabilities.
2023-09-11 08:49:08 +07:00
Yifei Zhang
9770b65146 Merge pull request #2798 from Yidadaa/backup 2023-09-11 00:41:25 +08:00
Yidadaa
57158890c3 fixup 2023-09-11 00:39:56 +08:00
Yifei Zhang
6f8f490fdd Merge pull request #2797 from Yidadaa/backup 2023-09-11 00:36:24 +08:00
Yidadaa
415e9dc913 fixup: minor sync fixup 2023-09-11 00:34:51 +08:00
Yifei Zhang
1487762925 Merge pull request #2796 from Yidadaa/backup 2023-09-11 00:27:51 +08:00
Yidadaa
c73a91a0f5 fixup: fix type errors 2023-09-11 00:24:05 +08:00
Yidadaa
5dced28088 fixup: add en locales 2023-09-11 00:22:14 +08:00
Yidadaa
38f6956e71 feat: close #2754 add import/export to file 2023-09-11 00:20:23 +08:00
Yifei Zhang
93da9dd1f4 Merge pull request #2791 from shoito/patch-1 2023-09-10 21:47:17 +08:00
shoito
505c8cde81 improve japanese translations 2023-09-09 16:10:24 +09:00
Yifei Zhang
f7a6fa9873 Update README_CN.md 2023-09-07 17:43:17 +08:00
Yifei Zhang
70a2d11b8c Merge pull request #2781 from rickyrobinett/main 2023-09-07 10:40:26 +08:00
Ricky Robinett
2c077aca5a fix cloudflare deployment instructions 2023-09-06 15:36:12 -04:00
Yifei Zhang
efef6b098d Merge pull request #2764 from Yidadaa/bugfix-0905 2023-09-05 02:10:53 +08:00
Yidadaa
885f2a3226 feat: close #2752 auto re-fill unfinished input 2023-09-05 01:54:28 +08:00
Yifei Zhang
1f94ae7888 Merge pull request #2721 from Yidadaa/revert-2673-dependabot/npm_and_yarn/next-13.4.19 2023-08-28 11:10:34 +08:00
Yifei Zhang
05aa520669 Revert "chore(deps): bump next from 13.4.9 to 13.4.19" 2023-08-28 11:06:12 +08:00
179 changed files with 8139 additions and 2930 deletions

8
.dockerignore Normal file
View File

@@ -0,0 +1,8 @@
# local env files
.env*.local
# docker-compose env files
.env
*.key
*.key.pub

View File

@@ -8,6 +8,16 @@ CODE=your-password
# You can start service behind a proxy
PROXY_URL=http://localhost:7890
# (optional)
# Default: Empty
# Googel Gemini Pro API key, set if you want to use Google Gemini Pro API.
GOOGLE_API_KEY=
# (optional)
# Default: https://generativelanguage.googleapis.com/
# Googel Gemini Pro API url without pathname, set if you want to customize Google Gemini Pro API url.
GOOGLE_URL=
# Override openai api request base url. (optional)
# Default: https://api.openai.com
# Examples: http://your-openai-proxy.com
@@ -15,14 +25,8 @@ BASE_URL=
# Specify OpenAI organization ID.(optional)
# Default: Empty
# If you do not want users to input their own API key, set this value to 1.
OPENAI_ORG_ID=
# (optional)
# Default: Empty
# If you do not want users to input their own API key, set this value to 1.
HIDE_USER_API_KEY=
# (optional)
# Default: Empty
# If you do not want users to use GPT-4, set this value to 1.
@@ -30,5 +34,16 @@ DISABLE_GPT4=
# (optional)
# Default: Empty
# If you do not want users to query balance, set this value to 1.
HIDE_BALANCE_QUERY=
# If you do not want users to input their own API key, set this value to 1.
HIDE_USER_API_KEY=
# (optional)
# Default: Empty
# If you do want users to query balance, set this value to 1.
ENABLE_BALANCE_QUERY=
# (optional)
# Default: Empty
# If you want to disable parse settings from url, set this value to 1.
DISABLE_FAST_LINK=

View File

@@ -1,43 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[Bug] "
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Deployment**
- [ ] Docker
- [ ] Vercel
- [ ] Server
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional Logs**
Add any logs about the problem here.

146
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,146 @@
name: Bug report
description: Create a report to help us improve
title: "[Bug] "
labels: ["bug"]
body:
- type: markdown
attributes:
value: "## Describe the bug"
- type: textarea
id: bug-description
attributes:
label: "Bug Description"
description: "A clear and concise description of what the bug is."
placeholder: "Explain the bug..."
validations:
required: true
- type: markdown
attributes:
value: "## To Reproduce"
- type: textarea
id: steps-to-reproduce
attributes:
label: "Steps to Reproduce"
description: "Steps to reproduce the behavior:"
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: markdown
attributes:
value: "## Expected behavior"
- type: textarea
id: expected-behavior
attributes:
label: "Expected Behavior"
description: "A clear and concise description of what you expected to happen."
placeholder: "Describe what you expected to happen..."
validations:
required: true
- type: markdown
attributes:
value: "## Screenshots"
- type: textarea
id: screenshots
attributes:
label: "Screenshots"
description: "If applicable, add screenshots to help explain your problem."
placeholder: "Paste your screenshots here or write 'N/A' if not applicable..."
validations:
required: false
- type: markdown
attributes:
value: "## Deployment"
- type: checkboxes
id: deployment
attributes:
label: "Deployment Method"
description: "Please select the deployment method you are using."
options:
- label: "Docker"
- label: "Vercel"
- label: "Server"
- type: markdown
attributes:
value: "## Desktop (please complete the following information):"
- type: input
id: desktop-os
attributes:
label: "Desktop OS"
description: "Your desktop operating system."
placeholder: "e.g., Windows 10"
validations:
required: false
- type: input
id: desktop-browser
attributes:
label: "Desktop Browser"
description: "Your desktop browser."
placeholder: "e.g., Chrome, Safari"
validations:
required: false
- type: input
id: desktop-version
attributes:
label: "Desktop Browser Version"
description: "Version of your desktop browser."
placeholder: "e.g., 89.0"
validations:
required: false
- type: markdown
attributes:
value: "## Smartphone (please complete the following information):"
- type: input
id: smartphone-device
attributes:
label: "Smartphone Device"
description: "Your smartphone device."
placeholder: "e.g., iPhone X"
validations:
required: false
- type: input
id: smartphone-os
attributes:
label: "Smartphone OS"
description: "Your smartphone operating system."
placeholder: "e.g., iOS 14.4"
validations:
required: false
- type: input
id: smartphone-browser
attributes:
label: "Smartphone Browser"
description: "Your smartphone browser."
placeholder: "e.g., Safari"
validations:
required: false
- type: input
id: smartphone-version
attributes:
label: "Smartphone Browser Version"
description: "Version of your smartphone browser."
placeholder: "e.g., 14"
validations:
required: false
- type: markdown
attributes:
value: "## Additional Logs"
- type: textarea
id: additional-logs
attributes:
label: "Additional Logs"
description: "Add any logs about the problem here."
placeholder: "Paste any relevant logs here..."
validations:
required: false

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature] "
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,53 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature Request]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: "## Is your feature request related to a problem? Please describe."
- type: textarea
id: problem-description
attributes:
label: Problem Description
description: "A clear and concise description of what the problem is. Example: I'm always frustrated when [...]"
placeholder: "Explain the problem you are facing..."
validations:
required: true
- type: markdown
attributes:
value: "## Describe the solution you'd like"
- type: textarea
id: desired-solution
attributes:
label: Solution Description
description: A clear and concise description of what you want to happen.
placeholder: "Describe the solution you'd like..."
validations:
required: true
- type: markdown
attributes:
value: "## Describe alternatives you've considered"
- type: textarea
id: alternatives-considered
attributes:
label: Alternatives Considered
description: A clear and concise description of any alternative solutions or features you've considered.
placeholder: "Describe any alternative solutions or features you've considered..."
validations:
required: false
- type: markdown
attributes:
value: "## Additional context"
- type: textarea
id: additional-context
attributes:
label: Additional Context
description: Add any other context or screenshots about the feature request here.
placeholder: "Add any other context or screenshots about the feature request here..."
validations:
required: false

View File

@@ -1,24 +0,0 @@
---
name: 功能建议
about: 请告诉我们你的灵光一闪
title: "[Feature] "
labels: ''
assignees: ''
---
> 为了提高交流效率,我们设立了官方 QQ 群和 QQ 频道,如果你在使用或者搭建过程中遇到了任何问题,请先第一时间加群或者频道咨询解决,除非是可以稳定复现的 Bug 或者较为有创意的功能建议,否则请不要随意往 Issue 区发送低质无意义帖子。
> [点击加入官方群聊](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724)
**这个功能与现有的问题有关吗?**
如果有关,请在此列出链接或者描述问题。
**你想要什么功能或者有什么建议?**
尽管告诉我们。
**有没有可以参考的同类竞品?**
可以给出参考产品的链接或者截图。
**其他信息**
可以说说你的其他考虑。

View File

@@ -1,36 +0,0 @@
---
name: 反馈问题
about: 请告诉我们你遇到的问题
title: "[Bug] "
labels: ''
assignees: ''
---
> 为了提高交流效率,我们设立了官方 QQ 群和 QQ 频道,如果你在使用或者搭建过程中遇到了任何问题,请先第一时间加群或者频道咨询解决,除非是可以稳定复现的 Bug 或者较为有创意的功能建议,否则请不要随意往 Issue 区发送低质无意义帖子。
> [点击加入官方群聊](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724)
**反馈须知**
⚠️ 注意:不遵循此模板的任何帖子都会被立即关闭,如果没有提供下方的信息,我们无法定位你的问题。
请在下方中括号内输入 x 来表示你已经知晓相关内容。
- [ ] 我确认已经在 [常见问题](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/docs/faq-cn.md) 中搜索了此次反馈的问题,没有找到解答;
- [ ] 我确认已经在 [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) 列表(包括已经 Close 的)中搜索了此次反馈的问题,没有找到解答。
- [ ] 我确认已经在 [Vercel 使用教程](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/docs/vercel-cn.md) 中搜索了此次反馈的问题,没有找到解答。
**描述问题**
请在此描述你遇到了什么问题。
**如何复现**
请告诉我们你是通过什么操作触发的该问题。
**截图**
请在此提供控制台截图、屏幕截图或者服务端的 log 截图。
**一些必要的信息**
- 系统:[比如 windows 10/ macos 12/ linux / android 11 / ios 16]
- 浏览器: [比如 chrome, safari]
- 版本: [填写设置页面的版本号]
- 部署方式:[比如 vercel、docker 或者服务器部署]

View File

@@ -18,7 +18,7 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: get version
run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV
- name: create release
@@ -43,12 +43,9 @@ jobs:
- os: ubuntu-latest
arch: x86_64
rust_target: x86_64-unknown-linux-gnu
- os: macos-latest
arch: x86_64
rust_target: x86_64-apple-darwin
- os: macos-latest
arch: aarch64
rust_target: aarch64-apple-darwin
rust_target: x86_64-apple-darwin,aarch64-apple-darwin
- os: windows-latest
arch: x86_64
rust_target: x86_64-pc-windows-msvc
@@ -59,14 +56,15 @@ jobs:
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.config.rust_target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.config.rust_target }}
key: ${{ matrix.config.os }}
- name: install dependencies (ubuntu only)
if: matrix.config.os == 'ubuntu-latest'
run: |
@@ -79,8 +77,15 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
releaseId: ${{ needs.create-release.outputs.release_id }}
args: ${{ matrix.config.os == 'macos-latest' && '--target universal-apple-darwin' || '' }}
publish-release:
permissions:

84
.github/workflows/deploy_preview.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: VercelPreviewDeployment
on:
pull_request_target:
types:
- opened
- synchronize
- reopened
env:
VERCEL_TEAM: ${{ secrets.VERCEL_TEAM }}
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
VERCEL_PR_DOMAIN_SUFFIX: ${{ secrets.VERCEL_PR_DOMAIN_SUFFIX }}
permissions:
contents: read
statuses: write
pull-requests: write
jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_OUTPUT"
id: extract_branch
- name: Hash branch name
uses: pplanel/hash-calculator-action@v1.3.1
id: hash_branch
with:
input: ${{ steps.extract_branch.outputs.branch }}
method: MD5
- name: Set Environment Variables
id: set_env
if: github.event_name == 'pull_request_target'
run: |
echo "VERCEL_ALIAS_DOMAIN=${{ github.event.pull_request.number }}-${{ github.workflow }}.${VERCEL_PR_DOMAIN_SUFFIX}" >> $GITHUB_OUTPUT
- name: Install Vercel CLI
run: npm install --global vercel@latest
- name: Cache dependencies
uses: actions/cache@v2
id: cache-npm
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=preview --token=${VERCEL_TOKEN}
- name: Deploy Project Artifacts to Vercel
id: vercel
env:
META_TAG: ${{ steps.hash_branch.outputs.digest }}-${{ github.run_number }}-${{ github.run_attempt}}
run: |
set -e
vercel pull --yes --environment=preview --token=${VERCEL_TOKEN}
vercel build --token=${VERCEL_TOKEN}
vercel deploy --prebuilt --archive=tgz --token=${VERCEL_TOKEN} --meta base_hash=${{ env.META_TAG }}
DEFAULT_URL=$(vercel ls --token=${VERCEL_TOKEN} --meta base_hash=${{ env.META_TAG }})
ALIAS_URL=$(vercel alias set ${DEFAULT_URL} ${{ steps.set_env.outputs.VERCEL_ALIAS_DOMAIN }} --token=${VERCEL_TOKEN} --scope ${VERCEL_TEAM}| awk '{print $3}')
echo "New preview URL: ${DEFAULT_URL}"
echo "New alias URL: ${ALIAS_URL}"
echo "VERCEL_URL=${ALIAS_URL}" >> "$GITHUB_OUTPUT"
- uses: mshick/add-pr-comment@v2
with:
message: |
Your build has completed!
[Preview deployment](${{ steps.vercel.outputs.VERCEL_URL }})

View File

@@ -0,0 +1,40 @@
name: Removedeploypreview
permissions:
contents: read
statuses: write
pull-requests: write
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
pull_request_target:
types:
- closed
jobs:
delete-deployments:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
- name: Hash branch name
uses: pplanel/hash-calculator-action@v1.3.1
id: hash_branch
with:
input: ${{ steps.extract_branch.outputs.branch }}
method: MD5
- name: Call the delete-deployment-preview.sh script
env:
META_TAG: ${{ steps.hash_branch.outputs.digest }}
run: |
bash ./scripts/delete-deployment-preview.sh

View File

@@ -24,7 +24,7 @@ jobs:
id: sync
uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
with:
upstream_sync_repo: Yidadaa/ChatGPT-Next-Web
upstream_sync_repo: ChatGPTNextWeb/ChatGPT-Next-Web
upstream_sync_branch: main
target_sync_branch: main
target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set

View File

@@ -16,6 +16,7 @@ FROM base AS builder
RUN apk update && apk add --no-cache git
ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY=""
ENV CODE=""
WORKDIR /app
@@ -31,6 +32,7 @@ RUN apk add proxychains-ng
ENV PROXY_URL=""
ENV OPENAI_API_KEY=""
ENV GOOGLE_API_KEY=""
ENV CODE=""
COPY --from=builder /app/public ./public
@@ -41,22 +43,22 @@ COPY --from=builder /app/.next/server ./.next/server
EXPOSE 3000
CMD if [ -n "$PROXY_URL" ]; then \
export HOSTNAME="127.0.0.1"; \
protocol=$(echo $PROXY_URL | cut -d: -f1); \
host=$(echo $PROXY_URL | cut -d/ -f3 | cut -d: -f1); \
port=$(echo $PROXY_URL | cut -d: -f3); \
conf=/etc/proxychains.conf; \
echo "strict_chain" > $conf; \
echo "proxy_dns" >> $conf; \
echo "remote_dns_subnet 224" >> $conf; \
echo "tcp_read_time_out 15000" >> $conf; \
echo "tcp_connect_time_out 8000" >> $conf; \
echo "localnet 127.0.0.0/255.0.0.0" >> $conf; \
echo "localnet ::1/128" >> $conf; \
echo "[ProxyList]" >> $conf; \
echo "$protocol $host $port" >> $conf; \
cat /etc/proxychains.conf; \
proxychains -f $conf node server.js; \
export HOSTNAME="127.0.0.1"; \
protocol=$(echo $PROXY_URL | cut -d: -f1); \
host=$(echo $PROXY_URL | cut -d/ -f3 | cut -d: -f1); \
port=$(echo $PROXY_URL | cut -d: -f3); \
conf=/etc/proxychains.conf; \
echo "strict_chain" > $conf; \
echo "proxy_dns" >> $conf; \
echo "remote_dns_subnet 224" >> $conf; \
echo "tcp_read_time_out 15000" >> $conf; \
echo "tcp_connect_time_out 8000" >> $conf; \
echo "localnet 127.0.0.0/255.0.0.0" >> $conf; \
echo "localnet ::1/128" >> $conf; \
echo "[ProxyList]" >> $conf; \
echo "$protocol $host $port" >> $conf; \
cat /etc/proxychains.conf; \
proxychains -f $conf node server.js; \
else \
node server.js; \
node server.js; \
fi

View File

@@ -1,22 +1,22 @@
<div align="center">
<img src="./docs/images/icon.svg" alt="icon"/>
<img src="./docs/images/head-cover.png" alt="icon"/>
<h1 align="center">ChatGPT Next Web</h1>
<h1 align="center">NextChat (ChatGPT Next Web)</h1>
English / [简体中文](./README_CN.md) / [日本語](./README_JA.md)
English / [简体中文](./README_CN.md)
One-Click to get well-designed cross-platform ChatGPT web UI.
One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4 & Gemini Pro support.
一键免费部署你的跨平台私人 ChatGPT 应用。
一键免费部署你的跨平台私人 ChatGPT 应用, 支持 GPT3, GPT4 & Gemini Pro 模型
[![Web][Web-image]][web-url]
[![Windows][Windows-image]][download-url]
[![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url]
[Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Twitter](https://twitter.com/mortiest_ricky) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
[Web App](https://app.nextchat.dev/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Discord](https://discord.gg/YCkeafCafC) / [Twitter](https://twitter.com/NextChatDev)
[网页版](https://chatgpt.nextweb.fun/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
[网页版](https://app.nextchat.dev/) / [客户端](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues)
[web-url]: https://chatgpt.nextweb.fun
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
@@ -25,7 +25,9 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/ZBUEFA)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
@@ -37,8 +39,8 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
- **Deploy for free with one-click** on Vercel in under 1 minute
- Compact client (~5MB) on Linux/Windows/MacOS, [download it now](https://github.com/Yidadaa/ChatGPT-Next-Web/releases)
- Fully compatible with self-deployed llms, recommended for use with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) or [LocalAI](https://github.com/go-skynet/LocalAI)
- Privacy first, all data stored locally in the browser
- Fully compatible with self-deployed LLMs, recommended for use with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) or [LocalAI](https://github.com/go-skynet/LocalAI)
- Privacy first, all data is stored locally in the browser
- Markdown support: LaTex, mermaid, code highlight, etc.
- Responsive design, dark mode and PWA
- Fast first screen loading speed (~100kb), support streaming response
@@ -59,9 +61,11 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
## What's New
- 🚀 v2.0 is released, now you can create prompt templates, turn your ideas into reality! Read this: [ChatGPT Prompt Engineering Tips: Zero, One and Few Shot Prompting](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/).
- 🚀 v2.7 let's share conversations as image, or share to ShareGPT!
- 🚀 v2.10.1 support Google Gemini Pro model.
- 🚀 v2.9.11 you can use azure endpoint now.
- 🚀 v2.8 now we have a client that runs across all platforms!
- 🚀 v2.7 let's share conversations as image, or share to ShareGPT!
- 🚀 v2.0 is released, now you can create prompt templates, turn your ideas into reality! Read this: [ChatGPT Prompt Engineering Tips: Zero, One and Few Shot Prompting](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/).
## 主要功能
@@ -74,7 +78,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
- 预制角色功能(面具),方便地创建、分享和调试你的个性化对话
- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
- 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
- 多国语言支持English, 简体中文, 繁体中文, 日本語, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština
- 多国语言支持English, 简体中文, 繁体中文, 日本語, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština, 한국어, Indonesia
- 拥有自己的域名?好上加好,绑定后即可在任何地方**无障碍**快速访问
## 开发计划
@@ -93,6 +97,7 @@ One-Click to get well-designed cross-platform ChatGPT web UI.
- 💡 想要更方便地随时随地使用本项目可以试下这款桌面插件https://github.com/mushan0x0/AI0x0.com
- 🚀 v2.7 现在可以将会话分享为图片了,也可以分享到 ShareGPT 的在线链接。
- 🚀 v2.8 发布了横跨 Linux/Windows/MacOS 的体积极小的客户端。
- 🚀 v2.9.11 现在可以使用自定义 Azure 服务了。
## Get Started
@@ -153,13 +158,13 @@ After adding or modifying this environment variable, please redeploy the project
> [简体中文 > 如何配置 api key、访问密码、接口代理](./README_CN.md#环境变量)
### `OPENAI_API_KEY` (required)
Your openai api key.
### `CODE` (optional)
Access passsword, separated by comma.
Access password, separated by comma.
### `OPENAI_API_KEY` (required)
Your openai api key, join multiple api keys with comma.
### `BASE_URL` (optional)
@@ -173,6 +178,28 @@ Override openai api request base url.
Specify OpenAI organization ID.
### `AZURE_URL` (optional)
> Example: https://{azure-resource-url}/openai/deployments/{deploy-name}
Azure deploy url.
### `AZURE_API_KEY` (optional)
Azure Api Key.
### `AZURE_API_VERSION` (optional)
Azure Api Version, find it at [Azure Documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions).
### `GOOGLE_API_KEY` (optional)
Google Gemini Pro Api Key.
### `GOOGLE_URL` (optional)
Google Gemini Pro Api Url.
### `HIDE_USER_API_KEY` (optional)
> Default: Empty
@@ -185,11 +212,26 @@ If you do not want users to input their own API key, set this value to 1.
If you do not want users to use GPT-4, set this value to 1.
### `HIDE_BALANCE_QUERY` (optional)
### `ENABLE_BALANCE_QUERY` (optional)
> Default: Empty
If you do not want users to query balance, set this value to 1.
If you do want users to query balance, set this value to 1, or you should set it to 0.
### `DISABLE_FAST_LINK` (optional)
> Default: Empty
If you want to disable parse settings from url, set this to 1.
### `CUSTOM_MODELS` (optional)
> Default: Empty
> Example: `+llama,+claude-2,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` means add `llama, claude-2` to model list, and remove `gpt-3.5-turbo` from list, and display `gpt-4-1106-preview` as `gpt-4-turbo`.
To control custom models, use `+` to add a custom model, use `-` to hide a model, use `name=displayName` to customize model name, separated by comma.
User `-all` to disable all default models, `+all` to enable all default models.
## Requirements
@@ -257,6 +299,10 @@ If your proxy needs password, use:
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
## Synchronizing Chat Records (UpStash)
| [简体中文](./docs/synchronise-chat-logs-cn.md) | [English](./docs/synchronise-chat-logs-en.md) | [Italiano](./docs/synchronise-chat-logs-es.md) | [日本語](./docs/synchronise-chat-logs-ja.md) | [한국어](./docs/synchronise-chat-logs-ko.md)
## Documentation
> Please go to the [docs][./docs] directory for more documentation instructions.
@@ -309,10 +355,17 @@ If you want to add a new translation, read this [document](./docs/translation.md
[@AnsonHyq](https://github.com/AnsonHyq)
[@synwith](https://github.com/synwith)
[@piksonGit](https://github.com/piksonGit)
[@ouyangzhiping](https://github.com/ouyangzhiping)
[@wenjiavv](https://github.com/wenjiavv)
[@LeXwDeX](https://github.com/LeXwDeX)
[@Licoy](https://github.com/Licoy)
[@shangmin2009](https://github.com/shangmin2009)
### Contributor
### Contributors
[Contributors](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
<a href="https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/graphs/contributors">
<img src="https://contrib.rocks/image?repo=ChatGPTNextWeb/ChatGPT-Next-Web" />
</a>
## LICENSE

View File

@@ -1,14 +1,16 @@
<div align="center">
<img src="./docs/images/icon.svg" alt="预览"/>
<h1 align="center">ChatGPT Next Web</h1>
<h1 align="center">NextChat</h1>
一键免费部署你的私人 ChatGPT 网页应用。
一键免费部署你的私人 ChatGPT 网页应用,支持 GPT3, GPT4 & Gemini Pro 模型
[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N) / [QQ 群](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donate](#捐赠-donate-usdt)
[演示 Demo](https://chat-gpt-next-web.vercel.app/) / [反馈 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [加入 Discord](https://discord.gg/zrhvHCr79N)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/ZBUEFA)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
![主界面](./docs/images/cover.png)
@@ -19,7 +21,7 @@
1. 准备好你的 [OpenAI API Key](https://platform.openai.com/account/api-keys);
2. 点击右侧按钮开始部署:
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web),直接使用 Github 账号登录即可,记得在环境变量页填入 API Key 和[页面访问密码](#配置页面访问密码) CODE
3. 部署完毕后,即可开始使用;
4. (可选)[绑定自定义域名](https://vercel.com/docs/concepts/projects/domains/add-a-domain)Vercel 分配的域名 DNS 在某些区域被污染了,绑定自定义域名即可直连。
@@ -68,7 +70,7 @@ code1,code2,code3
### `OPENAI_API_KEY` (必填项)
OpanAI 密钥,你在 openai 账户页面申请的 api key。
OpanAI 密钥,你在 openai 账户页面申请的 api key使用英文逗号隔开多个 key这样可以随机轮询这些 key。
### `CODE` (可选)
@@ -90,6 +92,28 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
指定 OpenAI 中的组织 ID。
### `AZURE_URL` (可选)
> 形如https://{azure-resource-url}/openai/deployments/{deploy-name}
Azure 部署地址。
### `AZURE_API_KEY` (可选)
Azure 密钥。
### `AZURE_API_VERSION` (可选)
Azure Api 版本,你可以在这里找到:[Azure 文档](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#chat-completions)。
### `GOOGLE_API_KEY` (optional)
Google Gemini Pro 密钥.
### `GOOGLE_URL` (optional)
Google Gemini Pro Api Url.
### `HIDE_USER_API_KEY` (可选)
如果你不想让用户自行填入 API Key将此环境变量设置为 1 即可。
@@ -98,9 +122,20 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
如果你不想让用户使用 GPT-4将此环境变量设置为 1 即可。
### `HIDE_BALANCE_QUERY` (可选)
### `ENABLE_BALANCE_QUERY` (可选)
如果你不想让用户查询余额,将此环境变量设置为 1 即可。
如果你想启用余额查询功能,将此环境变量设置为 1 即可。
### `DISABLE_FAST_LINK` (可选)
如果你想禁用从链接解析预制设置,将此环境变量设置为 1 即可。
### `CUSTOM_MODELS` (可选)
> 示例:`+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-1106-preview=gpt-4-turbo` 表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-1106-preview` 模型名字展示为 `gpt-4-turbo`。
> 如果你想先禁用所有模型,再启用指定模型,可以使用 `-all,+gpt-3.5-turbo`,则表示仅启用 `gpt-3.5-turbo`
用来控制模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。
## 开发
@@ -114,7 +149,7 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
OPENAI_API_KEY=<your api key here>
# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址
BASE_URL=https://chatgpt1.nextweb.fun/api/proxy
BASE_URL=https://b.nextweb.fun/api/proxy
```
### 本地开发
@@ -180,6 +215,7 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
[见项目贡献者列表](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
### 相关项目
- [one-api](https://github.com/songquanpeng/one-api): 一站式大模型额度管理平台,支持市面上所有主流大语言模型
## 开源协议

View File

@@ -1,173 +0,0 @@
<div align="center">
<img src="./docs/images/icon.svg" alt="预览"/>
<h1 align="center">ChatGPT Next Web</h1>
Implemente su aplicación web privada ChatGPT de forma gratuita con un solo clic.
[Demo demo](https://chat-gpt-next-web.vercel.app/) / [Problemas de comentarios](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Únete a Discord](https://discord.gg/zrhvHCr79N) / [Grupo QQ](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [Desarrolladores de consejos](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [Donar](#捐赠-donate-usdt)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
![主界面](./docs/images/cover.png)
</div>
## Comenzar
1. Prepara el tuyo [Clave API OpenAI](https://platform.openai.com/account/api-keys);
2. Haga clic en el botón de la derecha para iniciar la implementación:
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web), inicie sesión directamente con su cuenta de Github y recuerde completar la clave API y la suma en la página de variables de entorno[Contraseña de acceso a la página](#配置页面访问密码) CÓDIGO;
3. Una vez implementado, puede comenzar;
4. (Opcional)[Enlazar un nombre de dominio personalizado](https://vercel.com/docs/concepts/projects/domains/add-a-domain): El nombre de dominio DNS asignado por Vercel está contaminado en algunas regiones y puede conectarse directamente enlazando un nombre de dominio personalizado.
## Manténgase actualizado
Si sigue los pasos anteriores para implementar su proyecto con un solo clic, es posible que siempre diga "La actualización existe" porque Vercel creará un nuevo proyecto para usted de forma predeterminada en lugar de bifurcar el proyecto, lo que evitará que la actualización se detecte correctamente.
Le recomendamos que siga estos pasos para volver a implementar:
- Eliminar el repositorio original;
- Utilice el botón de bifurcación en la esquina superior derecha de la página para bifurcar este proyecto;
- En Vercel, vuelva a seleccionar e implementar,[Echa un vistazo al tutorial detallado](./docs/vercel-cn.md#如何新建项目)。
### Activar actualizaciones automáticas
> Si encuentra un error de ejecución de Upstream Sync, ¡Sync Fork manualmente una vez!
Cuando bifurca el proyecto, debido a las limitaciones de Github, debe ir manualmente a la página Acciones de su proyecto bifurcado para habilitar Flujos de trabajo y habilitar Upstream Sync Action, después de habilitarlo, puede activar las actualizaciones automáticas cada hora:
![自动更新](./docs/images/enable-actions.jpg)
![启用自动更新](./docs/images/enable-actions-sync.jpg)
### Actualizar el código manualmente
Si desea que el manual se actualice inmediatamente, puede consultarlo [Documentación para Github](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) Aprenda a sincronizar un proyecto bifurcado con código ascendente.
Puede destacar / ver este proyecto o seguir al autor para recibir notificaciones de nuevas actualizaciones de funciones.
## Configurar la contraseña de acceso a la página
> Después de configurar la contraseña, el usuario debe completar manualmente el código de acceso en la página de configuración para chatear normalmente, de lo contrario, se solicitará el estado no autorizado a través de un mensaje.
> **advertir**: Asegúrese de establecer el número de dígitos de la contraseña lo suficientemente largo, preferiblemente más de 7 dígitos, de lo contrario[Será volado](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518)。
Este proyecto proporciona control de permisos limitado, agregue el nombre al nombre en la página Variables de entorno del Panel de control del proyecto Vercel `CODE` Variables de entorno con valores para contraseñas personalizadas separadas por comas:
code1,code2,code3
Después de agregar o modificar la variable de entorno, por favor**Redesplegar**proyecto para poner en vigor los cambios.
## Variable de entorno
> La mayoría de los elementos de configuración de este proyecto se establecen a través de variables de entorno, tutorial:[Cómo modificar las variables de entorno de Vercel](./docs/vercel-cn.md)。
### `OPENAI_API_KEY` (Requerido)
OpanAI key, la clave API que solicita en la página de su cuenta openai.
### `CODE` (Opcional)
Las contraseñas de acceso, opcionalmente, se pueden separar por comas.
**advertir**: Si no completa este campo, cualquiera puede usar directamente su sitio web implementado, lo que puede hacer que su token se consuma rápidamente, se recomienda completar esta opción.
### `BASE_URL` (Opcional)
> Predeterminado: `https://api.openai.com`
> Ejemplos: `http://your-openai-proxy.com`
URL del proxy de interfaz OpenAI, complete esta opción si configuró manualmente el proxy de interfaz openAI.
> Si encuentra problemas con el certificado SSL, establezca el `BASE_URL` El protocolo se establece en http.
### `OPENAI_ORG_ID` (Opcional)
Especifica el identificador de la organización en OpenAI.
### `HIDE_USER_API_KEY` (Opcional)
Si no desea que los usuarios rellenen la clave de API ellos mismos, establezca esta variable de entorno en 1.
### `DISABLE_GPT4` (Opcional)
Si no desea que los usuarios utilicen GPT-4, establezca esta variable de entorno en 1.
### `HIDE_BALANCE_QUERY` (Opcional)
Si no desea que los usuarios consulte el saldo, establezca esta variable de entorno en 1.
## explotación
> No se recomienda encarecidamente desarrollar o implementar localmente, debido a algunas razones técnicas, es difícil configurar el agente API de OpenAI localmente, a menos que pueda asegurarse de que puede conectarse directamente al servidor OpenAI.
Haga clic en el botón de abajo para iniciar el desarrollo secundario:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
Antes de empezar a escribir código, debe crear uno nuevo en la raíz del proyecto `.env.local` archivo, lleno de variables de entorno:
OPENAI_API_KEY=<your api key here>
### Desarrollo local
1. Instale nodejs 18 e hilo, pregunte a ChatGPT para obtener más detalles;
2. ejecutar `yarn install && yarn dev` Enlatar. ⚠️ Nota: Este comando es solo para desarrollo local, no para implementación.
3. Úselo si desea implementar localmente `yarn install && yarn start` comando, puede cooperar con pm2 a daemon para evitar ser asesinado, pregunte a ChatGPT para obtener más detalles.
## desplegar
### Implementación de contenedores (recomendado)
> La versión de Docker debe ser 20 o posterior, de lo contrario se indicará que no se puede encontrar la imagen.
> ⚠️ Nota: Las versiones de Docker están de 1 a 2 días por detrás de la última versión la mayor parte del tiempo, por lo que es normal que sigas diciendo "La actualización existe" después de la implementación.
```shell
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
yidadaa/chatgpt-next-web
```
También puede especificar proxy:
```shell
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
--net=host \
-e PROXY_URL=http://127.0.0.1:7890 \
yidadaa/chatgpt-next-web
```
Si necesita especificar otras variables de entorno, agréguelas usted mismo en el comando anterior `-e 环境变量=环境变量值` para especificar.
### Implementación local
Ejecute el siguiente comando en la consola:
```shell
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
⚠️ Nota: Si tiene problemas durante la instalación, utilice la implementación de Docker.
## Reconocimiento
### donante
> Ver versión en inglés.
### Colaboradores
[Ver la lista de colaboradores del proyecto](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
## Licencia de código abierto
[MIT](https://opensource.org/license/mit/)

View File

@@ -1,275 +0,0 @@
<div align="center">
<img src="./docs/images/icon.svg" alt="icon"/>
<h1 align="center">ChatGPT Next Web</h1>
[English](./README.md) / [简体中文](./README_CN.md) / 日本語
ワンクリックで、クロスプラットフォーム ChatGPT ウェブ UI が表示されます。
[![Web][Web-image]][web-url]
[![Windows][Windows-image]][download-url]
[![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url]
[Web App](https://chatgpt.nextweb.fun/) / [Desktop App](https://github.com/Yidadaa/ChatGPT-Next-Web/releases) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord](https://discord.gg/YCkeafCafC) / [コーヒーをおごる](https://www.buymeacoffee.com/yidadaa) / [QQ グループ](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724) / [開発者への報酬](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
[web-url]: https://chatgpt.nextweb.fun
[download-url]: https://github.com/Yidadaa/ChatGPT-Next-Web/releases
[Web-image]: https://img.shields.io/badge/Web-PWA-orange?logo=microsoftedge
[Windows-image]: https://img.shields.io/badge/-Windows-blue?logo=windows
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
![cover](./docs/images/cover.png)
</div>
## 特徴
- Vercel で 1 分以内に**ワンクリックで無料デプロイ**。
- コンパクトなクライアント (~5MB) on Linux/Windows/MacOS、[今すぐダウンロード](https://github.com/Yidadaa/ChatGPT-Next-Web/releases)
- [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) または [LocalAI](https://github.com/go-skynet/LocalAI) との使用をお勧めします
- プライバシー第一、すべてのデータはブラウザにローカルに保存されます
- マークダウンのサポート: LaTex、マーメイド、コードハイライトなど
- レスポンシブデザイン、ダークモード、PWA
- 最初の画面読み込み速度が速い(~100kb)、ストリーミングレスポンスをサポート
- v2 の新機能:プロンプトテンプレート(マスク)でチャットツールを作成、共有、デバッグ
- [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) と [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) による素晴らしいプロンプト
- トークンを保存しながら、長い会話をサポートするために自動的にチャット履歴を圧縮します
- 国際化: English、简体中文、繁体中文、日本語、Français、Español、Italiano、Türkçe、Deutsch、Tiếng Việt、Русский、Čeština、한국어
## ロードマップ
- [x] システムプロンプト: ユーザー定義のプロンプトをシステムプロンプトとして固定 [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)
- [x] ユーザープロンプト: ユーザはカスタムプロンプトを編集し、プロンプトリストに保存することができます。
- [x] プロンプトテンプレート: 事前に定義されたインコンテキストプロンプトで新しいチャットを作成 [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993)
- [x] イメージとして共有、ShareGPT への共有 [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741)
- [x] tauri を使ったデスクトップアプリ
- [x] セルフホストモデル: [RWKV-Runner](https://github.com/josStorer/RWKV-Runner) と完全に互換性があり、[LocalAI](https://github.com/go-skynet/LocalAI) のサーバーデプロイも可能です: llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly など
- [ ] プラグイン: ネットワーク検索、計算機、その他のAPIなどをサポート [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
## 新機能
- 🚀 v2.0 がリリースされ、プロンプト・テンプレートが作成できるようになりました!こちらをお読みください: [ChatGPT プロンプトエンジニアリング Tips: ゼロ、一発、数発プロンプト](https://www.allabtai.com/prompt-engineering-tips-zero-one-and-few-shot-prompting/)。
- 💡 このプロジェクトをいつでもどこでも簡単に使いたいですか?このデスクトッププラグインをお試しください: https://github.com/mushan0x0/AI0x0.com
- 🚀 v2.7 では、会話を画像として共有したり、ShareGPT に共有することができます!
- 🚀 v2.8 全てのプラットフォームで動作するクライアントができました!
## 始める
> [簡体字中国語 > 始め方](./README_CN.md#开始使用)
1. [OpenAI API Key](https://platform.openai.com/account/api-keys) を取得する;
2. クリック
[![Vercel でデプロイ](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)をクリックします。`CODE` はあなたのページのパスワードであることを忘れないでください;
3. お楽しみください :)
## FAQ
[簡体字中国語 > よくある質問](./docs/faq-cn.md)
[English > FAQ](./docs/faq-en.md)
## 更新を継続する
> [簡体字中国語 > コードを最新の状態に保つ方法](./README_CN.md#保持更新)
上記の手順に沿ってワンクリックで自分のプロジェクトをデプロイした場合、"Updates Available" が常に表示される問題に遭遇するかもしれません。これは、Vercel がこのプロジェクトをフォークする代わりに、デフォルトで新しいプロジェクトを作成するため、アップデートを正しく検出できないためです。
以下の手順で再デプロイすることをお勧めします:
- 元のリポジトリを削除してください;
- ページの右上にあるフォークボタンを使って、このプロジェクトをフォークする;
- Vercel を選択し、再度デプロイする。[詳しいチュートリアルを参照](./docs/vercel-cn.md)。
### 自動アップデートを有効にする
> Upstream Sync の実行に失敗した場合は、手動で一度フォークしてください。
プロジェクトをフォークした後、GitHub の制限により、フォークしたプロジェクトの Actions ページで Workflows と Upstream Sync Action を手動で有効にする必要があります。有効にすると、1 時間ごとに自動更新がスケジュールされます:
![Automatic Updates](./docs/images/enable-actions.jpg)
![Enable Automatic Updates](./docs/images/enable-actions-sync.jpg)
### 手動でコードを更新する
すぐに更新したい場合は、[GitHub ドキュメント](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) をチェックして、フォークしたプロジェクトを上流のコードと同期させる方法を学んでください。
このプロジェクトにスターをつけたり、ウォッチしたり、作者をフォローすることで、リリースの通知を受け取ることができます。
## アクセスパスワード
> [簡体字中国語 > アクセスパスワードを増やす方法](./README_CN.md#配置页面访问密码)
このプロジェクトではアクセス制御を制限しています。vercel の環境変数のページに `CODE` という環境変数を追加してください。その値は次のようにカンマで区切られたパスワードでなければなりません:
```
code1,code2,code3
```
この環境変数を追加または変更した後は、変更を有効にするためにプロジェクトを再デプロイしてください。
## 環境変数
> [簡体字中国語 > API キー、アクセスパスワード、インターフェイスプロキシ設定方法](./README_CN.md#环境变量)
### `OPENAI_API_KEY` (必須)
OpenAI の api キー。
### `CODE` (オプション)
カンマで区切られたアクセスパスワード。
### `BASE_URL` (オプション)
> デフォルト: `https://api.openai.com`
> 例: `http://your-openai-proxy.com`
OpenAI api のリクエストベースの url をオーバーライドします。
### `OPENAI_ORG_ID` (オプション)
OpenAI の組織 ID を指定します。
### `HIDE_USER_API_KEY` (オプション)
> デフォルト: 空
ユーザーに自分の API キーを入力させたくない場合は、この値を 1 に設定する。
### `DISABLE_GPT4` (オプション)
> デフォルト: 空
ユーザーに GPT-4 を使用させたくない場合は、この値を 1 に設定する。
### `HIDE_BALANCE_QUERY` (オプション)
> デフォルト: 空
ユーザーに残高を照会させたくない場合は、この値を 1 に設定する。
## 必要条件
NodeJS >= 18、Docker >= 20
## Development
> [簡体字中国語 > 二次開発の進め方](./README_CN.md#开发)
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
開発を始める前に、プロジェクトのルートに新しい `.env.local` ファイルを作成し、そこに api キーを置く必要があります:
```
OPENAI_API_KEY=<your api key here>
# OpenAI サービスにアクセスできない場合は、この BASE_URL を使用してください
BASE_URL=https://chatgpt1.nextweb.fun/api/proxy
```
### ローカルデプロイ
```shell
# 1. nodejs と yarn をまずインストールする
# 2. `.env.local` にローカルの env vars を設定する
# 3. 実行
yarn install
yarn dev
```
## デプロイ
> [簡体字中国語 > プライベートサーバーへのデプロイ方法](./README_CN.md#部署)
### Docker (推奨)
```shell
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
yidadaa/chatgpt-next-web
```
プロキシの後ろでサービスを開始することができる:
```shell
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
-e PROXY_URL=http://localhost:7890 \
yidadaa/chatgpt-next-web
```
プロキシにパスワードが必要な場合:
```shell
-e PROXY_URL="http://127.0.0.1:7890 user pass"
```
### シェル
```shell
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
## スクリーンショット
![Settings](./docs/images/settings.png)
![More](./docs/images/more.png)
## 翻訳
新しい翻訳を追加したい場合は、この[ドキュメント](./docs/translation.md)をお読みください。
## 寄付
[コーヒーをおごる](https://www.buymeacoffee.com/yidadaa)
## スペシャルサンクス
### スポンサー
> 寄付金額が 100 元以上のユーザーのみリストアップしています
[@mushan0x0](https://github.com/mushan0x0)
[@ClarenceDan](https://github.com/ClarenceDan)
[@zhangjia](https://github.com/zhangjia)
[@hoochanlon](https://github.com/hoochanlon)
[@relativequantum](https://github.com/relativequantum)
[@desenmeng](https://github.com/desenmeng)
[@webees](https://github.com/webees)
[@chazzhou](https://github.com/chazzhou)
[@hauy](https://github.com/hauy)
[@Corwin006](https://github.com/Corwin006)
[@yankunsong](https://github.com/yankunsong)
[@ypwhs](https://github.com/ypwhs)
[@fxxxchao](https://github.com/fxxxchao)
[@hotic](https://github.com/hotic)
[@WingCH](https://github.com/WingCH)
[@jtung4](https://github.com/jtung4)
[@micozhu](https://github.com/micozhu)
[@jhansion](https://github.com/jhansion)
[@Sha1rholder](https://github.com/Sha1rholder)
[@AnsonHyq](https://github.com/AnsonHyq)
[@synwith](https://github.com/synwith)
[@piksonGit](https://github.com/piksonGit)
### コントリビューター
[コントリビューター達](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
## ライセンス
[MIT](https://opensource.org/license/mit/)

View File

@@ -1,187 +0,0 @@
<div align="center">
<img src="./docs/images/icon.svg" alt="프리뷰"/>
<h1 align="center">ChatGPT Next Web</h1>
개인 ChatGPT 웹 애플리케이션을 한 번의 클릭으로 무료로 배포하세요.
[데모 Demo](https://chat-gpt-next-web.vercel.app/) / [피드백 Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Discord 참여](https://discord.gg/zrhvHCr79N) / [QQ 그룹](https://user-images.githubusercontent.com/16968934/228190818-7dd00845-e9b9-4363-97e5-44c507ac76da.jpeg) / [개발자에게 기부](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) / [기부 Donate](#기부-donate-usdt)
[![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
[![Gitpod에서 열기](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
![메인 화면](./docs/images/cover.png)
</div>
## 사용 시작
1. [OpenAI API Key](https://platform.openai.com/account/api-keys)를 준비합니다.
2. 오른쪽 버튼을 클릭하여 배포를 시작하십시오:
[![Vercel로 배포하기](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web). Github 계정으로 바로 로그인하십시오. API Key와 [페이지 접근 비밀번호](#페이지-접근-비밀번호-설정) CODE를 환경 변수 페이지에 입력하십시오.
3. 배포가 완료되면 사용을 시작하십시오.
4. (선택 사항) [사용자 정의 도메인 바인딩](https://vercel.com/docs/concepts/projects/domains/add-a-domain) : Vercel에서 할당한 도메인 DNS가 일부 지역에서 오염되어 있습니다. 사용자 정의 도메인을 바인딩하면 직접 연결할 수 있습니다.
## 업데이트 유지
위의 단계대로 프로젝트를 배포한 경우 "업데이트가 있습니다"라는 메시지가 항상 표시될 수 있습니다. 이는 Vercel이 기본적으로 새 프로젝트를 생성하고이 프로젝트를 포크하지 않기 때문입니다. 이 문제는 업데이트를 올바르게 감지할 수 없습니다.
아래 단계를 따라 다시 배포하십시오:
- 기존 저장소를 삭제합니다.
- 페이지 오른쪽 상단의 포크 버튼을 사용하여 이 프로젝트를 포크합니다.
- Vercel에서 다시 선택하여 배포하십시오. [자세한 튜토리얼 보기](./docs/vercel-cn.md#새-프로젝트-만드는-방법).
### 자동 업데이트 활성화
> Upstream Sync 오류가 발생한 경우 수동으로 Sync Fork를 한 번 실행하십시오!
프로젝트를 포크한 후 GitHub의 제한으로 인해 포크한 프로젝트의 동작 페이지에서 워크플로우를 수동으로 활성화해야 합니다. Upstream Sync Action을 활성화하면 매시간마다 자동 업데이트가 활성화됩니다:
![자동 업데이트](./docs/images/enable-actions.jpg)
![자동 업데이트 활성화](./docs/images/enable-actions-sync.jpg)
### 수동으로 코드 업데이트
수동으로 즉시 업데이트하려면 [GitHub 문서](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)에서 포크된 프로젝트를 어떻게 원본 코드와 동기화하는지 확인하십시오.
이 프로젝트에 별표/감시를 부여하거나 작성자를 팔로우하여 새 기능 업데이트 알림을 받을 수 있습니다.
## 페이지 접근 비밀번호 설정
> 비밀번호가 설정된 후, 사용자는 설정 페이지에서 접근 코드를 수동으로 입력하여 정상적으로 채팅할 수 있습니다. 그렇지 않으면 메시지를 통해 권한이 없는 상태가 표시됩니다.
> **경고** : 비밀번호의 길이를 충분히 길게 설정하십시오. 최소 7 자리 이상이 좋습니다. 그렇지 않으면 [해킹될 수 있습니다](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/518).
이 프로젝트는 제한된 권한 제어 기능을 제공합니다. Vercel 프로젝트 컨트롤 패널의 환경 변수 페이지에서 `CODE`라는 환경 변수를 추가하십시오. 값은 쉼표로 구분된 사용자 정의 비밀번호로 설정됩니다. (아래 예시의 경우 `code1` `code2` `code3` 3개의 비밀번호가 생성됩니다.)
```
code1,code2,code3
```
이 환경 변수를 추가하거나 수정한 후에는 프로젝트를 다시 배포하여 변경 사항을 적용해야 합니다.
## 환경 변수
> 이 프로젝트에서 대부분의 설정 요소들은 환경 변수를 통해 설정됩니다. [Vercel 환경변수 수정 방법.](./docs/vercel-ko.md)。
## OPENAI_API_KEY (필수 항목)
OpenAI 키로, openai 계정 페이지에서 신청한 api key입니다.
## CODE (선택 가능)
접근 비밀번호로, 선택적입니다. 쉼표를 사용하여 여러 비밀번호를 구분할 수 있습니다.
**경고** : 이 항목을 입력하지 않으면, 누구나 여러분이 배포한 웹사이트를 직접 사용할 수 있게 됩니다. 이로 인해 토큰이 빠르게 소진될 수 있으므로, 이 항목을 반드시 입력하는 것이 좋습니다.
## BASE_URL (선택 가능)
> 기본값: `https://api.openai.com`
> 예시: `http://your-openai-proxy.com`
OpenAI 인터페이스 프록시 URL입니다. 만약, 수동으로 openai 인터페이스 proxy를 설정했다면, 이 항목을 입력하셔야 합니다.
**참고**: SSL 인증서 문제가 발생한 경우, BASE_URL의 프로토콜을 http로 설정하세요.
## OPENAI_ORG_ID (선택 가능)
OpenAI 내의 조직 ID를 지정합니다.
## HIDE_USER_API_KEY (선택 가능)
사용자가 API Key를 직접 입력하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요.
## DISABLE_GPT4 (선택 가능)
사용자가 GPT-4를 사용하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요.
## HIDE_BALANCE_QUERY (선택 가능)
사용자가 잔액을 조회하는 것을 원하지 않는 경우, 이 환경 변수를 1로 설정하세요.
## 개발
아래 버튼을 클릭하여 개발을 시작하세요:
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/Yidadaa/ChatGPT-Next-Web)
코드 작성을 전, 프로젝트 루트 디렉토리에 `.env.local` 파일을 새로 만들고 해당 파일에 환경 변수를 입력해야 합니다:
```
OPENAI_API_KEY=<여기에 여러분의 api 키를 입력하세요>
#중국 사용자들은 이 프로젝트에 포함된 프록시를 사용하여 개발할 수 있습니다. 또는 다른 프록시 주소를 자유롭게 선택할 수 있습니다.
BASE_URL=https://chatgpt1.nextweb.fun/api/proxy
```
### 로컬 환경에서의 개발
1. nodejs 18과 yarn을 설치하세요. 자세한 사항은 ChatGPT에 문의하십시오.
2. `yarn install && yarn dev` 명령을 실행하세요. ⚠️ 주의: 이 명령은 로컬 개발 전용입니다. 배포용으로 사용하지 마십시오!
3. 로컬에서 배포하고 싶다면, `yarn install && yarn build && yarn start` 명령을 사용하세요. pm2와 함께 사용하여 프로세스를 보호하고, 강제 종료되지 않도록 할 수 있습니다. 자세한 내용은 ChatGPT에 문의하세요.
## 배포
### 컨테이너 배포 (추천)
> Docker 버전은 20 이상이어야 합니다. 그렇지 않으면 이미지를 찾을 수 없다는 메시지가 표시됩니다.
> ⚠️ 주의: docker 버전은 대부분의 경우 최신 버전보다 1~2일 뒤처집니다. 따라서 배포 후 "업데이트 가능" 알림이 지속적으로 나타날 수 있으며, 이는 정상적인 현상입니다.
```shell
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=페이지 접근 비밀번호 \
yidadaa/chatgpt-next-web
```
프록시를 지정하려면 다음을 사용하세요:
```shell
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=페이지 접근 비밀번호 \
--net=host \
-e PROXY_URL=http://127.0.0.1:7890 \
yidadaa/chatgpt-next-web
```
로컬 프록시에 사용자 이름과 비밀번호가 필요한 경우, 아래와 같이 사용하세요:
```shell
-e PROXY_URL="http://127.0.0.1:7890 사용자이름 비밀번호"
```
다른 환경 변수를 지정해야 하는 경우, 위의 명령에 `-e 환경변수=환경변수값`을 추가하여 지정하세요.
### 로컬 배포
콘솔에서 아래의 명령을 실행하세요:
```shell
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
⚠️ 주의: 설치 중 문제가 발생한 경우, docker로 배포하세요.
## 감사의 말
### 기부자
> 영문 버전 참조.
### 기여자
[프로젝트 기여자 목록 보기](https://github.com/Yidadaa/ChatGPT-Next-Web/graphs/contributors)
### 관련 프로젝트
- [one-api](https://github.com/songquanpeng/one-api): 통합 대형 모델 할당 관리 플랫폼, 주요 대형 언어 모델 모두 지원
## 오픈소스 라이센스
[MIT](https://opensource.org/license/mit/)

View File

@@ -1,7 +1,7 @@
import { NextRequest } from "next/server";
import { getServerSideConfig } from "../config/server";
import md5 from "spark-md5";
import { ACCESS_CODE_PREFIX } from "../constant";
import { ACCESS_CODE_PREFIX, ModelProvider } from "../constant";
function getIP(req: NextRequest) {
let ip = req.ip ?? req.headers.get("x-real-ip");
@@ -16,19 +16,19 @@ function getIP(req: NextRequest) {
function parseApiKey(bearToken: string) {
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
const isOpenAiKey = !token.startsWith(ACCESS_CODE_PREFIX);
const isApiKey = !token.startsWith(ACCESS_CODE_PREFIX);
return {
accessCode: isOpenAiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
apiKey: isOpenAiKey ? token : "",
accessCode: isApiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
apiKey: isApiKey ? token : "",
};
}
export function auth(req: NextRequest) {
export function auth(req: NextRequest, modelProvider: ModelProvider) {
const authToken = req.headers.get("Authorization") ?? "";
// check if it is openai api key or user token
const { accessCode, apiKey: token } = parseApiKey(authToken);
const { accessCode, apiKey } = parseApiKey(authToken);
const hashedCode = md5.hash(accessCode ?? "").trim();
@@ -39,19 +39,33 @@ export function auth(req: NextRequest) {
console.log("[User IP] ", getIP(req));
console.log("[Time] ", new Date().toLocaleString());
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) {
return {
error: true,
msg: !accessCode ? "empty access code" : "wrong access code",
};
}
if (serverConfig.hideUserApiKey && !!apiKey) {
return {
error: true,
msg: "you are not allowed to access with your own api key",
};
}
// if user does not provide an api key, inject system api key
if (!token) {
const apiKey = serverConfig.apiKey;
if (apiKey) {
if (!apiKey) {
const serverConfig = getServerSideConfig();
const systemApiKey =
modelProvider === ModelProvider.GeminiPro
? serverConfig.googleApiKey
: serverConfig.isAzure
? serverConfig.azureApiKey
: serverConfig.apiKey;
if (systemApiKey) {
console.log("[Auth] use system api key");
req.headers.set("Authorization", `Bearer ${apiKey}`);
req.headers.set("Authorization", `Bearer ${systemApiKey}`);
} else {
console.log("[Auth] admin did not provide an api key");
}

View File

@@ -1,48 +1,78 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSideConfig } from "../config/server";
import { DEFAULT_MODELS, OPENAI_BASE_URL, GEMINI_BASE_URL } from "../constant";
import { collectModelTable } from "../utils/model";
import { makeAzurePath } from "../azure";
export const OPENAI_URL = "api.openai.com";
const DEFAULT_PROTOCOL = "https";
const PROTOCOL = process.env.PROTOCOL || DEFAULT_PROTOCOL;
const BASE_URL = process.env.BASE_URL || OPENAI_URL;
const DISABLE_GPT4 = !!process.env.DISABLE_GPT4;
const serverConfig = getServerSideConfig();
export async function requestOpenai(req: NextRequest) {
const controller = new AbortController();
const authValue = req.headers.get("Authorization") ?? "";
const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
var authValue,
authHeaderName = "";
if (serverConfig.isAzure) {
authValue =
req.headers
.get("Authorization")
?.trim()
.replaceAll("Bearer ", "")
.trim() ?? "";
authHeaderName = "api-key";
} else {
authValue = req.headers.get("Authorization") ?? "";
authHeaderName = "Authorization";
}
let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
"/api/openai/",
"",
);
let baseUrl = BASE_URL;
let baseUrl =
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;
if (!baseUrl.startsWith("http")) {
baseUrl = `${PROTOCOL}://${baseUrl}`;
baseUrl = `https://${baseUrl}`;
}
if (baseUrl.endsWith('/')) {
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, -1);
}
console.log("[Proxy] ", openaiPath);
console.log("[Proxy] ", path);
console.log("[Base Url]", baseUrl);
if (process.env.OPENAI_ORG_ID) {
console.log("[Org ID]", process.env.OPENAI_ORG_ID);
// this fix [Org ID] undefined in server side if not using custom point
if (serverConfig.openaiOrgId !== undefined) {
console.log("[Org ID]", serverConfig.openaiOrgId);
}
const timeoutId = setTimeout(() => {
controller.abort();
}, 10 * 60 * 1000);
const timeoutId = setTimeout(
() => {
controller.abort();
},
10 * 60 * 1000,
);
const fetchUrl = `${baseUrl}/${openaiPath}`;
if (serverConfig.isAzure) {
if (!serverConfig.azureApiVersion) {
return NextResponse.json({
error: true,
message: `missing AZURE_API_VERSION in server env vars`,
});
}
path = makeAzurePath(path, serverConfig.azureApiVersion);
}
const fetchUrl = `${baseUrl}/${path}`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
Authorization: authValue,
...(process.env.OPENAI_ORG_ID && {
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
[authHeaderName]: authValue,
...(serverConfig.openaiOrgId && {
"OpenAI-Organization": serverConfig.openaiOrgId,
}),
},
method: req.method,
@@ -55,18 +85,23 @@ export async function requestOpenai(req: NextRequest) {
};
// #1815 try to refuse gpt4 request
if (DISABLE_GPT4 && req.body) {
if (serverConfig.customModels && req.body) {
try {
const modelTable = collectModelTable(
DEFAULT_MODELS,
serverConfig.customModels,
);
const clonedBody = await req.text();
fetchOptions.body = clonedBody;
const jsonBody = JSON.parse(clonedBody);
const jsonBody = JSON.parse(clonedBody) as { model?: string };
if ((jsonBody?.model ?? "").includes("gpt-4")) {
// not undefined and is false
if (modelTable[jsonBody?.model ?? ""].available === false) {
return NextResponse.json(
{
error: true,
message: "you are not allowed to use gpt-4 model",
message: `you are not allowed to use ${jsonBody?.model} model`,
},
{
status: 403,
@@ -87,6 +122,12 @@ export async function requestOpenai(req: NextRequest) {
// to disable nginx buffering
newHeaders.set("X-Accel-Buffering", "no");
// The latest version of the OpenAI API forced the content-encoding to be "br" in json response
// So if the streaming is disabled, we need to remove the content-encoding header
// Because Vercel uses gzip to compress the response, if we don't remove the content-encoding header
// The browser will try to decode the response with brotli and fail
newHeaders.delete("content-encoding");
return new Response(res.body, {
status: res.status,
statusText: res.statusText,

View File

@@ -4,13 +4,15 @@ import { getServerSideConfig } from "../../config/server";
const serverConfig = getServerSideConfig();
// Danger! Don not write any secret value here!
// Danger! Do not hard code any secret value here!
// 警告!不要在这里写入任何敏感信息!
const DANGER_CONFIG = {
needCode: serverConfig.needCode,
hideUserApiKey: serverConfig.hideUserApiKey,
disableGPT4: serverConfig.disableGPT4,
hideBalanceQuery: serverConfig.hideBalanceQuery,
disableFastLink: serverConfig.disableFastLink,
customModels: serverConfig.customModels,
};
declare global {

View File

@@ -0,0 +1,116 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
import { getServerSideConfig } from "@/app/config/server";
import { GEMINI_BASE_URL, Google, ModelProvider } from "@/app/constant";
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[Google Route] params ", params);
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const controller = new AbortController();
const serverConfig = getServerSideConfig();
let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL;
if (!baseUrl.startsWith("http")) {
baseUrl = `https://${baseUrl}`;
}
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, -1);
}
let path = `${req.nextUrl.pathname}`.replaceAll("/api/google/", "");
console.log("[Proxy] ", path);
console.log("[Base Url]", baseUrl);
const timeoutId = setTimeout(
() => {
controller.abort();
},
10 * 60 * 1000,
);
const authResult = auth(req, ModelProvider.GeminiPro);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}
const bearToken = req.headers.get("Authorization") ?? "";
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
const key = token ? token : serverConfig.googleApiKey;
if (!key) {
return NextResponse.json(
{
error: true,
message: `missing GOOGLE_API_KEY in server env vars`,
},
{
status: 401,
},
);
}
const fetchUrl = `${baseUrl}/${path}?key=${key}`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
},
method: req.method,
body: req.body,
// to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body
redirect: "manual",
// @ts-ignore
duplex: "half",
signal: controller.signal,
};
try {
const res = await fetch(fetchUrl, fetchOptions);
// to prevent browser prompt for credentials
const newHeaders = new Headers(res.headers);
newHeaders.delete("www-authenticate");
// to disable nginx buffering
newHeaders.set("X-Accel-Buffering", "no");
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers: newHeaders,
});
} finally {
clearTimeout(timeoutId);
}
}
export const GET = handle;
export const POST = handle;
export const runtime = "edge";
export const preferredRegion = [
"bom1",
"cle1",
"cpt1",
"gru1",
"hnd1",
"iad1",
"icn1",
"kix1",
"pdx1",
"sfo1",
"sin1",
"syd1",
];

View File

@@ -1,6 +1,6 @@
import { type OpenAIListModelResponse } from "@/app/client/platforms/openai";
import { getServerSideConfig } from "@/app/config/server";
import { OpenaiPath } from "@/app/constant";
import { ModelProvider, OpenaiPath } from "@/app/constant";
import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
@@ -45,7 +45,7 @@ async function handle(
);
}
const authResult = auth(req);
const authResult = auth(req, ModelProvider.GPT);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
@@ -75,3 +75,22 @@ export const GET = handle;
export const POST = handle;
export const runtime = "edge";
export const preferredRegion = [
"arn1",
"bom1",
"cdg1",
"cle1",
"cpt1",
"dub1",
"fra1",
"gru1",
"hnd1",
"iad1",
"icn1",
"kix1",
"lhr1",
"pdx1",
"sfo1",
"sin1",
"syd1",
];

View File

@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from "next/server";
async function handle(
req: NextRequest,
{ params }: { params: { action: string; key: string[] } },
) {
const requestUrl = new URL(req.url);
const endpoint = requestUrl.searchParams.get("endpoint");
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const [...key] = params.key;
// only allow to request to *.upstash.io
if (!endpoint || !new URL(endpoint).hostname.endsWith(".upstash.io")) {
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.key.join("/"),
},
{
status: 403,
},
);
}
// only allow upstash get and set method
if (params.action !== "get" && params.action !== "set") {
console.log("[Upstash Route] forbidden action ", params.action);
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.action,
},
{
status: 403,
},
);
}
const targetUrl = `${endpoint}/${params.action}/${params.key.join("/")}`;
const method = req.method;
const shouldNotHaveBody = ["get", "head"].includes(
method?.toLowerCase() ?? "",
);
const fetchOptions: RequestInit = {
headers: {
authorization: req.headers.get("authorization") ?? "",
},
body: shouldNotHaveBody ? null : req.body,
method,
// @ts-ignore
duplex: "half",
};
console.log("[Upstash Proxy]", targetUrl, fetchOptions);
const fetchResult = await fetch(targetUrl, fetchOptions);
console.log("[Any Proxy]", targetUrl, {
status: fetchResult.status,
statusText: fetchResult.statusText,
});
return fetchResult;
}
export const POST = handle;
export const GET = handle;
export const OPTIONS = handle;
export const runtime = "edge";

View File

@@ -0,0 +1,112 @@
import { NextRequest, NextResponse } from "next/server";
import { STORAGE_KEY } from "../../../constant";
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const folder = STORAGE_KEY;
const fileName = `${folder}/backup.json`;
const requestUrl = new URL(req.url);
let endpoint = requestUrl.searchParams.get("endpoint");
if (!endpoint?.endsWith("/")) {
endpoint += "/";
}
const endpointPath = params.path.join("/");
// only allow MKCOL, GET, PUT
if (req.method !== "MKCOL" && req.method !== "GET" && req.method !== "PUT") {
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.path.join("/"),
},
{
status: 403,
},
);
}
// for MKCOL request, only allow request ${folder}
if (
req.method == "MKCOL" &&
!new URL(endpointPath).pathname.endsWith(folder)
) {
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.path.join("/"),
},
{
status: 403,
},
);
}
// for GET request, only allow request ending with fileName
if (
req.method == "GET" &&
!new URL(endpointPath).pathname.endsWith(fileName)
) {
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.path.join("/"),
},
{
status: 403,
},
);
}
// for PUT request, only allow request ending with fileName
if (
req.method == "PUT" &&
!new URL(endpointPath).pathname.endsWith(fileName)
) {
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + params.path.join("/"),
},
{
status: 403,
},
);
}
const targetUrl = `${endpoint + endpointPath}`;
const method = req.method;
const shouldNotHaveBody = ["get", "head"].includes(
method?.toLowerCase() ?? "",
);
const fetchOptions: RequestInit = {
headers: {
authorization: req.headers.get("authorization") ?? "",
},
body: shouldNotHaveBody ? null : req.body,
method,
// @ts-ignore
duplex: "half",
};
const fetchResult = await fetch(targetUrl, fetchOptions);
console.log("[Any Proxy]", targetUrl, {
status: fetchResult.status,
statusText: fetchResult.statusText,
});
return fetchResult;
}
export const POST = handle;
export const GET = handle;
export const OPTIONS = handle;
export const runtime = "edge";

9
app/azure.ts Normal file
View File

@@ -0,0 +1,9 @@
export function makeAzurePath(path: string, apiVersion: string) {
// should omit /v1 prefix
path = path.replaceAll("v1/", "");
// should add api-key to query string
path += `${path.includes("?") ? "&" : "?"}api-version=${apiVersion}`;
return path;
}

View File

@@ -1,17 +1,30 @@
import { getClientConfig } from "../config/client";
import { ACCESS_CODE_PREFIX } from "../constant";
import { ChatMessage, ModelType, useAccessStore } from "../store";
import {
ACCESS_CODE_PREFIX,
Azure,
ModelProvider,
ServiceProvider,
} from "../constant";
import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
import { ChatGPTApi } from "./platforms/openai";
import { GeminiProApi } from "./platforms/google";
export const ROLES = ["system", "user", "assistant"] as const;
export type MessageRole = (typeof ROLES)[number];
export const Models = ["gpt-3.5-turbo", "gpt-4"] as const;
export type ChatModel = ModelType;
export interface MultimodalContent {
type: "text" | "image_url";
text?: string;
image_url?: {
url: string;
};
}
export interface RequestMessage {
role: MessageRole;
content: string;
content: string | MultimodalContent[];
}
export interface LLMConfig {
@@ -41,6 +54,13 @@ export interface LLMUsage {
export interface LLMModel {
name: string;
available: boolean;
provider: LLMModelProvider;
}
export interface LLMModelProvider {
id: string;
providerName: string;
providerType: string;
}
export abstract class LLMApi {
@@ -73,7 +93,11 @@ interface ChatProvider {
export class ClientApi {
public llm: LLMApi;
constructor() {
constructor(provider: ModelProvider = ModelProvider.GPT) {
if (provider === ModelProvider.GeminiPro) {
this.llm = new GeminiProApi();
return;
}
this.llm = new ChatGPTApi();
}
@@ -93,7 +117,7 @@ export class ClientApi {
{
from: "human",
value:
"Share from [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web",
"Share from [NextChat]: https://github.com/Yidadaa/ChatGPT-Next-Web",
},
]);
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
@@ -123,28 +147,38 @@ export class ClientApi {
}
}
export const api = new ClientApi();
export function getHeaders() {
const accessStore = useAccessStore.getState();
let headers: Record<string, string> = {
const headers: Record<string, string> = {
"Content-Type": "application/json",
"x-requested-with": "XMLHttpRequest",
Accept: "application/json",
};
const makeBearer = (token: string) => `Bearer ${token.trim()}`;
const modelConfig = useChatStore.getState().currentSession().mask.modelConfig;
const isGoogle = modelConfig.model.startsWith("gemini");
const isAzure = accessStore.provider === ServiceProvider.Azure;
const authHeader = isAzure ? "api-key" : "Authorization";
const apiKey = isGoogle
? accessStore.googleApiKey
: isAzure
? accessStore.azureApiKey
: accessStore.openaiApiKey;
const clientConfig = getClientConfig();
const makeBearer = (s: string) => `${isAzure ? "" : "Bearer "}${s.trim()}`;
const validString = (x: string) => x && x.length > 0;
// use user's api key first
if (validString(accessStore.token)) {
headers.Authorization = makeBearer(accessStore.token);
} else if (
accessStore.enabledAccessControl() &&
validString(accessStore.accessCode)
) {
headers.Authorization = makeBearer(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
// when using google api in app, not set auth header
if (!(isGoogle && clientConfig?.isApp)) {
// use user's api key first
if (validString(apiKey)) {
headers[authHeader] = makeBearer(apiKey);
} else if (
accessStore.enabledAccessControl() &&
validString(accessStore.accessCode)
) {
headers[authHeader] = makeBearer(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
}
}
return headers;

View File

@@ -0,0 +1,278 @@
import { Google, REQUEST_TIMEOUT_MS } from "@/app/constant";
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { getClientConfig } from "@/app/config/client";
import { DEFAULT_API_HOST } from "@/app/constant";
import {
getMessageTextContent,
getMessageImages,
isVisionModel,
} from "@/app/utils";
export class GeminiProApi implements LLMApi {
extractMessage(res: any) {
console.log("[Response] gemini-pro response: ", res);
return (
res?.candidates?.at(0)?.content?.parts.at(0)?.text ||
res?.error?.message ||
""
);
}
async chat(options: ChatOptions): Promise<void> {
// const apiClient = this;
const visionModel = isVisionModel(options.config.model);
let multimodal = false;
const messages = options.messages.map((v) => {
let parts: any[] = [{ text: getMessageTextContent(v) }];
if (visionModel) {
const images = getMessageImages(v);
if (images.length > 0) {
multimodal = true;
parts = parts.concat(
images.map((image) => {
const imageType = image.split(";")[0].split(":")[1];
const imageData = image.split(",")[1];
return {
inline_data: {
mime_type: imageType,
data: imageData,
},
};
}),
);
}
}
return {
role: v.role.replace("assistant", "model").replace("system", "user"),
parts: parts,
};
});
// google requires that role in neighboring messages must not be the same
for (let i = 0; i < messages.length - 1; ) {
// Check if current and next item both have the role "model"
if (messages[i].role === messages[i + 1].role) {
// Concatenate the 'parts' of the current and next item
messages[i].parts = messages[i].parts.concat(messages[i + 1].parts);
// Remove the next item
messages.splice(i + 1, 1);
} else {
// Move to the next item
i++;
}
}
// if (visionModel && messages.length > 1) {
// options.onError?.(new Error("Multiturn chat is not enabled for models/gemini-pro-vision"));
// }
const modelConfig = {
...useAppConfig.getState().modelConfig,
...useChatStore.getState().currentSession().mask.modelConfig,
...{
model: options.config.model,
},
};
const requestPayload = {
contents: messages,
generationConfig: {
// stopSequences: [
// "Title"
// ],
temperature: modelConfig.temperature,
maxOutputTokens: modelConfig.max_tokens,
topP: modelConfig.top_p,
// "topK": modelConfig.top_k,
},
safetySettings: [
{
category: "HARM_CATEGORY_HARASSMENT",
threshold: "BLOCK_ONLY_HIGH",
},
{
category: "HARM_CATEGORY_HATE_SPEECH",
threshold: "BLOCK_ONLY_HIGH",
},
{
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold: "BLOCK_ONLY_HIGH",
},
{
category: "HARM_CATEGORY_DANGEROUS_CONTENT",
threshold: "BLOCK_ONLY_HIGH",
},
],
};
const accessStore = useAccessStore.getState();
let baseUrl = accessStore.googleUrl;
const isApp = !!getClientConfig()?.isApp;
let shouldStream = !!options.config.stream;
const controller = new AbortController();
options.onController?.(controller);
try {
let googleChatPath = visionModel
? Google.VisionChatPath
: Google.ChatPath;
let chatPath = this.path(googleChatPath);
// let baseUrl = accessStore.googleUrl;
if (!baseUrl) {
baseUrl = isApp
? DEFAULT_API_HOST + "/api/proxy/google/" + googleChatPath
: chatPath;
}
if (isApp) {
baseUrl += `?key=${accessStore.googleApiKey}`;
}
const chatPayload = {
method: "POST",
body: JSON.stringify(requestPayload),
signal: controller.signal,
headers: getHeaders(),
};
// make a fetch request
const requestTimeoutId = setTimeout(
() => controller.abort(),
REQUEST_TIMEOUT_MS,
);
if (shouldStream) {
let responseText = "";
let remainText = "";
let finished = false;
let existingTexts: string[] = [];
const finish = () => {
finished = true;
options.onFinish(existingTexts.join(""));
};
// animate response to make it looks smooth
function animateResponseText() {
if (finished || controller.signal.aborted) {
responseText += remainText;
finish();
return;
}
if (remainText.length > 0) {
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
const fetchText = remainText.slice(0, fetchCount);
responseText += fetchText;
remainText = remainText.slice(fetchCount);
options.onUpdate?.(responseText, fetchText);
}
requestAnimationFrame(animateResponseText);
}
// start animaion
animateResponseText();
fetch(
baseUrl.replace("generateContent", "streamGenerateContent"),
chatPayload,
)
.then((response) => {
const reader = response?.body?.getReader();
const decoder = new TextDecoder();
let partialData = "";
return reader?.read().then(function processText({
done,
value,
}): Promise<any> {
if (done) {
if (response.status !== 200) {
try {
let data = JSON.parse(ensureProperEnding(partialData));
if (data && data[0].error) {
options.onError?.(new Error(data[0].error.message));
} else {
options.onError?.(new Error("Request failed"));
}
} catch (_) {
options.onError?.(new Error("Request failed"));
}
}
console.log("Stream complete");
// options.onFinish(responseText + remainText);
finished = true;
return Promise.resolve();
}
partialData += decoder.decode(value, { stream: true });
try {
let data = JSON.parse(ensureProperEnding(partialData));
const textArray = data.reduce(
(acc: string[], item: { candidates: any[] }) => {
const texts = item.candidates.map((candidate) =>
candidate.content.parts
.map((part: { text: any }) => part.text)
.join(""),
);
return acc.concat(texts);
},
[],
);
if (textArray.length > existingTexts.length) {
const deltaArray = textArray.slice(existingTexts.length);
existingTexts = textArray;
remainText += deltaArray.join("");
}
} catch (error) {
// console.log("[Response Animation] error: ", error,partialData);
// skip error message when parsing json
}
return reader.read().then(processText);
});
})
.catch((error) => {
console.error("Error:", error);
});
} else {
const res = await fetch(baseUrl, chatPayload);
clearTimeout(requestTimeoutId);
const resJson = await res.json();
if (resJson?.promptFeedback?.blockReason) {
// being blocked
options.onError?.(
new Error(
"Message is being blocked for reason: " +
resJson.promptFeedback.blockReason,
),
);
}
const message = this.extractMessage(resJson);
options.onFinish(message);
}
} catch (e) {
console.log("[Request] failed to make a chat request", e);
options.onError?.(e as Error);
}
}
usage(): Promise<LLMUsage> {
throw new Error("Method not implemented.");
}
async models(): Promise<LLMModel[]> {
return [];
}
path(path: string): string {
return "/api/google/" + path;
}
}
function ensureProperEnding(str: string) {
if (str.startsWith("[") && !str.endsWith("]")) {
return str + "]";
}
return str;
}

View File

@@ -1,12 +1,22 @@
"use client";
import {
ApiPath,
DEFAULT_API_HOST,
DEFAULT_MODELS,
OpenaiPath,
REQUEST_TIMEOUT_MS,
ServiceProvider,
} from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
import {
ChatOptions,
getHeaders,
LLMApi,
LLMModel,
LLMUsage,
MultimodalContent,
} from "../api";
import Locale from "../../locales";
import {
EventStreamContentType,
@@ -14,6 +24,12 @@ import {
} from "@fortaine/fetch-event-source";
import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client";
import { makeAzurePath } from "@/app/azure";
import {
getMessageTextContent,
getMessageImages,
isVisionModel,
} from "@/app/utils";
export interface OpenAIListModelResponse {
object: string;
@@ -28,20 +44,39 @@ export class ChatGPTApi implements LLMApi {
private disableListModels = true;
path(path: string): string {
let openaiUrl = useAccessStore.getState().openaiUrl;
const apiPath = "/api/openai";
const accessStore = useAccessStore.getState();
if (openaiUrl.length === 0) {
const isAzure = accessStore.provider === ServiceProvider.Azure;
if (isAzure && !accessStore.isValidAzure()) {
throw Error(
"incomplete azure config, please check it in your settings page",
);
}
let baseUrl = isAzure ? accessStore.azureUrl : accessStore.openaiUrl;
if (baseUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp;
openaiUrl = isApp ? DEFAULT_API_HOST : apiPath;
baseUrl = isApp
? DEFAULT_API_HOST + "/proxy" + ApiPath.OpenAI
: ApiPath.OpenAI;
}
if (openaiUrl.endsWith("/")) {
openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1);
if (baseUrl.endsWith("/")) {
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
}
if (!openaiUrl.startsWith("http") && !openaiUrl.startsWith(apiPath)) {
openaiUrl = "https://" + openaiUrl;
if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.OpenAI)) {
baseUrl = "https://" + baseUrl;
}
return [openaiUrl, path].join("/");
if (isAzure) {
path = makeAzurePath(path, accessStore.azureApiVersion);
}
console.log("[Proxy Endpoint] ", baseUrl, path);
return [baseUrl, path].join("/");
}
extractMessage(res: any) {
@@ -49,9 +84,10 @@ export class ChatGPTApi implements LLMApi {
}
async chat(options: ChatOptions) {
const visionModel = isVisionModel(options.config.model);
const messages = options.messages.map((v) => ({
role: v.role,
content: v.content,
content: visionModel ? v.content : getMessageTextContent(v),
}));
const modelConfig = {
@@ -70,8 +106,20 @@ export class ChatGPTApi implements LLMApi {
presence_penalty: modelConfig.presence_penalty,
frequency_penalty: modelConfig.frequency_penalty,
top_p: modelConfig.top_p,
// max_tokens: Math.max(modelConfig.max_tokens, 1024),
// Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
};
// add max_tokens to vision model
if (visionModel) {
Object.defineProperty(requestPayload, "max_tokens", {
enumerable: true,
configurable: true,
writable: true,
value: modelConfig.max_tokens,
});
}
console.log("[Request] openai payload: ", requestPayload);
const shouldStream = !!options.config.stream;
@@ -95,12 +143,35 @@ export class ChatGPTApi implements LLMApi {
if (shouldStream) {
let responseText = "";
let remainText = "";
let finished = false;
// animate response to make it looks smooth
function animateResponseText() {
if (finished || controller.signal.aborted) {
responseText += remainText;
console.log("[Response Animation] finished");
return;
}
if (remainText.length > 0) {
const fetchCount = Math.max(1, Math.round(remainText.length / 60));
const fetchText = remainText.slice(0, fetchCount);
responseText += fetchText;
remainText = remainText.slice(fetchCount);
options.onUpdate?.(responseText, fetchText);
}
requestAnimationFrame(animateResponseText);
}
// start animaion
animateResponseText();
const finish = () => {
if (!finished) {
options.onFinish(responseText);
finished = true;
options.onFinish(responseText + remainText);
}
};
@@ -154,14 +225,19 @@ export class ChatGPTApi implements LLMApi {
}
const text = msg.data;
try {
const json = JSON.parse(text);
const delta = json.choices[0].delta.content;
const json = JSON.parse(text) as {
choices: Array<{
delta: {
content: string;
};
}>;
};
const delta = json.choices[0]?.delta?.content;
if (delta) {
responseText += delta;
options.onUpdate?.(responseText, delta);
remainText += delta;
}
} catch (e) {
console.error("[Request] parse error", text, msg);
console.error("[Request] parse error", text);
}
},
onclose() {
@@ -275,6 +351,11 @@ export class ChatGPTApi implements LLMApi {
return chatModels.map((m) => ({
name: m.id,
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
}));
}
}

View File

@@ -12,9 +12,16 @@ import { getClientConfig } from "../config/client";
export function AuthPage() {
const navigate = useNavigate();
const access = useAccessStore();
const accessStore = useAccessStore();
const goHome = () => navigate(Path.Home);
const goChat = () => navigate(Path.Chat);
const resetAccessCode = () => {
accessStore.update((access) => {
access.openaiApiKey = "";
access.accessCode = "";
});
}; // Reset access code to empty string
useEffect(() => {
if (getClientConfig()?.isApp) {
@@ -36,19 +43,54 @@ export function AuthPage() {
className={styles["auth-input"]}
type="password"
placeholder={Locale.Auth.Input}
value={access.accessCode}
value={accessStore.accessCode}
onChange={(e) => {
access.updateCode(e.currentTarget.value);
accessStore.update(
(access) => (access.accessCode = e.currentTarget.value),
);
}}
/>
{!accessStore.hideUserApiKey ? (
<>
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
value={accessStore.openaiApiKey}
onChange={(e) => {
accessStore.update(
(access) => (access.openaiApiKey = e.currentTarget.value),
);
}}
/>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
value={accessStore.googleApiKey}
onChange={(e) => {
accessStore.update(
(access) => (access.googleApiKey = e.currentTarget.value),
);
}}
/>
</>
) : null}
<div className={styles["auth-actions"]}>
<IconButton
text={Locale.Auth.Confirm}
type="primary"
onClick={goHome}
onClick={goChat}
/>
<IconButton
text={Locale.Auth.Later}
onClick={() => {
resetAccessCode();
goHome();
}}
/>
<IconButton text={Locale.Auth.Later} onClick={goHome} />
</div>
</div>
);

View File

@@ -18,6 +18,7 @@ import { MaskAvatar } from "./mask";
import { Mask } from "../store/mask";
import { useRef, useEffect } from "react";
import { showConfirm } from "./ui-lib";
import { useMobileScreen } from "../utils";
export function ChatItem(props: {
onClick?: () => void;
@@ -60,7 +61,10 @@ export function ChatItem(props: {
{props.narrow ? (
<div className={styles["chat-item-narrow"]}>
<div className={styles["chat-item-avatar"] + " no-dark"}>
<MaskAvatar mask={props.mask} />
<MaskAvatar
avatar={props.mask.avatar}
model={props.mask.modelConfig.model}
/>
</div>
<div className={styles["chat-item-narrow-count"]}>
{props.count}
@@ -80,7 +84,11 @@ export function ChatItem(props: {
<div
className={styles["chat-item-delete"]}
onClickCapture={props.onDelete}
onClickCapture={(e) => {
props.onDelete?.();
e.preventDefault();
e.stopPropagation();
}}
>
<DeleteIcon />
</div>
@@ -101,6 +109,7 @@ export function ChatList(props: { narrow?: boolean }) {
);
const chatStore = useChatStore();
const navigate = useNavigate();
const isMobileScreen = useMobileScreen();
const onDragEnd: OnDragEndResponder = (result) => {
const { destination, source } = result;
@@ -142,7 +151,7 @@ export function ChatList(props: { narrow?: boolean }) {
}}
onDelete={async () => {
if (
!props.narrow ||
(!props.narrow && !isMobileScreen) ||
(await showConfirm(Locale.Home.DeleteChat))
) {
chatStore.deleteSession(i);

View File

@@ -1,5 +1,47 @@
@import "../styles/animation.scss";
.attach-images {
position: absolute;
left: 30px;
bottom: 32px;
display: flex;
}
.attach-image {
cursor: default;
width: 64px;
height: 64px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
border-radius: 5px;
margin-right: 10px;
background-size: cover;
background-position: center;
background-color: var(--white);
.attach-image-mask {
width: 100%;
height: 100%;
opacity: 0;
transition: all ease 0.2s;
}
.attach-image-mask:hover {
opacity: 1;
}
.delete-image {
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
float: right;
background-color: var(--white);
}
}
.chat-input-actions {
display: flex;
flex-wrap: wrap;
@@ -189,12 +231,10 @@
animation: slide-in ease 0.3s;
$linear: linear-gradient(
to right,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0)
);
$linear: linear-gradient(to right,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0));
mask-image: $linear;
@mixin show {
@@ -327,7 +367,7 @@
}
}
.chat-message-user > .chat-message-container {
.chat-message-user>.chat-message-container {
align-items: flex-end;
}
@@ -349,6 +389,7 @@
padding: 7px;
}
}
/* Specific styles for iOS devices */
@media screen and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 2) {
@supports (-webkit-touch-callout: none) {
@@ -381,6 +422,64 @@
transition: all ease 0.3s;
}
.chat-message-item-image {
width: 100%;
margin-top: 10px;
}
.chat-message-item-images {
width: 100%;
display: grid;
justify-content: left;
grid-gap: 10px;
grid-template-columns: repeat(var(--image-count), auto);
margin-top: 10px;
}
.chat-message-item-image-multi {
object-fit: cover;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.chat-message-item-image,
.chat-message-item-image-multi {
box-sizing: border-box;
border-radius: 10px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
}
@media only screen and (max-width: 600px) {
$calc-image-width: calc(100vw/3*2/var(--image-count));
.chat-message-item-image-multi {
width: $calc-image-width;
height: $calc-image-width;
}
.chat-message-item-image {
max-width: calc(100vw/3*2);
}
}
@media screen and (min-width: 600px) {
$max-image-width: calc(calc(1200px - var(--sidebar-width))/3*2/var(--image-count));
$image-width: calc(calc(var(--window-width) - var(--sidebar-width))/3*2/var(--image-count));
.chat-message-item-image-multi {
width: $image-width;
height: $image-width;
max-width: $max-image-width;
max-height: $max-image-width;
}
.chat-message-item-image {
max-width: calc(calc(1200px - var(--sidebar-width))/3*2);
}
}
.chat-message-action-date {
font-size: 12px;
opacity: 0.2;
@@ -395,7 +494,7 @@
z-index: 1;
}
.chat-message-user > .chat-message-container > .chat-message-item {
.chat-message-user>.chat-message-container>.chat-message-item {
background-color: var(--second);
&:hover {
@@ -460,6 +559,7 @@
@include single-line();
}
.hint-content {
font-size: 12px;
@@ -474,15 +574,26 @@
}
.chat-input-panel-inner {
cursor: text;
display: flex;
flex: 1;
border-radius: 10px;
border: var(--border-in-light);
}
.chat-input-panel-inner-attach {
padding-bottom: 80px;
}
.chat-input-panel-inner:has(.chat-input:focus) {
border: 1px solid var(--primary);
}
.chat-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: var(--border-in-light);
border: none;
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03);
background-color: var(--white);
color: var(--black);
@@ -494,9 +605,7 @@
min-height: 68px;
}
.chat-input:focus {
border: 1px solid var(--primary);
}
.chat-input:focus {}
.chat-input-send {
background-color: var(--primary);
@@ -515,4 +624,4 @@
.chat-input-send {
bottom: 30px;
}
}
}

View File

@@ -6,6 +6,7 @@ import React, {
useMemo,
useCallback,
Fragment,
RefObject,
} from "react";
import SendWhiteIcon from "../icons/send-white.svg";
@@ -15,6 +16,7 @@ import ExportIcon from "../icons/share.svg";
import ReturnIcon from "../icons/return.svg";
import CopyIcon from "../icons/copy.svg";
import LoadingIcon from "../icons/three-dots.svg";
import LoadingButtonIcon from "../icons/loading.svg";
import PromptIcon from "../icons/prompt.svg";
import MaskIcon from "../icons/mask.svg";
import MaxIcon from "../icons/max.svg";
@@ -27,6 +29,7 @@ import PinIcon from "../icons/pin.svg";
import EditIcon from "../icons/rename.svg";
import ConfirmIcon from "../icons/confirm.svg";
import CancelIcon from "../icons/cancel.svg";
import ImageIcon from "../icons/image.svg";
import LightIcon from "../icons/light.svg";
import DarkIcon from "../icons/dark.svg";
@@ -53,6 +56,10 @@ import {
selectOrCopy,
autoGrowTextArea,
useMobileScreen,
getMessageTextContent,
getMessageImages,
isVisionModel,
compressImage,
} from "../utils";
import dynamic from "next/dynamic";
@@ -73,13 +80,13 @@ import {
showPrompt,
showToast,
} from "./ui-lib";
import { useLocation, useNavigate } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import {
CHAT_PAGE_SIZE,
LAST_INPUT_KEY,
MAX_RENDER_MSG_COUNT,
Path,
REQUEST_TIMEOUT_MS,
UNFINISHED_INPUT,
} from "../constant";
import { Avatar } from "./emoji";
import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask";
@@ -88,6 +95,8 @@ import { ChatCommandPrefix, useChatCommand, useCommand } from "../command";
import { prettyObject } from "../utils/format";
import { ExportMessageModal } from "./exporter";
import { getClientConfig } from "../config/client";
import { useAllModels } from "../utils/hooks";
import { MultimodalContent } from "../client/api";
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () => <LoadingIcon />,
@@ -143,6 +152,7 @@ export function SessionConfigModel(props: { onClose: () => void }) {
extraListItems={
session.mask.modelConfig.sendMemory ? (
<ListItem
className="copyable"
title={`${Locale.Memory.Title} (${session.lastSummarizeIndex} of ${session.messages.length})`}
subTitle={session.memoryPrompt || Locale.Memory.EmptyContent}
></ListItem>
@@ -373,11 +383,13 @@ function ChatAction(props: {
);
}
function useScrollToBottom() {
function useScrollToBottom(
scrollRef: RefObject<HTMLDivElement>,
detach: boolean = false,
) {
// for auto-scroll
const scrollRef = useRef<HTMLDivElement>(null);
const [autoScroll, setAutoScroll] = useState(true);
const [autoScroll, setAutoScroll] = useState(true);
function scrollDomToBottom() {
const dom = scrollRef.current;
if (dom) {
@@ -390,7 +402,7 @@ function useScrollToBottom() {
// auto scroll
useEffect(() => {
if (autoScroll) {
if (autoScroll && !detach) {
scrollDomToBottom();
}
});
@@ -404,10 +416,14 @@ function useScrollToBottom() {
}
export function ChatActions(props: {
uploadImage: () => void;
setAttachImages: (images: string[]) => void;
setUploading: (uploading: boolean) => void;
showPromptModal: () => void;
scrollToBottom: () => void;
showPromptHints: () => void;
hitBottom: boolean;
uploading: boolean;
}) {
const config = useAppConfig();
const navigate = useNavigate();
@@ -429,15 +445,33 @@ export function ChatActions(props: {
// switch model
const currentModel = chatStore.currentSession().mask.modelConfig.model;
const allModels = useAllModels();
const models = useMemo(
() =>
config
.allModels()
.filter((m) => m.available)
.map((m) => m.name),
[config],
() => allModels.filter((m) => m.available),
[allModels],
);
const [showModelSelector, setShowModelSelector] = useState(false);
const [showUploadImage, setShowUploadImage] = useState(false);
useEffect(() => {
const show = isVisionModel(currentModel);
setShowUploadImage(show);
if (!show) {
props.setAttachImages([]);
props.setUploading(false);
}
// if current model is not available
// switch to first available model
const isUnavaliableModel = !models.some((m) => m.name === currentModel);
if (isUnavaliableModel && models.length > 0) {
const nextModel = models[0].name as ModelType;
chatStore.updateCurrentSession(
(session) => (session.mask.modelConfig.model = nextModel),
);
showToast(nextModel);
}
}, [chatStore, currentModel, models]);
return (
<div className={styles["chat-input-actions"]}>
@@ -463,6 +497,13 @@ export function ChatActions(props: {
/>
)}
{showUploadImage && (
<ChatAction
onClick={props.uploadImage}
text={Locale.Chat.InputActions.UploadImage}
icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
/>
)}
<ChatAction
onClick={nextTheme}
text={Locale.Chat.InputActions.Theme[theme]}
@@ -518,8 +559,8 @@ export function ChatActions(props: {
<Selector
defaultSelectedValue={currentModel}
items={models.map((m) => ({
title: m,
value: m,
title: m.displayName,
value: m.name,
}))}
onClose={() => setShowModelSelector(false)}
onSelection={(s) => {
@@ -598,6 +639,14 @@ export function EditMessageModal(props: { onClose: () => void }) {
);
}
export function DeleteImageButton(props: { deleteImage: () => void }) {
return (
<div className={styles["delete-image"]} onClick={props.deleteImage}>
<DeleteIcon />
</div>
);
}
function _Chat() {
type RenderMessage = ChatMessage & { preview?: boolean };
@@ -612,10 +661,22 @@ function _Chat() {
const [userInput, setUserInput] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { submitKey, shouldSubmit } = useSubmitHandler();
const { scrollRef, setAutoScroll, scrollDomToBottom } = useScrollToBottom();
const scrollRef = useRef<HTMLDivElement>(null);
const isScrolledToBottom = scrollRef?.current
? Math.abs(
scrollRef.current.scrollHeight -
(scrollRef.current.scrollTop + scrollRef.current.clientHeight),
) <= 1
: false;
const { setAutoScroll, scrollDomToBottom } = useScrollToBottom(
scrollRef,
isScrolledToBottom,
);
const [hitBottom, setHitBottom] = useState(true);
const isMobileScreen = useMobileScreen();
const navigate = useNavigate();
const [attachImages, setAttachImages] = useState<string[]>([]);
const [uploading, setUploading] = useState(false);
// prompt hints
const promptStore = usePromptStore();
@@ -693,7 +754,10 @@ function _Chat() {
return;
}
setIsLoading(true);
chatStore.onUserInput(userInput).then(() => setIsLoading(false));
chatStore
.onUserInput(userInput, attachImages)
.then(() => setIsLoading(false));
setAttachImages([]);
localStorage.setItem(LAST_INPUT_KEY, userInput);
setUserInput("");
setPromptHints([]);
@@ -771,9 +835,9 @@ function _Chat() {
};
const onRightClick = (e: any, message: ChatMessage) => {
// copy to clipboard
if (selectOrCopy(e.currentTarget, message.content)) {
if (selectOrCopy(e.currentTarget, getMessageTextContent(message))) {
if (userInput.length === 0) {
setUserInput(message.content);
setUserInput(getMessageTextContent(message));
}
e.preventDefault();
@@ -841,7 +905,9 @@ function _Chat() {
// resend the message
setIsLoading(true);
chatStore.onUserInput(userMessage.content).then(() => setIsLoading(false));
const textContent = getMessageTextContent(userMessage);
const images = getMessageImages(userMessage);
chatStore.onUserInput(textContent, images).then(() => setIsLoading(false));
inputRef.current?.focus();
};
@@ -935,7 +1001,8 @@ function _Chat() {
const isTouchTopEdge = e.scrollTop <= edgeThreshold;
const isTouchBottomEdge = bottomHeight >= e.scrollHeight - edgeThreshold;
const isHitBottom = bottomHeight >= e.scrollHeight - (isMobileScreen ? 0 : 10);
const isHitBottom =
bottomHeight >= e.scrollHeight - (isMobileScreen ? 4 : 10);
const prevPageMsgIndex = msgRenderIndex - CHAT_PAGE_SIZE;
const nextPageMsgIndex = msgRenderIndex + CHAT_PAGE_SIZE;
@@ -949,7 +1016,6 @@ function _Chat() {
setHitBottom(isHitBottom);
setAutoScroll(isHitBottom);
};
function scrollToBottom() {
setMsgRenderIndex(renderMessages.length - CHAT_PAGE_SIZE);
scrollDomToBottom();
@@ -974,14 +1040,17 @@ function _Chat() {
doSubmit(text);
},
code: (text) => {
if (accessStore.disableFastLink) return;
console.log("[Command] got code from url: ", text);
showConfirm(Locale.URLCommand.Code + `code = ${text}`).then((res) => {
if (res) {
accessStore.updateCode(text);
accessStore.update((access) => (access.accessCode = text));
}
});
},
settings: (text) => {
if (accessStore.disableFastLink) return;
try {
const payload = JSON.parse(text) as {
key?: string;
@@ -997,10 +1066,12 @@ function _Chat() {
).then((res) => {
if (!res) return;
if (payload.key) {
accessStore.updateToken(payload.key);
accessStore.update(
(access) => (access.openaiApiKey = payload.key!),
);
}
if (payload.url) {
accessStore.updateOpenAiUrl(payload.url);
accessStore.update((access) => (access.openaiUrl = payload.url!));
}
});
}
@@ -1013,6 +1084,109 @@ function _Chat() {
// edit / insert message modal
const [isEditingMessage, setIsEditingMessage] = useState(false);
// remember unfinished input
useEffect(() => {
// try to load from local storage
const key = UNFINISHED_INPUT(session.id);
const mayBeUnfinishedInput = localStorage.getItem(key);
if (mayBeUnfinishedInput && userInput.length === 0) {
setUserInput(mayBeUnfinishedInput);
localStorage.removeItem(key);
}
const dom = inputRef.current;
return () => {
localStorage.setItem(key, dom?.value ?? "");
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handlePaste = useCallback(
async (event: React.ClipboardEvent<HTMLTextAreaElement>) => {
const currentModel = chatStore.currentSession().mask.modelConfig.model;
if(!isVisionModel(currentModel)){return;}
const items = (event.clipboardData || window.clipboardData).items;
for (const item of items) {
if (item.kind === "file" && item.type.startsWith("image/")) {
event.preventDefault();
const file = item.getAsFile();
if (file) {
const images: string[] = [];
images.push(...attachImages);
images.push(
...(await new Promise<string[]>((res, rej) => {
setUploading(true);
const imagesData: string[] = [];
compressImage(file, 256 * 1024)
.then((dataUrl) => {
imagesData.push(dataUrl);
setUploading(false);
res(imagesData);
})
.catch((e) => {
setUploading(false);
rej(e);
});
})),
);
const imagesLength = images.length;
if (imagesLength > 3) {
images.splice(3, imagesLength - 3);
}
setAttachImages(images);
}
}
}
},
[attachImages, chatStore],
);
async function uploadImage() {
const images: string[] = [];
images.push(...attachImages);
images.push(
...(await new Promise<string[]>((res, rej) => {
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept =
"image/png, image/jpeg, image/webp, image/heic, image/heif";
fileInput.multiple = true;
fileInput.onchange = (event: any) => {
setUploading(true);
const files = event.target.files;
const imagesData: string[] = [];
for (let i = 0; i < files.length; i++) {
const file = event.target.files[i];
compressImage(file, 256 * 1024)
.then((dataUrl) => {
imagesData.push(dataUrl);
if (
imagesData.length === 3 ||
imagesData.length === files.length
) {
setUploading(false);
res(imagesData);
}
})
.catch((e) => {
setUploading(false);
rej(e);
});
}
};
fileInput.click();
})),
);
const imagesLength = images.length;
if (imagesLength > 3) {
images.splice(3, imagesLength - 3);
}
setAttachImages(images);
}
return (
<div className={styles.chat} key={session.id}>
<div className="window-header" data-tauri-drag-region>
@@ -1119,15 +1293,29 @@ function _Chat() {
onClick={async () => {
const newMessage = await showPrompt(
Locale.Chat.Actions.Edit,
message.content,
getMessageTextContent(message),
10,
);
let newContent: string | MultimodalContent[] =
newMessage;
const images = getMessageImages(message);
if (images.length > 0) {
newContent = [{ type: "text", text: newMessage }];
for (let i = 0; i < images.length; i++) {
newContent.push({
type: "image_url",
image_url: {
url: images[i],
},
});
}
}
chatStore.updateCurrentSession((session) => {
const m = session.mask.context
.concat(session.messages)
.find((m) => m.id === message.id);
if (m) {
m.content = newMessage;
m.content = newContent;
}
});
}}
@@ -1136,7 +1324,18 @@ function _Chat() {
{isUser ? (
<Avatar avatar={config.avatar} />
) : (
<MaskAvatar mask={session.mask} />
<>
{["system"].includes(message.role) ? (
<Avatar avatar="2699-fe0f" />
) : (
<MaskAvatar
avatar={session.mask.avatar}
model={
message.model || session.mask.modelConfig.model
}
/>
)}
</>
)}
</div>
@@ -1171,7 +1370,11 @@ function _Chat() {
<ChatAction
text={Locale.Chat.Actions.Copy}
icon={<CopyIcon />}
onClick={() => copyToClipboard(message.content)}
onClick={() =>
copyToClipboard(
getMessageTextContent(message),
)
}
/>
</>
)}
@@ -1186,7 +1389,7 @@ function _Chat() {
)}
<div className={styles["chat-message-item"]}>
<Markdown
content={message.content}
content={getMessageTextContent(message)}
loading={
(message.preview || message.streaming) &&
message.content.length === 0 &&
@@ -1195,12 +1398,42 @@ function _Chat() {
onContextMenu={(e) => onRightClick(e, message)}
onDoubleClickCapture={() => {
if (!isMobileScreen) return;
setUserInput(message.content);
setUserInput(getMessageTextContent(message));
}}
fontSize={fontSize}
parentRef={scrollRef}
defaultShow={i >= messages.length - 6}
/>
{getMessageImages(message).length == 1 && (
<img
className={styles["chat-message-item-image"]}
src={getMessageImages(message)[0]}
alt=""
/>
)}
{getMessageImages(message).length > 1 && (
<div
className={styles["chat-message-item-images"]}
style={
{
"--image-count": getMessageImages(message).length,
} as React.CSSProperties
}
>
{getMessageImages(message).map((image, index) => {
return (
<img
className={
styles["chat-message-item-image-multi"]
}
key={index}
src={image}
alt=""
/>
);
})}
</div>
)}
</div>
<div className={styles["chat-message-action-date"]}>
@@ -1220,9 +1453,13 @@ function _Chat() {
<PromptHints prompts={promptHints} onPromptSelect={onPromptSelect} />
<ChatActions
uploadImage={uploadImage}
setAttachImages={setAttachImages}
setUploading={setUploading}
showPromptModal={() => setShowPromptModal(true)}
scrollToBottom={scrollToBottom}
hitBottom={hitBottom}
uploading={uploading}
showPromptHints={() => {
// Click again to close
if (promptHints.length > 0) {
@@ -1235,8 +1472,16 @@ function _Chat() {
onSearch("");
}}
/>
<div className={styles["chat-input-panel-inner"]}>
<label
className={`${styles["chat-input-panel-inner"]} ${
attachImages.length != 0
? styles["chat-input-panel-inner-attach"]
: ""
}`}
htmlFor="chat-input"
>
<textarea
id="chat-input"
ref={inputRef}
className={styles["chat-input"]}
placeholder={Locale.Chat.Input(submitKey)}
@@ -1245,12 +1490,36 @@ function _Chat() {
onKeyDown={onInputKeyDown}
onFocus={scrollToBottom}
onClick={scrollToBottom}
onPaste={handlePaste}
rows={inputRows}
autoFocus={autoFocus}
style={{
fontSize: config.fontSize,
}}
/>
{attachImages.length != 0 && (
<div className={styles["attach-images"]}>
{attachImages.map((image, index) => {
return (
<div
key={index}
className={styles["attach-image"]}
style={{ backgroundImage: `url("${image}")` }}
>
<div className={styles["attach-image-mask"]}>
<DeleteImageButton
deleteImage={() => {
setAttachImages(
attachImages.filter((_, i) => i !== index),
);
}}
/>
</div>
</div>
);
})}
</div>
)}
<IconButton
icon={<SendWhiteIcon />}
text={Locale.Chat.Send}
@@ -1258,7 +1527,7 @@ function _Chat() {
type="primary"
onClick={() => doSubmit(userInput)}
/>
</div>
</label>
</div>
{showExport && (

View File

@@ -10,7 +10,10 @@ import BotIcon from "../icons/bot.svg";
import BlackBotIcon from "../icons/black-bot.svg";
export function getEmojiUrl(unified: string, style: EmojiStyle) {
return `https://cdn.staticfile.org/emoji-datasource-apple/14.0.0/img/${style}/64/${unified}.png`;
// Whoever owns this Content Delivery Network (CDN), I am using your CDN to serve emojis
// Old CDN broken, so I had to switch to this one
// Author: https://github.com/H0llyW00dzZ
return `https://fastly.jsdelivr.net/npm/emoji-datasource-apple/img/${style}/64/${unified}.png`;
}
export function AvatarPicker(props: {
@@ -18,6 +21,7 @@ export function AvatarPicker(props: {
}) {
return (
<EmojiPicker
width={"100%"}
lazyLoadEmojis
theme={EmojiTheme.AUTO}
getEmojiUrl={getEmojiUrl}

View File

@@ -4,8 +4,8 @@ import GithubIcon from "../icons/github.svg";
import ResetIcon from "../icons/reload.svg";
import { ISSUE_URL } from "../constant";
import Locale from "../locales";
import { downloadAs } from "../utils";
import { showConfirm } from "./ui-lib";
import { useSyncStore } from "../store/sync";
interface IErrorBoundaryState {
hasError: boolean;
@@ -26,10 +26,7 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
clearAndSaveData() {
try {
downloadAs(
JSON.stringify(localStorage),
"chatgpt-next-web-snapshot.json",
);
useSyncStore.getState().export();
} finally {
localStorage.clear();
location.reload();

View File

@@ -94,6 +94,7 @@
button {
flex-grow: 1;
&:not(:last-child) {
margin-right: 10px;
}
@@ -186,9 +187,63 @@
box-shadow: var(--card-shadow);
border: var(--border-in-light);
*:not(li) {
code,
pre {
overflow: hidden;
}
.message-image {
width: 100%;
margin-top: 10px;
}
.message-images {
display: grid;
justify-content: left;
grid-gap: 10px;
grid-template-columns: repeat(var(--image-count), auto);
margin-top: 10px;
}
@media screen and (max-width: 600px) {
$image-width: calc(calc(100vw/2)/var(--image-count));
.message-image-multi {
width: $image-width;
height: $image-width;
}
.message-image {
max-width: calc(100vw/3*2);
}
}
@media screen and (min-width: 600px) {
$max-image-width: calc(900px/3*2/var(--image-count));
$image-width: calc(80vw/3*2/var(--image-count));
.message-image-multi {
width: $image-width;
height: $image-width;
max-width: $max-image-width;
max-height: $max-image-width;
}
.message-image {
max-width: calc(100vw/3*2);
}
}
.message-image-multi {
object-fit: cover;
}
.message-image,
.message-image-multi {
box-sizing: border-box;
border-radius: 10px;
border: rgba($color: #888, $alpha: 0.2) 1px solid;
}
}
&-assistant {
@@ -212,6 +267,5 @@
}
}
.default-theme {
}
}
.default-theme {}
}

View File

@@ -1,5 +1,5 @@
/* eslint-disable @next/next/no-img-element */
import { ChatMessage, useAppConfig, useChatStore } from "../store";
import { ChatMessage, ModelType, useAppConfig, useChatStore } from "../store";
import Locale from "../locales";
import styles from "./exporter.module.scss";
import {
@@ -12,7 +12,12 @@ import {
showToast,
} from "./ui-lib";
import { IconButton } from "./button";
import { copyToClipboard, downloadAs, useMobileScreen } from "../utils";
import {
copyToClipboard,
downloadAs,
getMessageImages,
useMobileScreen,
} from "../utils";
import CopyIcon from "../icons/copy.svg";
import LoadingIcon from "../icons/three-dots.svg";
@@ -27,12 +32,14 @@ import { Avatar } from "./emoji";
import dynamic from "next/dynamic";
import NextImage from "next/image";
import { toBlob, toJpeg, toPng } from "html-to-image";
import { toBlob, toPng } from "html-to-image";
import { DEFAULT_MASK_AVATAR } from "../store/mask";
import { api } from "../client/api";
import { prettyObject } from "../utils/format";
import { EXPORT_MESSAGE_CLASS_NAME } from "../constant";
import { EXPORT_MESSAGE_CLASS_NAME, ModelProvider } from "../constant";
import { getClientConfig } from "../config/client";
import { ClientApi } from "../client/api";
import { getMessageTextContent } from "../utils";
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () => <LoadingIcon />,
@@ -41,7 +48,22 @@ const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
export function ExportMessageModal(props: { onClose: () => void }) {
return (
<div className="modal-mask">
<Modal title={Locale.Export.Title} onClose={props.onClose}>
<Modal
title={Locale.Export.Title}
onClose={props.onClose}
footer={
<div
style={{
width: "100%",
textAlign: "center",
fontSize: 14,
opacity: 0.5,
}}
>
{Locale.Exporter.Description.Title}
</div>
}
>
<div style={{ minHeight: "40vh" }}>
<MessageExporter />
</div>
@@ -149,7 +171,7 @@ export function MessageExporter() {
if (exportConfig.includeContext) {
ret.push(...session.mask.context);
}
ret.push(...session.messages.filter((m, i) => selection.has(m.id)));
ret.push(...session.messages.filter((m) => selection.has(m.id)));
return ret;
}, [
exportConfig.includeContext,
@@ -260,7 +282,8 @@ export function RenderExport(props: {
});
props.onRender(renderMsgs);
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div ref={domRef}>
@@ -270,7 +293,7 @@ export function RenderExport(props: {
id={`${m.role}:${i}`}
className={EXPORT_MESSAGE_CLASS_NAME}
>
<Markdown content={m.content} defaultShow />
<Markdown content={getMessageTextContent(m)} defaultShow />
</div>
))}
</div>
@@ -285,10 +308,17 @@ export function PreviewActions(props: {
}) {
const [loading, setLoading] = useState(false);
const [shouldExport, setShouldExport] = useState(false);
const config = useAppConfig();
const onRenderMsgs = (msgs: ChatMessage[]) => {
setShouldExport(false);
var api: ClientApi;
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
}
api
.share(msgs)
.then((res) => {
@@ -433,25 +463,55 @@ export function ImagePreviewer(props: {
const isMobile = useMobileScreen();
const download = () => {
const download = async () => {
showToast(Locale.Export.Image.Toast);
const dom = previewRef.current;
if (!dom) return;
toPng(dom)
.then((blob) => {
if (!blob) return;
if (isMobile || getClientConfig()?.isApp) {
showImageModal(blob);
const isApp = getClientConfig()?.isApp;
try {
const blob = await toPng(dom);
if (!blob) return;
if (isMobile || (isApp && window.__TAURI__)) {
if (isApp && window.__TAURI__) {
const result = await window.__TAURI__.dialog.save({
defaultPath: `${props.topic}.png`,
filters: [
{
name: "PNG Files",
extensions: ["png"],
},
{
name: "All Files",
extensions: ["*"],
},
],
});
if (result !== null) {
const response = await fetch(blob);
const buffer = await response.arrayBuffer();
const uint8Array = new Uint8Array(buffer);
await window.__TAURI__.fs.writeBinaryFile(result, uint8Array);
showToast(Locale.Download.Success);
} else {
showToast(Locale.Download.Failed);
}
} else {
const link = document.createElement("a");
link.download = `${props.topic}.png`;
link.href = blob;
link.click();
refreshPreview();
showImageModal(blob);
}
})
.catch((e) => console.log("[Export Image] ", e));
} else {
const link = document.createElement("a");
link.download = `${props.topic}.png`;
link.href = blob;
link.click();
refreshPreview();
}
} catch (error) {
showToast(Locale.Download.Failed);
}
};
const refreshPreview = () => {
@@ -484,7 +544,7 @@ export function ImagePreviewer(props: {
</div>
<div>
<div className={styles["main-title"]}>ChatGPT Next Web</div>
<div className={styles["main-title"]}>NextChat</div>
<div className={styles["sub-title"]}>
github.com/Yidadaa/ChatGPT-Next-Web
</div>
@@ -526,10 +586,37 @@ export function ImagePreviewer(props: {
<div className={styles["body"]}>
<Markdown
content={m.content}
content={getMessageTextContent(m)}
fontSize={config.fontSize}
defaultShow
/>
{getMessageImages(m).length == 1 && (
<img
key={i}
src={getMessageImages(m)[0]}
alt="message"
className={styles["message-image"]}
/>
)}
{getMessageImages(m).length > 1 && (
<div
className={styles["message-images"]}
style={
{
"--image-count": getMessageImages(m).length,
} as React.CSSProperties
}
>
{getMessageImages(m).map((src, i) => (
<img
key={i}
src={src}
alt="message"
className={styles["message-image-multi"]}
/>
))}
</div>
)}
</div>
</div>
);
@@ -548,8 +635,10 @@ export function MarkdownPreviewer(props: {
props.messages
.map((m) => {
return m.role === "user"
? `## ${Locale.Export.MessageFromYou}:\n${m.content}`
: `## ${Locale.Export.MessageFromChatGPT}:\n${m.content.trim()}`;
? `## ${Locale.Export.MessageFromYou}:\n${getMessageTextContent(m)}`
: `## ${Locale.Export.MessageFromChatGPT}:\n${getMessageTextContent(
m,
).trim()}`;
})
.join("\n\n");
@@ -574,8 +663,6 @@ export function MarkdownPreviewer(props: {
);
}
// modified by BackTrackZ now it's looks better
export function JsonPreviewer(props: {
messages: ChatMessage[];
topic: string;

View File

@@ -6,7 +6,7 @@
color: var(--black);
background-color: var(--white);
min-width: 600px;
min-height: 480px;
min-height: 370px;
max-width: 1200px;
display: flex;

View File

@@ -12,7 +12,7 @@ import LoadingIcon from "../icons/three-dots.svg";
import { getCSSVar, useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
import { Path, SlotID } from "../constant";
import { ModelProvider, Path, SlotID } from "../constant";
import { ErrorBoundary } from "./error";
import { getISOLang, getLang } from "../locales";
@@ -27,7 +27,7 @@ import { SideBar } from "./sidebar";
import { useAppConfig } from "../store/config";
import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client";
import { api } from "../client/api";
import { ClientApi } from "../client/api";
import { useAccessStore } from "../store";
export function Loading(props: { noLogo?: boolean }) {
@@ -115,7 +115,10 @@ const loadAsyncGoogleFont = () => {
getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl;
linkEl.rel = "stylesheet";
linkEl.href =
googleFontUrl + "/css2?family=Noto+Sans:wght@300;400;700;900&display=swap";
googleFontUrl +
"/css2?family=" +
encodeURIComponent("Noto Sans:wght@300;400;700;900") +
"&display=swap";
document.head.appendChild(linkEl);
};
@@ -125,6 +128,8 @@ function Screen() {
const isHome = location.pathname === Path.Home;
const isAuth = location.pathname === Path.Auth;
const isMobileScreen = useMobileScreen();
const shouldTightBorder =
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
useEffect(() => {
loadAsyncGoogleFont();
@@ -134,11 +139,9 @@ function Screen() {
<div
className={
styles.container +
` ${
config.tightBorder && !isMobileScreen
? styles["tight-container"]
: styles.container
} ${getLang() === "ar" ? styles["rtl-screen"] : ""}`
` ${shouldTightBorder ? styles["tight-container"] : styles.container} ${
getLang() === "ar" ? styles["rtl-screen"] : ""
}`
}
>
{isAuth ? (
@@ -167,6 +170,12 @@ function Screen() {
export function useLoadData() {
const config = useAppConfig();
var api: ClientApi;
if (config.modelConfig.model.startsWith("gemini")) {
api = new ClientApi(ModelProvider.GeminiPro);
} else {
api = new ClientApi(ModelProvider.GPT);
}
useEffect(() => {
(async () => {
const models = await api.llm.models();

View File

@@ -5,13 +5,13 @@ import RemarkBreaks from "remark-breaks";
import RehypeKatex from "rehype-katex";
import RemarkGfm from "remark-gfm";
import RehypeHighlight from "rehype-highlight";
import { useRef, useState, RefObject, useEffect } from "react";
import { useRef, useState, RefObject, useEffect, useMemo } from "react";
import { copyToClipboard } from "../utils";
import mermaid from "mermaid";
import LoadingIcon from "../icons/three-dots.svg";
import React from "react";
import { useDebouncedCallback, useThrottledCallback } from "use-debounce";
import { useDebouncedCallback } from "use-debounce";
import { showImageModal } from "./ui-lib";
export function Mermaid(props: { code: string }) {
@@ -99,7 +99,29 @@ export function PreCode(props: { children: any }) {
);
}
function escapeDollarNumber(text: string) {
let escapedText = "";
for (let i = 0; i < text.length; i += 1) {
let char = text[i];
const nextChar = text[i + 1] || " ";
if (char === "$" && nextChar >= "0" && nextChar <= "9") {
char = "\\$";
}
escapedText += char;
}
return escapedText;
}
function _MarkDownContent(props: { content: string }) {
const escapedContent = useMemo(
() => escapeDollarNumber(props.content),
[props.content],
);
return (
<ReactMarkdown
remarkPlugins={[RemarkMath, RemarkGfm, RemarkBreaks]}
@@ -124,7 +146,7 @@ function _MarkDownContent(props: { content: string }) {
},
}}
>
{props.content}
{escapedContent}
</ReactMarkdown>
);
}
@@ -151,6 +173,7 @@ export function Markdown(
ref={mdRef}
onContextMenu={props.onContextMenu}
onDoubleClickCapture={props.onDoubleClickCapture}
dir="auto"
>
{props.loading ? (
<LoadingIcon />

View File

@@ -18,10 +18,11 @@ import {
ChatMessage,
createMessage,
ModelConfig,
ModelType,
useAppConfig,
useChatStore,
} from "../store";
import { ROLES } from "../client/api";
import { MultimodalContent, ROLES } from "../client/api";
import {
Input,
List,
@@ -37,7 +38,12 @@ import { useNavigate } from "react-router-dom";
import chatStyle from "./chat.module.scss";
import { useEffect, useState } from "react";
import { copyToClipboard, downloadAs, readFromFile } from "../utils";
import {
copyToClipboard,
downloadAs,
getMessageImages,
readFromFile,
} from "../utils";
import { Updater } from "../typing";
import { ModelConfigList } from "./model-config";
import { FileName, Path } from "../constant";
@@ -49,6 +55,7 @@ import {
Draggable,
OnDragEndResponder,
} from "@hello-pangea/dnd";
import { getMessageTextContent } from "../utils";
// drag and drop helper function
function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
@@ -58,11 +65,11 @@ function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
return result;
}
export function MaskAvatar(props: { mask: Mask }) {
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
<Avatar avatar={props.mask.avatar} />
export function MaskAvatar(props: { avatar: string; model?: ModelType }) {
return props.avatar !== DEFAULT_MASK_AVATAR ? (
<Avatar avatar={props.avatar} />
) : (
<Avatar model={props.mask.modelConfig.model} />
<Avatar model={props.model} />
);
}
@@ -123,7 +130,10 @@ export function MaskConfig(props: {
onClick={() => setShowPicker(true)}
style={{ cursor: "pointer" }}
>
<MaskAvatar mask={props.mask} />
<MaskAvatar
avatar={props.mask.avatar}
model={props.mask.modelConfig.model}
/>
</div>
</Popover>
</ListItem>
@@ -240,7 +250,7 @@ function ContextPromptItem(props: {
</>
)}
<Input
value={props.prompt.content}
value={getMessageTextContent(props.prompt)}
type="text"
className={chatStyle["context-content"]}
rows={focusingInput ? 5 : 1}
@@ -285,7 +295,18 @@ export function ContextPrompts(props: {
};
const updateContextPrompt = (i: number, prompt: ChatMessage) => {
props.updateContext((context) => (context[i] = prompt));
props.updateContext((context) => {
const images = getMessageImages(context[i]);
context[i] = prompt;
if (images.length > 0) {
const text = getMessageTextContent(context[i]);
const newContext: MultimodalContent[] = [{ type: "text", text }];
for (const img of images) {
newContext.push({ type: "image_url", image_url: { url: img } });
}
context[i].content = newContext;
}
});
};
const onDragEnd: OnDragEndResponder = (result) => {
@@ -393,11 +414,13 @@ export function MaskPage() {
const [searchText, setSearchText] = useState("");
const masks = searchText.length > 0 ? searchMasks : allMasks;
// simple search, will refactor later
// refactored already, now it accurate
const onSearch = (text: string) => {
setSearchText(text);
if (text.length > 0) {
const result = allMasks.filter((m) => m.name.includes(text));
const result = allMasks.filter((m) =>
m.name.toLowerCase().includes(text.toLowerCase()),
);
setSearchMasks(result);
} else {
setSearchMasks(allMasks);
@@ -410,7 +433,7 @@ export function MaskPage() {
const closeMaskModal = () => setEditingMaskId(undefined);
const downloadAll = () => {
downloadAs(JSON.stringify(masks), FileName.Masks);
downloadAs(JSON.stringify(masks.filter((v) => !v.builtin)), FileName.Masks);
};
const importFromFile = () => {
@@ -452,11 +475,13 @@ export function MaskPage() {
icon={<DownloadIcon />}
bordered
onClick={downloadAll}
text={Locale.UI.Export}
/>
</div>
<div className="window-action-button">
<IconButton
icon={<UploadIcon />}
text={Locale.UI.Import}
bordered
onClick={() => importFromFile()}
/>
@@ -519,7 +544,7 @@ export function MaskPage() {
<div className={styles["mask-item"]} key={m.id}>
<div className={styles["mask-header"]}>
<div className={styles["mask-icon"]}>
<MaskAvatar mask={m} />
<MaskAvatar avatar={m.avatar} model={m.modelConfig.model} />
</div>
<div className={styles["mask-title"]}>
<div className={styles["mask-name"]}>{m.name}</div>
@@ -604,7 +629,7 @@ export function MaskPage() {
<MaskConfig
mask={editingMask}
updateMask={(updater) =>
maskStore.update(editingMaskId!, updater)
maskStore.updateMask(editingMaskId!, updater)
}
readonly={editingMask.builtin}
/>

View File

@@ -58,8 +58,8 @@
}
.body {
flex-grow: 1;
max-width: calc(100% - 40px);
flex: 1;
max-width: calc(100% - 80px);
.date {
font-size: 12px;
@@ -71,6 +71,12 @@
font-size: 12px;
}
}
.checkbox {
display: flex;
justify-content: flex-end;
flex: 1;
}
}
}
}

View File

@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { ChatMessage, useAppConfig, useChatStore } from "../store";
import { Updater } from "../typing";
import { IconButton } from "./button";
@@ -7,6 +7,7 @@ import { MaskAvatar } from "./mask";
import Locale from "../locales";
import styles from "./message-selector.module.scss";
import { getMessageTextContent } from "../utils";
function useShiftRange() {
const [startIndex, setStartIndex] = useState<number>();
@@ -73,11 +74,23 @@ export function MessageSelector(props: {
const chatStore = useChatStore();
const session = chatStore.currentSession();
const isValid = (m: ChatMessage) => m.content && !m.isError && !m.streaming;
const messages = session.messages.filter(
(m, i) =>
m.id && // message must have id
isValid(m) &&
(i >= session.messages.length - 1 || isValid(session.messages[i + 1])),
const allMessages = useMemo(() => {
let startIndex = Math.max(0, session.clearContextIndex ?? 0);
if (startIndex === session.messages.length - 1) {
startIndex = 0;
}
return session.messages.slice(startIndex);
}, [session.messages, session.clearContextIndex]);
const messages = useMemo(
() =>
allMessages.filter(
(m, i) =>
m.id && // message must have id
isValid(m) &&
(i >= allMessages.length - 1 || isValid(allMessages[i + 1])),
),
[allMessages],
);
const messageCount = messages.length;
const config = useAppConfig();
@@ -91,7 +104,9 @@ export function MessageSelector(props: {
const searchResults = new Set<string>();
if (text.length > 0) {
messages.forEach((m) =>
m.content.includes(text) ? searchResults.add(m.id!) : null,
getMessageTextContent(m).includes(text)
? searchResults.add(m.id!)
: null,
);
}
setSearchIds(searchResults);
@@ -176,6 +191,8 @@ export function MessageSelector(props: {
<div className={styles["messages"]}>
{messages.map((m, i) => {
if (!isInSearchResult(m.id!)) return null;
const id = m.id ?? i;
const isSelected = props.selection.has(id);
return (
<div
@@ -185,7 +202,6 @@ export function MessageSelector(props: {
key={i}
onClick={() => {
props.updateSelection((selection) => {
const id = m.id ?? i;
selection.has(id) ? selection.delete(id) : selection.add(id);
});
onClickIndex(i);
@@ -195,7 +211,10 @@ export function MessageSelector(props: {
{m.role === "user" ? (
<Avatar avatar={config.avatar}></Avatar>
) : (
<MaskAvatar mask={session.mask} />
<MaskAvatar
avatar={session.mask.avatar}
model={m.model || session.mask.modelConfig.model}
/>
)}
</div>
<div className={styles["body"]}>
@@ -203,9 +222,13 @@ export function MessageSelector(props: {
{new Date(m.date).toLocaleString()}
</div>
<div className={`${styles["content"]} one-line`}>
{m.content}
{getMessageTextContent(m)}
</div>
</div>
<div className={styles["checkbox"]}>
<input type="checkbox" checked={isSelected}></input>
</div>
</div>
);
})}

View File

@@ -1,14 +1,15 @@
import { ModalConfigValidator, ModelConfig, useAppConfig } from "../store";
import { ModalConfigValidator, ModelConfig } from "../store";
import Locale from "../locales";
import { InputRange } from "./input-range";
import { ListItem, Select } from "./ui-lib";
import { useAllModels } from "../utils/hooks";
export function ModelConfigList(props: {
modelConfig: ModelConfig;
updateConfig: (updater: (config: ModelConfig) => void) => void;
}) {
const config = useAppConfig();
const allModels = useAllModels();
return (
<>
@@ -24,11 +25,13 @@ export function ModelConfigList(props: {
);
}}
>
{config.allModels().map((v, i) => (
<option value={v.name} key={i} disabled={!v.available}>
{v.name}
</option>
))}
{allModels
.filter((v) => v.available)
.map((v, i) => (
<option value={v.name} key={i}>
{v.displayName}({v.provider?.providerName})
</option>
))}
</Select>
</ListItem>
<ListItem
@@ -75,8 +78,8 @@ export function ModelConfigList(props: {
>
<input
type="number"
min={100}
max={100000}
min={1024}
max={512000}
value={props.modelConfig.max_tokens}
onChange={(e) =>
props.updateConfig(
@@ -88,79 +91,84 @@ export function ModelConfigList(props: {
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.PresencePenalty.Title}
subTitle={Locale.Settings.PresencePenalty.SubTitle}
>
<InputRange
value={props.modelConfig.presence_penalty?.toFixed(1)}
min="-2"
max="2"
step="0.1"
onChange={(e) => {
props.updateConfig(
(config) =>
(config.presence_penalty =
ModalConfigValidator.presence_penalty(
e.currentTarget.valueAsNumber,
)),
);
}}
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.FrequencyPenalty.Title}
subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
>
<InputRange
value={props.modelConfig.frequency_penalty?.toFixed(1)}
min="-2"
max="2"
step="0.1"
onChange={(e) => {
props.updateConfig(
(config) =>
(config.frequency_penalty =
ModalConfigValidator.frequency_penalty(
e.currentTarget.valueAsNumber,
)),
);
}}
></InputRange>
</ListItem>
{props.modelConfig.model.startsWith("gemini") ? null : (
<>
<ListItem
title={Locale.Settings.PresencePenalty.Title}
subTitle={Locale.Settings.PresencePenalty.SubTitle}
>
<InputRange
value={props.modelConfig.presence_penalty?.toFixed(1)}
min="-2"
max="2"
step="0.1"
onChange={(e) => {
props.updateConfig(
(config) =>
(config.presence_penalty =
ModalConfigValidator.presence_penalty(
e.currentTarget.valueAsNumber,
)),
);
}}
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.InjectSystemPrompts.Title}
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
>
<input
type="checkbox"
checked={props.modelConfig.enableInjectSystemPrompts}
onChange={(e) =>
props.updateConfig(
(config) =>
(config.enableInjectSystemPrompts = e.currentTarget.checked),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.FrequencyPenalty.Title}
subTitle={Locale.Settings.FrequencyPenalty.SubTitle}
>
<InputRange
value={props.modelConfig.frequency_penalty?.toFixed(1)}
min="-2"
max="2"
step="0.1"
onChange={(e) => {
props.updateConfig(
(config) =>
(config.frequency_penalty =
ModalConfigValidator.frequency_penalty(
e.currentTarget.valueAsNumber,
)),
);
}}
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.InputTemplate.Title}
subTitle={Locale.Settings.InputTemplate.SubTitle}
>
<input
type="text"
value={props.modelConfig.template}
onChange={(e) =>
props.updateConfig(
(config) => (config.template = e.currentTarget.value),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.InjectSystemPrompts.Title}
subTitle={Locale.Settings.InjectSystemPrompts.SubTitle}
>
<input
type="checkbox"
checked={props.modelConfig.enableInjectSystemPrompts}
onChange={(e) =>
props.updateConfig(
(config) =>
(config.enableInjectSystemPrompts =
e.currentTarget.checked),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.InputTemplate.Title}
subTitle={Locale.Settings.InputTemplate.SubTitle}
>
<input
type="text"
value={props.modelConfig.template}
onChange={(e) =>
props.updateConfig(
(config) => (config.template = e.currentTarget.value),
)
}
></input>
</ListItem>
</>
)}
<ListItem
title={Locale.Settings.HistoryCount.Title}
subTitle={Locale.Settings.HistoryCount.SubTitle}

View File

@@ -17,21 +17,13 @@ import { useCommand } from "../command";
import { showConfirm } from "./ui-lib";
import { BUILTIN_MASK_STORE } from "../masks";
function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
const xmin = Math.max(aRect.x, bRect.x);
const xmax = Math.min(aRect.x + aRect.width, bRect.x + bRect.width);
const ymin = Math.max(aRect.y, bRect.y);
const ymax = Math.min(aRect.y + aRect.height, bRect.y + bRect.height);
const width = xmax - xmin;
const height = ymax - ymin;
const intersectionArea = width < 0 || height < 0 ? 0 : width * height;
return intersectionArea;
}
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
return (
<div className={styles["mask"]} onClick={props.onClick}>
<MaskAvatar mask={props.mask} />
<MaskAvatar
avatar={props.mask.avatar}
model={props.mask.modelConfig.model}
/>
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
</div>
);

View File

@@ -5,6 +5,8 @@
.avatar {
cursor: pointer;
position: relative;
z-index: 1;
}
.edit-prompt-modal {

View File

@@ -10,6 +10,15 @@ import ClearIcon from "../icons/clear.svg";
import LoadingIcon from "../icons/three-dots.svg";
import EditIcon from "../icons/edit.svg";
import EyeIcon from "../icons/eye.svg";
import DownloadIcon from "../icons/download.svg";
import UploadIcon from "../icons/upload.svg";
import ConfigIcon from "../icons/config.svg";
import ConfirmIcon from "../icons/confirm.svg";
import ConnectionIcon from "../icons/connection.svg";
import CloudSuccessIcon from "../icons/cloud-success.svg";
import CloudFailIcon from "../icons/cloud-fail.svg";
import {
Input,
List,
@@ -19,6 +28,7 @@ import {
Popover,
Select,
showConfirm,
showToast,
} from "./ui-lib";
import { ModelConfigList } from "./model-config";
@@ -40,7 +50,17 @@ import Locale, {
} from "../locales";
import { copyToClipboard } from "../utils";
import Link from "next/link";
import { Path, RELEASE_URL, UPDATE_URL } from "../constant";
import {
Azure,
Google,
OPENAI_BASE_URL,
Path,
RELEASE_URL,
STORAGE_KEY,
ServiceProvider,
SlotID,
UPDATE_URL,
} from "../constant";
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
import { ErrorBoundary } from "./error";
import { InputRange } from "./input-range";
@@ -49,6 +69,8 @@ import { Avatar, AvatarPicker } from "./emoji";
import { getClientConfig } from "../config/client";
import { useSyncStore } from "../store/sync";
import { nanoid } from "nanoid";
import { useMaskStore } from "../store/mask";
import { ProviderType } from "../utils/cloud";
function EditPromptModal(props: { id: string; onClose: () => void }) {
const promptStore = usePromptStore();
@@ -75,7 +97,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) {
readOnly={!prompt.isUser}
className={styles["edit-prompt-title"]}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.title = e.currentTarget.value),
)
@@ -87,7 +109,7 @@ function EditPromptModal(props: { id: string; onClose: () => void }) {
className={styles["edit-prompt-content"]}
rows={10}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.content = e.currentTarget.value),
)
@@ -127,14 +149,15 @@ function UserPromptModal(props: { onClose?: () => void }) {
actions={[
<IconButton
key="add"
onClick={() =>
promptStore.add({
onClick={() => {
const promptId = promptStore.add({
id: nanoid(),
createdAt: Date.now(),
title: "Empty Prompt",
content: "Empty Prompt Content",
})
}
});
setEditingPromptId(promptId);
}}
icon={<AddIcon />}
bordered
text={Locale.Settings.Prompt.Modal.Add}
@@ -241,75 +264,297 @@ function DangerItems() {
);
}
function SyncItems() {
function CheckButton() {
const syncStore = useSyncStore();
const webdav = syncStore.webDavConfig;
// not ready: https://github.com/Yidadaa/ChatGPT-Next-Web/issues/920#issuecomment-1609866332
return null;
const couldCheck = useMemo(() => {
return syncStore.cloudSync();
}, [syncStore]);
const [checkState, setCheckState] = useState<
"none" | "checking" | "success" | "failed"
>("none");
async function check() {
setCheckState("checking");
const valid = await syncStore.check();
setCheckState(valid ? "success" : "failed");
}
if (!couldCheck) return null;
return (
<List>
<ListItem
title={"上次同步:" + new Date().toLocaleString()}
subTitle={"20 次对话100 条消息200 提示词20 面具"}
<IconButton
text={Locale.Settings.Sync.Config.Modal.Check}
bordered
onClick={check}
icon={
checkState === "none" ? (
<ConnectionIcon />
) : checkState === "checking" ? (
<LoadingIcon />
) : checkState === "success" ? (
<CloudSuccessIcon />
) : checkState === "failed" ? (
<CloudFailIcon />
) : (
<ConnectionIcon />
)
}
></IconButton>
);
}
function SyncConfigModal(props: { onClose?: () => void }) {
const syncStore = useSyncStore();
return (
<div className="modal-mask">
<Modal
title={Locale.Settings.Sync.Config.Modal.Title}
onClose={() => props.onClose?.()}
actions={[
<CheckButton key="check" />,
<IconButton
key="confirm"
onClick={props.onClose}
icon={<ConfirmIcon />}
bordered
text={Locale.UI.Confirm}
/>,
]}
>
<IconButton
icon={<ResetIcon />}
text="同步"
onClick={() => {
syncStore.check().then(console.log);
}}
/>
</ListItem>
<List>
<ListItem
title={Locale.Settings.Sync.Config.SyncType.Title}
subTitle={Locale.Settings.Sync.Config.SyncType.SubTitle}
>
<select
value={syncStore.provider}
onChange={(e) => {
syncStore.update(
(config) =>
(config.provider = e.target.value as ProviderType),
);
}}
>
{Object.entries(ProviderType).map(([k, v]) => (
<option value={v} key={k}>
{k}
</option>
))}
</select>
</ListItem>
<ListItem
title={"本地备份"}
subTitle={"20 次对话100 条消息200 提示词20 面具"}
></ListItem>
<ListItem
title={Locale.Settings.Sync.Config.Proxy.Title}
subTitle={Locale.Settings.Sync.Config.Proxy.SubTitle}
>
<input
type="checkbox"
checked={syncStore.useProxy}
onChange={(e) => {
syncStore.update(
(config) => (config.useProxy = e.currentTarget.checked),
);
}}
></input>
</ListItem>
{syncStore.useProxy ? (
<ListItem
title={Locale.Settings.Sync.Config.ProxyUrl.Title}
subTitle={Locale.Settings.Sync.Config.ProxyUrl.SubTitle}
>
<input
type="text"
value={syncStore.proxyUrl}
onChange={(e) => {
syncStore.update(
(config) => (config.proxyUrl = e.currentTarget.value),
);
}}
></input>
</ListItem>
) : null}
</List>
<ListItem
title={"Web Dav Server"}
subTitle={Locale.Settings.AccessCode.SubTitle}
>
<input
value={webdav.server}
type="text"
placeholder={"https://example.com"}
onChange={(e) => {
syncStore.update(
(config) => (config.server = e.currentTarget.value),
);
}}
/>
</ListItem>
{syncStore.provider === ProviderType.WebDAV && (
<>
<List>
<ListItem title={Locale.Settings.Sync.Config.WebDav.Endpoint}>
<input
type="text"
value={syncStore.webdav.endpoint}
onChange={(e) => {
syncStore.update(
(config) =>
(config.webdav.endpoint = e.currentTarget.value),
);
}}
></input>
</ListItem>
<ListItem title="Web Dav User Name" subTitle="user name here">
<input
value={webdav.username}
type="text"
placeholder={"username"}
onChange={(e) => {
syncStore.update(
(config) => (config.username = e.currentTarget.value),
);
}}
/>
</ListItem>
<ListItem title={Locale.Settings.Sync.Config.WebDav.UserName}>
<input
type="text"
value={syncStore.webdav.username}
onChange={(e) => {
syncStore.update(
(config) =>
(config.webdav.username = e.currentTarget.value),
);
}}
></input>
</ListItem>
<ListItem title={Locale.Settings.Sync.Config.WebDav.Password}>
<PasswordInput
value={syncStore.webdav.password}
onChange={(e) => {
syncStore.update(
(config) =>
(config.webdav.password = e.currentTarget.value),
);
}}
></PasswordInput>
</ListItem>
</List>
</>
)}
<ListItem title="Web Dav Password" subTitle="password here">
<input
value={webdav.password}
type="text"
placeholder={"password"}
onChange={(e) => {
syncStore.update(
(config) => (config.password = e.currentTarget.value),
);
}}
/>
</ListItem>
</List>
{syncStore.provider === ProviderType.UpStash && (
<List>
<ListItem title={Locale.Settings.Sync.Config.UpStash.Endpoint}>
<input
type="text"
value={syncStore.upstash.endpoint}
onChange={(e) => {
syncStore.update(
(config) =>
(config.upstash.endpoint = e.currentTarget.value),
);
}}
></input>
</ListItem>
<ListItem title={Locale.Settings.Sync.Config.UpStash.UserName}>
<input
type="text"
value={syncStore.upstash.username}
placeholder={STORAGE_KEY}
onChange={(e) => {
syncStore.update(
(config) =>
(config.upstash.username = e.currentTarget.value),
);
}}
></input>
</ListItem>
<ListItem title={Locale.Settings.Sync.Config.UpStash.Password}>
<PasswordInput
value={syncStore.upstash.apiKey}
onChange={(e) => {
syncStore.update(
(config) => (config.upstash.apiKey = e.currentTarget.value),
);
}}
></PasswordInput>
</ListItem>
</List>
)}
</Modal>
</div>
);
}
function SyncItems() {
const syncStore = useSyncStore();
const chatStore = useChatStore();
const promptStore = usePromptStore();
const maskStore = useMaskStore();
const couldSync = useMemo(() => {
return syncStore.cloudSync();
}, [syncStore]);
const [showSyncConfigModal, setShowSyncConfigModal] = useState(false);
const stateOverview = useMemo(() => {
const sessions = chatStore.sessions;
const messageCount = sessions.reduce((p, c) => p + c.messages.length, 0);
return {
chat: sessions.length,
message: messageCount,
prompt: Object.keys(promptStore.prompts).length,
mask: Object.keys(maskStore.masks).length,
};
}, [chatStore.sessions, maskStore.masks, promptStore.prompts]);
return (
<>
<List>
<ListItem
title={Locale.Settings.Sync.CloudState}
subTitle={
syncStore.lastProvider
? `${new Date(syncStore.lastSyncTime).toLocaleString()} [${
syncStore.lastProvider
}]`
: Locale.Settings.Sync.NotSyncYet
}
>
<div style={{ display: "flex" }}>
<IconButton
icon={<ConfigIcon />}
text={Locale.UI.Config}
onClick={() => {
setShowSyncConfigModal(true);
}}
/>
{couldSync && (
<IconButton
icon={<ResetIcon />}
text={Locale.UI.Sync}
onClick={async () => {
try {
await syncStore.sync();
showToast(Locale.Settings.Sync.Success);
} catch (e) {
showToast(Locale.Settings.Sync.Fail);
console.error("[Sync]", e);
}
}}
/>
)}
</div>
</ListItem>
<ListItem
title={Locale.Settings.Sync.LocalState}
subTitle={Locale.Settings.Sync.Overview(stateOverview)}
>
<div style={{ display: "flex" }}>
<IconButton
icon={<UploadIcon />}
text={Locale.UI.Export}
onClick={() => {
syncStore.export();
}}
/>
<IconButton
icon={<DownloadIcon />}
text={Locale.UI.Import}
onClick={() => {
syncStore.import();
}}
/>
</div>
</ListItem>
</List>
{showSyncConfigModal && (
<SyncConfigModal onClose={() => setShowSyncConfigModal(false)} />
)}
</>
);
}
@@ -336,13 +581,28 @@ export function Settings() {
console.log("[Update] remote version ", updateStore.remoteVersion);
}
const accessStore = useAccessStore();
const shouldHideBalanceQuery = useMemo(() => {
const isOpenAiUrl = accessStore.openaiUrl.includes(OPENAI_BASE_URL);
return (
accessStore.hideBalanceQuery ||
isOpenAiUrl ||
accessStore.provider === ServiceProvider.Azure
);
}, [
accessStore.hideBalanceQuery,
accessStore.openaiUrl,
accessStore.provider,
]);
const usage = {
used: updateStore.used,
subscription: updateStore.subscription,
};
const [loadingUsage, setLoadingUsage] = useState(false);
function checkUsage(force = false) {
if (accessStore.hideBalanceQuery) {
if (shouldHideBalanceQuery) {
return;
}
@@ -352,7 +612,6 @@ export function Settings() {
});
}
const accessStore = useAccessStore();
const enabledAccessControl = useMemo(
() => accessStore.enabledAccessControl(),
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -378,6 +637,12 @@ export function Settings() {
navigate(Path.Home);
}
};
if (clientConfig?.isApp) {
// Force to set custom endpoint to true if it's app
accessStore.update((state) => {
state.useCustomConfig = true;
});
}
document.addEventListener("keydown", keydownEvent);
return () => {
document.removeEventListener("keydown", keydownEvent);
@@ -428,7 +693,9 @@ export function Settings() {
>
<div
className={styles.avatar}
onClick={() => setShowEmojiPicker(true)}
onClick={() => {
setShowEmojiPicker(!showEmojiPicker);
}}
>
<Avatar avatar={config.avatar} />
</div>
@@ -518,7 +785,7 @@ export function Settings() {
title={`${config.fontSize ?? 14}px`}
value={config.fontSize}
min="12"
max="18"
max="40"
step="1"
onChange={(e) =>
updateConfig(
@@ -562,6 +829,8 @@ export function Settings() {
</ListItem>
</List>
<SyncItems />
<List>
<ListItem
title={Locale.Settings.Mask.Splash.Title}
@@ -629,57 +898,235 @@ export function Settings() {
</ListItem>
</List>
<List>
{showAccessCode ? (
<List id={SlotID.CustomModel}>
{showAccessCode && (
<ListItem
title={Locale.Settings.AccessCode.Title}
subTitle={Locale.Settings.AccessCode.SubTitle}
title={Locale.Settings.Access.AccessCode.Title}
subTitle={Locale.Settings.Access.AccessCode.SubTitle}
>
<PasswordInput
value={accessStore.accessCode}
type="text"
placeholder={Locale.Settings.AccessCode.Placeholder}
placeholder={Locale.Settings.Access.AccessCode.Placeholder}
onChange={(e) => {
accessStore.updateCode(e.currentTarget.value);
accessStore.update(
(access) => (access.accessCode = e.currentTarget.value),
);
}}
/>
</ListItem>
) : (
<></>
)}
{!accessStore.hideUserApiKey ? (
{!accessStore.hideUserApiKey && (
<>
<ListItem
title={Locale.Settings.Endpoint.Title}
subTitle={Locale.Settings.Endpoint.SubTitle}
>
<input
type="text"
value={accessStore.openaiUrl}
placeholder="https://api.openai.com/"
onChange={(e) =>
accessStore.updateOpenAiUrl(e.currentTarget.value)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
>
<PasswordInput
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
/>
</ListItem>
</>
) : null}
{
// Conditionally render the following ListItem based on clientConfig.isApp
!clientConfig?.isApp && ( // only show if isApp is false
<ListItem
title={Locale.Settings.Access.CustomEndpoint.Title}
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
>
<input
type="checkbox"
checked={accessStore.useCustomConfig}
onChange={(e) =>
accessStore.update(
(access) =>
(access.useCustomConfig = e.currentTarget.checked),
)
}
></input>
</ListItem>
)
}
{accessStore.useCustomConfig && (
<>
<ListItem
title={Locale.Settings.Access.Provider.Title}
subTitle={Locale.Settings.Access.Provider.SubTitle}
>
<Select
value={accessStore.provider}
onChange={(e) => {
accessStore.update(
(access) =>
(access.provider = e.target
.value as ServiceProvider),
);
}}
>
{Object.entries(ServiceProvider).map(([k, v]) => (
<option value={v} key={k}>
{k}
</option>
))}
</Select>
</ListItem>
{!accessStore.hideBalanceQuery ? (
{accessStore.provider === "OpenAI" ? (
<>
<ListItem
title={Locale.Settings.Access.OpenAI.Endpoint.Title}
subTitle={
Locale.Settings.Access.OpenAI.Endpoint.SubTitle
}
>
<input
type="text"
value={accessStore.openaiUrl}
placeholder={OPENAI_BASE_URL}
onChange={(e) =>
accessStore.update(
(access) =>
(access.openaiUrl = e.currentTarget.value),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.Access.OpenAI.ApiKey.Title}
subTitle={Locale.Settings.Access.OpenAI.ApiKey.SubTitle}
>
<PasswordInput
value={accessStore.openaiApiKey}
type="text"
placeholder={
Locale.Settings.Access.OpenAI.ApiKey.Placeholder
}
onChange={(e) => {
accessStore.update(
(access) =>
(access.openaiApiKey = e.currentTarget.value),
);
}}
/>
</ListItem>
</>
) : accessStore.provider === "Azure" ? (
<>
<ListItem
title={Locale.Settings.Access.Azure.Endpoint.Title}
subTitle={
Locale.Settings.Access.Azure.Endpoint.SubTitle +
Azure.ExampleEndpoint
}
>
<input
type="text"
value={accessStore.azureUrl}
placeholder={Azure.ExampleEndpoint}
onChange={(e) =>
accessStore.update(
(access) =>
(access.azureUrl = e.currentTarget.value),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.Access.Azure.ApiKey.Title}
subTitle={Locale.Settings.Access.Azure.ApiKey.SubTitle}
>
<PasswordInput
value={accessStore.azureApiKey}
type="text"
placeholder={
Locale.Settings.Access.Azure.ApiKey.Placeholder
}
onChange={(e) => {
accessStore.update(
(access) =>
(access.azureApiKey = e.currentTarget.value),
);
}}
/>
</ListItem>
<ListItem
title={Locale.Settings.Access.Azure.ApiVerion.Title}
subTitle={
Locale.Settings.Access.Azure.ApiVerion.SubTitle
}
>
<input
type="text"
value={accessStore.azureApiVersion}
placeholder="2023-08-01-preview"
onChange={(e) =>
accessStore.update(
(access) =>
(access.azureApiVersion =
e.currentTarget.value),
)
}
></input>
</ListItem>
</>
) : accessStore.provider === "Google" ? (
<>
<ListItem
title={Locale.Settings.Access.Google.Endpoint.Title}
subTitle={
Locale.Settings.Access.Google.Endpoint.SubTitle +
Google.ExampleEndpoint
}
>
<input
type="text"
value={accessStore.googleUrl}
placeholder={Google.ExampleEndpoint}
onChange={(e) =>
accessStore.update(
(access) =>
(access.googleUrl = e.currentTarget.value),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.Access.Google.ApiKey.Title}
subTitle={Locale.Settings.Access.Google.ApiKey.SubTitle}
>
<PasswordInput
value={accessStore.googleApiKey}
type="text"
placeholder={
Locale.Settings.Access.Google.ApiKey.Placeholder
}
onChange={(e) => {
accessStore.update(
(access) =>
(access.googleApiKey = e.currentTarget.value),
);
}}
/>
</ListItem>
<ListItem
title={Locale.Settings.Access.Google.ApiVersion.Title}
subTitle={
Locale.Settings.Access.Google.ApiVersion.SubTitle
}
>
<input
type="text"
value={accessStore.googleApiVersion}
placeholder="2023-08-01-preview"
onChange={(e) =>
accessStore.update(
(access) =>
(access.googleApiVersion =
e.currentTarget.value),
)
}
></input>
</ListItem>
</>
) : null}
</>
)}
</>
)}
{!shouldHideBalanceQuery && !clientConfig?.isApp ? (
<ListItem
title={Locale.Settings.Usage.Title}
subTitle={
@@ -706,8 +1153,8 @@ export function Settings() {
) : null}
<ListItem
title={Locale.Settings.CustomModel.Title}
subTitle={Locale.Settings.CustomModel.SubTitle}
title={Locale.Settings.Access.CustomModel.Title}
subTitle={Locale.Settings.Access.CustomModel.SubTitle}
>
<input
type="text"
@@ -722,8 +1169,6 @@ export function Settings() {
</ListItem>
</List>
<SyncItems />
<List>
<ModelConfigList
modelConfig={config.modelConfig}

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef, useMemo } from "react";
import styles from "./home.module.scss";
@@ -8,6 +8,7 @@ import GithubIcon from "../icons/github.svg";
import ChatGptIcon from "../icons/chatgpt.svg";
import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg";
import DeleteIcon from "../icons/delete.svg";
import MaskIcon from "../icons/mask.svg";
import PluginIcon from "../icons/plugin.svg";
import DragIcon from "../icons/drag.svg";
@@ -17,6 +18,7 @@ import Locale from "../locales";
import { useAppConfig, useChatStore } from "../store";
import {
DEFAULT_SIDEBAR_WIDTH,
MAX_SIDEBAR_WIDTH,
MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH,
@@ -25,7 +27,7 @@ import {
} from "../constant";
import { Link, useNavigate } from "react-router-dom";
import { useMobileScreen } from "../utils";
import { isIOS, useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
import { showConfirm, showToast } from "./ui-lib";
@@ -57,31 +59,57 @@ function useDragSideBar() {
const config = useAppConfig();
const startX = useRef(0);
const startDragWidth = useRef(config.sidebarWidth ?? 300);
const startDragWidth = useRef(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
const lastUpdateTime = useRef(Date.now());
const handleMouseMove = useRef((e: MouseEvent) => {
if (Date.now() < lastUpdateTime.current + 50) {
return;
}
lastUpdateTime.current = Date.now();
const d = e.clientX - startX.current;
const nextWidth = limit(startDragWidth.current + d);
config.update((config) => (config.sidebarWidth = nextWidth));
});
const handleMouseUp = useRef(() => {
startDragWidth.current = config.sidebarWidth ?? 300;
window.removeEventListener("mousemove", handleMouseMove.current);
window.removeEventListener("mouseup", handleMouseUp.current);
});
const onDragMouseDown = (e: MouseEvent) => {
startX.current = e.clientX;
window.addEventListener("mousemove", handleMouseMove.current);
window.addEventListener("mouseup", handleMouseUp.current);
const toggleSideBar = () => {
config.update((config) => {
if (config.sidebarWidth < MIN_SIDEBAR_WIDTH) {
config.sidebarWidth = DEFAULT_SIDEBAR_WIDTH;
} else {
config.sidebarWidth = NARROW_SIDEBAR_WIDTH;
}
});
};
const onDragStart = (e: MouseEvent) => {
// Remembers the initial width each time the mouse is pressed
startX.current = e.clientX;
startDragWidth.current = config.sidebarWidth;
const dragStartTime = Date.now();
const handleDragMove = (e: MouseEvent) => {
if (Date.now() < lastUpdateTime.current + 20) {
return;
}
lastUpdateTime.current = Date.now();
const d = e.clientX - startX.current;
const nextWidth = limit(startDragWidth.current + d);
config.update((config) => {
if (nextWidth < MIN_SIDEBAR_WIDTH) {
config.sidebarWidth = NARROW_SIDEBAR_WIDTH;
} else {
config.sidebarWidth = nextWidth;
}
});
};
const handleDragEnd = () => {
// In useRef the data is non-responsive, so `config.sidebarWidth` can't get the dynamic sidebarWidth
window.removeEventListener("pointermove", handleDragMove);
window.removeEventListener("pointerup", handleDragEnd);
// if user click the drag icon, should toggle the sidebar
const shouldFireClick = Date.now() - dragStartTime < 300;
if (shouldFireClick) {
toggleSideBar();
}
};
window.addEventListener("pointermove", handleDragMove);
window.addEventListener("pointerup", handleDragEnd);
};
const isMobileScreen = useMobileScreen();
const shouldNarrow =
!isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH;
@@ -89,13 +117,13 @@ function useDragSideBar() {
useEffect(() => {
const barWidth = shouldNarrow
? NARROW_SIDEBAR_WIDTH
: limit(config.sidebarWidth ?? 300);
: limit(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`;
document.documentElement.style.setProperty("--sidebar-width", sideBarWidth);
}, [config.sidebarWidth, isMobileScreen, shouldNarrow]);
return {
onDragMouseDown,
onDragStart,
shouldNarrow,
};
}
@@ -104,9 +132,14 @@ export function SideBar(props: { className?: string }) {
const chatStore = useChatStore();
// drag side bar
const { onDragMouseDown, shouldNarrow } = useDragSideBar();
const { onDragStart, shouldNarrow } = useDragSideBar();
const navigate = useNavigate();
const config = useAppConfig();
const isMobileScreen = useMobileScreen();
const isIOSMobile = useMemo(
() => isIOS() && isMobileScreen,
[isMobileScreen],
);
useHotKey();
@@ -115,10 +148,14 @@ export function SideBar(props: { className?: string }) {
className={`${styles.sidebar} ${props.className} ${
shouldNarrow && styles["narrow-sidebar"]
}`}
style={{
// #3016 disable transition on ios mobile screen
transition: isMobileScreen && isIOSMobile ? "none" : undefined,
}}
>
<div className={styles["sidebar-header"]} data-tauri-drag-region>
<div className={styles["sidebar-title"]} data-tauri-drag-region>
ChatGPT Next
NextChat
</div>
<div className={styles["sidebar-sub-title"]}>
Build your own AI assistant.
@@ -133,7 +170,13 @@ export function SideBar(props: { className?: string }) {
icon={<MaskIcon />}
text={shouldNarrow ? undefined : Locale.Mask.Name}
className={styles["sidebar-bar-button"]}
onClick={() => navigate(Path.NewChat, { state: { fromHome: true } })}
onClick={() => {
if (config.dontShowMaskSplashScreen !== true) {
navigate(Path.NewChat, { state: { fromHome: true } });
} else {
navigate(Path.Masks, { state: { fromHome: true } });
}
}}
shadow
/>
<IconButton
@@ -160,7 +203,7 @@ export function SideBar(props: { className?: string }) {
<div className={styles["sidebar-actions"]}>
<div className={styles["sidebar-action"] + " " + styles.mobile}>
<IconButton
icon={<CloseIcon />}
icon={<DeleteIcon />}
onClick={async () => {
if (await showConfirm(Locale.Home.DeleteChat)) {
chatStore.deleteSession(chatStore.currentSessionIndex);
@@ -198,7 +241,7 @@ export function SideBar(props: { className?: string }) {
<div
className={styles["sidebar-drag"]}
onMouseDown={(e) => onDragMouseDown(e as any)}
onPointerDown={(e) => onDragStart(e as any)}
>
<DragIcon />
</div>

View File

@@ -14,17 +14,24 @@
.popover-content {
position: absolute;
width: 350px;
animation: slide-in 0.3s ease;
right: 0;
top: calc(100% + 10px);
}
@media screen and (max-width: 600px) {
.popover-content {
width: auto;
}
}
.popover-mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(5px);
}
.list-item {
@@ -235,7 +242,7 @@
.select-with-icon-select {
height: 100%;
border: var(--border-in-light);
padding: 10px 25px 10px 10px;
padding: 10px 35px 10px 10px;
border-radius: 10px;
appearance: none;
cursor: pointer;

View File

@@ -26,10 +26,10 @@ export function Popover(props: {
<div className={styles.popover}>
{props.children}
{props.open && (
<div className={styles["popover-content"]}>
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
{props.content}
</div>
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
)}
{props.open && (
<div className={styles["popover-content"]}>{props.content}</div>
)}
</div>
);
@@ -70,14 +70,12 @@ export function ListItem(props: {
);
}
export function List(props: {
children:
| Array<JSX.Element | null | undefined>
| JSX.Element
| null
| undefined;
}) {
return <div className={styles.list}>{props.children}</div>;
export function List(props: { children: React.ReactNode; id?: string }) {
return (
<div className={styles.list} id={props.id}>
{props.children}
</div>
);
}
export function Loading() {
@@ -99,8 +97,9 @@ export function Loading() {
interface ModalProps {
title: string;
children?: any;
actions?: JSX.Element[];
actions?: React.ReactNode[];
defaultMax?: boolean;
footer?: React.ReactNode;
onClose?: () => void;
}
export function Modal(props: ModalProps) {
@@ -149,6 +148,7 @@ export function Modal(props: ModalProps) {
<div className={styles["modal-content"]}>{props.children}</div>
<div className={styles["modal-footer"]}>
{props.footer}
<div className={styles["modal-actions"]}>
{props.actions?.map((action, i) => (
<div key={i} className={styles["modal-action"]}>

View File

@@ -1,18 +1,38 @@
import md5 from "spark-md5";
import { DEFAULT_MODELS } from "../constant";
declare global {
namespace NodeJS {
interface ProcessEnv {
PROXY_URL?: string; // docker only
OPENAI_API_KEY?: string;
CODE?: string;
BASE_URL?: string;
PROXY_URL?: string;
OPENAI_ORG_ID?: string; // openai only
VERCEL?: string;
HIDE_USER_API_KEY?: string; // disable user's api key input
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
BUILD_MODE?: "standalone" | "export";
BUILD_APP?: string; // is building desktop app
HIDE_BALANCE_QUERY?: string; // allow user to query balance or not
HIDE_USER_API_KEY?: string; // disable user's api key input
DISABLE_GPT4?: string; // allow user to use gpt-4 or not
ENABLE_BALANCE_QUERY?: string; // allow user to query balance or not
DISABLE_FAST_LINK?: string; // disallow parse settings from url or not
CUSTOM_MODELS?: string; // to control custom models
// azure only
AZURE_URL?: string; // https://{azure-url}/openai/deployments/{deploy-name}
AZURE_API_KEY?: string;
AZURE_API_VERSION?: string;
// google only
GOOGLE_API_KEY?: string;
GOOGLE_URL?: string;
// google tag manager
GTM_ID?: string;
}
}
}
@@ -37,16 +57,54 @@ export const getServerSideConfig = () => {
);
}
const disableGPT4 = !!process.env.DISABLE_GPT4;
let customModels = process.env.CUSTOM_MODELS ?? "";
if (disableGPT4) {
if (customModels) customModels += ",";
customModels += DEFAULT_MODELS.filter((m) => m.name.startsWith("gpt-4"))
.map((m) => "-" + m.name)
.join(",");
}
const isAzure = !!process.env.AZURE_URL;
const isGoogle = !!process.env.GOOGLE_API_KEY;
const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? "";
const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim());
const randomIndex = Math.floor(Math.random() * apiKeys.length);
const apiKey = apiKeys[randomIndex];
console.log(
`[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`,
);
return {
apiKey: process.env.OPENAI_API_KEY,
baseUrl: process.env.BASE_URL,
apiKey,
openaiOrgId: process.env.OPENAI_ORG_ID,
isAzure,
azureUrl: process.env.AZURE_URL,
azureApiKey: process.env.AZURE_API_KEY,
azureApiVersion: process.env.AZURE_API_VERSION,
isGoogle,
googleApiKey: process.env.GOOGLE_API_KEY,
googleUrl: process.env.GOOGLE_URL,
gtmId: process.env.GTM_ID,
needCode: ACCESS_CODES.size > 0,
code: process.env.CODE,
codes: ACCESS_CODES,
needCode: ACCESS_CODES.size > 0,
baseUrl: process.env.BASE_URL,
proxyUrl: process.env.PROXY_URL,
isVercel: !!process.env.VERCEL,
hideUserApiKey: !!process.env.HIDE_USER_API_KEY,
disableGPT4: !!process.env.DISABLE_GPT4,
hideBalanceQuery: !!process.env.HIDE_BALANCE_QUERY,
disableGPT4,
hideBalanceQuery: !process.env.ENABLE_BALANCE_QUERY,
disableFastLink: !!process.env.DISABLE_FAST_LINK,
customModels,
};
};

View File

@@ -7,7 +7,11 @@ export const RELEASE_URL = `${REPO_URL}/releases`;
export const FETCH_COMMIT_URL = `https://api.github.com/repos/${OWNER}/${REPO}/commits?per_page=1`;
export const FETCH_TAG_URL = `https://api.github.com/repos/${OWNER}/${REPO}/tags?per_page=1`;
export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
export const DEFAULT_API_HOST = "https://chatgpt1.nextweb.fun/api/proxy";
export const DEFAULT_API_HOST = "https://api.nextchat.dev";
export const OPENAI_BASE_URL = "https://api.openai.com";
export const GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/";
export enum Path {
Home = "/",
@@ -18,8 +22,14 @@ export enum Path {
Auth = "/auth",
}
export enum ApiPath {
Cors = "",
OpenAI = "/api/openai",
}
export enum SlotID {
AppBody = "app-body",
CustomModel = "custom-model",
}
export enum FileName {
@@ -37,6 +47,7 @@ export enum StoreKey {
Sync = "sync",
}
export const DEFAULT_SIDEBAR_WIDTH = 300;
export const MAX_SIDEBAR_WIDTH = 500;
export const MIN_SIDEBAR_WIDTH = 230;
export const NARROW_SIDEBAR_WIDTH = 100;
@@ -44,11 +55,25 @@ export const NARROW_SIDEBAR_WIDTH = 100;
export const ACCESS_CODE_PREFIX = "nk-";
export const LAST_INPUT_KEY = "last-input";
export const UNFINISHED_INPUT = (id: string) => "unfinished-input-" + id;
export const STORAGE_KEY = "chatgpt-next-web";
export const REQUEST_TIMEOUT_MS = 60000;
export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
export enum ServiceProvider {
OpenAI = "OpenAI",
Azure = "Azure",
Google = "Google",
}
export enum ModelProvider {
GPT = "GPT",
GeminiPro = "GeminiPro",
}
export const OpenaiPath = {
ChatPath: "v1/chat/completions",
UsagePath: "dashboard/billing/usage",
@@ -56,59 +81,213 @@ export const OpenaiPath = {
ListModelPath: "v1/models",
};
export const Azure = {
ExampleEndpoint: "https://{resource-url}/openai/deployments/{deploy-id}",
};
export const Google = {
ExampleEndpoint: "https://generativelanguage.googleapis.com/",
ChatPath: "v1beta/models/gemini-pro:generateContent",
VisionChatPath: "v1beta/models/gemini-pro-vision:generateContent",
// /api/openai/v1/chat/completions
};
export const DEFAULT_INPUT_TEMPLATE = `{{input}}`; // input / time / model / lang
export const DEFAULT_SYSTEM_TEMPLATE = `
You are ChatGPT, a large language model trained by OpenAI.
Knowledge cutoff: 2021-09
You are ChatGPT, a large language model trained by {{ServiceProvider}}.
Knowledge cutoff: {{cutoff}}
Current model: {{model}}
Current time: {{time}}`;
Current time: {{time}}
Latex inline: $x^2$
Latex block: $$e=mc^2$$
`;
export const SUMMARIZE_MODEL = "gpt-3.5-turbo";
export const GEMINI_SUMMARIZE_MODEL = "gemini-pro";
export const KnowledgeCutOffDate: Record<string, string> = {
default: "2021-09",
"gpt-4-turbo-preview": "2023-12",
"gpt-4-1106-preview": "2023-04",
"gpt-4-0125-preview": "2023-12",
"gpt-4-vision-preview": "2023-04",
// After improvements,
// it's now easier to add "KnowledgeCutOffDate" instead of stupid hardcoding it, as was done previously.
"gemini-pro": "2023-12",
};
export const DEFAULT_MODELS = [
{
name: "gpt-4",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-0314",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k-0314",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-32k-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-turbo-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-1106-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-0125-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-4-vision-preview",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0125",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0301",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-1106",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-16k",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gpt-3.5-turbo-16k-0613",
available: true,
provider: {
id: "openai",
providerName: "OpenAI",
providerType: "openai",
},
},
{
name: "gemini-pro",
available: true,
provider: {
id: "google",
providerName: "Google",
providerType: "google",
},
},
{
name: "gemini-pro-vision",
available: true,
provider: {
id: "google",
providerName: "Google",
providerType: "google",
},
},
] as const;

13
app/global.d.ts vendored
View File

@@ -13,5 +13,18 @@ declare module "*.svg";
declare interface Window {
__TAURI__?: {
writeText(text: string): Promise<void>;
invoke(command: string, payload?: Record<string, unknown>): Promise<any>;
dialog: {
save(options?: Record<string, unknown>): Promise<string | null>;
};
fs: {
writeBinaryFile(path: string, data: Uint8Array): Promise<void>;
writeTextFile(path: string, data: string): Promise<void>;
};
notification:{
requestPermission(): Promise<Permission>;
isPermissionGranted(): Promise<boolean>;
sendNotification(options: string | Options): void;
};
};
}

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="分组 1" style="stroke:#333333; stroke-width:1; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.666666666666666 5.333333333333333) rotate(0 2.333750009536743 2.6666666666666665)" d="M0 5.33667L0.73 3.66667 M4.6675 5.33667L3.9375 3.66667 M0.729167 3.67L2.32917 0L3.93917 3.67 M0.729167 3.66667L3.93917 3.66667 " /><path id="路径 5" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.533316666666666 2.6666666666666665)" d="M13.07,5.33C12.45,2.29 9.76,0 6.53,0C3.31,0 0.62,2.29 0,5.33L2,4.67 " /><path id="路径 6" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 9.333333333333332) rotate(0 6.533316666666666 2.6666666666666665)" d="M0,0C0.62,3.04 3.31,5.33 6.53,5.33C9.76,5.33 12.45,3.04 13.07,0L11.33,0.67 " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="分组 1" style="stroke:#333;stroke-width:1;stroke-opacity:1;stroke-dasharray:0 0" d="M0 5.33667L0.73 3.66667 M4.6675 5.33667L3.9375 3.66667 M0.729167 3.67L2.32917 0L3.93917 3.67 M0.729167 3.66667L3.93917 3.66667" transform="translate(5.666666666666666 5.333333333333333) rotate(0 2.333750009536743 2.6666666666666665)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M13.07,5.33C12.45,2.29 9.76,0 6.53,0C3.31,0 0.62,2.29 0,5.33L2,4.67" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.533316666666666 2.6666666666666665)"/><path id="路径 6" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0C0.62,3.04 3.31,5.33 6.53,5.33C9.76,5.33 12.45,3.04 13.07,0L11.33,0.67" transform="translate(1.3333333333333333 9.333333333333332) rotate(0 6.533316666666666 2.6666666666666665)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,28 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30"
height="30" viewBox="0 0 30 30" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="29.999999999999996" height="29.999999999999996" />
<rect id="path_1" x="0" y="0" width="20.45454545454545" height="20.45454545454545" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 14.999999999999998 14.999999999999998)">
<rect fill="#E7F8FF" opacity="1"
transform="translate(0 0) rotate(0 14.999999999999998 14.999999999999998)" x="0" y="0"
width="29.999999999999996" height="29.999999999999996" rx="10" />
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<g opacity="1"
transform="translate(4.772727272727272 4.772727272727273) rotate(0 10.227272727272725 10.227272727272725)">
<mask id="bg-mask-1" fill="white">
<use xlink:href="#path_1"></use>
</mask>
<g mask="url(#bg-mask-1)">
<path id="分组 1" fill-rule="evenodd" style="fill:#1F948C"
transform="translate(0 0) rotate(0 10.227272727272725 10.227272727272725)" opacity="1"
d="M19.11 8.37L19.11 8.37C19.28 7.85 19.37 7.31 19.37 6.76C19.37 5.86 19.13 4.97 18.66 4.19C17.73 2.59 16 1.6 14.13 1.6C13.76 1.6 13.4 1.64 13.04 1.71C12.06 0.62 10.65 0 9.17 0L9.14 0L9.13 0C6.86 0 4.86 1.44 4.16 3.57C2.7 3.86 1.44 4.76 0.71 6.04C0.24 6.83 0 7.72 0 8.63C0 9.9 0.48 11.14 1.35 12.08C1.17 12.6 1.08 13.15 1.08 13.69C1.08 14.6 1.33 15.49 1.79 16.27C2.92 18.21 5.2 19.21 7.42 18.74C8.4 19.83 9.8 20.45 11.28 20.45L11.31 20.45L11.33 20.45C13.59 20.45 15.6 19.01 16.3 16.88C17.76 16.59 19.01 15.69 19.75 14.41C20.21 13.63 20.45 12.74 20.45 11.83C20.45 10.55 19.97 9.32 19.11 8.37Z M8.94734 18.1579C8.90734 18.1879 8.86734 18.2079 8.82734 18.2279C9.52734 18.8079 10.3973 19.1179 11.3073 19.1179L11.3173 19.1179C13.4573 19.1179 15.1973 17.3979 15.1973 15.2879L15.1973 10.5279C15.1973 10.5079 15.1773 10.4879 15.1573 10.4779L13.4173 9.48792L13.4173 15.2379C13.4173 15.4679 13.2873 15.6879 13.0773 15.8079L8.94734 18.1579Z M8.27654 17.0048L12.4465 14.6248C12.4665 14.6148 12.4765 14.5948 12.4765 14.5748L12.4765 14.5748L12.4765 12.5848L7.43654 15.4548C7.22654 15.5748 6.96654 15.5748 6.75654 15.4548L2.62654 13.1048C2.58654 13.0848 2.53654 13.0448 2.50654 13.0348C2.46654 13.2448 2.44654 13.4648 2.44654 13.6848C2.44654 14.3548 2.62654 15.0148 2.96654 15.6048L2.96654 15.5948C3.66654 16.7848 4.94654 17.5148 6.33654 17.5148C7.01654 17.5148 7.68654 17.3348 8.27654 17.0048Z M3.90324 5.16818C3.90324 5.12818 3.90324 5.06818 3.90324 5.02818C3.05324 5.33818 2.33324 5.92818 1.88324 6.70818L1.88324 6.70818C1.54324 7.28818 1.36324 7.94818 1.36324 8.61818C1.36324 9.98818 2.10324 11.2582 3.30324 11.9482L7.47324 14.3182C7.49324 14.3282 7.51324 14.3282 7.53324 14.3182L9.28324 13.3182L4.24324 10.4482C4.03324 10.3382 3.90324 10.1182 3.90324 9.87818L3.90324 9.87818L3.90324 5.16818Z M17.1561 8.50521L12.9761 6.1252C12.9561 6.1252 12.9361 6.1252 12.9161 6.1352L11.1761 7.1252L16.2161 9.9952C16.4261 10.1152 16.5561 10.3352 16.5561 10.5752C16.5561 10.5752 16.5561 10.5752 16.5561 10.5752L16.5561 15.4252C18.0761 14.8652 19.0961 13.4352 19.0961 11.8252C19.0961 10.4552 18.3561 9.1952 17.1561 8.50521Z M8.01418 5.82927C7.99418 5.83927 7.98418 5.85927 7.98418 5.87927L7.98418 5.87927L7.98418 7.86927L13.0242 4.99927C13.1242 4.93927 13.2442 4.90927 13.3642 4.90927C13.4842 4.90927 13.5942 4.93927 13.7042 4.99927L17.8342 7.34927C17.8742 7.36927 17.9142 7.39927 17.9542 7.41927L17.9542 7.41927C17.9842 7.20927 18.0042 6.98927 18.0042 6.76927C18.0042 4.65927 16.2642 2.93927 14.1242 2.93927C13.4442 2.93927 12.7742 3.11927 12.1842 3.44927L8.01418 5.82927Z M9.14676 1.33731C6.99676 1.33731 5.25676 3.05731 5.25676 5.16731L5.25676 9.92731C5.25676 9.94731 5.27676 9.95731 5.28676 9.96731L7.03676 10.9673L7.03676 5.22731L7.03676 5.21731C7.03676 4.98731 7.16676 4.76731 7.37676 4.64731L11.5068 2.29731C11.5468 2.26731 11.5968 2.23731 11.6268 2.22731C10.9268 1.64731 10.0468 1.33731 9.14676 1.33731Z M7.98345 11.5093L10.2235 12.7793L12.4735 11.5093L12.4735 8.9493L10.2235 7.6693L7.98345 8.9493L7.98345 11.5093Z " />
</g>
</g>
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30" height="30" fill="none" viewBox="0 0 30 30"><defs><rect id="path_0" width="30" height="30" x="0" y="0"/><rect id="path_1" width="20.455" height="20.455" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 14.999999999999998 14.999999999999998)"><rect width="30" height="30" x="0" y="0" fill="#E7F8FF" opacity="1" rx="10" transform="translate(0 0) rotate(0 14.999999999999998 14.999999999999998)"/><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><g opacity="1" transform="translate(4.772727272727272 4.772727272727273) rotate(0 10.227272727272725 10.227272727272725)"><mask id="bg-mask-1" fill="#fff"><use xlink:href="#path_1"/></mask><g mask="url(#bg-mask-1)"><path id="分组 1" fill-rule="evenodd" style="fill:#1f948c" d="M19.11 8.37L19.11 8.37C19.28 7.85 19.37 7.31 19.37 6.76C19.37 5.86 19.13 4.97 18.66 4.19C17.73 2.59 16 1.6 14.13 1.6C13.76 1.6 13.4 1.64 13.04 1.71C12.06 0.62 10.65 0 9.17 0L9.14 0L9.13 0C6.86 0 4.86 1.44 4.16 3.57C2.7 3.86 1.44 4.76 0.71 6.04C0.24 6.83 0 7.72 0 8.63C0 9.9 0.48 11.14 1.35 12.08C1.17 12.6 1.08 13.15 1.08 13.69C1.08 14.6 1.33 15.49 1.79 16.27C2.92 18.21 5.2 19.21 7.42 18.74C8.4 19.83 9.8 20.45 11.28 20.45L11.31 20.45L11.33 20.45C13.59 20.45 15.6 19.01 16.3 16.88C17.76 16.59 19.01 15.69 19.75 14.41C20.21 13.63 20.45 12.74 20.45 11.83C20.45 10.55 19.97 9.32 19.11 8.37Z M8.94734 18.1579C8.90734 18.1879 8.86734 18.2079 8.82734 18.2279C9.52734 18.8079 10.3973 19.1179 11.3073 19.1179L11.3173 19.1179C13.4573 19.1179 15.1973 17.3979 15.1973 15.2879L15.1973 10.5279C15.1973 10.5079 15.1773 10.4879 15.1573 10.4779L13.4173 9.48792L13.4173 15.2379C13.4173 15.4679 13.2873 15.6879 13.0773 15.8079L8.94734 18.1579Z M8.27654 17.0048L12.4465 14.6248C12.4665 14.6148 12.4765 14.5948 12.4765 14.5748L12.4765 14.5748L12.4765 12.5848L7.43654 15.4548C7.22654 15.5748 6.96654 15.5748 6.75654 15.4548L2.62654 13.1048C2.58654 13.0848 2.53654 13.0448 2.50654 13.0348C2.46654 13.2448 2.44654 13.4648 2.44654 13.6848C2.44654 14.3548 2.62654 15.0148 2.96654 15.6048L2.96654 15.5948C3.66654 16.7848 4.94654 17.5148 6.33654 17.5148C7.01654 17.5148 7.68654 17.3348 8.27654 17.0048Z M3.90324 5.16818C3.90324 5.12818 3.90324 5.06818 3.90324 5.02818C3.05324 5.33818 2.33324 5.92818 1.88324 6.70818L1.88324 6.70818C1.54324 7.28818 1.36324 7.94818 1.36324 8.61818C1.36324 9.98818 2.10324 11.2582 3.30324 11.9482L7.47324 14.3182C7.49324 14.3282 7.51324 14.3282 7.53324 14.3182L9.28324 13.3182L4.24324 10.4482C4.03324 10.3382 3.90324 10.1182 3.90324 9.87818L3.90324 9.87818L3.90324 5.16818Z M17.1561 8.50521L12.9761 6.1252C12.9561 6.1252 12.9361 6.1252 12.9161 6.1352L11.1761 7.1252L16.2161 9.9952C16.4261 10.1152 16.5561 10.3352 16.5561 10.5752C16.5561 10.5752 16.5561 10.5752 16.5561 10.5752L16.5561 15.4252C18.0761 14.8652 19.0961 13.4352 19.0961 11.8252C19.0961 10.4552 18.3561 9.1952 17.1561 8.50521Z M8.01418 5.82927C7.99418 5.83927 7.98418 5.85927 7.98418 5.87927L7.98418 5.87927L7.98418 7.86927L13.0242 4.99927C13.1242 4.93927 13.2442 4.90927 13.3642 4.90927C13.4842 4.90927 13.5942 4.93927 13.7042 4.99927L17.8342 7.34927C17.8742 7.36927 17.9142 7.39927 17.9542 7.41927L17.9542 7.41927C17.9842 7.20927 18.0042 6.98927 18.0042 6.76927C18.0042 4.65927 16.2642 2.93927 14.1242 2.93927C13.4442 2.93927 12.7742 3.11927 12.1842 3.44927L8.01418 5.82927Z M9.14676 1.33731C6.99676 1.33731 5.25676 3.05731 5.25676 5.16731L5.25676 9.92731C5.25676 9.94731 5.27676 9.95731 5.28676 9.96731L7.03676 10.9673L7.03676 5.22731L7.03676 5.21731C7.03676 4.98731 7.16676 4.76731 7.37676 4.64731L11.5068 2.29731C11.5468 2.26731 11.5968 2.23731 11.6268 2.22731C10.9268 1.64731 10.0468 1.33731 9.14676 1.33731Z M7.98345 11.5093L10.2235 12.7793L12.4735 11.5093L12.4735 8.9493L10.2235 7.6693L7.98345 8.9493L7.98345 11.5093Z" opacity="1" transform="translate(0 0) rotate(0 10.227272727272725 10.227272727272725)"/></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><g opacity="1" transform="translate(1.0001220703125 2) rotate(0)"><path id="路径 1" style="fill:#333333; opacity:1;" d="M13.275,-0.27515c0.261,0.26101 0.3915,0.57606 0.3915,0.94515v10.66c0,0.36907 -0.1305,0.68413 -0.3915,0.9452c-0.261,0.261 -0.57603,0.3915 -0.9451,0.3915h-10.66002c-0.36909,0 -0.68415,-0.1305 -0.94516,-0.3915c-0.26101,-0.26107 -0.39151,-0.57613 -0.39151,-0.9452v-10.66c0,-0.3691 0.1305,-0.68415 0.39151,-0.94515c0.26101,-0.26101 0.57606,-0.39151 0.94516,-0.39151h10.66002c0.36907,0 0.6841,0.1305 0.9451,0.39151zM1.66655,11.33c0,0.0022 0.00111,0.0033 0.00333,0.0033h10.66002c0.0022,0 0.0033,-0.0011 0.0033,-0.0033v-10.66c0,-0.00222 -0.0011,-0.00333 -0.0033,-0.00333l-10.66002,0c-0.00222,0 -0.00333,0.00111 -0.00333,0.00333z"></path><path id="路径 2" style="fill:#333333; opacity:1;" d="M9.76327,7.50715c-0.02999,0.02563 -0.06201,0.04842 -0.09604,0.06837c-0.03403,0.01995 -0.06956,0.03674 -0.10658,0.05039c-0.03702,0.01364 -0.07495,0.02391 -0.11379,0.03082c-0.03885,0.00691 -0.07799,0.01035 -0.11744,0.0103c-0.03945,-0.00004 -0.07859,-0.00356 -0.11742,-0.01055c-0.03883,-0.00699 -0.07674,-0.01734 -0.11373,-0.03106c-0.03699,-0.01372 -0.07248,-0.03059 -0.10647,-0.05061c-0.03399,-0.02002 -0.06596,-0.04288 -0.0959,-0.06858l-1.89578,-1.62728l-1.89578,1.62728c-0.02993,0.0257 -0.0619,0.04856 -0.09589,0.06858c-0.03399,0.02002 -0.06949,0.03689 -0.10648,0.05061c-0.03699,0.01372 -0.07489,0.02407 -0.11372,0.03106c-0.03883,0.00699 -0.07797,0.01051 -0.11742,0.01055c-0.03945,0.00005 -0.0786,-0.00339 -0.11744,-0.0103c-0.03885,-0.00691 -0.07678,-0.01718 -0.11379,-0.03082c-0.03702,-0.01365 -0.07255,-0.03044 -0.10658,-0.05039c-0.03404,-0.01995 -0.06605,-0.04274 -0.09604,-0.06837l-1.90593,-1.629l-1.89671,1.62808c-0.06708,0.05758 -0.14263,0.10013 -0.22664,0.12766c-0.08401,0.02753 -0.17009,0.03793 -0.25824,0.03121c-0.08815,-0.00671 -0.17166,-0.03004 -0.25053,-0.06998c-0.07887,-0.03994 -0.14709,-0.09345 -0.20467,-0.16054c-0.02851,-0.03321 -0.05351,-0.06889 -0.07499,-0.10703c-0.02148,-0.03814 -0.03904,-0.07801 -0.05267,-0.11961c-0.01363,-0.04159 -0.02307,-0.08412 -0.02832,-0.12758c-0.00525,-0.04346 -0.00622,-0.08701 -0.00289,-0.13066c0.00333,-0.04365 0.01088,-0.08655 0.02266,-0.12871c0.01178,-0.04216 0.02755,-0.08277 0.04733,-0.12182c0.01978,-0.03905 0.04317,-0.07579 0.07019,-0.11024c0.02701,-0.03444 0.05713,-0.06592 0.09035,-0.09443l2.32999,-2c0.02994,-0.02569 0.06191,-0.04855 0.0959,-0.06857c0.03399,-0.02003 0.06948,-0.0369 0.10647,-0.05062c0.03699,-0.01372 0.0749,-0.02407 0.11373,-0.03106c0.03883,-0.00699 0.07797,-0.01051 0.11742,-0.01055c0.03945,-0.00004 0.0786,0.00339 0.11744,0.0103c0.03884,0.00691 0.07677,0.01718 0.11379,0.03082c0.03702,0.01365 0.07255,0.03044 0.10658,0.05039c0.03404,0.01995 0.06605,0.04274 0.09604,0.06837l1.90592,1.629l1.89671,-1.62808c0.02998,-0.02573 0.062,-0.04862 0.09605,-0.06866c0.03405,-0.02005 0.0696,-0.03693 0.10665,-0.05065c0.03705,-0.01372 0.07503,-0.02407 0.11392,-0.03104c0.03889,-0.00697 0.07809,-0.01045 0.1176,-0.01045c0.03951,0 0.07872,0.00348 0.11761,0.01045c0.03889,0.00697 0.07686,0.01732 0.11391,0.03104c0.03705,0.01372 0.0726,0.0306 0.10665,0.05065c0.03405,0.02004 0.06607,0.04293 0.09605,0.06866l1.89671,1.62808l1.90595,-1.629c0.03,-0.02563 0.062,-0.04842 0.096,-0.06837c0.03407,-0.01995 0.0696,-0.03674 0.1066,-0.05038c0.037,-0.01365 0.07493,-0.02392 0.1138,-0.03083c0.03887,-0.00691 0.078,-0.01034 0.1174,-0.0103c0.03947,0.00004 0.0786,0.00356 0.1174,0.01055c0.03887,0.00699 0.0768,0.01734 0.1138,0.03106c0.037,0.01372 0.07247,0.03059 0.1064,0.05062c0.034,0.02002 0.06597,0.04288 0.0959,0.06857l2.33,2c0.06713,0.05758 0.12067,0.12581 0.1606,0.20468c0.03993,0.07887 0.06327,0.16237 0.07,0.25052c0.00667,0.08815 -0.00377,0.17424 -0.0313,0.25825c-0.02747,0.08401 -0.07,0.15955 -0.1276,0.22663c-0.02853,0.03322 -0.06,0.06334 -0.0944,0.09035c-0.03447,0.02701 -0.07123,0.05041 -0.1103,0.07019c-0.03907,0.01977 -0.07967,0.03555 -0.1218,0.04733c-0.04213,0.01177 -0.08503,0.01932 -0.1287,0.02265c-0.04367,0.00333 -0.08723,0.00236 -0.1307,-0.00289c-0.04347,-0.00525 -0.086,-0.01469 -0.1276,-0.02832c-0.0416,-0.01363 -0.08147,-0.03118 -0.1196,-0.05267c-0.03813,-0.02148 -0.0738,-0.04648 -0.107,-0.07499l-1.8967,-1.62808z"></path></g><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ></g></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1"><g opacity="1" transform="translate(0 0) rotate(0) translate(1.0001220703125 2) rotate(0)"><path id="路径 1" style="fill:#333;opacity:1" d="M13.275,-0.27515c0.261,0.26101 0.3915,0.57606 0.3915,0.94515v10.66c0,0.36907 -0.1305,0.68413 -0.3915,0.9452c-0.261,0.261 -0.57603,0.3915 -0.9451,0.3915h-10.66002c-0.36909,0 -0.68415,-0.1305 -0.94516,-0.3915c-0.26101,-0.26107 -0.39151,-0.57613 -0.39151,-0.9452v-10.66c0,-0.3691 0.1305,-0.68415 0.39151,-0.94515c0.26101,-0.26101 0.57606,-0.39151 0.94516,-0.39151h10.66002c0.36907,0 0.6841,0.1305 0.9451,0.39151zM1.66655,11.33c0,0.0022 0.00111,0.0033 0.00333,0.0033h10.66002c0.0022,0 0.0033,-0.0011 0.0033,-0.0033v-10.66c0,-0.00222 -0.0011,-0.00333 -0.0033,-0.00333l-10.66002,0c-0.00222,0 -0.00333,0.00111 -0.00333,0.00333z"/><path id="路径 2" style="fill:#333;opacity:1" d="M9.76327,7.50715c-0.02999,0.02563 -0.06201,0.04842 -0.09604,0.06837c-0.03403,0.01995 -0.06956,0.03674 -0.10658,0.05039c-0.03702,0.01364 -0.07495,0.02391 -0.11379,0.03082c-0.03885,0.00691 -0.07799,0.01035 -0.11744,0.0103c-0.03945,-0.00004 -0.07859,-0.00356 -0.11742,-0.01055c-0.03883,-0.00699 -0.07674,-0.01734 -0.11373,-0.03106c-0.03699,-0.01372 -0.07248,-0.03059 -0.10647,-0.05061c-0.03399,-0.02002 -0.06596,-0.04288 -0.0959,-0.06858l-1.89578,-1.62728l-1.89578,1.62728c-0.02993,0.0257 -0.0619,0.04856 -0.09589,0.06858c-0.03399,0.02002 -0.06949,0.03689 -0.10648,0.05061c-0.03699,0.01372 -0.07489,0.02407 -0.11372,0.03106c-0.03883,0.00699 -0.07797,0.01051 -0.11742,0.01055c-0.03945,0.00005 -0.0786,-0.00339 -0.11744,-0.0103c-0.03885,-0.00691 -0.07678,-0.01718 -0.11379,-0.03082c-0.03702,-0.01365 -0.07255,-0.03044 -0.10658,-0.05039c-0.03404,-0.01995 -0.06605,-0.04274 -0.09604,-0.06837l-1.90593,-1.629l-1.89671,1.62808c-0.06708,0.05758 -0.14263,0.10013 -0.22664,0.12766c-0.08401,0.02753 -0.17009,0.03793 -0.25824,0.03121c-0.08815,-0.00671 -0.17166,-0.03004 -0.25053,-0.06998c-0.07887,-0.03994 -0.14709,-0.09345 -0.20467,-0.16054c-0.02851,-0.03321 -0.05351,-0.06889 -0.07499,-0.10703c-0.02148,-0.03814 -0.03904,-0.07801 -0.05267,-0.11961c-0.01363,-0.04159 -0.02307,-0.08412 -0.02832,-0.12758c-0.00525,-0.04346 -0.00622,-0.08701 -0.00289,-0.13066c0.00333,-0.04365 0.01088,-0.08655 0.02266,-0.12871c0.01178,-0.04216 0.02755,-0.08277 0.04733,-0.12182c0.01978,-0.03905 0.04317,-0.07579 0.07019,-0.11024c0.02701,-0.03444 0.05713,-0.06592 0.09035,-0.09443l2.32999,-2c0.02994,-0.02569 0.06191,-0.04855 0.0959,-0.06857c0.03399,-0.02003 0.06948,-0.0369 0.10647,-0.05062c0.03699,-0.01372 0.0749,-0.02407 0.11373,-0.03106c0.03883,-0.00699 0.07797,-0.01051 0.11742,-0.01055c0.03945,-0.00004 0.0786,0.00339 0.11744,0.0103c0.03884,0.00691 0.07677,0.01718 0.11379,0.03082c0.03702,0.01365 0.07255,0.03044 0.10658,0.05039c0.03404,0.01995 0.06605,0.04274 0.09604,0.06837l1.90592,1.629l1.89671,-1.62808c0.02998,-0.02573 0.062,-0.04862 0.09605,-0.06866c0.03405,-0.02005 0.0696,-0.03693 0.10665,-0.05065c0.03705,-0.01372 0.07503,-0.02407 0.11392,-0.03104c0.03889,-0.00697 0.07809,-0.01045 0.1176,-0.01045c0.03951,0 0.07872,0.00348 0.11761,0.01045c0.03889,0.00697 0.07686,0.01732 0.11391,0.03104c0.03705,0.01372 0.0726,0.0306 0.10665,0.05065c0.03405,0.02004 0.06607,0.04293 0.09605,0.06866l1.89671,1.62808l1.90595,-1.629c0.03,-0.02563 0.062,-0.04842 0.096,-0.06837c0.03407,-0.01995 0.0696,-0.03674 0.1066,-0.05038c0.037,-0.01365 0.07493,-0.02392 0.1138,-0.03083c0.03887,-0.00691 0.078,-0.01034 0.1174,-0.0103c0.03947,0.00004 0.0786,0.00356 0.1174,0.01055c0.03887,0.00699 0.0768,0.01734 0.1138,0.03106c0.037,0.01372 0.07247,0.03059 0.1064,0.05062c0.034,0.02002 0.06597,0.04288 0.0959,0.06857l2.33,2c0.06713,0.05758 0.12067,0.12581 0.1606,0.20468c0.03993,0.07887 0.06327,0.16237 0.07,0.25052c0.00667,0.08815 -0.00377,0.17424 -0.0313,0.25825c-0.02747,0.08401 -0.07,0.15955 -0.1276,0.22663c-0.02853,0.03322 -0.06,0.06334 -0.0944,0.09035c-0.03447,0.02701 -0.07123,0.05041 -0.1103,0.07019c-0.03907,0.01977 -0.07967,0.03555 -0.1218,0.04733c-0.04213,0.01177 -0.08503,0.01932 -0.1287,0.02265c-0.04367,0.00333 -0.08723,0.00236 -0.1307,-0.00289c-0.04347,-0.00525 -0.086,-0.01469 -0.1276,-0.02832c-0.0416,-0.01363 -0.08147,-0.03118 -0.1196,-0.05267c-0.03813,-0.02148 -0.0738,-0.04648 -0.107,-0.07499l-1.8967,-1.62808z"/></g><g opacity="1" transform="translate(0 0) rotate(0) translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="fill:#333333; opacity:1;" d="M13.9967,8.00337c0,-0.81625 -0.1569,-1.59615 -0.4707,-2.3397c-0.30307,-0.71824 -0.73117,-1.3542 -1.2843,-1.90789c-0.55287,-0.55348 -1.18783,-0.98185 -1.9049,-1.28512c-0.74182,-0.31375 -1.51963,-0.47062 -2.33343,-0.47062c-0.81634,0 -1.59621,0.15693 -2.3396,0.47079c-0.71828,0.30325 -1.35419,0.73165 -1.90774,1.2852c-0.55355,0.55354 -0.98195,1.18945 -1.2852,1.90774c-0.31386,0.74339 -0.47079,1.52326 -0.47079,2.3396c0,0.8138 0.15687,1.59161 0.47062,2.33343c0.30327,0.71707 0.73164,1.35203 1.28512,1.9049c0.55369,0.55313 1.18965,0.98123 1.90789,1.2843c0.74355,0.3138 1.52345,0.4707 2.3397,0.4707c0.81371,0 1.59155,-0.15683 2.33353,-0.4705c0.717,-0.30313 1.35203,-0.7312 1.9051,-1.2842c0.553,-0.55307 0.98107,-1.1881 1.2842,-1.9051c0.31367,-0.74198 0.4705,-1.51982 0.4705,-2.33353zM15.33,8.00337c0,0.99387 -0.1919,1.94478 -0.5757,2.85273c-0.37067,0.87673 -0.89383,1.65297 -1.5695,2.3287c-0.67573,0.67567 -1.45197,1.19883 -2.3287,1.5695c-0.90795,0.3838 -1.85886,0.5757 -2.85273,0.5757c-0.99612,0 -1.94882,-0.19183 -2.8581,-0.5755c-0.8781,-0.3706 -1.65537,-0.89377 -2.33181,-1.5695c-0.67631,-0.6756 -1.19992,-1.45187 -1.57081,-2.3288c-0.38396,-0.90784 -0.57594,-1.85878 -0.57594,-2.85283c0,-0.99629 0.19192,-1.94903 0.57577,-2.8582c0.37081,-0.87829 0.89439,-1.6556 1.57074,-2.33195c0.67635,-0.67635 1.45367,-1.19993 2.33195,-1.57074c0.90917,-0.38385 1.86191,-0.57577 2.8582,-0.57577c0.99405,0 1.94499,0.19198 2.85283,0.57594c0.87693,0.37089 1.6532,0.8945 2.3288,1.57081c0.67573,0.67644 1.1989,1.45371 1.5695,2.33181c0.38367,0.90928 0.5755,1.86198 0.5755,2.8581z"></path><path id="路径 2" style="fill:#333333; opacity:1;" d="M5.4714,4.5286l6,6c0.03093,0.03093 0.05857,0.0646 0.0829,0.101c0.02433,0.0364 0.04487,0.07483 0.0616,0.1153c0.01673,0.0404 0.0294,0.08207 0.038,0.125c0.00853,0.04293 0.0128,0.0863 0.0128,0.1301c0,0.0438 -0.00427,0.08717 -0.0128,0.1301c-0.0086,0.04293 -0.02127,0.0846 -0.038,0.125c-0.01673,0.04047 -0.03727,0.0789 -0.0616,0.1153c-0.02433,0.0364 -0.05197,0.07007 -0.0829,0.101c-0.03093,0.03093 -0.0646,0.05857 -0.101,0.0829c-0.0364,0.02433 -0.07483,0.04487 -0.1153,0.0616c-0.0404,0.01673 -0.08207,0.0294 -0.125,0.038c-0.04293,0.00853 -0.0863,0.0128 -0.1301,0.0128c-0.0438,0 -0.08717,-0.00427 -0.1301,-0.0128c-0.04293,-0.0086 -0.0846,-0.02127 -0.125,-0.038c-0.04047,-0.01673 -0.0789,-0.03727 -0.1153,-0.0616c-0.0364,-0.02433 -0.07007,-0.05197 -0.101,-0.0829l-6,-6c-0.03095,-0.03095 -0.05859,-0.06463 -0.08291,-0.10102c-0.02432,-0.0364 -0.04486,-0.07482 -0.06161,-0.11526c-0.01675,-0.04044 -0.0294,-0.08213 -0.03794,-0.12506c-0.00854,-0.04293 -0.01281,-0.08629 -0.01281,-0.13006c0,-0.04377 0.00427,-0.08713 0.01281,-0.13006c0.00854,-0.04293 0.02119,-0.08462 0.03794,-0.12506c0.01675,-0.04045 0.03729,-0.07887 0.06161,-0.11526c0.02432,-0.0364 0.05196,-0.07007 0.08291,-0.10102c0.03095,-0.03095 0.06462,-0.05859 0.10102,-0.08291c0.03639,-0.02432 0.07481,-0.04486 0.11526,-0.06161c0.04044,-0.01675 0.08213,-0.0294 0.12506,-0.03794c0.04293,-0.00854 0.08629,-0.01281 0.13006,-0.01281c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08462,0.02119 0.12506,0.03794c0.04044,0.01675 0.07886,0.03729 0.11526,0.06161c0.03639,0.02432 0.07007,0.05196 0.10102,0.08291z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="fill:#333;opacity:1" d="M13.9967,8.00337c0,-0.81625 -0.1569,-1.59615 -0.4707,-2.3397c-0.30307,-0.71824 -0.73117,-1.3542 -1.2843,-1.90789c-0.55287,-0.55348 -1.18783,-0.98185 -1.9049,-1.28512c-0.74182,-0.31375 -1.51963,-0.47062 -2.33343,-0.47062c-0.81634,0 -1.59621,0.15693 -2.3396,0.47079c-0.71828,0.30325 -1.35419,0.73165 -1.90774,1.2852c-0.55355,0.55354 -0.98195,1.18945 -1.2852,1.90774c-0.31386,0.74339 -0.47079,1.52326 -0.47079,2.3396c0,0.8138 0.15687,1.59161 0.47062,2.33343c0.30327,0.71707 0.73164,1.35203 1.28512,1.9049c0.55369,0.55313 1.18965,0.98123 1.90789,1.2843c0.74355,0.3138 1.52345,0.4707 2.3397,0.4707c0.81371,0 1.59155,-0.15683 2.33353,-0.4705c0.717,-0.30313 1.35203,-0.7312 1.9051,-1.2842c0.553,-0.55307 0.98107,-1.1881 1.2842,-1.9051c0.31367,-0.74198 0.4705,-1.51982 0.4705,-2.33353zM15.33,8.00337c0,0.99387 -0.1919,1.94478 -0.5757,2.85273c-0.37067,0.87673 -0.89383,1.65297 -1.5695,2.3287c-0.67573,0.67567 -1.45197,1.19883 -2.3287,1.5695c-0.90795,0.3838 -1.85886,0.5757 -2.85273,0.5757c-0.99612,0 -1.94882,-0.19183 -2.8581,-0.5755c-0.8781,-0.3706 -1.65537,-0.89377 -2.33181,-1.5695c-0.67631,-0.6756 -1.19992,-1.45187 -1.57081,-2.3288c-0.38396,-0.90784 -0.57594,-1.85878 -0.57594,-2.85283c0,-0.99629 0.19192,-1.94903 0.57577,-2.8582c0.37081,-0.87829 0.89439,-1.6556 1.57074,-2.33195c0.67635,-0.67635 1.45367,-1.19993 2.33195,-1.57074c0.90917,-0.38385 1.86191,-0.57577 2.8582,-0.57577c0.99405,0 1.94499,0.19198 2.85283,0.57594c0.87693,0.37089 1.6532,0.8945 2.3288,1.57081c0.67573,0.67644 1.1989,1.45371 1.5695,2.33181c0.38367,0.90928 0.5755,1.86198 0.5755,2.8581z"/><path id="路径 2" style="fill:#333;opacity:1" d="M5.4714,4.5286l6,6c0.03093,0.03093 0.05857,0.0646 0.0829,0.101c0.02433,0.0364 0.04487,0.07483 0.0616,0.1153c0.01673,0.0404 0.0294,0.08207 0.038,0.125c0.00853,0.04293 0.0128,0.0863 0.0128,0.1301c0,0.0438 -0.00427,0.08717 -0.0128,0.1301c-0.0086,0.04293 -0.02127,0.0846 -0.038,0.125c-0.01673,0.04047 -0.03727,0.0789 -0.0616,0.1153c-0.02433,0.0364 -0.05197,0.07007 -0.0829,0.101c-0.03093,0.03093 -0.0646,0.05857 -0.101,0.0829c-0.0364,0.02433 -0.07483,0.04487 -0.1153,0.0616c-0.0404,0.01673 -0.08207,0.0294 -0.125,0.038c-0.04293,0.00853 -0.0863,0.0128 -0.1301,0.0128c-0.0438,0 -0.08717,-0.00427 -0.1301,-0.0128c-0.04293,-0.0086 -0.0846,-0.02127 -0.125,-0.038c-0.04047,-0.01673 -0.0789,-0.03727 -0.1153,-0.0616c-0.0364,-0.02433 -0.07007,-0.05197 -0.101,-0.0829l-6,-6c-0.03095,-0.03095 -0.05859,-0.06463 -0.08291,-0.10102c-0.02432,-0.0364 -0.04486,-0.07482 -0.06161,-0.11526c-0.01675,-0.04044 -0.0294,-0.08213 -0.03794,-0.12506c-0.00854,-0.04293 -0.01281,-0.08629 -0.01281,-0.13006c0,-0.04377 0.00427,-0.08713 0.01281,-0.13006c0.00854,-0.04293 0.02119,-0.08462 0.03794,-0.12506c0.01675,-0.04045 0.03729,-0.07887 0.06161,-0.11526c0.02432,-0.0364 0.05196,-0.07007 0.08291,-0.10102c0.03095,-0.03095 0.06462,-0.05859 0.10102,-0.08291c0.03639,-0.02432 0.07481,-0.04486 0.11526,-0.06161c0.04044,-0.01675 0.08213,-0.0294 0.12506,-0.03794c0.04293,-0.00854 0.08629,-0.01281 0.13006,-0.01281c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08462,0.02119 0.12506,0.03794c0.04044,0.01675 0.07886,0.03729 0.11526,0.06161c0.03639,0.02432 0.07007,0.05196 0.10102,0.08291z"/></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@@ -1,27 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="0.8" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#A6A6A6; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(1.3333533333333334 1.3333333333333333) rotate(0 6.666673333333334 6.666666666666666)"
d="M6.67,0C2.98,0 0,2.98 0,6.67C0,8.36 0,13.33 0,13.33C0,13.33 4.68,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67C13.33,2.98 10.35,0 6.67,0Z " />
<path id="路径 2"
style="stroke:#A6A6A6; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(4.666666666666666 6) rotate(0 3 0)" d="M0,0L6,0 " />
<path id="路径 3"
style="stroke:#A6A6A6; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(4.666666666666666 8.666666666666666) rotate(0 3 0)" d="M0,0L6,0 " />
<path id="路径 4"
style="stroke:#A6A6A6; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(4.666666666666666 11.333333333333332) rotate(0 1.6666666666666665 0)"
d="M0,0L3.33,0 " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity=".8" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#a6a6a6;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M6.67,0C2.98,0 0,2.98 0,6.67C0,8.36 0,13.33 0,13.33C0,13.33 4.68,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67C13.33,2.98 10.35,0 6.67,0Z" transform="translate(1.3333533333333334 1.3333333333333333) rotate(0 6.666673333333334 6.666666666666666)"/><path id="路径 2" style="stroke:#a6a6a6;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L6,0" transform="translate(4.666666666666666 6) rotate(0 3 0)"/><path id="路径 3" style="stroke:#a6a6a6;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L6,0" transform="translate(4.666666666666666 8.666666666666666) rotate(0 3 0)"/><path id="路径 4" style="stroke:#a6a6a6;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L3.33,0" transform="translate(4.666666666666666 11.333333333333332) rotate(0 1.6666666666666665 0)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,16 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="43"
height="44" viewBox="0 0 43 44" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="43" height="43.580135196270106" />
</defs>
<g opacity="1" transform="translate(0 0.000001981943071882597) rotate(0 21.5 21.790067598135053)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="分组 1" fill-rule="evenodd" style="fill:#8BCAE0"
transform="translate(0 0) rotate(0 21.5 21.790067598135053)" opacity="0.27"
d="M40.17 17.84L40.17 17.84C40.53 16.73 40.72 15.57 40.72 14.41C40.72 12.48 40.21 10.58 39.23 8.92C37.27 5.51 33.64 3.41 29.71 3.41C28.94 3.41 28.16 3.49 27.41 3.65C25.35 1.33 22.39 0 19.29 0L19.22 0L19.19 0C14.43 0 10.21 3.07 8.74 7.6C5.68 8.23 3.03 10.15 1.48 12.87C0.51 14.54 0 16.45 0 18.38C0 21.1 1.01 23.73 2.83 25.74C2.47 26.85 2.28 28.01 2.28 29.17C2.28 31.1 2.79 33 3.77 34.66C6.14 38.8 10.92 40.93 15.59 39.93C17.65 42.25 20.61 43.58 23.71 43.58L23.78 43.58L23.81 43.58C28.57 43.58 32.8 40.51 34.26 35.97C37.33 35.35 39.97 33.43 41.52 30.71C42.49 29.03 43 27.13 43 25.2C43 22.48 41.99 19.86 40.17 17.84Z M18.817 38.6948C18.727 38.7448 18.647 38.7948 18.557 38.8448C20.017 40.0648 21.867 40.7348 23.777 40.7348L23.787 40.7348C28.287 40.7248 31.937 37.0648 31.947 32.5648L31.947 22.4348C31.937 22.3848 31.907 22.3548 31.877 22.3348L28.207 20.2148L28.207 32.4548C28.207 32.9648 27.937 33.4348 27.487 33.6848L18.817 38.6948Z M17.3932 36.223L26.1632 31.163C26.2032 31.133 26.2232 31.093 26.2232 31.053L26.2132 31.053L26.2132 26.813L15.6232 32.933C15.1832 33.183 14.6432 33.183 14.2032 32.933L5.52317 27.923C5.44317 27.873 5.32317 27.803 5.26317 27.763C5.18317 28.223 5.14317 28.693 5.14317 29.163C5.14317 30.593 5.52317 31.993 6.23317 33.233L6.23317 33.233C7.70317 35.763 10.3932 37.313 13.3132 37.313C14.7432 37.313 16.1532 36.943 17.3932 36.223Z M8.20584 11.013C8.20584 10.923 8.20584 10.783 8.20584 10.713C6.41583 11.373 4.90584 12.643 3.95583 14.293L3.95583 14.293C3.24583 15.533 2.86583 16.943 2.86583 18.373C2.86583 21.293 4.41583 23.983 6.94584 25.443L15.7158 30.513C15.7558 30.533 15.8058 30.533 15.8358 30.503L19.5058 28.383L8.91584 22.273C8.47583 22.023 8.20584 21.553 8.20584 21.043L8.20584 21.033L8.20584 11.013Z M36.0546 18.1303L27.2846 13.0603C27.2446 13.0403 27.1946 13.0503 27.1646 13.0703L23.4946 15.1903L34.0846 21.3103C34.5246 21.5603 34.7946 22.0203 34.7946 22.5303C34.7946 22.5303 34.7946 22.5403 34.7946 22.5403L34.7946 32.8603C38.0046 31.6803 40.1446 28.6203 40.1446 25.2003C40.1446 22.2803 38.5846 19.5903 36.0546 18.1303Z M16.8345 12.4124C16.8045 12.4424 16.7845 12.4824 16.7845 12.5224L16.7845 12.5224L16.7845 16.7624L27.3745 10.6424C27.5945 10.5224 27.8445 10.4524 28.0945 10.4524C28.3445 10.4524 28.5845 10.5224 28.8045 10.6424L37.4845 15.6624C37.5645 15.7124 37.6545 15.7624 37.7345 15.8124L37.7345 15.8124C37.8145 15.3524 37.8545 14.8924 37.8545 14.4324C37.8545 9.92236 34.1945 6.26236 29.6845 6.26236C28.2545 6.26236 26.8545 6.64236 25.6045 7.35236L16.8345 12.4124Z M19.2209 2.84925C14.7109 2.84925 11.0509 6.49925 11.0509 11.0093L11.0509 21.1393C11.0609 21.1893 11.0809 21.2193 11.1209 21.2393L14.7909 23.3593L14.8009 11.1293L14.8009 11.1193C14.8009 10.6193 15.0709 10.1493 15.5109 9.89925L24.1909 4.88925C24.2609 4.83925 24.3809 4.77925 24.4409 4.73925C22.9809 3.51925 21.1309 2.84925 19.2209 2.84925Z M16.783 24.5101L21.503 27.2401L26.223 24.5101L26.223 19.0601L21.503 16.3401L16.783 19.0701L16.783 24.5101Z " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="43" height="44" fill="none" viewBox="0 0 43 44"><defs><rect id="path_0" width="43" height="43.58" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0.000001981943071882597) rotate(0 21.5 21.790067598135053)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="分组 1" fill-rule="evenodd" style="fill:#8bcae0" d="M40.17 17.84L40.17 17.84C40.53 16.73 40.72 15.57 40.72 14.41C40.72 12.48 40.21 10.58 39.23 8.92C37.27 5.51 33.64 3.41 29.71 3.41C28.94 3.41 28.16 3.49 27.41 3.65C25.35 1.33 22.39 0 19.29 0L19.22 0L19.19 0C14.43 0 10.21 3.07 8.74 7.6C5.68 8.23 3.03 10.15 1.48 12.87C0.51 14.54 0 16.45 0 18.38C0 21.1 1.01 23.73 2.83 25.74C2.47 26.85 2.28 28.01 2.28 29.17C2.28 31.1 2.79 33 3.77 34.66C6.14 38.8 10.92 40.93 15.59 39.93C17.65 42.25 20.61 43.58 23.71 43.58L23.78 43.58L23.81 43.58C28.57 43.58 32.8 40.51 34.26 35.97C37.33 35.35 39.97 33.43 41.52 30.71C42.49 29.03 43 27.13 43 25.2C43 22.48 41.99 19.86 40.17 17.84Z M18.817 38.6948C18.727 38.7448 18.647 38.7948 18.557 38.8448C20.017 40.0648 21.867 40.7348 23.777 40.7348L23.787 40.7348C28.287 40.7248 31.937 37.0648 31.947 32.5648L31.947 22.4348C31.937 22.3848 31.907 22.3548 31.877 22.3348L28.207 20.2148L28.207 32.4548C28.207 32.9648 27.937 33.4348 27.487 33.6848L18.817 38.6948Z M17.3932 36.223L26.1632 31.163C26.2032 31.133 26.2232 31.093 26.2232 31.053L26.2132 31.053L26.2132 26.813L15.6232 32.933C15.1832 33.183 14.6432 33.183 14.2032 32.933L5.52317 27.923C5.44317 27.873 5.32317 27.803 5.26317 27.763C5.18317 28.223 5.14317 28.693 5.14317 29.163C5.14317 30.593 5.52317 31.993 6.23317 33.233L6.23317 33.233C7.70317 35.763 10.3932 37.313 13.3132 37.313C14.7432 37.313 16.1532 36.943 17.3932 36.223Z M8.20584 11.013C8.20584 10.923 8.20584 10.783 8.20584 10.713C6.41583 11.373 4.90584 12.643 3.95583 14.293L3.95583 14.293C3.24583 15.533 2.86583 16.943 2.86583 18.373C2.86583 21.293 4.41583 23.983 6.94584 25.443L15.7158 30.513C15.7558 30.533 15.8058 30.533 15.8358 30.503L19.5058 28.383L8.91584 22.273C8.47583 22.023 8.20584 21.553 8.20584 21.043L8.20584 21.033L8.20584 11.013Z M36.0546 18.1303L27.2846 13.0603C27.2446 13.0403 27.1946 13.0503 27.1646 13.0703L23.4946 15.1903L34.0846 21.3103C34.5246 21.5603 34.7946 22.0203 34.7946 22.5303C34.7946 22.5303 34.7946 22.5403 34.7946 22.5403L34.7946 32.8603C38.0046 31.6803 40.1446 28.6203 40.1446 25.2003C40.1446 22.2803 38.5846 19.5903 36.0546 18.1303Z M16.8345 12.4124C16.8045 12.4424 16.7845 12.4824 16.7845 12.5224L16.7845 12.5224L16.7845 16.7624L27.3745 10.6424C27.5945 10.5224 27.8445 10.4524 28.0945 10.4524C28.3445 10.4524 28.5845 10.5224 28.8045 10.6424L37.4845 15.6624C37.5645 15.7124 37.6545 15.7624 37.7345 15.8124L37.7345 15.8124C37.8145 15.3524 37.8545 14.8924 37.8545 14.4324C37.8545 9.92236 34.1945 6.26236 29.6845 6.26236C28.2545 6.26236 26.8545 6.64236 25.6045 7.35236L16.8345 12.4124Z M19.2209 2.84925C14.7109 2.84925 11.0509 6.49925 11.0509 11.0093L11.0509 21.1393C11.0609 21.1893 11.0809 21.2193 11.1209 21.2393L14.7909 23.3593L14.8009 11.1293L14.8009 11.1193C14.8009 10.6193 15.0709 10.1493 15.5109 9.89925L24.1909 4.88925C24.2609 4.83925 24.3809 4.77925 24.4409 4.73925C22.9809 3.51925 21.1309 2.84925 19.2209 2.84925Z M16.783 24.5101L21.503 27.2401L26.223 24.5101L26.223 19.0601L21.503 16.3401L16.783 19.0701L16.783 24.5101Z" opacity=".27" transform="translate(0 0) rotate(0 21.5 21.790067598135053)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(2.6666666666666665 5) rotate(0 5.333333333333333 4.833333333333333)" d="M1,9.67L9.67,9.67L10.67,0L0,0L1,9.67Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6.667333333333333 8.334133333333334) rotate(0 0 1.6666999999999998)" d="M0,0L0,3.33 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.334133333333334 8.333166666666667) rotate(0 0 1.666283333333333)" d="M0,0L0,3.33 " /><path id="路径 4" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 1) rotate(0 4 2)" d="M0,4L5.44,0L8,4 " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M1,9.67L9.67,9.67L10.67,0L0,0L1,9.67Z" transform="translate(2.6666666666666665 5) rotate(0 5.333333333333333 4.833333333333333)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,3.33" transform="translate(6.667333333333333 8.334133333333334) rotate(0 0 1.6666999999999998)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,3.33" transform="translate(9.334133333333334 8.333166666666667) rotate(0 0 1.666283333333333)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,4L5.44,0L8,4" transform="translate(4 1) rotate(0 4 2)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,21 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.6666666666666665 2.6666666666666665) rotate(0 5.333333333333333 5.333333333333333)"
d="M0,0L10.67,10.67 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.6666666666666665 2.6666666666666665) rotate(0 5.333333333333333 5.333333333333333)"
d="M0,10.67L10.67,0 " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L10.67,10.67" transform="translate(2.6666666666666665 2.6666666666666665) rotate(0 5.333333333333333 5.333333333333333)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,10.67L10.67,0" transform="translate(2.6666666666666665 2.6666666666666665) rotate(0 5.333333333333333 5.333333333333333)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 846 B

1
app/icons/cloud-fail.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use transform="translate(0 0) rotate(0)" xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="fill:#333;opacity:1" d="M4.00337,11.6633M4.00337,11.6633c-0.8391,0 -1.5514,-0.20763 -2.13691,-0.6229c-0.79984,-0.56727 -1.19975,-1.41519 -1.19975,-2.54377c0,-1.2521 0.52073,-2.20348 1.56218,-2.85415c0.68561,-0.42835 1.38711,-0.64252 2.10448,-0.64252v0.66667l-0.64163,-0.18098l0.64163,0.18098l-0.64163,-0.18098c0.65543,-2.32379 2.09264,-3.48569 4.31163,-3.48569c2.30635,0 3.74023,0.99523 4.30163,2.98569l-0.6416,0.18098l0.0729,-0.66267l-0.0729,0.66267l0.0729,-0.66267c2.3958,0.26354 3.5937,1.5411 3.5937,3.83267c0,2.21778 -1.10887,3.32667 -3.3266,3.32667c-0.0438,0 -0.08717,-0.00427 -0.1301,-0.0128c-0.04293,-0.00853 -0.0846,-0.0212 -0.125,-0.038c-0.04047,-0.01673 -0.0789,-0.03727 -0.1153,-0.0616c-0.0364,-0.02427 -0.07007,-0.0519 -0.101,-0.0829c-0.031,-0.03093 -0.05863,-0.0646 -0.0829,-0.101c-0.02433,-0.0364 -0.04487,-0.07483 -0.0616,-0.1153c-0.0168,-0.0404 -0.02947,-0.08207 -0.038,-0.125c-0.00853,-0.04293 -0.0128,-0.0863 -0.0128,-0.1301c0,-0.04373 0.00427,-0.08707 0.0128,-0.13c0.00853,-0.04293 0.0212,-0.08463 0.038,-0.1251c0.01673,-0.04047 0.03727,-0.0789 0.0616,-0.1153c0.02427,-0.0364 0.0519,-0.07007 0.0829,-0.101c0.03093,-0.03093 0.0646,-0.05857 0.101,-0.0829c0.0364,-0.02433 0.07483,-0.04487 0.1153,-0.0616c0.0404,-0.01673 0.08207,-0.02937 0.125,-0.0379c0.04293,-0.00853 0.0863,-0.0128 0.1301,-0.0128c1.32887,0 1.9933,-0.66446 1.9933,-1.99337c0,-1.4951 -0.80207,-2.33088 -2.4062,-2.50733c-0.27,-0.02971 -0.495,-0.22026 0,0c-0.27,-0.02971 -0.495,-0.22026 -0.5688,-0.4817c-0.37873,-1.34287 -1.38484,-2.01431 -3.01833,-2.01431c-1.54613,0 -2.55559,0.8381 -3.02836,2.51431c-0.08103,0.28728 -0.34314,0.48569 0,0c-0.08103,0.28728 -0.34314,0.48569 -0.64164,0.48569c-0.46252,0 -0.92852,0.14666 -1.39801,0.43998c-0.62355,0.38957 -0.93532,0.96402 -0.93532,1.72336c0,0.66927 0.21258,1.15467 0.63775,1.45621c0.35449,0.25144 0.80969,0.37716 1.36558,0.37716c0.04378,0 0.08713,0.00427 0.13006,0.0128c0.04293,0.00853 0.08462,0.02117 0.12507,0.0379c0.04044,0.01673 0.07886,0.03727 0.11525,0.0616c0.0364,0.02433 0.07008,0.05197 0.10103,0.0829c0.03095,0.03093 0.05859,0.0646 0.08291,0.101c0.02432,0.0364 0.04485,0.07483 0.0616,0.1153c0.01675,0.04047 0.0294,0.08217 0.03794,0.1251c0.00854,0.04293 0.01281,0.08627 0.01281,0.13c0,0.0438 -0.00427,0.08717 -0.01281,0.1301c-0.00854,0.04293 -0.02119,0.0846 -0.03794,0.125c-0.01675,0.04047 -0.03728,0.0789 -0.0616,0.1153c-0.02432,0.0364 -0.05196,0.07007 -0.08291,0.101c-0.03095,0.031 -0.06463,0.05863 -0.10103,0.0829c-0.03639,0.02433 -0.07481,0.04487 -0.11525,0.0616c-0.04045,0.0168 -0.08214,0.02947 -0.12507,0.038c-0.04293,0.00853 -0.08628,0.0128 -0.13006,0.0128z"/><path id="路径 2" style="fill:#333;opacity:1" d="M6.42578,10.4903l2,1.66l-0.42578,0.513l-0.52012,-0.4171l2.67002,-3.32998c0.0274,-0.03415 0.05783,-0.06531 0.0913,-0.09346c0.03353,-0.02815 0.0695,-0.05277 0.1079,-0.07384c0.03833,-0.02107 0.07837,-0.0382 0.1201,-0.05138c0.04173,-0.01319 0.08437,-0.02217 0.1279,-0.02696c0.04353,-0.00479 0.0871,-0.00528 0.1307,-0.00149c0.0436,0.00379 0.0864,0.01181 0.1284,0.02404c0.04207,0.01223 0.0825,0.02844 0.1213,0.04863c0.03887,0.0202 0.07537,0.04399 0.1095,0.07137c0.0342,0.02738 0.06537,0.05783 0.0935,0.09135c0.02813,0.03352 0.05273,0.06946 0.0738,0.10783c0.02107,0.03837 0.0382,0.07843 0.0514,0.12017c0.0132,0.04174 0.0222,0.08437 0.027,0.12788c0.00473,0.04351 0.00523,0.08707 0.0015,0.13068c-0.0038,0.04361 -0.01183,0.08643 -0.0241,0.12846c-0.0122,0.04203 -0.0284,0.08247 -0.0486,0.1213c-0.0202,0.03884 -0.044,0.07534 -0.0714,0.10949l-2.66998,3.33001c-0.23308,0.2907 -0.65919,0.3339 -0.9459,0.0959l-2,-1.66c-0.03369,-0.02793 -0.06432,-0.0589 -0.0919,-0.0929c-0.02758,-0.034 -0.05158,-0.07037 -0.072,-0.1091c-0.02042,-0.03867 -0.03687,-0.079 -0.04934,-0.121c-0.01248,-0.04193 -0.02074,-0.0847 -0.02479,-0.1283c-0.00405,-0.0436 -0.0038,-0.08717 0.00073,-0.1307c0.00453,-0.04353 0.01326,-0.0862 0.0262,-0.128c0.01294,-0.0418 0.02983,-0.08197 0.05068,-0.1205c0.02085,-0.03847 0.04526,-0.07453 0.07321,-0.1082c0.02796,-0.03373 0.05893,-0.06437 0.09292,-0.0919c0.03399,-0.0276 0.07035,-0.0516 0.10907,-0.072c0.03872,-0.02047 0.07906,-0.03693 0.12102,-0.0494c0.04196,-0.01247 0.08473,-0.02073 0.12831,-0.0248c0.04359,-0.004 0.08715,-0.00373 0.13069,0.0008c0.04354,0.00453 0.08622,0.01327 0.12804,0.0262c0.04181,0.01293 0.08197,0.02983 0.12046,0.0507c0.03849,0.0208 0.07457,0.0452 0.10826,0.0732z"/></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

1
app/icons/config.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="fill:#333333; opacity:1;" d="M5.99607,12.8916c-0.03633,0.02413 -0.07466,0.04453 -0.11499,0.0612c-0.04034,0.01667 -0.08191,0.02923 -0.12471,0.0377c-0.04281,0.00853 -0.08603,0.0128 -0.12967,0.0128c-0.04364,0 -0.08686,-0.00423 -0.12966,-0.0127c-0.04281,-0.00847 -0.08438,-0.02103 -0.12472,-0.0377c-0.04034,-0.01667 -0.07867,-0.03707 -0.115,-0.0612c-0.03633,-0.0242 -0.06997,-0.0517 -0.1009,-0.0825l-3.96,-3.93998c-0.03103,-0.03087 -0.05876,-0.06448 -0.08317,-0.10081c-0.02441,-0.03634 -0.04505,-0.07471 -0.0619,-0.1151c-0.01685,-0.0404 -0.0296,-0.08206 -0.03825,-0.12497c-0.00865,-0.04291 -0.01303,-0.08626 -0.01314,-0.13003c-0.00011,-0.04377 0.00405,-0.08714 0.01248,-0.13009c0.00843,-0.04295 0.02097,-0.08467 0.03762,-0.12516c0.01665,-0.04048 0.03709,-0.07895 0.06132,-0.11541c0.02423,-0.03646 0.05178,-0.0702 0.08265,-0.10123c0.03087,-0.03103 0.06448,-0.05876 0.10081,-0.08317c0.03634,-0.02441 0.07471,-0.04505 0.11511,-0.0619c0.04039,-0.01685 0.08205,-0.0296 0.12496,-0.03825c0.04291,-0.00865 0.08626,-0.01303 0.13003,-0.01314c0.04377,-0.00011 0.08714,0.00405 0.13009,0.01248c0.04295,0.00843 0.08467,0.02097 0.12516,0.03762c0.04048,0.01665 0.07895,0.03709 0.11541,0.06132c0.03646,0.02423 0.07021,0.05178 0.10124,0.08265l3.48968,3.47207l8.23978,-8.20196c0.031,-0.03088 0.06473,-0.05844 0.1012,-0.08268c0.03647,-0.02423 0.07493,-0.04468 0.1154,-0.06134c0.04047,-0.01666 0.0822,-0.02921 0.1252,-0.03765c0.04293,-0.00844 0.0863,-0.01261 0.1301,-0.01251c0.04373,0.0001 0.08707,0.00447 0.13,0.01311c0.04293,0.00864 0.0846,0.02138 0.125,0.03822c0.0404,0.01685 0.07877,0.03747 0.1151,0.06188c0.03633,0.0244 0.06993,0.05211 0.1008,0.08314c0.0624,0.06265 0.1104,0.13486 0.144,0.21661c0.03367,0.08175 0.0504,0.16683 0.0502,0.25524c-0.0002,0.08841 -0.0173,0.17341 -0.0513,0.25501c-0.03407,0.08159 -0.08243,0.15357 -0.1451,0.21594l-8.70996,8.66999c-0.03093,0.0308 -0.06455,0.0583 -0.10087,0.0825z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="fill:#333;opacity:1" d="M5.99607,12.8916c-0.03633,0.02413 -0.07466,0.04453 -0.11499,0.0612c-0.04034,0.01667 -0.08191,0.02923 -0.12471,0.0377c-0.04281,0.00853 -0.08603,0.0128 -0.12967,0.0128c-0.04364,0 -0.08686,-0.00423 -0.12966,-0.0127c-0.04281,-0.00847 -0.08438,-0.02103 -0.12472,-0.0377c-0.04034,-0.01667 -0.07867,-0.03707 -0.115,-0.0612c-0.03633,-0.0242 -0.06997,-0.0517 -0.1009,-0.0825l-3.96,-3.93998c-0.03103,-0.03087 -0.05876,-0.06448 -0.08317,-0.10081c-0.02441,-0.03634 -0.04505,-0.07471 -0.0619,-0.1151c-0.01685,-0.0404 -0.0296,-0.08206 -0.03825,-0.12497c-0.00865,-0.04291 -0.01303,-0.08626 -0.01314,-0.13003c-0.00011,-0.04377 0.00405,-0.08714 0.01248,-0.13009c0.00843,-0.04295 0.02097,-0.08467 0.03762,-0.12516c0.01665,-0.04048 0.03709,-0.07895 0.06132,-0.11541c0.02423,-0.03646 0.05178,-0.0702 0.08265,-0.10123c0.03087,-0.03103 0.06448,-0.05876 0.10081,-0.08317c0.03634,-0.02441 0.07471,-0.04505 0.11511,-0.0619c0.04039,-0.01685 0.08205,-0.0296 0.12496,-0.03825c0.04291,-0.00865 0.08626,-0.01303 0.13003,-0.01314c0.04377,-0.00011 0.08714,0.00405 0.13009,0.01248c0.04295,0.00843 0.08467,0.02097 0.12516,0.03762c0.04048,0.01665 0.07895,0.03709 0.11541,0.06132c0.03646,0.02423 0.07021,0.05178 0.10124,0.08265l3.48968,3.47207l8.23978,-8.20196c0.031,-0.03088 0.06473,-0.05844 0.1012,-0.08268c0.03647,-0.02423 0.07493,-0.04468 0.1154,-0.06134c0.04047,-0.01666 0.0822,-0.02921 0.1252,-0.03765c0.04293,-0.00844 0.0863,-0.01261 0.1301,-0.01251c0.04373,0.0001 0.08707,0.00447 0.13,0.01311c0.04293,0.00864 0.0846,0.02138 0.125,0.03822c0.0404,0.01685 0.07877,0.03747 0.1151,0.06188c0.03633,0.0244 0.06993,0.05211 0.1008,0.08314c0.0624,0.06265 0.1104,0.13486 0.144,0.21661c0.03367,0.08175 0.0504,0.16683 0.0502,0.25524c-0.0002,0.08841 -0.0173,0.17341 -0.0513,0.25501c-0.03407,0.08159 -0.08243,0.15357 -0.1451,0.21594l-8.70996,8.66999c-0.03093,0.0308 -0.06455,0.0583 -0.10087,0.0825z"/></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

1
app/icons/connection.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.666666666666666 6.666666666666666)" d="M6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67C13.33,6.2 13.29,5.75 13.2,5.32C12.72,7.14 11.06,8.48 9.09,8.48C6.75,8.48 4.85,6.59 4.85,4.24C4.85,2.27 6.19,0.61 8.02,0.14C7.58,0.05 7.13,0 6.67,0Z " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67C13.33,6.2 13.29,5.75 13.2,5.32C12.72,7.14 11.06,8.48 9.09,8.48C6.75,8.48 4.85,6.59 4.85,4.24C4.85,2.27 6.19,0.61 8.02,0.14C7.58,0.05 7.13,0 6.67,0Z" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.666666666666666 6.666666666666666)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 852 B

After

Width:  |  Height:  |  Size: 832 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use transform="translate(0 0) rotate(0)" xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M6.33663,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.33,-0.6 -1.33,-1.34c0,-0.73 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.6 1.34,1.33zM4.99663,9.33c-0.73,0 -1.33,-0.59 -1.33,-1.33c0,-0.74 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM4.99663,14c-0.73,0 -1.33,-0.6 -1.33,-1.33c0,-0.74 0.6,-1.34 1.33,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path><path id="路径 2" fill-rule="evenodd" style="fill:#333333" opacity="1" d="M12.3366,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.32997,-0.6 -1.32997,-1.34c0,-0.73 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.6 1.34,1.33zM10.9966,9.33c-0.73,0 -1.32997,-0.59 -1.32997,-1.33c0,-0.74 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM10.9966,14c-0.73,0 -1.32997,-0.6 -1.32997,-1.33c0,-0.74 0.59997,-1.34 1.32997,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use transform="translate(0 0) rotate(0)" xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" fill-rule="evenodd" style="fill:#333" d="M6.33663,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.33,-0.6 -1.33,-1.34c0,-0.73 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.6 1.34,1.33zM4.99663,9.33c-0.73,0 -1.33,-0.59 -1.33,-1.33c0,-0.74 0.6,-1.33 1.33,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM4.99663,14c-0.73,0 -1.33,-0.6 -1.33,-1.33c0,-0.74 0.6,-1.34 1.33,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z" opacity="1"/><path id="路径 2" fill-rule="evenodd" style="fill:#333" d="M12.3366,3.33c0,0.74 -0.6,1.34 -1.34,1.34c-0.73,0 -1.32997,-0.6 -1.32997,-1.34c0,-0.73 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.6 1.34,1.33zM10.9966,9.33c-0.73,0 -1.32997,-0.59 -1.32997,-1.33c0,-0.74 0.59997,-1.33 1.32997,-1.33c0.74,0 1.34,0.59 1.34,1.33c0,0.74 -0.6,1.33 -1.34,1.33zM10.9966,14c-0.73,0 -1.32997,-0.6 -1.32997,-1.33c0,-0.74 0.59997,-1.34 1.32997,-1.34c0.74,0 1.34,0.6 1.34,1.34c0,0.73 -0.6,1.33 -1.34,1.33z" opacity="1"/></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(10.5 11) rotate(0 1.4166666666666665 1.8333333333333333)" d="M2.83,0L2.83,3C2.83,3.37 2.53,3.67 2.17,3.67L0,3.67 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(2.6666666666666665 1.3333333333333333) rotate(0 5.333333333333333 6.666666666666666)" d="M10.67,4L10.67,0.67C10.67,0.3 10.37,0 10,0L0.67,0C0.3,0 0,0.3 0,0.67L0,12.67C0,13.03 0.3,13.33 0.67,13.33L2.67,13.33 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.333333333333333 5.333333333333333) rotate(0 2.333333333333333 0)" d="M0,0L4.67,0 " /><path id="路径 4" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(7.666666666666666 7.666666666666666) rotate(0 2.833333333333333 3.5)" d="M0,7L5.67,0 " /><path id="路径 5" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.333333333333333 8) rotate(0 1.3333333333333333 0)" d="M0,0L2.67,0 " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M2.83,0L2.83,3C2.83,3.37 2.53,3.67 2.17,3.67L0,3.67" transform="translate(10.5 11) rotate(0 1.4166666666666665 1.8333333333333333)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M10.67,4L10.67,0.67C10.67,0.3 10.37,0 10,0L0.67,0C0.3,0 0,0.3 0,0.67L0,12.67C0,13.03 0.3,13.33 0.67,13.33L2.67,13.33" transform="translate(2.6666666666666665 1.3333333333333333) rotate(0 5.333333333333333 6.666666666666666)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L4.67,0" transform="translate(5.333333333333333 5.333333333333333) rotate(0 2.333333333333333 0)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,7L5.67,0" transform="translate(7.666666666666666 7.666666666666666) rotate(0 2.833333333333333 3.5)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L2.67,0" transform="translate(5.333333333333333 8) rotate(0 1.3333333333333333 0)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L14.032 8.55382C13.4365 8.20193 12.7418 8 12 8C9.79086 8 8 9.79086 8 12C8 12.7418 8.20193 13.4365 8.55382 14.032L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L9.96803 15.4462C10.5635 15.7981 11.2582 16 12 16C14.2091 16 16 14.2091 16 12C16 11.2582 15.7981 10.5635 15.4462 9.96803L19.7071 5.70711ZM12.518 10.0677C12.3528 10.0236 12.1792 10 12 10C10.8954 10 10 10.8954 10 12C10 12.1792 10.0236 12.3528 10.0677 12.518L12.518 10.0677ZM11.482 13.9323L13.9323 11.482C13.9764 11.6472 14 11.8208 14 12C14 13.1046 13.1046 14 12 14C11.8208 14 11.6472 13.9764 11.482 13.9323ZM15.7651 4.8207C14.6287 4.32049 13.3675 4 12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C1.92276 13.7326 2.86706 15.0637 4.21194 16.3739L5.62626 14.9596C4.4555 13.8229 3.61144 12.6531 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C12.7719 6 13.5135 6.13385 14.2193 6.36658L15.7651 4.8207ZM12 18C11.2282 18 10.4866 17.8661 9.78083 17.6334L8.23496 19.1793C9.37136 19.6795 10.6326 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C22.0773 10.2674 21.133 8.93627 19.7881 7.62611L18.3738 9.04043C19.5446 10.1771 20.3887 11.3469 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18Z" fill="#000000"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M19.7071 5.70711C20.0976 5.31658 20.0976 4.68342 19.7071 4.29289C19.3166 3.90237 18.6834 3.90237 18.2929 4.29289L14.032 8.55382C13.4365 8.20193 12.7418 8 12 8C9.79086 8 8 9.79086 8 12C8 12.7418 8.20193 13.4365 8.55382 14.032L4.29289 18.2929C3.90237 18.6834 3.90237 19.3166 4.29289 19.7071C4.68342 20.0976 5.31658 20.0976 5.70711 19.7071L9.96803 15.4462C10.5635 15.7981 11.2582 16 12 16C14.2091 16 16 14.2091 16 12C16 11.2582 15.7981 10.5635 15.4462 9.96803L19.7071 5.70711ZM12.518 10.0677C12.3528 10.0236 12.1792 10 12 10C10.8954 10 10 10.8954 10 12C10 12.1792 10.0236 12.3528 10.0677 12.518L12.518 10.0677ZM11.482 13.9323L13.9323 11.482C13.9764 11.6472 14 11.8208 14 12C14 13.1046 13.1046 14 12 14C11.8208 14 11.6472 13.9764 11.482 13.9323ZM15.7651 4.8207C14.6287 4.32049 13.3675 4 12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C1.92276 13.7326 2.86706 15.0637 4.21194 16.3739L5.62626 14.9596C4.4555 13.8229 3.61144 12.6531 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C12.7719 6 13.5135 6.13385 14.2193 6.36658L15.7651 4.8207ZM12 18C11.2282 18 10.4866 17.8661 9.78083 17.6334L8.23496 19.1793C9.37136 19.6795 10.6326 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C22.0773 10.2674 21.133 8.93627 19.7881 7.62611L18.3738 9.04043C19.5446 10.1771 20.3887 11.3469 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18Z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,4 +1 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.30147 15.5771C4.77832 14.2684 3.6904 12.7726 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C14.1843 6 16.1261 7.07185 17.6986 8.42294C19.2218 9.73158 20.3097 11.2274 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18C9.81574 18 7.87402 16.9282 6.30147 15.5771ZM12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C2.00757 13.8624 3.23268 15.5772 4.99812 17.0941C6.75717 18.6054 9.14754 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C21.9925 10.1376 20.7674 8.42276 19.002 6.90595C17.2429 5.39462 14.8525 4 12 4ZM10 12C10 10.8954 10.8955 10 12 10C13.1046 10 14 10.8954 14 12C14 13.1046 13.1046 14 12 14C10.8955 14 10 13.1046 10 12ZM12 8C9.7909 8 8.00004 9.79086 8.00004 12C8.00004 14.2091 9.7909 16 12 16C14.2092 16 16 14.2091 16 12C16 9.79086 14.2092 8 12 8Z" fill="#000000"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" fill="none" viewBox="0 0 24 24"><path fill="#000" fill-rule="evenodd" d="M6.30147 15.5771C4.77832 14.2684 3.6904 12.7726 3.18002 12C3.6904 11.2274 4.77832 9.73158 6.30147 8.42294C7.87402 7.07185 9.81574 6 12 6C14.1843 6 16.1261 7.07185 17.6986 8.42294C19.2218 9.73158 20.3097 11.2274 20.8201 12C20.3097 12.7726 19.2218 14.2684 17.6986 15.5771C16.1261 16.9282 14.1843 18 12 18C9.81574 18 7.87402 16.9282 6.30147 15.5771ZM12 4C9.14754 4 6.75717 5.39462 4.99812 6.90595C3.23268 8.42276 2.00757 10.1376 1.46387 10.9698C1.05306 11.5985 1.05306 12.4015 1.46387 13.0302C2.00757 13.8624 3.23268 15.5772 4.99812 17.0941C6.75717 18.6054 9.14754 20 12 20C14.8525 20 17.2429 18.6054 19.002 17.0941C20.7674 15.5772 21.9925 13.8624 22.5362 13.0302C22.947 12.4015 22.947 11.5985 22.5362 10.9698C21.9925 10.1376 20.7674 8.42276 19.002 6.90595C17.2429 5.39462 14.8525 4 12 4ZM10 12C10 10.8954 10.8955 10 12 10C13.1046 10 14 10.8954 14 12C14 13.1046 13.1046 14 12 14C10.8955 14 10 13.1046 10 12ZM12 8C9.7909 8 8.00004 9.79086 8.00004 12C8.00004 14.2091 9.7909 16 12 16C14.2092 16 16 14.2091 16 12C16 9.79086 14.2092 8 12 8Z" clip-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

1
app/icons/image.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" height="16" width="16" version="1.1" xml:space="preserve" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none"/><g class="currentLayer" style=""><title>Layer 1</title><g id="svg_1" class="" fill="#333" fill-opacity="1"><polygon points="2.4690866470336914,2.4690725803375244 4.447190761566162,2.4690725803375244 4.447190761566162,1.6882386207580566 1.6882381439208984,1.6882386207580566 1.6882381439208984,4.44719123840332 2.4690866470336914,4.44719123840332 " id="svg_2" fill="#333" fill-opacity="1"/><polygon points="11.552804470062256,1.6882386207580566 11.552804470062256,2.4690725803375244 13.530910968780518,2.4690725803375244 13.530910968780518,4.44719123840332 14.311760425567627,4.44719123840332 14.311760425567627,1.6882386207580566 " id="svg_3" fill="#333" fill-opacity="1"/><polygon points="13.530910968780518,13.530919075012207 11.552804470062256,13.530919075012207 11.552804470062256,14.311760902404785 14.311760425567627,14.311760902404785 14.311760425567627,11.552801132202148 13.530910968780518,11.552801132202148 " id="svg_4" fill="#333" fill-opacity="1"/><polygon points="2.4690866470336914,11.552801132202148 1.6882381439208984,11.552801132202148 1.6882381439208984,14.311760902404785 4.447190761566162,14.311760902404785 4.447190761566162,13.530919075012207 2.4690866470336914,13.530919075012207 " id="svg_5" fill="#333" fill-opacity="1"/><path d="M8.830417847409231,6.243117030680995 c0.68169614081525,0 1.2363241834494423,-0.5546280426341942 1.2363241834494423,-1.2363241834494423 S9.51214001610201,3.770468663782117 8.830417847409231,3.770468663782117 s-1.2363241834494423,0.5546280426341942 -1.2363241834494423,1.2363241834494423 S8.14872170659398,6.243117030680995 8.830417847409231,6.243117030680995 z" id="svg_6" fill="#333" fill-opacity="1"/><polygon points="3.7704806327819824,12.229532241821289 12.229516506195068,12.229532241821289 12.229516506195068,9.709510803222656 10.70320463180542,8.099010467529297 8.852166652679443,9.175727844238281 6.275332450866699,7.334256172180176 3.7704806327819824,9.977211952209473 " id="svg_7" fill="#333" fill-opacity="1"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(3.6666666666666665 3.6666666666666665) rotate(0 4.333333333333333 4.333333333333333)" d="M8.67,4.33C8.67,1.94 6.73,0 4.33,0C1.94,0 0,1.94 0,4.33C0,6.73 1.94,8.67 4.33,8.67C6.73,8.67 8.67,6.73 8.67,4.33Z " /><path id="路径 2" fill-rule="evenodd" style="fill:#333333" transform="translate(7.166666666666666 0.3333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 3" fill-rule="evenodd" style="fill:#333333" transform="translate(12 2.333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 4" fill-rule="evenodd" style="fill:#333333" transform="translate(14 7.166666666666666) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 5" fill-rule="evenodd" style="fill:#333333" transform="translate(12 12) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 6" fill-rule="evenodd" style="fill:#333333" transform="translate(7.166666666666666 14) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 7" fill-rule="evenodd" style="fill:#333333" transform="translate(2.333333333333333 12) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 8" fill-rule="evenodd" style="fill:#333333" transform="translate(0.3333333333333333 7.166666666666666) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /><path id="路径 9" fill-rule="evenodd" style="fill:#333333" transform="translate(2.333333333333333 2.333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)" opacity="1" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M8.67,4.33C8.67,1.94 6.73,0 4.33,0C1.94,0 0,1.94 0,4.33C0,6.73 1.94,8.67 4.33,8.67C6.73,8.67 8.67,6.73 8.67,4.33Z" transform="translate(3.6666666666666665 3.6666666666666665) rotate(0 4.333333333333333 4.333333333333333)"/><path id="路径 2" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(7.166666666666666 0.3333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 3" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(12 2.333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 4" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(14 7.166666666666666) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 5" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(12 12) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 6" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(7.166666666666666 14) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 7" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(2.333333333333333 12) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 8" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(0.3333333333333333 7.166666666666666) rotate(0 0.8333333333333333 0.8333333333333333)"/><path id="路径 9" fill-rule="evenodd" style="fill:#333" d="M1.67,0.83C1.67,0.37 1.29,0 0.83,0C0.37,0 0,0.37 0,0.83C0,1.29 0.37,1.67 0.83,1.67C1.29,1.67 1.67,1.29 1.67,0.83Z" opacity="1" transform="translate(2.333333333333333 2.333333333333333) rotate(0 0.8333333333333333 0.8333333333333333)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="fill:#333333; opacity:1;" d="M2.24783,8.85208c-0.03406,-0.0275 -0.06511,-0.05806 -0.09315,-0.09167c-0.02804,-0.03362 -0.05253,-0.06965 -0.07347,-0.10809c-0.02095,-0.03843 -0.03794,-0.07854 -0.05098,-0.12033c-0.01304,-0.04179 -0.02188,-0.08445 -0.02652,-0.12798c-0.00464,-0.04353 -0.00499,-0.08709 -0.00104,-0.13068c0.00394,-0.04359 0.0121,-0.08639 0.02447,-0.12838c0.01237,-0.04199 0.02872,-0.08236 0.04905,-0.12113l3.67,-7c0.02824,-0.05386 0.06319,-0.10294 0.10486,-0.14724c0.04167,-0.0443 0.08852,-0.08218 0.14056,-0.11365c0.05204,-0.03147 0.10735,-0.05538 0.16593,-0.07172c0.05857,-0.01634 0.11827,-0.02451 0.17909,-0.02451h5.99997c0.07473,0 0.1474,0.01221 0.218,0.03663c0.0706,0.02442 0.13527,0.05971 0.194,0.10586c0.0344,0.02705 0.06583,0.05719 0.0943,0.09044c0.02847,0.03324 0.05343,0.06894 0.0749,0.1071c0.02147,0.03816 0.03897,0.07805 0.0525,0.11966c0.0136,0.04161 0.02303,0.08414 0.0283,0.12761c0.0052,0.04346 0.0061,0.08701 0.0027,0.13066c-0.00333,0.04364 -0.01093,0.08654 -0.0228,0.12869c-0.0118,0.04215 -0.0276,0.08274 -0.0474,0.12177c-0.0198,0.03903 -0.04323,0.07576 -0.0703,0.11017l-2.8224,3.59141h3.6282c0.08387,0 0.16483,0.01527 0.2429,0.04581c0.07807,0.03055 0.1479,0.07427 0.2095,0.13116c0.03213,0.0297 0.06107,0.06225 0.0868,0.09766c0.0258,0.03541 0.04787,0.07298 0.0662,0.11273c0.01833,0.03975 0.0326,0.0809 0.0428,0.12346c0.01027,0.04256 0.01627,0.08571 0.018,0.12945c0.00173,0.04374 -0.00083,0.08723 -0.0077,0.13047c-0.0068,0.04324 -0.01777,0.08539 -0.0329,0.12646c-0.01513,0.04108 -0.03413,0.08028 -0.057,0.11761c-0.02287,0.03733 -0.04917,0.07208 -0.0789,0.10423l-7.99998,8.65995c-0.04107,0.04447 -0.08735,0.08263 -0.13882,0.1145c-0.05147,0.03187 -0.10626,0.05627 -0.16438,0.0732c-0.05812,0.01693 -0.11744,0.0258 -0.17797,0.0266c-0.06053,0.00073 -0.12006,-0.0066 -0.17859,-0.022c-0.08548,-0.02253 -0.16339,-0.0606 -0.23373,-0.1142c-0.07035,-0.05353 -0.12777,-0.1185 -0.17227,-0.1949c-0.04449,-0.0764 -0.07268,-0.1584 -0.08456,-0.246c-0.01187,-0.0876 -0.00653,-0.17413 0.01602,-0.2596l1.44925,-5.49326h-3.80464c-0.10945,0 -0.21263,-0.02541 -0.30956,-0.07623c-0.03877,-0.02033 -0.07519,-0.04424 -0.10924,-0.07173zM3.76889,7.66671h3.56774c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08461,0.02118 0.12506,0.03793c0.04044,0.01675 0.07886,0.03729 0.11526,0.06161c0.03639,0.02432 0.07007,0.05196 0.10102,0.08291c0.03095,0.03095 0.05859,0.06463 0.08291,0.10102c0.02432,0.0364 0.04485,0.07482 0.0616,0.11526c0.01675,0.04044 0.0294,0.08213 0.03794,0.12506c0.00854,0.04293 0.01281,0.08629 0.01281,0.13006c0,0.05765 -0.00735,0.11434 -0.02205,0.17007l-0.9867,3.73996l5.14866,-5.57336h-3.47657c-0.04378,0 -0.08713,-0.00427 -0.13006,-0.01281c-0.04293,-0.00854 -0.08462,-0.02119 -0.12507,-0.03794c-0.04044,-0.01675 -0.07886,-0.03729 -0.11525,-0.0616c-0.0364,-0.02432 -0.07008,-0.05196 -0.10103,-0.08291c-0.03095,-0.03095 -0.05859,-0.06463 -0.08291,-0.10103c-0.02432,-0.03639 -0.04485,-0.07481 -0.0616,-0.11525c-0.01675,-0.04045 -0.0294,-0.08214 -0.03794,-0.12507c-0.00854,-0.04293 -0.01281,-0.08629 -0.01281,-0.13006c0,-0.07469 0.01221,-0.14734 0.03663,-0.21794c0.02442,-0.07059 0.05971,-0.13526 0.10586,-0.19399l2.82235,-3.5914h-4.22496z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><g opacity="1" transform="translate(0 0) rotate(0)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="fill:#333;opacity:1" d="M2.24783,8.85208c-0.03406,-0.0275 -0.06511,-0.05806 -0.09315,-0.09167c-0.02804,-0.03362 -0.05253,-0.06965 -0.07347,-0.10809c-0.02095,-0.03843 -0.03794,-0.07854 -0.05098,-0.12033c-0.01304,-0.04179 -0.02188,-0.08445 -0.02652,-0.12798c-0.00464,-0.04353 -0.00499,-0.08709 -0.00104,-0.13068c0.00394,-0.04359 0.0121,-0.08639 0.02447,-0.12838c0.01237,-0.04199 0.02872,-0.08236 0.04905,-0.12113l3.67,-7c0.02824,-0.05386 0.06319,-0.10294 0.10486,-0.14724c0.04167,-0.0443 0.08852,-0.08218 0.14056,-0.11365c0.05204,-0.03147 0.10735,-0.05538 0.16593,-0.07172c0.05857,-0.01634 0.11827,-0.02451 0.17909,-0.02451h5.99997c0.07473,0 0.1474,0.01221 0.218,0.03663c0.0706,0.02442 0.13527,0.05971 0.194,0.10586c0.0344,0.02705 0.06583,0.05719 0.0943,0.09044c0.02847,0.03324 0.05343,0.06894 0.0749,0.1071c0.02147,0.03816 0.03897,0.07805 0.0525,0.11966c0.0136,0.04161 0.02303,0.08414 0.0283,0.12761c0.0052,0.04346 0.0061,0.08701 0.0027,0.13066c-0.00333,0.04364 -0.01093,0.08654 -0.0228,0.12869c-0.0118,0.04215 -0.0276,0.08274 -0.0474,0.12177c-0.0198,0.03903 -0.04323,0.07576 -0.0703,0.11017l-2.8224,3.59141h3.6282c0.08387,0 0.16483,0.01527 0.2429,0.04581c0.07807,0.03055 0.1479,0.07427 0.2095,0.13116c0.03213,0.0297 0.06107,0.06225 0.0868,0.09766c0.0258,0.03541 0.04787,0.07298 0.0662,0.11273c0.01833,0.03975 0.0326,0.0809 0.0428,0.12346c0.01027,0.04256 0.01627,0.08571 0.018,0.12945c0.00173,0.04374 -0.00083,0.08723 -0.0077,0.13047c-0.0068,0.04324 -0.01777,0.08539 -0.0329,0.12646c-0.01513,0.04108 -0.03413,0.08028 -0.057,0.11761c-0.02287,0.03733 -0.04917,0.07208 -0.0789,0.10423l-7.99998,8.65995c-0.04107,0.04447 -0.08735,0.08263 -0.13882,0.1145c-0.05147,0.03187 -0.10626,0.05627 -0.16438,0.0732c-0.05812,0.01693 -0.11744,0.0258 -0.17797,0.0266c-0.06053,0.00073 -0.12006,-0.0066 -0.17859,-0.022c-0.08548,-0.02253 -0.16339,-0.0606 -0.23373,-0.1142c-0.07035,-0.05353 -0.12777,-0.1185 -0.17227,-0.1949c-0.04449,-0.0764 -0.07268,-0.1584 -0.08456,-0.246c-0.01187,-0.0876 -0.00653,-0.17413 0.01602,-0.2596l1.44925,-5.49326h-3.80464c-0.10945,0 -0.21263,-0.02541 -0.30956,-0.07623c-0.03877,-0.02033 -0.07519,-0.04424 -0.10924,-0.07173zM3.76889,7.66671h3.56774c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08461,0.02118 0.12506,0.03793c0.04044,0.01675 0.07886,0.03729 0.11526,0.06161c0.03639,0.02432 0.07007,0.05196 0.10102,0.08291c0.03095,0.03095 0.05859,0.06463 0.08291,0.10102c0.02432,0.0364 0.04485,0.07482 0.0616,0.11526c0.01675,0.04044 0.0294,0.08213 0.03794,0.12506c0.00854,0.04293 0.01281,0.08629 0.01281,0.13006c0,0.05765 -0.00735,0.11434 -0.02205,0.17007l-0.9867,3.73996l5.14866,-5.57336h-3.47657c-0.04378,0 -0.08713,-0.00427 -0.13006,-0.01281c-0.04293,-0.00854 -0.08462,-0.02119 -0.12507,-0.03794c-0.04044,-0.01675 -0.07886,-0.03729 -0.11525,-0.0616c-0.0364,-0.02432 -0.07008,-0.05196 -0.10103,-0.08291c-0.03095,-0.03095 -0.05859,-0.06463 -0.08291,-0.10103c-0.02432,-0.03639 -0.04485,-0.07481 -0.0616,-0.11525c-0.01675,-0.04045 -0.0294,-0.08214 -0.03794,-0.12507c-0.00854,-0.04293 -0.01281,-0.08629 -0.01281,-0.13006c0,-0.07469 0.01221,-0.14734 0.03663,-0.21794c0.02442,-0.07059 0.05971,-0.13526 0.10586,-0.19399l2.82235,-3.5914h-4.22496z"/></g></g><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs></svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

1
app/icons/loading.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#fff" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="none" stroke="none" style="" class="" /><g class="currentLayer" style=""><title>Layer 1</title><circle cx="4" cy="8" r="1.926" fill="#333" id="svg_1" class=""><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="2" repeatCount="indefinite" to="2" values="2;1.2;2" /><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from="1" repeatCount="indefinite" to="1" values="1;.5;1" /></circle><circle cx="8" cy="8" r="1.2736" fill="#333" fill-opacity=".3" id="svg_2" class=""><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="1.2" repeatCount="indefinite" to="1.2" values="1.2;2;1.2" /><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from=".5" repeatCount="indefinite" to=".5" values=".5;1;.5" /></circle><circle cx="12" cy="8" r="1.926" fill="#333" id="svg_3" class=""><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="2" repeatCount="indefinite" to="2" values="2;1.2;2" /><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from="1" repeatCount="indefinite" to="1" values="1;.5;1" /></circle></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.666666666666666 6.666666666666666)" d="M13.33,6.67C13.33,2.98 10.35,0 6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6.333333333333333 6) rotate(0 0 2)" d="M0,0L0,4 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.666666666666666 6) rotate(0 0 2)" d="M0,0L0,4 " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M13.33,6.67C13.33,2.98 10.35,0 6.67,0C2.98,0 0,2.98 0,6.67C0,10.35 2.98,13.33 6.67,13.33C10.35,13.33 13.33,10.35 13.33,6.67Z" transform="translate(1.3333333333333333 1.3333333333333333) rotate(0 6.666666666666666 6.666666666666666)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,4" transform="translate(6.333333333333333 6) rotate(0 0 2)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,4" transform="translate(9.666666666666666 6) rotate(0 0 2)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,24 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(14 2.6666666666666665) rotate(0 0 2.6666666666666665)"
d="M0,0L0,5.33 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 8) rotate(0 0 2.6666666666666665)" d="M0,0L0,5.33 " />
<path id="分组 1"
style="stroke:#333333; stroke-width:1.333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.000000057161194 2) rotate(0 6.001349925994873 6)"
d="M12.0027 6C12.0027 2.69 9.3127 0 6.0027 0C4.3027 0 2.7727 0.7 1.6827 1.83 M-5.71612e-08 6C-5.71612e-08 9.31 2.69 12 6 12C7.62 12 9.09 11.36 10.17 10.32 " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,5.33" transform="translate(14 2.6666666666666665) rotate(0 0 2.6666666666666665)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,5.33" transform="translate(2 8) rotate(0 0 2.6666666666666665)"/><path id="分组 1" style="stroke:#333;stroke-width:1.333;stroke-opacity:1;stroke-dasharray:0 0" d="M12.0027 6C12.0027 2.69 9.3127 0 6.0027 0C4.3027 0 2.7727 0.7 1.6827 1.83 M-5.71612e-08 6C-5.71612e-08 9.31 2.69 12 6 12C7.62 12 9.09 11.36 10.17 10.32" transform="translate(2.000000057161194 2) rotate(0 6.001349925994873 6)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path id="路径 1" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.774903333333333 1.3006199999999999) rotate(0 6.599664999999999 6.599656666666666)" d="M2.83,13.2L13.2,2.83L10.37,0L0,10.37L0,13.2L2.83,13.2Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.317366666666667 4.129066666666667) rotate(0 1.4142166666666658 1.4142166666666665)" d="M0,0L2.83,2.83 " /></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M2.83,13.2L13.2,2.83L10.37,0L0,10.37L0,13.2L2.83,13.2Z" transform="translate(1.774903333333333 1.3006199999999999) rotate(0 6.599664999999999 6.599656666666666)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L2.83,2.83" transform="translate(9.317366666666667 4.129066666666667) rotate(0 1.4142166666666658 1.4142166666666665)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 881 B

View File

@@ -1,21 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 2.6666666666666665) rotate(0 1.1666333333333334 2.1666666666666665)"
d="M2.33,0L0,2L2.33,4.33 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 4.666666666666666) rotate(0 6.000006859869576 4.333333333333333)"
d="M0,0L7.66,0C9.96,0 11.91,1.87 12,4.17C12.09,6.59 10.09,8.67 7.66,8.67L2,8.67 " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M2.33,0L0,2L2.33,4.33" transform="translate(2 2.6666666666666665) rotate(0 1.1666333333333334 2.1666666666666665)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L7.66,0C9.96,0 11.91,1.87 12,4.17C12.09,6.59 10.09,8.67 7.66,8.67L2,8.67" transform="translate(2 4.666666666666666) rotate(0 6.000006859869576 4.333333333333333)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 878 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,21 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#FFFFFF; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(1.3333333333333333 2) rotate(0 6.333333333333333 6.333333333333333)"
d="M0,4.71L6.67,6L8.34,12.67L12.67,0L0,4.71Z " />
<path id="路径 2"
style="stroke:#FFFFFF; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(8.002766666666666 6.1172) rotate(0 0.9428000000000001 0.9428000000000001)"
d="M0,1.89L1.89,0 " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#fff;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,4.71L6.67,6L8.34,12.67L12.67,0L0,4.71Z" transform="translate(1.3333333333333333 2) rotate(0 6.333333333333333 6.333333333333333)"/><path id="路径 2" style="stroke:#fff;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,1.89L1.89,0" transform="translate(8.002766666666666 6.1172) rotate(0 0.9428000000000001 0.9428000000000001)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 976 B

After

Width:  |  Height:  |  Size: 841 B

View File

@@ -1,21 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16"
height="16" viewBox="0 0 16 16" fill="none">
<defs>
<rect id="path_0" x="0" y="0" width="16" height="16" />
</defs>
<g opacity="1" transform="translate(0 0) rotate(0 8 8)">
<mask id="bg-mask-0" fill="white">
<use xlink:href="#path_0"></use>
</mask>
<g mask="url(#bg-mask-0)">
<path id="路径 1"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(1.3333333333333333 2.333333333333333) rotate(0 6.666666666666666 5.666666666666666)"
d="M13.33,5.67L10,0L3.33,0L0,5.67L3.33,11.33L10,11.33L13.33,5.67Z " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(6.333333333333333 6.333333333333333) rotate(0 1.6666666666666665 1.6666666666666665)"
d="M3.33,1.67C3.33,0.75 2.59,0 1.67,0C0.75,0 0,0.75 0,1.67C0,2.59 0.75,3.33 1.67,3.33C2.59,3.33 3.33,2.59 3.33,1.67Z " />
</g>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="none" viewBox="0 0 16 16"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(0 8 8)"><mask id="bg-mask-0" fill="#fff"><use xlink:href="#path_0"/></mask><g mask="url(#bg-mask-0)"><path id="路径 1" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M13.33,5.67L10,0L3.33,0L0,5.67L3.33,11.33L10,11.33L13.33,5.67Z" transform="translate(1.3333333333333333 2.333333333333333) rotate(0 6.666666666666666 5.666666666666666)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3.33,1.67C3.33,0.75 2.59,0 1.67,0C0.75,0 0,0.75 0,1.67C0,2.59 0.75,3.33 1.67,3.33C2.59,3.33 3.33,2.59 3.33,1.67Z" transform="translate(6.333333333333333 6.333333333333333) rotate(0 1.6666666666666665 1.6666666666666665)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 988 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -4,9 +4,13 @@ import "./styles/markdown.scss";
import "./styles/highlight.scss";
import { getClientConfig } from "./config/client";
import { type Metadata } from "next";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { getServerSideConfig } from "./config/server";
import { GoogleTagManager } from "@next/third-parties/google";
const serverConfig = getServerSideConfig();
export const metadata: Metadata = {
title: "ChatGPT Next Web",
title: "NextChat",
description: "Your personal ChatGPT Chat Bot.",
viewport: {
width: "device-width",
@@ -18,7 +22,7 @@ export const metadata: Metadata = {
{ media: "(prefers-color-scheme: dark)", color: "#151515" },
],
appleWebApp: {
title: "ChatGPT Next Web",
title: "NextChat",
statusBarStyle: "default",
},
};
@@ -35,7 +39,19 @@ export default function RootLayout({
<link rel="manifest" href="/site.webmanifest"></link>
<script src="/serviceWorkerRegister.js" defer></script>
</head>
<body>{children}</body>
<body>
{children}
{serverConfig?.isVercel && (
<>
<SpeedInsights />
</>
)}
{serverConfig?.gtmId && (
<>
<GoogleTagManager gtmId={serverConfig.gtmId} />
</>
)}
</body>
</html>
);
}

View File

@@ -10,6 +10,7 @@ const ar: PartialLocaleType = {
Auth: {
Title: "تحتاج إلى رمز الوصول",
Tips: "يرجى إدخال رمز الوصول أدناه",
SubTips: "أو أدخل مفتاح واجهة برمجة تطبيقات OpenAI الخاص بك",
Input: "رمز الوصول",
Confirm: "تأكيد",
Later: "لاحقًا",
@@ -166,11 +167,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد
Title: "حد الضغط للتاريخ",
SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد",
},
Token: {
Title: "مفتاح API",
SubTitle: "استخدم مفتاحك لتجاوز حد رمز الوصول",
Placeholder: "مفتاح OpenAI API",
},
Usage: {
Title: "رصيد الحساب",
SubTitle(used: any, total: any) {
@@ -180,15 +177,7 @@ ${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخد
Check: "التحقق",
NoAccess: "أدخل مفتاح API للتحقق من الرصيد",
},
AccessCode: {
Title: "رمز الوصول",
SubTitle: "تم تمكين التحكم في الوصول",
Placeholder: "رمز الوصول المطلوب",
},
Endpoint: {
Title: "نقطة النهاية",
SubTitle: "يجب أن تبدأ نقطة النهاية المخصصة بـ http(s)://",
},
Model: "النموذج",
Temperature: {
Title: "الحرارة",

View File

@@ -10,6 +10,7 @@ const bn: PartialLocaleType = {
Auth: {
Title: "একটি অ্যাক্সেস কোড প্রয়োজন",
Tips: "নীচে অ্যাক্সেস কোড ইনপুট করুন",
SubTips: "অথবা আপনার OpenAI API কী প্রবেশ করুন",
Input: "অ্যাক্সেস কোড",
Confirm: "নিশ্চিত করুন",
Later: "পরে",
@@ -198,11 +199,7 @@ const bn: PartialLocaleType = {
SubTitle:
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
},
Token: {
Title: "অ্যাপি কী",
SubTitle: "অ্যাক্সেস কোড সীমা উপেক্ষা করতে আপনার কীটি ব্যবহার করুন",
Placeholder: "OpenAI API কী",
},
Usage: {
Title: "একাউন্ট ব্যালেন্স",
SubTitle(used: any, total: any) {
@@ -212,15 +209,7 @@ const bn: PartialLocaleType = {
Check: "চেক",
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
},
AccessCode: {
Title: "অ্যাক্সেস কোড",
SubTitle: "অ্যাক্সেস নিয়ন্ত্রণ সক্রিয়",
Placeholder: "অ্যাক্সেস কোড প্রয়োজন",
},
Endpoint: {
Title: "ইনটারপয়েন্ট",
SubTitle: "কাস্টম এন্ডপয়েন্টটি হতে হবে http(s):// দিয়ে শুরু হতে হবে",
},
Model: "মডেল",
Temperature: {
Title: "তাপমাত্রা",

View File

@@ -13,6 +13,7 @@ const cn = {
Auth: {
Title: "需要密码",
Tips: "管理员开启了密码验证,请在下方填入访问码",
SubTips: "或者输入你的 OpenAI 或 Google API 密钥",
Input: "在此处填写访问码",
Confirm: "确认",
Later: "稍后再说",
@@ -62,6 +63,7 @@ const cn = {
Masks: "所有面具",
Clear: "清除聊天",
Settings: "对话设置",
UploadImage: "上传图片",
},
Rename: "重命名对话",
Typing: "正在输入…",
@@ -84,8 +86,8 @@ const cn = {
Copy: "全部复制",
Download: "下载文件",
Share: "分享到 ShareGPT",
MessageFromYou: "来自你的消息",
MessageFromChatGPT: "来自 ChatGPT 的消息",
MessageFromYou: "用户",
MessageFromChatGPT: "ChatGPT",
Format: {
Title: "导出格式",
SubTitle: "可以导出 Markdown 文本或者 PNG 图片",
@@ -178,6 +180,49 @@ const cn = {
Title: "自动生成标题",
SubTitle: "根据对话内容生成合适的标题",
},
Sync: {
CloudState: "云端数据",
NotSyncYet: "还没有进行过同步",
Success: "同步成功",
Fail: "同步失败",
Config: {
Modal: {
Title: "配置云同步",
Check: "检查可用性",
},
SyncType: {
Title: "同步类型",
SubTitle: "选择喜爱的同步服务器",
},
Proxy: {
Title: "启用代理",
SubTitle: "在浏览器中同步时,必须启用代理以避免跨域限制",
},
ProxyUrl: {
Title: "代理地址",
SubTitle: "仅适用于本项目自带的跨域代理",
},
WebDav: {
Endpoint: "WebDAV 地址",
UserName: "用户名",
Password: "密码",
},
UpStash: {
Endpoint: "UpStash Redis REST Url",
UserName: "备份名称",
Password: "UpStash Redis REST Token",
},
},
LocalState: "本地数据",
Overview: (overview: any) => {
return `${overview.chat} 次对话,${overview.message} 条消息,${overview.prompt} 条提示词,${overview.mask} 个面具`;
},
ImportFailed: "导入失败",
},
Mask: {
Splash: {
Title: "面具启动页",
@@ -214,11 +259,6 @@ const cn = {
Title: "历史消息长度压缩阈值",
SubTitle: "当未压缩的历史消息超过该值时,将进行压缩",
},
Token: {
Title: "API Key",
SubTitle: "使用自己的 Key 可绕过密码访问限制",
Placeholder: "OpenAI API Key",
},
Usage: {
Title: "余额查询",
@@ -229,19 +269,73 @@ const cn = {
Check: "重新检查",
NoAccess: "输入 API Key 或访问密码查看余额",
},
AccessCode: {
Title: "访问密码",
SubTitle: "管理员已开启加密访问",
Placeholder: "请输入访问密码",
},
Endpoint: {
Title: "接口地址",
SubTitle: "除默认地址外,必须包含 http(s)://",
},
CustomModel: {
Title: "自定义模型名",
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
Access: {
AccessCode: {
Title: "访问密码",
SubTitle: "管理员已开启加密访问",
Placeholder: "请输入访问密码",
},
CustomEndpoint: {
Title: "自定义接口",
SubTitle: "是否使用自定义 Azure 或 OpenAI 服务",
},
Provider: {
Title: "模型服务商",
SubTitle: "切换不同的服务商",
},
OpenAI: {
ApiKey: {
Title: "API Key",
SubTitle: "使用自定义 OpenAI Key 绕过密码访问限制",
Placeholder: "OpenAI API Key",
},
Endpoint: {
Title: "接口地址",
SubTitle: "除默认地址外,必须包含 http(s)://",
},
},
Azure: {
ApiKey: {
Title: "接口密钥",
SubTitle: "使用自定义 Azure Key 绕过密码访问限制",
Placeholder: "Azure API Key",
},
Endpoint: {
Title: "接口地址",
SubTitle: "样例:",
},
ApiVerion: {
Title: "接口版本 (azure api version)",
SubTitle: "选择指定的部分版本",
},
},
Google: {
ApiKey: {
Title: "API 密钥",
SubTitle: "从 Google AI 获取您的 API 密钥",
Placeholder: "输入您的 Google AI Studio API 密钥",
},
Endpoint: {
Title: "终端地址",
SubTitle: "示例:",
},
ApiVersion: {
Title: "API 版本(仅适用于 gemini-pro",
SubTitle: "选择一个特定的 API 版本",
},
},
CustomModel: {
Title: "自定义模型名",
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
},
},
Model: "模型 (model)",
Temperature: {
Title: "随机性 (temperature)",
@@ -271,7 +365,7 @@ const cn = {
Prompt: {
History: (content: string) => "这是历史聊天总结作为前情提要:" + content,
Topic:
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,如果没有主题,请直接返回“闲聊”",
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,不要加粗,如果没有主题,请直接返回“闲聊”",
Summarize:
"简要总结一下对话内容,用作后续的上下文提示 prompt控制在 200 字以内",
},
@@ -280,6 +374,10 @@ const cn = {
Success: "已写入剪切板",
Failed: "复制失败,请赋予剪切板权限",
},
Download: {
Success: "内容已下载到您的目录。",
Failed: "下载失败。",
},
Context: {
Toast: (x: any) => `包含 ${x} 条预设提示词`,
Edit: "当前对话设置",
@@ -355,8 +453,15 @@ const cn = {
Close: "关闭",
Create: "新建",
Edit: "编辑",
Export: "导出",
Import: "导入",
Sync: "同步",
Config: "配置",
},
Exporter: {
Description: {
Title: "只有清除上下文之后的消息会被展示",
},
Model: "模型",
Messages: "消息",
Topic: "主题",

View File

@@ -124,11 +124,7 @@ const cs: PartialLocaleType = {
SubTitle:
"Komprese proběhne, pokud délka nekomprimovaných zpráv přesáhne tuto hodnotu",
},
Token: {
Title: "API klíč",
SubTitle: "Použitím klíče ignorujete omezení přístupového kódu",
Placeholder: "Klíč API OpenAI",
},
Usage: {
Title: "Stav účtu",
SubTitle(used: any, total: any) {
@@ -138,11 +134,7 @@ const cs: PartialLocaleType = {
Check: "Zkontrolovat",
NoAccess: "Pro kontrolu zůstatku zadejte klíč API",
},
AccessCode: {
Title: "Přístupový kód",
SubTitle: "Kontrola přístupu povolena",
Placeholder: "Potřebujete přístupový kód",
},
Model: "Model",
Temperature: {
Title: "Teplota",

View File

@@ -124,12 +124,7 @@ const de: PartialLocaleType = {
SubTitle:
"Komprimierung, wenn die Länge der unkomprimierten Nachrichten den Wert überschreitet",
},
Token: {
Title: "API-Schlüssel",
SubTitle:
"Verwenden Sie Ihren Schlüssel, um das Zugangscode-Limit zu ignorieren",
Placeholder: "OpenAI API-Schlüssel",
},
Usage: {
Title: "Kontostand",
SubTitle(used: any, total: any) {
@@ -139,11 +134,6 @@ const de: PartialLocaleType = {
Check: "Erneut prüfen",
NoAccess: "API-Schlüssel eingeben, um den Kontostand zu überprüfen",
},
AccessCode: {
Title: "Zugangscode",
SubTitle: "Zugangskontrolle aktiviert",
Placeholder: "Zugangscode erforderlich",
},
Model: "Modell",
Temperature: {
Title: "Temperature", //Temperatur

View File

@@ -15,6 +15,7 @@ const en: LocaleType = {
Auth: {
Title: "Need Access Code",
Tips: "Please enter access code below",
SubTips: "Or enter your OpenAI or Google API Key",
Input: "access code",
Confirm: "Confirm",
Later: "Later",
@@ -64,6 +65,7 @@ const en: LocaleType = {
Masks: "Masks",
Clear: "Clear Context",
Settings: "Settings",
UploadImage: "Upload Images",
},
Rename: "Rename Chat",
Typing: "Typing…",
@@ -180,6 +182,50 @@ const en: LocaleType = {
Title: "Auto Generate Title",
SubTitle: "Generate a suitable title based on the conversation content",
},
Sync: {
CloudState: "Last Update",
NotSyncYet: "Not sync yet",
Success: "Sync Success",
Fail: "Sync Fail",
Config: {
Modal: {
Title: "Config Sync",
Check: "Check Connection",
},
SyncType: {
Title: "Sync Type",
SubTitle: "Choose your favorite sync service",
},
Proxy: {
Title: "Enable CORS Proxy",
SubTitle: "Enable a proxy to avoid cross-origin restrictions",
},
ProxyUrl: {
Title: "Proxy Endpoint",
SubTitle:
"Only applicable to the built-in CORS proxy for this project",
},
WebDav: {
Endpoint: "WebDAV Endpoint",
UserName: "User Name",
Password: "Password",
},
UpStash: {
Endpoint: "UpStash Redis REST Url",
UserName: "Backup Name",
Password: "UpStash Redis REST Token",
},
},
LocalState: "Local Data",
Overview: (overview: any) => {
return `${overview.chat} chats${overview.message} messages${overview.prompt} prompts${overview.mask} masks`;
},
ImportFailed: "Failed to import from file",
},
Mask: {
Splash: {
Title: "Mask Splash Screen",
@@ -217,11 +263,7 @@ const en: LocaleType = {
SubTitle:
"Will compress if uncompressed messages length exceeds the value",
},
Token: {
Title: "API Key",
SubTitle: "Use your key to ignore access code limit",
Placeholder: "OpenAI API Key",
},
Usage: {
Title: "Account Balance",
SubTitle(used: any, total: any) {
@@ -231,19 +273,72 @@ const en: LocaleType = {
Check: "Check",
NoAccess: "Enter API Key to check balance",
},
AccessCode: {
Title: "Access Code",
SubTitle: "Access control enabled",
Placeholder: "Need Access Code",
},
Endpoint: {
Title: "Endpoint",
SubTitle: "Custom endpoint must start with http(s)://",
},
CustomModel: {
Title: "Custom Models",
SubTitle: "Add extra model options, separate by comma",
Access: {
AccessCode: {
Title: "Access Code",
SubTitle: "Access control Enabled",
Placeholder: "Enter Code",
},
CustomEndpoint: {
Title: "Custom Endpoint",
SubTitle: "Use custom Azure or OpenAI service",
},
Provider: {
Title: "Model Provider",
SubTitle: "Select Azure or OpenAI",
},
OpenAI: {
ApiKey: {
Title: "OpenAI API Key",
SubTitle: "User custom OpenAI Api Key",
Placeholder: "sk-xxx",
},
Endpoint: {
Title: "OpenAI Endpoint",
SubTitle: "Must starts with http(s):// or use /api/openai as default",
},
},
Azure: {
ApiKey: {
Title: "Azure Api Key",
SubTitle: "Check your api key from Azure console",
Placeholder: "Azure Api Key",
},
Endpoint: {
Title: "Azure Endpoint",
SubTitle: "Example: ",
},
ApiVerion: {
Title: "Azure Api Version",
SubTitle: "Check your api version from azure console",
},
},
CustomModel: {
Title: "Custom Models",
SubTitle: "Custom model options, seperated by comma",
},
Google: {
ApiKey: {
Title: "API Key",
SubTitle: "Obtain your API Key from Google AI",
Placeholder: "Enter your Google AI Studio API Key",
},
Endpoint: {
Title: "Endpoint Address",
SubTitle: "Example:",
},
ApiVersion: {
Title: "API Version (specific to gemini-pro)",
SubTitle: "Select a specific API version",
},
},
},
Model: "Model",
Temperature: {
Title: "Temperature",
@@ -276,7 +371,7 @@ const en: LocaleType = {
History: (content: string) =>
"This is a summary of the chat history as a recap: " + content,
Topic:
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, or additional text. Remove enclosing quotation marks.",
"Please generate a four to five word title summarizing our conversation without any lead-in, punctuation, quotation marks, periods, symbols, bold text, or additional text. Remove enclosing quotation marks.",
Summarize:
"Summarize the discussion briefly in 200 words or less to use as a prompt for future context.",
},
@@ -285,6 +380,10 @@ const en: LocaleType = {
Success: "Copied to clipboard",
Failed: "Copy failed, please grant permission to access clipboard",
},
Download: {
Success: "Content downloaded to your directory.",
Failed: "Download failed.",
},
Context: {
Toast: (x: any) => `With ${x} contextual prompts`,
Edit: "Current Chat Settings",
@@ -355,8 +454,15 @@ const en: LocaleType = {
Close: "Close",
Create: "Create",
Edit: "Edit",
Export: "Export",
Import: "Import",
Sync: "Sync",
Config: "Config",
},
Exporter: {
Description: {
Title: "Only messages after clearing the context will be displayed",
},
Model: "Model",
Messages: "Messages",
Topic: "Topic",

View File

@@ -124,11 +124,7 @@ const es: PartialLocaleType = {
SubTitle:
"Se comprimirán los mensajes si la longitud de los mensajes no comprimidos supera el valor",
},
Token: {
Title: "Clave de API",
SubTitle: "Utiliza tu clave para ignorar el límite de código de acceso",
Placeholder: "Clave de la API de OpenAI",
},
Usage: {
Title: "Saldo de la cuenta",
SubTitle(used: any, total: any) {
@@ -138,11 +134,7 @@ const es: PartialLocaleType = {
Check: "Comprobar de nuevo",
NoAccess: "Introduzca la clave API para comprobar el saldo",
},
AccessCode: {
Title: "Código de acceso",
SubTitle: "Control de acceso habilitado",
Placeholder: "Necesita código de acceso",
},
Model: "Modelo",
Temperature: {
Title: "Temperatura",

View File

@@ -173,11 +173,7 @@ const fr: PartialLocaleType = {
SubTitle:
"Comprimera si la longueur des messages non compressés dépasse cette valeur",
},
Token: {
Title: "Clé API",
SubTitle: "Utilisez votre clé pour ignorer la limite du code d'accès",
Placeholder: "Clé OpenAI API",
},
Usage: {
Title: "Solde du compte",
SubTitle(used: any, total: any) {
@@ -187,11 +183,7 @@ const fr: PartialLocaleType = {
Check: "Vérifier",
NoAccess: "Entrez la clé API pour vérifier le solde",
},
AccessCode: {
Title: "Code d'accès",
SubTitle: "Contrôle d'accès activé",
Placeholder: "Code d'accès requis",
},
Model: "Modèle",
Temperature: {
Title: "Température",

View File

@@ -5,11 +5,12 @@ const id: PartialLocaleType = {
WIP: "Coming Soon...",
Error: {
Unauthorized:
"Akses tidak diizinkan. Silakan [otorisasi](/#/auth) dengan memasukkan kode akses.",
"Akses tidak diizinkan, silakan masukkan kode akses atau masukkan kunci API OpenAI Anda. di halaman [autentikasi](/#/auth) atau di halaman [Pengaturan](/#/settings).",
},
Auth: {
Title: "Diperlukan Kode Akses",
Tips: "Masukkan kode akses di bawah",
SubTips: "Atau masukkan kunci API OpenAI Anda",
Input: "Kode Akses",
Confirm: "Konfirmasi",
Later: "Nanti",
@@ -60,7 +61,9 @@ const id: PartialLocaleType = {
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter untuk membalut";
}
return inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah";
return (
inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah"
);
},
Send: "Kirim",
Config: {
@@ -117,33 +120,35 @@ const id: PartialLocaleType = {
Title: "Setel Ulang Semua Pengaturan",
SubTitle: "Mengembalikan semua pengaturan ke nilai default",
Action: "Setel Ulang",
Confirm: "Anda yakin ingin mengembalikan semua pengaturan ke nilai default?",
Confirm:
"Anda yakin ingin mengembalikan semua pengaturan ke nilai default?",
},
Clear: {
Title: "Hapus Semua Data",
SubTitle: "Menghapus semua pesan dan pengaturan",
SubTitle: "Semua data yang tersimpan secara lokal akan dihapus",
Action: "Hapus",
Confirm: "Anda yakin ingin menghapus semua pesan dan pengaturan?",
Confirm:
"Apakah Anda yakin ingin menghapus semua data yang tersimpan secara lokal?",
},
},
Lang: {
Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "Semua Bahasa",
},
Avatar: "Avatar",
FontSize: {
Title: "Ukuran Font",
SubTitle: "Ubah ukuran font konten chat",
},
InjectSystemPrompts: {
Title: "Suntikkan Petunjuk Sistem",
SubTitle:
"Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan",
},
InputTemplate: {
Title: "Template Input",
SubTitle: "Pesan baru akan diisi menggunakan template ini",
},
Name: "Bahasa", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "Semua Bahasa",
},
Avatar: "Avatar",
FontSize: {
Title: "Ukuran Font",
SubTitle: "Ubah ukuran font konten chat",
},
InjectSystemPrompts: {
Title: "Suntikkan Petunjuk Sistem",
SubTitle:
"Tambahkan petunjuk simulasi sistem ChatGPT di awal daftar pesan yang diminta dalam setiap permintaan",
},
InputTemplate: {
Title: "Template Input",
SubTitle: "Pesan baru akan diisi menggunakan template ini",
},
Update: {
Version: (x: string) => `Version: ${x}`,
@@ -154,9 +159,40 @@ const id: PartialLocaleType = {
GoToUpdate: "Perbarui Sekarang",
},
AutoGenerateTitle: {
Title: "Hasilkan Judul Otomatis",
SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan",
Title: "Hasilkan Judul Otomatis",
SubTitle: "Hasilkan judul yang sesuai berdasarkan konten percakapan",
},
Sync: {
CloudState: "Pembaruan Terakhir",
NotSyncYet: "Belum disinkronkan",
Success: "Sinkronisasi Berhasil",
Fail: "Sinkronisasi Gagal",
Config: {
Modal: {
Title: "Konfigurasi Sinkronisasi",
},
SyncType: {
Title: "Tipe Sinkronisasi",
SubTitle: "Pilih layanan sinkronisasi favorit Anda",
},
Proxy: {
Title: "Aktifkan Proxy CORS",
SubTitle:
"Aktifkan Proxy untuk menghindari pembatasan atau pemblokiran lintas sumber",
},
ProxyUrl: {
Title: "Lokasi Titik Akhir Proxy CORS",
SubTitle: "Hanya berlaku untuk Proxy CORS bawaan untuk proyek ini",
},
WebDav: {
Endpoint: "Lokasi Titik Akhir WebDAV",
UserName: "User Pengguna",
Password: "Kata Sandi",
},
},
},
SendKey: "Kirim",
Theme: "Tema",
TightBorder: "Batas Ketat",
@@ -176,76 +212,65 @@ const id: PartialLocaleType = {
},
},
Prompt: {
Disable: {
Title: "Nonaktifkan Otomatisasi",
SubTitle: "Aktifkan/Matikan otomatisasi",
},
List: "Daftar Prompt",
ListCount: (builtin: number, custom: number) =>
`${builtin} bawaan, ${custom} penggunaan khusus`,
Edit: "Edit",
Modal: {
Title: "Daftar Prompt",
Add: "Tambahkan",
Search: "Cari Prompt",
},
EditModal: {
Title: "Edit Prompt",
},
Disable: {
Title: "Nonaktifkan Otomatisasi",
SubTitle: "Aktifkan/Matikan otomatisasi",
},
HistoryCount: {
Title: "Jumlah Pesan Riwayat",
SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan",
List: "Daftar Prompt",
ListCount: (builtin: number, custom: number) =>
`${builtin} bawaan, ${custom} penggunaan khusus`,
Edit: "Edit",
Modal: {
Title: "Daftar Prompt",
Add: "Tambahkan",
Search: "Cari Prompt",
},
CompressThreshold: {
Title: "Batas Kompresi Riwayat",
SubTitle:
"Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi",
},
Token: {
Title: "Kunci API",
SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses",
Placeholder: "Kunci API OpenAI",
EditModal: {
Title: "Edit Prompt",
},
Usage: {
Title: "Saldo Akun",
SubTitle(used: any, total: any) {
return `Digunakan bulan ini: ${used}, total langganan: ${total}`;
},
IsChecking: "Memeriksa...",
Check: "Periksa",
NoAccess: "Masukkan kunci API untuk memeriksa saldo",
},
HistoryCount: {
Title: "Jumlah Pesan Riwayat",
SubTitle: "Jumlah pesan yang akan dikirim setiap permintaan",
},
CompressThreshold: {
Title: "Batas Kompresi Riwayat",
SubTitle:
"Jika panjang pesan melebihi batas yang ditentukan, pesan tersebut akan dikompresi",
},
Usage: {
Title: "Saldo Akun",
SubTitle(used: any, total: any) {
return `Digunakan bulan ini: ${used}, total langganan: ${total}`;
},
AccessCode: {
Title: "Kode Akses",
SubTitle: "Kontrol akses diaktifkan",
Placeholder: "Diperlukan kode akses",
},
Endpoint: {
Title: "Endpoint",
SubTitle: "Harus dimulai dengan http(s):// untuk endpoint kustom",
},
Model: "Model",
Temperature: {
Title: "Suhu",
SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya",
},
TopP: {
Title: "Top P",
SubTitle: "Tidak mengubah nilai dengan suhu",
},
MaxTokens: {
Title: "Token Maksimum",
SubTitle: "Panjang maksimum token input dan output",
},
PresencePenalty: {
Title: "Penalti Kehadiran",
SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul",
},
FrequencyPenalty: {
Title: "Penalti Frekuensi",
SubTitle: "Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama",
},
IsChecking: "Memeriksa...",
Check: "Periksa",
NoAccess: "Masukkan kunci API untuk memeriksa saldo",
},
Model: "Model",
Temperature: {
Title: "Suhu",
SubTitle: "Semakin tinggi nilainya, semakin acak keluarannya",
},
TopP: {
Title: "Top P",
SubTitle: "Tidak mengubah nilai dengan suhu",
},
MaxTokens: {
Title: "Token Maksimum",
SubTitle: "Panjang maksimum token input dan output",
},
PresencePenalty: {
Title: "Penalti Kehadiran",
SubTitle: "Semakin tinggi nilai, semakin mungkin topik baru muncul",
},
FrequencyPenalty: {
Title: "Penalti Frekuensi",
SubTitle:
"Semakin tinggi nilai, semakin rendah kemungkinan penggunaan ulang baris yang sama",
},
},
Store: {
DefaultTopic: "Percakapan Baru",
@@ -261,8 +286,13 @@ const id: PartialLocaleType = {
},
},
Copy: {
Success: "Berhasil disalin ke clipboard",
Failed: "Gagal menyalin, berikan izin untuk memberikan izin",
Success: "Tersalin ke clipboard",
Failed:
"Gagal menyalin, mohon berikan izin untuk mengakses clipboard atau Clipboard API tidak didukung (Tauri)",
},
Download: {
Success: "Konten berhasil diunduh ke direktori Anda.",
Failed: "Unduhan gagal.",
},
Context: {
Toast: (x: any) => `Dengan ${x} promp kontekstual`,
@@ -338,10 +368,13 @@ const id: PartialLocaleType = {
Edit: "Edit",
},
Exporter: {
Description: {
Title: "Hanya pesan setelah menghapus konteks yang akan ditampilkan"
},
Model: "Model",
Messages: "Pesan",
Topic: "Topik",
Time: "Waktu",
Time: "Tanggal & Waktu",
},
URLCommand: {
Code: "Kode akses terdeteksi dari url, konfirmasi untuk mendaftar ? ",

View File

@@ -1,5 +1,6 @@
import cn from "./cn";
import en from "./en";
import pt from "./pt";
import tw from "./tw";
import id from "./id";
import fr from "./fr";
@@ -15,6 +16,7 @@ import cs from "./cs";
import ko from "./ko";
import ar from "./ar";
import bn from "./bn";
import sk from "./sk";
import { merge } from "../utils/merge";
import type { LocaleType } from "./cn";
@@ -24,6 +26,7 @@ const ALL_LANGS = {
cn,
en,
tw,
pt,
jp,
ko,
id,
@@ -38,6 +41,7 @@ const ALL_LANGS = {
no,
ar,
bn,
sk,
};
export type Lang = keyof typeof ALL_LANGS;
@@ -47,6 +51,7 @@ export const AllLangs = Object.keys(ALL_LANGS) as Lang[];
export const ALL_LANG_OPTIONS: Record<Lang, string> = {
cn: "简体中文",
en: "English",
pt: "Português",
tw: "繁體中文",
jp: "日本語",
ko: "한국어",
@@ -62,6 +67,7 @@ export const ALL_LANG_OPTIONS: Record<Lang, string> = {
no: "Nynorsk",
ar: "العربية",
bn: "বাংলা",
sk: "Slovensky",
};
const LANG_KEY = "lang";

View File

@@ -124,12 +124,7 @@ const it: PartialLocaleType = {
SubTitle:
"Comprimerà se la lunghezza dei messaggi non compressi supera il valore",
},
Token: {
Title: "API Key",
SubTitle:
"Utilizzare la chiave per ignorare il limite del codice di accesso",
Placeholder: "OpenAI API Key",
},
Usage: {
Title: "Bilancio Account",
SubTitle(used: any, total: any) {
@@ -139,11 +134,7 @@ const it: PartialLocaleType = {
Check: "Controlla ancora",
NoAccess: "Inserire la chiave API per controllare il saldo",
},
AccessCode: {
Title: "Codice d'accesso",
SubTitle: "Controllo d'accesso abilitato",
Placeholder: "Inserisci il codice d'accesso",
},
Model: "Modello GPT",
Temperature: {
Title: "Temperature",

View File

@@ -12,6 +12,13 @@ const jp: PartialLocaleType = {
},
Chat: {
SubTitle: (count: number) => `ChatGPTとの ${count} 通のチャット`,
EditMessage: {
Title: "全てのメッセージを修正",
Topic: {
Title: "トピック",
SubTitle: "このトピックを変える",
},
},
Actions: {
ChatList: "メッセージリストを表示",
CompressedHistory: "圧縮された履歴プロンプトを表示",
@@ -19,7 +26,12 @@ const jp: PartialLocaleType = {
Copy: "コピー",
Stop: "停止",
Retry: "リトライ",
Pin: "ピン",
PinToastContent:
"コンテキストプロンプトに1つのメッセージをピン留めしました",
PinToastAction: "表示",
Delete: "削除",
Edit: "編集",
},
Rename: "チャットの名前を変更",
Typing: "入力中…",
@@ -33,7 +45,7 @@ const jp: PartialLocaleType = {
Send: "送信",
Config: {
Reset: "リセット",
SaveAs: "另存为面具",
SaveAs: "保存",
},
},
Export: {
@@ -42,6 +54,28 @@ const jp: PartialLocaleType = {
Download: "ファイルをダウンロード",
MessageFromYou: "あなたからのメッセージ",
MessageFromChatGPT: "ChatGPTからのメッセージ",
Format: {
Title: "フォーマットをエクスポート",
SubTitle: "マークダウン形式、PNG画像形式を選択できます。",
},
IncludeContext: {
Title: "コンテキストを含みますか?",
SubTitle: "コンテキストを含ませるか否か",
},
Steps: {
Select: "エクスポート設定",
Preview: "プレビュー",
},
Image: {
Toast: "画像生成中...",
Modal: "長押し、または右クリックで保存してください。",
},
},
Select: {
Search: "検索",
All: "すべて選択",
Latest: "新しいメッセージを選択",
Clear: "クリア",
},
Memory: {
Title: "履歴メモリ",
@@ -113,6 +147,10 @@ const jp: PartialLocaleType = {
Title: "キャラクターページ",
SubTitle: "新規チャット作成時にキャラクターページを表示する",
},
Builtin: {
Title: "ビルトインマスクを非表示",
SubTitle: "マスクリストからビルトインを非表示する",
},
},
Prompt: {
Disable: {
@@ -142,11 +180,7 @@ const jp: PartialLocaleType = {
SubTitle:
"圧縮されていない履歴メッセージがこの値を超えた場合、圧縮が行われます。",
},
Token: {
Title: "APIキー",
SubTitle: "自分のキーを使用してパスワードアクセス制限を迂回する",
Placeholder: "OpenAI APIキー",
},
Usage: {
Title: "残高照会",
SubTitle(used: any, total: any) {
@@ -156,11 +190,6 @@ const jp: PartialLocaleType = {
Check: "再確認",
NoAccess: "APIキーまたはアクセスパスワードを入力して残高を表示",
},
AccessCode: {
Title: "アクセスパスワード",
SubTitle: "暗号化アクセスが有効になっています",
Placeholder: "アクセスパスワードを入力してください",
},
Model: "モデル (model)",
Temperature: {
Title: "ランダム性 (temperature)",
@@ -179,6 +208,10 @@ const jp: PartialLocaleType = {
Title: "話題の頻度 (frequency_penalty)",
SubTitle: "値が大きいほど、重複語を低減する可能性が高くなります",
},
AutoGenerateTitle: {
Title: "タイトルの自動生成",
SubTitle: "会話内容に基づいて適切なタイトルを生成する",
},
},
Store: {
DefaultTopic: "新しいチャット",

View File

@@ -124,11 +124,7 @@ const ko: PartialLocaleType = {
Title: "기록 압축 임계값",
SubTitle: "미압축 메시지 길이가 임계값을 초과하면 압축됨",
},
Token: {
Title: "API 키",
SubTitle: "액세스 코드 제한을 무시하기 위해 키 사용",
Placeholder: "OpenAI API 키",
},
Usage: {
Title: "계정 잔액",
SubTitle(used: any, total: any) {
@@ -138,11 +134,7 @@ const ko: PartialLocaleType = {
Check: "확인",
NoAccess: "잔액 확인을 위해 API 키를 입력하세요.",
},
AccessCode: {
Title: "액세스 코드",
SubTitle: "액세스 제어가 활성화됨",
Placeholder: "액세스 코드 입력",
},
Model: "모델",
Temperature: {
Title: "온도 (temperature)",

View File

@@ -106,12 +106,7 @@ const no: PartialLocaleType = {
SubTitle:
"Komprimer dersom ikke-komprimert lengde på meldinger overskrider denne verdien",
},
Token: {
Title: "API Key",
SubTitle:
"Bruk din egen API-nøkkel for å ignorere tilgangskoden begrensning",
Placeholder: "OpenAI API-nøkkel",
},
Usage: {
Title: "Saldo for konto",
SubTitle(used: any, total: any) {
@@ -121,11 +116,7 @@ const no: PartialLocaleType = {
Check: "Sjekk",
NoAccess: "Skriv inn API-nøkkelen for å sjekke saldo",
},
AccessCode: {
Title: "Tilgangskode",
SubTitle: "Tilgangskontroll på",
Placeholder: "Trenger tilgangskode",
},
Model: "Model",
Temperature: {
Title: "Temperatur",

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