Compare commits

...

841 Commits
v2.1 ... v2.9.8

Author SHA1 Message Date
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
Yifei Zhang
ef3e8e6fac Merge pull request #2933 from H0llyW00dzZ/authpage 2023-10-07 23:03:56 +08: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
Yifei Zhang
b0c32159e7 Update tauri.conf.json 2023-08-28 01:15:46 +08:00
Yifei Zhang
4a182517da Update README.md 2023-08-28 01:04:24 +08:00
Yifei Zhang
107b584085 Merge pull request #2705 from kfear1337/language 2023-08-28 01:02:23 +08:00
Yifei Zhang
6d8416f838 Merge pull request #2713 from kfear1337/CodeQL-Report 2023-08-28 00:54:58 +08:00
Yifei Zhang
876a0cb6f7 Merge pull request #2717 from Yidadaa/bugfix-0827 2023-08-28 00:51:54 +08:00
Yidadaa
4904612523 feat: close #2430 add a simple user maual 2023-08-28 00:50:53 +08:00
Yidadaa
3bd76b9156 feat: close #2580 only use 3.5 to summarize when not using custom models 2023-08-28 00:02:52 +08:00
Yifei Zhang
dd757e7d04 Merge pull request #2711 from huhuhuni/main 2023-08-27 19:33:03 +08:00
B0zal
71d06647d0 Delete codeql.yml
it won't work if someone try, must have licence advance security
2023-08-27 09:15:05 +07:00
B0zal
0ae57589a9 This branch only for scanner 2023-08-27 06:09:55 +07:00
B0zal
22a6819f7b Security Update
[+] Protect Prototype
2023-08-26 22:16:29 +07:00
B0zal
5e23ad2db1 Security Update
Potentially unsafe external link
2023-08-26 22:16:29 +07:00
Yifei Zhang
e009ea57b9 Merge pull request #2706 from kfear1337/bug 2023-08-26 19:58:46 +08:00
B0zal
63c93a42b5 [+] Fixed language missing for finetuned 2023-08-25 18:15:50 +07:00
B0zal
e6cc1625b5 [+] FineTuned Sysmessage Depends for local language
this for JSON Exporter
2023-08-25 17:48:25 +07:00
B0zal
f65b0128e7 Issue #2702 should be fixed now kiw kiw
where the ChatGPTicon.src
glitches and breaks when exporting the image for the second time without refreshing the page.
2023-08-25 17:46:19 +07:00
B0zal
507b7fee56 [+] Language indonesia (my country) 2023-08-25 17:42:02 +07:00
huni
925d28495a fix bug of ! near check of system. 2023-08-24 23:24:26 +08:00
huni
19dd71eb05 setup shell support for debian 2023-08-24 22:04:42 +08:00
Yifei Zhang
b3fd56c2c1 Merge pull request #2685 from reece00/456 2023-08-24 11:12:47 +08:00
Yifei Zhang
d8b6ebf6cb fix: #2699 remove double quotes in readme 2023-08-24 11:09:17 +08:00
Yifei Zhang
bdaac17886 Merge pull request #2698 from kfear1337/main 2023-08-24 10:58:47 +08:00
Yifei Zhang
8cac51abbe chore: #2699 Update README.md 2023-08-24 10:54:28 +08:00
B0zal
0113d4499b [Feature] Better JSON Exporter #2692
[+] A view looks better
[+] auto minify json when click a copy in markdown and download

Co-Authored-By: wangwentong-lunaon <39506652+wangwentong-lunaon@users.noreply.github.com>
Co-Authored-By: Yifei Zhang <yidadaa@qq.com>
Co-Authored-By: B0zal <48602426+kfear1337@users.noreply.github.com>
2023-08-23 21:14:43 +07:00
Yifei Zhang
7562ab3c1c Merge pull request #2690 from pengooseDev/main 2023-08-23 19:06:01 +08:00
pengoosedev
d3de07ecf3 docs: Add vercel-ko 2023-08-23 01:33:46 +09:00
pengoosedev
b146cd889f Add faq-ko 2023-08-23 01:28:54 +09:00
pengoosedev
bc0f184098 docs: Add cloudflare-pages-ko 2023-08-23 01:08:31 +09:00
pengoosedev
1debde3046 docs: Add README_KO 2023-08-23 00:08:08 +09:00
reece00
4d3fdbdd80 The mobile terminal ishitbottom does not perform -10 2023-08-22 12:35:51 +08:00
Yifei Zhang
90c201d438 Merge pull request #2673 from Yidadaa/dependabot/npm_and_yarn/next-13.4.19 2023-08-22 11:43:59 +08:00
Yifei Zhang
1e22abab80 Merge pull request #2674 from Yidadaa/dependabot/npm_and_yarn/eslint-config-next-13.4.19 2023-08-22 11:43:38 +08:00
Yifei Zhang
792c37735e Merge pull request #2675 from Yidadaa/dependabot/npm_and_yarn/mermaid-10.3.1 2023-08-22 11:43:26 +08:00
Yifei Zhang
6d8ece530f Merge pull request #2677 from Yidadaa/dependabot/npm_and_yarn/prettier-3.0.2 2023-08-22 11:43:06 +08:00
dependabot[bot]
3499dfb285 chore(deps-dev): bump prettier from 2.8.8 to 3.0.2
Bumps [prettier](https://github.com/prettier/prettier) from 2.8.8 to 3.0.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.8.8...3.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 10:57:49 +00:00
dependabot[bot]
30c656ceda chore(deps): bump mermaid from 10.2.3 to 10.3.1
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.2.3 to 10.3.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.2.3...v10.3.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-08-21 10:57:23 +00:00
dependabot[bot]
a1c7f86ff3 chore(deps-dev): bump eslint-config-next from 13.2.3 to 13.4.19
Bumps [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) from 13.2.3 to 13.4.19.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v13.4.19/packages/eslint-config-next)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 10:57:07 +00:00
dependabot[bot]
4952e41132 chore(deps): bump next from 13.4.9 to 13.4.19
Bumps [next](https://github.com/vercel/next.js) from 13.4.9 to 13.4.19.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.9...v13.4.19)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-21 10:56:27 +00:00
Yifei Zhang
e1142216ec fix: #2672 should use correct resend index 2023-08-21 18:33:45 +08:00
Yifei Zhang
a4040fc1ee Update README_CN.md 2023-08-21 18:30:44 +08:00
Yifei Zhang
f84572443f Update faq-cn.md 2023-08-21 16:00:19 +08:00
Yifei Zhang
fbf6bc1979 Merge pull request #2655 from ClarenceDan/main 2023-08-19 18:01:09 +08:00
GH Action - Upstream Sync
3aeb2c1230 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-08-19 01:06:19 +00:00
Yifei Zhang
50eb7a5f98 Update tauri.conf.json 2023-08-18 18:47:54 +08:00
Yifei Zhang
484d1e7396 Merge pull request #2649 from pichuang/zh-tw 2023-08-18 18:40:30 +08:00
Yifei Zhang
a632e13334 Merge pull request #2653 from Algorithm5838/Algorithm5838-patch-1 2023-08-18 18:40:18 +08:00
Clarence Dan
aa3f96f89c Update globals.scss 2023-08-18 18:18:16 +08:00
Clarence Dan
16685ddb6c Update chat.module.scss 2023-08-18 17:21:58 +08:00
Clarence Dan
e78b15b9f0 Update chat.module.scss
Specify styles for iOS devices.
2023-08-18 17:12:02 +08:00
Algorithm5838
35b0bd76f8 Update markdown.tsx 2023-08-18 08:12:27 +03:00
Phil Huang
1d7286c161 Improve the text in tw.ts
Adoption of text that is more closely aligned with the usage of zh-tw
2023-08-17 23:50:38 +08:00
Yifei Zhang
5a7ec38ecd Update README.md 2023-08-16 11:22:51 +08:00
Yifei Zhang
ada4e3cdcd Merge pull request #2641 from Yidadaa/bugfix-0815 2023-08-15 23:09:15 +08:00
Yidadaa
ed62c87156 feat: close #2638 hide auth page and use better unauth tips 2023-08-15 22:50:42 +08:00
Yidadaa
840277f584 fix: #2566 click avatar to edit context messages 2023-08-15 22:42:55 +08:00
Yifei Zhang
ffddb7fb2b Merge pull request #2636 from wangwentong-lunaon/main 2023-08-15 22:09:14 +08:00
wangwentong
b380421fd5 support json export 2023-08-15 13:32:34 +08:00
Yifei Zhang
20882a7598 Merge pull request #2629 from Yidadaa/bugfix-0814 2023-08-14 22:13:56 +08:00
Yifei Zhang
9d3dff47d2 Merge pull request #2627 from imldy/dev_title-generation-checkbox 2023-08-14 22:12:26 +08:00
Yidadaa
db5c7aba78 fix: #2615 scrollbar jitter under certain message counts 2023-08-14 22:11:38 +08:00
Yidadaa
e8e01aa60d feat: close #2618 use correct html lang attr 2023-08-14 21:55:18 +08:00
Yidadaa
ae8226907f feat: close #2621 use better default api url 2023-08-14 21:36:29 +08:00
imldy
803b66ae9d chore: Concise description 2023-08-14 20:47:02 +08:00
imldy
20a508e2d6 feat: add autoGenerateTitle option
(cherry picked from commit 656ab94a9c4edfee820616b8cfc39f5ee9952a3a)
2023-08-14 20:42:09 +08:00
Yifei Zhang
25ada1eae9 Merge pull request #2617 from Yidadaa/rtl-fix 2023-08-14 11:13:56 +08:00
Yifei Zhang
808e4b38a3 fixup 2023-08-14 11:10:02 +08:00
Yifei Zhang
a496bc5a63 fix: #2614 better rtl detecting algo 2023-08-14 10:57:24 +08:00
Yifei Zhang
c94713475f Update globals.scss 2023-08-13 02:47:07 +08:00
Yifei Zhang
d45c61109f Merge pull request #2607 from eltociear/add_ja-docs 2023-08-13 00:07:37 +08:00
Ikko Eltociear Ashimine
99220d72da Add faq-ja.md 2023-08-11 20:28:18 +09:00
Ikko Eltociear Ashimine
887eaef1aa Add vercel-ja.md 2023-08-11 19:58:54 +09:00
Ikko Eltociear Ashimine
836a00e104 Add cloudflare-pages-ja.md 2023-08-11 19:49:36 +09:00
Ikko Eltociear Ashimine
8ee5061046 Add Japanese README 2023-08-11 19:34:21 +09:00
Yifei Zhang
61852b4021 Merge pull request #2598 from Yidadaa/Yidadaa-patch-1 2023-08-10 10:49:16 +08:00
Yifei Zhang
0b7de6f7b2 fix: #2594 trim the / 2023-08-10 10:47:06 +08:00
Yifei Zhang
9834a67cbd Update tauri.conf.json 2023-08-09 15:37:13 +08:00
Yifei Zhang
d85a4a0c9f Merge pull request #2590 from ZhangYichi-ZYc/ZhangYichi-ZYc-patch-1 2023-08-09 15:29:46 +08:00
Yifei Zhang
67c8ec6d7e chore: change ACCESS_CODE_PREFIX to nk- 2023-08-09 15:27:08 +08:00
Yifei Zhang
2a2dd7ea19 Merge pull request #2588 from eltociear/eltociear-patch-1
Fix typo in README.md
2023-08-09 09:17:55 +08:00
Ikko Eltociear Ashimine
153e7ac7e4 Fix typo in README.md
notifictions -> notifications
2023-08-09 01:22:48 +09:00
Yifei Zhang
9420fd4946 Merge pull request #2585 from Yidadaa/bugfix-0808 2023-08-08 21:38:42 +08:00
Yidadaa
b14c5cd89c fix: #2485 one-time-use body 2023-08-08 21:36:37 +08:00
Yidadaa
4ab9141429 fix: #2564 should not clear message when error 2023-08-08 21:24:45 +08:00
Yidadaa
769c2f9f49 feat: close #2583 do not summarize with gpt-4 2023-08-08 21:22:41 +08:00
Yifei Zhang
c41c498a9c Merge pull request #2573 from 7lsu/main 2023-08-08 16:00:45 +08:00
7lsu
d1096582a5 font family display enhance 2023-08-07 17:44:32 +08:00
Yifei Zhang
543989151f Update constant.ts 2023-08-04 19:24:10 +08:00
Yifei Zhang
7da83987e4 chore: use bigger page size and render msg count 2023-08-04 15:45:16 +08:00
Yidadaa
523d553dac fix: clear btn should display in correct place 2023-08-04 02:58:02 +08:00
Yifei Zhang
ff6f0e9546 Merge pull request #2557 from Yidadaa/bugfix-0804
feat: disable auto focus on mobile screen
2023-08-04 02:45:19 +08:00
Yidadaa
3e63f6ba34 feat: disable auto focus on mobile screen 2023-08-04 02:43:55 +08:00
Yifei Zhang
811b92d24e Merge pull request #2556 from Yidadaa/bugfix-0804
fixup: improve auto scroll algo
2023-08-04 02:40:34 +08:00
Yidadaa
bc5ddc4541 fixup: improve auto scroll algo 2023-08-04 02:39:32 +08:00
Yifei Zhang
44e43729bf Merge pull request #2555 from Yidadaa/bugfix-0804
feat: close #2545 improve lazy load message list
2023-08-04 02:18:55 +08:00
Yidadaa
203067c936 feat: close #2545 improve lazy load message list 2023-08-04 02:16:44 +08:00
Zhang Yichi
f3b508c088 Update Model Pricing.md
OpenAI has updated their model prices, reducing the input of GPT-3.5 to $0.0015/1000 tokens
2023-08-03 23:52:18 +08:00
Yifei Zhang
081d84f848 Update tauri.conf.json 2023-08-03 10:41:45 +08:00
Yifei Zhang
e381add944 Merge pull request #2542 from Yidadaa/yifei-bugfix-0802
chore: smaller auto scroll interval
2023-08-02 23:54:19 +08:00
Yidadaa
75d4eca722 chore: smaller auto scroll interval 2023-08-02 23:51:01 +08:00
Yifei Zhang
56904ad6b2 Merge pull request #2541 from Yidadaa/yifei-bugfix-0802
feat: improve chat action, auto scroll and edit model title
2023-08-02 23:40:50 +08:00
Yidadaa
b5ef552c25 feat: improve auto scroll ux and edit model title 2023-08-02 23:35:51 +08:00
Yidadaa
cbabb9392c feat: improve ChatAction ux 2023-08-02 22:53:36 +08:00
Yifei Zhang
531b3dcf9e Merge pull request #2531 from LeviBorodenko/main
Add link to authentication page to i18n
2023-08-02 15:35:05 +08:00
Levi Borodenko
d975daf3f0 locale: add link to authentication page in i18n 2023-08-01 16:17:26 +02:00
Yifei Zhang
6137d551fe Merge pull request #2525 from 7lsu/main
typo fix
2023-08-01 15:17:26 +08:00
7lsu
cf625e3542 typo fix 2023-08-01 10:16:36 +08:00
Yifei Zhang
e354fca4a4 Merge pull request #2524 from Yidadaa/bugfix-0731
fix: #2514 should not clear the message after editing message
2023-07-31 22:23:11 +08:00
Yidadaa
129e7afc16 fix: #2514 should not clear the message after editing message 2023-07-31 22:20:39 +08:00
Yifei Zhang
e83e0f6a33 Merge pull request #2486 from fernandoxu/main
fix(typo): ngnix -> nginx
2023-07-26 17:13:46 +08:00
fernandoxu
cf4f928b25 fix(typo): ngnix -> nginx 2023-07-26 10:06:06 +08:00
Yifei Zhang
e4d955e3f9 Merge pull request #2483 from Yidadaa/bugfix-0725
fix: #2393 try to fix chat list lag
2023-07-25 23:01:46 +08:00
Yidadaa
13576087f4 fix: #2393 try to fix chat list lag 2023-07-25 22:59:21 +08:00
Yifei Zhang
e74f6f3183 Merge pull request #2469 from Yidadaa/dependabot/npm_and_yarn/types/react-dom-18.2.7
chore(deps-dev): bump @types/react-dom from 18.0.11 to 18.2.7
2023-07-24 22:06:40 +08:00
dependabot[bot]
8302d1d3c1 chore(deps-dev): bump @types/react-dom from 18.0.11 to 18.2.7
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.0.11 to 18.2.7.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-24 10:33:12 +00:00
Yifei Zhang
b7320e6834 Update tauri.conf.json 2023-07-21 00:37:38 +08:00
Yifei Zhang
3193fe0235 Merge pull request #2452 from Yidadaa/bugfix-0720
feat: close #2449 edit / insert / delete messages modal
2023-07-21 00:25:33 +08:00
Yidadaa
7c2fa9f8a4 feat: close #2449 edit / insert / delete messages modal 2023-07-21 00:24:26 +08:00
Yidadaa
e5f6133127 feat: close #2447 pre-fill key/code/url 2023-07-20 23:17:58 +08:00
Yidadaa
0198c5b781 feat: close #2445 switch to mit license 2023-07-20 22:51:49 +08:00
Yifei Zhang
f535073ae4 Merge pull request #2431 from Hopelwj/main
设置页面-参数过滤空格
2023-07-19 20:14:13 +08:00
Hopelwj
b9895ecadd Merge branch 'Yidadaa:main' into main 2023-07-18 19:39:59 +08:00
liuweijie
322eb66fdf fix: useAccessStore filter spaces 2023-07-18 19:38:16 +08:00
Yifei Zhang
f0abdc80eb Update tauri.conf.json 2023-07-18 10:50:44 +08:00
Yifei Zhang
70a4f94c81 Update home.tsx 2023-07-17 11:33:19 +08:00
Yifei Zhang
f7e4b36746 Merge pull request #2412 from Yidadaa/fix-2409
fix: #2409 empty prompt id cause crash
2023-07-17 11:03:33 +08:00
Yifei Zhang
4e6b71ace5 Merge pull request #2410 from Lantianyou/feature/typing-metadata
Feature/typing-metadata
2023-07-17 11:01:39 +08:00
Yifei Zhang
c916cd1a87 Update mask.tsx 2023-07-17 11:00:45 +08:00
兰天游
442a529a72 feat: add type for import 2023-07-16 21:35:13 +08:00
兰天游
e1243f3d59 feat: add typings for metadata 2023-07-16 21:34:01 +08:00
Yifei Zhang
62f8cd1db6 Merge pull request #2406 from Yidadaa/bugfix-0716
feat: close #2376 add babel polyfill
2023-07-16 16:34:45 +08:00
Yidadaa
af5f67d459 feat: close #2376 add babel polyfill 2023-07-16 16:32:22 +08:00
Yifei Zhang
13424a893e Merge pull request #2405 from Yidadaa/bugfix-0716
fix: #2367 do not copy in async callback after sharing to ShareGPT
2023-07-16 16:17:40 +08:00
Yidadaa
30473ec41e fix: #2367 do not copy in async callback after sharing to ShareGPT 2023-07-16 16:14:57 +08:00
Yifei Zhang
24d382c70d Merge pull request #2404 from Yidadaa/bugfix-0716
feat: improve dnd icon
2023-07-16 16:14:25 +08:00
Yidadaa
3ddedc903e feat: improve dnd icon 2023-07-16 15:49:15 +08:00
Yifei Zhang
e3c279b8be Merge pull request #2389 from MarkShawn2020/fix-default-openai-url
fixed openai base url if empty
2023-07-16 15:20:26 +08:00
Yifei Zhang
d909b676c5 Update README.md 2023-07-16 15:08:53 +08:00
Yifei Zhang
08a8ac9971 Merge pull request #2388 from yikayiyo/feat-drag-prompts
feat: #2297 drag and drop in contextual prompts
2023-07-16 14:30:49 +08:00
Yifei Zhang
7073fd2f3b Merge pull request #2392 from imldy/dev_SetInjectSystemPrompts
fix: enable `enableInjectSystemPrompts` attribute for old sessions
2023-07-16 14:29:50 +08:00
imldy
a9f67a48a1 dev: use current inject configuration 2023-07-15 02:48:47 +08:00
imldy
fd058cc693 fix: enable enableInjectSystemPrompts attribute for old sessions 2023-07-15 01:32:39 +08:00
Yifei Zhang
c98df33003 Merge pull request #2390 from MarkShawn2020/fix-react-key
fixed react key
2023-07-15 01:28:56 +08:00
markshawn2020
3f8b14f5f8 fixed react key 2023-07-14 21:15:34 +08:00
markshawn2020
1c335a68e0 fixed openai base url if empty 2023-07-14 21:03:32 +08:00
legao
fb98050d9f feat: drag and drop in contextual prompts 2023-07-14 18:08:03 +08:00
Yifei Zhang
91cb7aa13d Merge pull request #2372 from Yidadaa/dependabot/npm_and_yarn/semver-6.3.1
chore(deps): bump semver from 6.3.0 to 6.3.1
2023-07-13 16:46:00 +08:00
dependabot[bot]
ab0f7cc0c9 chore(deps): bump semver from 6.3.0 to 6.3.1
Bumps [semver](https://github.com/npm/node-semver) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v6.3.1/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-13 01:58:09 +00:00
Yifei Zhang
b51f7f9a25 Merge pull request #2370 from Yidadaa/bugfix-0712
fix: sharegpt roles
2023-07-12 23:21:22 +08:00
Yidadaa
d275e32e70 fix: sharegpt roles 2023-07-12 23:19:46 +08:00
Yifei Zhang
22cf78d506 Merge pull request #2355 from fyl080801/main
fix: cloudflare headers
2023-07-11 23:57:03 +08:00
Yifei Zhang
c7c318b31e Merge pull request #2360 from imldy/fix_can't_edit_old_mask
fix: 迁移面具对象的key以解决无法编辑旧面具的问题
2023-07-11 23:48:02 +08:00
imldy
a4d012828c fix: migrated mask object key 2023-07-11 21:02:09 +08:00
yuanliang feng
72cfebd2bc Merge pull request #3 from fyl080801/deploy
Deploy
2023-07-11 17:08:50 +08:00
yuanliang feng
a832cfb343 Update common.ts 2023-07-11 15:46:40 +08:00
yuanliang feng
9ef680db57 Merge pull request #2 from fyl080801/main
update
2023-07-10 23:35:19 +08:00
yuanliang feng
2c09f4ef90 Merge pull request #1 from fyl080801/main
update
2023-07-10 23:23:20 +08:00
Yifei Zhang
c44454bf74 Merge pull request #2346 from Yidadaa/bugfix-0710
fix: #2336 resending message should delete origional messages
2023-07-10 23:22:53 +08:00
Yidadaa
ad1b9b7f6d fixup 2023-07-10 23:21:22 +08:00
Yidadaa
15e063e1b5 feat: #2330 disable /list/models 2023-07-10 23:19:43 +08:00
Yidadaa
c00a63e4c3 fix: #2336 resending message should delete origional messages 2023-07-10 22:59:12 +08:00
Yifei Zhang
dbda27b1d6 Merge pull request #2343 from Yidadaa/dependabot/npm_and_yarn/next-13.4.9
chore(deps): bump next from 13.4.6 to 13.4.9
2023-07-10 21:52:19 +08:00
Yifei Zhang
9eef879dbe Merge pull request #2342 from Yidadaa/dependabot/npm_and_yarn/tauri-apps/cli-1.4.0
chore(deps-dev): bump @tauri-apps/cli from 1.3.1 to 1.4.0
2023-07-10 21:52:08 +08:00
Yifei Zhang
cb29423636 Merge pull request #2341 from Yidadaa/dependabot/npm_and_yarn/types/react-18.2.14
chore(deps-dev): bump @types/react from 18.2.12 to 18.2.14
2023-07-10 21:51:56 +08:00
Yifei Zhang
d754eecf07 Merge pull request #2340 from Yidadaa/dependabot/npm_and_yarn/eslint-8.44.0
chore(deps-dev): bump eslint from 8.37.0 to 8.44.0
2023-07-10 21:51:45 +08:00
Yifei Zhang
1e308782ee Merge pull request #2339 from Yidadaa/dependabot/npm_and_yarn/react-router-dom-6.14.1
chore(deps): bump react-router-dom from 6.10.0 to 6.14.1
2023-07-10 21:51:32 +08:00
Yifei Zhang
69139cd85b Merge pull request #2344 from imldy/dev_SetInjectSystemPrompts
Dev: 给是否注入全局系统prompt提供一个开关
2023-07-10 21:51:06 +08:00
imldy
f59235bd5a feat: 翻译InjectSystemPrompts配置项为其他语言 2023-07-10 20:14:10 +08:00
imldy
2930ba0457 fix: 修改InjectSystemPrompts.SubTitle使其更符合系统行为 2023-07-10 20:12:51 +08:00
imldy
1513881eed fix: 修改是否应该注入System Prompt的判断规则为根据设置项 2023-07-10 18:57:54 +08:00
imldy
5e361f6748 dev: 增加模型设置项:是否启用注入全局 System Prompt 功能 2023-07-10 18:56:22 +08:00
dependabot[bot]
b182543a21 chore(deps): bump next from 13.4.6 to 13.4.9
Bumps [next](https://github.com/vercel/next.js) from 13.4.6 to 13.4.9.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.6...v13.4.9)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 10:51:20 +00:00
dependabot[bot]
cbce8b444f chore(deps-dev): bump @tauri-apps/cli from 1.3.1 to 1.4.0
Bumps [@tauri-apps/cli](https://github.com/tauri-apps/tauri) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/cli.js-v1.3.1...@tauri-apps/cli-v1.4.0)

---
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-07-10 10:48:41 +00:00
dependabot[bot]
38e92cfc62 chore(deps-dev): bump @types/react from 18.2.12 to 18.2.14
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.12 to 18.2.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 10:47:57 +00:00
dependabot[bot]
a7764dc6d5 chore(deps-dev): bump eslint from 8.37.0 to 8.44.0
Bumps [eslint](https://github.com/eslint/eslint) from 8.37.0 to 8.44.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.37.0...v8.44.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-07-10 10:47:33 +00:00
dependabot[bot]
c74552c4c5 chore(deps): bump react-router-dom from 6.10.0 to 6.14.1
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.10.0 to 6.14.1.
- [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.14.1/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-07-10 10:46:56 +00:00
yuanliang feng
7a0b437626 Update common.ts 2023-07-10 17:26:28 +08:00
Yifei Zhang
c7e9f13d2e Update tauri.conf.json 2023-07-10 15:03:02 +08:00
Yifei Zhang
51e816ad6d Merge pull request #2320 from yanCode/main
fix: typos
2023-07-10 14:25:47 +08:00
ShengYan, Zhang
dd047fd58f fix: typos 2023-07-10 10:09:19 +08:00
Yifei Zhang
300f023d2a Merge pull request #2318 from Yidadaa/bugfix-0709-2
chore: suppress module not found warning
2023-07-09 22:23:43 +08:00
Yidadaa
a282a80a6e chore: suppress module not found warning 2023-07-09 22:18:48 +08:00
Yifei Zhang
e668e17655 Merge pull request #2317 from Yidadaa/bugfix-0709-2
fix: #2303 should be able to select custom models
2023-07-09 22:12:04 +08:00
Yidadaa
a913d9728c fixup 2023-07-09 22:06:41 +08:00
Yifei Zhang
15862d393b Merge pull request #2316 from SergeWilfried/main
Add Missing FR Translations
2023-07-09 22:01:23 +08:00
Yidadaa
48a6cdd50a feat: improve model selector ui 2023-07-09 22:01:04 +08:00
Yidadaa
09b05cde7f fix: #2303 should select custom models 2023-07-09 21:56:49 +08:00
SergeWilfried
da294038a8 Merge pull request #2 from Yidadaa/main
Upstream Sync
2023-07-09 13:21:05 +00:00
Yifei Zhang
332f3fb8e8 Merge pull request #2314 from Yidadaa/bugfix-0709-2
feat: #920  migrate id to nanoid
2023-07-09 19:42:06 +08:00
Yidadaa
8e4743e719 feat: #920 migrate id to nanoid 2023-07-09 19:37:42 +08:00
Yifei Zhang
4a663e539a Merge pull request #2312 from Yidadaa/bugfix-0709-2
feat: close #2303 add custom model name config
2023-07-09 18:17:38 +08:00
Yidadaa
98ac7ee277 feat: close #2303 add custom model name config 2023-07-09 18:15:52 +08:00
Yidadaa
28c457730a fix: #2280 auto-detect models from 'list/models' 2023-07-09 18:03:06 +08:00
Yifei Zhang
9b61fe9df2 Merge pull request #2309 from Yidadaa/bugfix-0709
feat: #2308 #2294 #2295
2023-07-09 16:50:00 +08:00
Yidadaa
b55b01cb13 feat: #2308 improve chat actions ux 2023-07-09 16:39:46 +08:00
Yidadaa
90d8f3117f fix: #2295 use correct methods to migrate state 2023-07-09 16:28:15 +08:00
Yidadaa
c7e976c8c5 chore: update readme 2023-07-09 16:26:18 +08:00
Yidadaa
6014b765f4 feat: close #2294 add documents for adding a new translation 2023-07-09 16:26:00 +08:00
Yidadaa
ca295588c4 fix: #2308 improve chat actions 2023-07-09 16:15:58 +08:00
serge
437334355f chore(fr.ts): update French translations for improved user experience and clarity
feat(fr.ts): add new translations for pinning messages, editing, and input actions
feat(fr.ts): add new translations for commands and input actions related to conversation management
feat(fr.ts): add new translations for settings options related to resetting and clearing data
feat(fr.ts): add new translations for settings options related to language, font size, and input template
feat(fr.ts): add new translations for settings options related to update version and presence penalty
feat(fr.ts): add new translations for settings options related to masking and built-in assistants
feat(fr.ts): add new translations for settings options related to temperature and top P
feat(fr.ts): add new translations for settings options related to max tokens and presence penalty
feat(fr.ts): add new translations for settings options related to cloning and configuration
feat(fr.ts): add new translations for settings options related to avatar, name, sync, hide context, and sharing
2023-07-08 14:12:46 +00:00
Yifei Zhang
3432d4df29 Update README.md 2023-07-07 17:57:53 +08:00
Yifei Zhang
05fcb96181 Merge pull request #2275 from Saber-Kurama/pref/api-models
perf:  models接口返回异常数据的容错处理
2023-07-07 11:23:30 +08:00
Yifei Zhang
6bb27f90a4 Merge pull request #2277 from PaRaD1SE98/main
fix: selector z-index bug
2023-07-07 11:16:22 +08:00
PaRaD1SE98
c10e8382a9 fix: selector z-index bug 2023-07-07 04:15:05 +09:00
guochao
6653a31eb7 perf: models接口返回数据的容错处理 2023-07-06 20:00:30 +08:00
Yifei Zhang
42561d04de Update tauri.conf.json 2023-07-06 11:21:55 +08:00
Yidadaa
9a285ab935 fixup 2023-07-06 02:12:37 +08:00
Yifei Zhang
81771568be Merge pull request #2269 from Yidadaa/bugfix-0705
feat: improve image preview & model switcher model ux
2023-07-06 02:09:05 +08:00
Yifei Zhang
795b43f174 Merge pull request #2268 from imabutahersiddik/main
Added support for Bangla language.
2023-07-06 02:08:03 +08:00
Yidadaa
52203b50eb fixup 2023-07-06 02:07:35 +08:00
Yidadaa
0373b2c9dd feat: close #2266 use modal to switch model 2023-07-06 02:03:31 +08:00
ki-ask
fe2c1c4ec6 Added support for Bangla language 2023-07-05 23:55:43 +06:00
ki-ask
596b6542e8 Added support for Bangla language 2023-07-05 23:55:14 +06:00
ki-ask
6897bf1254 Added support for Bangla language 2023-07-05 23:39:22 +06:00
ki-ask
8a6a13e583 Added support for Bangla language 2023-07-05 23:35:55 +06:00
Yidadaa
b718285125 feat: improve svg viewer 2023-07-06 01:33:30 +08:00
ki-ask
e5ab918ef9 Added support for Bangla language 2023-07-05 23:31:45 +06:00
Yidadaa
6c6a2d08db feat: close #2267 display a modal to export image 2023-07-06 01:26:06 +08:00
Yidadaa
9e6617e3ca feat: add max icon for modals 2023-07-06 01:11:50 +08:00
Yifei Zhang
94a50f92e1 Merge pull request #2264 from Yidadaa/bugfix-0705
feat: close #1072 share mask as link
2023-07-05 23:21:49 +08:00
Yidadaa
5c8be2a8f6 feat: close #1072 share mask as link 2023-07-05 23:19:54 +08:00
Yifei Zhang
1dcf2d80b4 Merge pull request #2263 from Yidadaa/bugfix-0705
fix: #2252 polyfill composing for old safari browsers & fix #2261
2023-07-05 22:51:49 +08:00
Yidadaa
1197521921 fix: #2252 polyfill composing for old safari browsers 2023-07-05 22:50:12 +08:00
Yidadaa
3863cfe786 fix: #2261 default enable gpt-4 models 2023-07-05 22:41:10 +08:00
Yidadaa
54bd07702c refactor: #2256 hide builtin masks 2023-07-05 22:39:25 +08:00
Yifei Zhang
a75e2b0c0e Merge pull request #2256 from marcusschiesser/main
feat: add setting to hide built-in masks
2023-07-05 22:30:28 +08:00
Yifei Zhang
29fd9b23fe Merge pull request #2260 from sunls233/fix-summarize
修复摘要总结后不会被使用
2023-07-05 22:28:03 +08:00
sunls233
089e3b8946 fix summarize don't send 2023-07-05 14:27:37 +08:00
GH Action - Upstream Sync
9c36fcec81 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-07-05 00:12:23 +00:00
Marcus Schiesser
74fa065266 feat: add setting to hide builtin masks 2023-07-05 00:07:26 +07:00
Yifei Zhang
38f2495cf6 Merge pull request #2253 from Yidadaa/bugfix-0704
feat: close #2192 use /list/models to get model ids
2023-07-04 23:21:32 +08:00
Yidadaa
4131fccbe0 feat: close #2192 use /list/models to get model ids 2023-07-04 23:16:24 +08:00
Yidadaa
f2d748cfe4 refactor: improve modal ux 2023-07-04 22:08:41 +08:00
Yifei Zhang
197ec0c29c Merge pull request #2242 from PaRaD1SE98/main
fix: temperature -> top_p
2023-07-04 11:28:43 +08:00
PaRaD1SE98
0a2af9335c fix: temperature -> top_p 2023-07-04 02:49:05 +09:00
Yifei Zhang
a52fa28ed1 Update tauri.conf.json 2023-07-04 01:10:16 +08:00
Yifei Zhang
78ed24dbf6 Merge pull request #2241 from Yidadaa/bugfix-0704
feat: add top p config
2023-07-04 00:42:41 +08:00
Yidadaa
cda074fe24 fixup 2023-07-04 00:40:57 +08:00
Yidadaa
823032617d feat: add top p config 2023-07-04 00:39:54 +08:00
Yidadaa
5963459499 fix: #2221 user prompts in front of all prompts 2023-07-04 00:22:30 +08:00
Yidadaa
0bc2c71b0c fix: #2230 hide chat actions for context prompts 2023-07-04 00:14:57 +08:00
Yifei Zhang
b4e350e189 Merge pull request #2234 from Yidadaa/dependabot/npm_and_yarn/prettier-2.8.8
chore(deps-dev): bump prettier from 2.8.7 to 2.8.8
2023-07-03 23:59:23 +08:00
Yifei Zhang
941e46490f Merge pull request #2235 from Yidadaa/dependabot/npm_and_yarn/rehype-katex-6.0.3
chore(deps): bump rehype-katex from 6.0.2 to 6.0.3
2023-07-03 23:59:12 +08:00
Yifei Zhang
4df92e903a Merge pull request #2236 from Yidadaa/dependabot/npm_and_yarn/hello-pangea/dnd-16.3.0
chore(deps): bump @hello-pangea/dnd from 16.2.0 to 16.3.0
2023-07-03 23:59:02 +08:00
Yifei Zhang
6ba02d0d50 Merge pull request #2237 from Yidadaa/dependabot/npm_and_yarn/types/node-20.3.3
chore(deps-dev): bump @types/node from 20.3.1 to 20.3.3
2023-07-03 23:58:47 +08:00
dependabot[bot]
2dc122831b chore(deps-dev): bump @types/node from 20.3.1 to 20.3.3
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.3.1 to 20.3.3.
- [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-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 10:27:32 +00:00
dependabot[bot]
f3f84e523a chore(deps): bump @hello-pangea/dnd from 16.2.0 to 16.3.0
Bumps [@hello-pangea/dnd](https://github.com/hello-pangea/dnd) from 16.2.0 to 16.3.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.2.0...v16.3.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-07-03 10:27:22 +00:00
dependabot[bot]
0cdee25b5b chore(deps): bump rehype-katex from 6.0.2 to 6.0.3
Bumps [rehype-katex](https://github.com/remarkjs/remark-math) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/remarkjs/remark-math/releases)
- [Commits](https://github.com/remarkjs/remark-math/compare/rehype-katex@6.0.2...rehype-katex@6.0.3)

---
updated-dependencies:
- dependency-name: rehype-katex
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 10:27:11 +00:00
dependabot[bot]
92b0314c14 chore(deps-dev): bump prettier from 2.8.7 to 2.8.8
Bumps [prettier](https://github.com/prettier/prettier) from 2.8.7 to 2.8.8.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.8.7...2.8.8)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-03 10:27:02 +00:00
Yidadaa
ad2bc7da96 Revert "feat: build universal app darwin bundle"
This reverts commit d8b606dc83.
2023-07-01 19:05:36 +08:00
Yidadaa
d8b606dc83 feat: build universal app darwin bundle 2023-07-01 18:54:12 +08:00
Yidadaa
5ce53dbcf4 fix: action ubuntu version 2023-07-01 18:39:48 +08:00
Yifei Zhang
c5c1a9ab3c Merge pull request #2209 from tdzz1102/main
Update Japanese translation
2023-07-01 18:32:12 +08:00
Yifei Zhang
564709aa98 Merge pull request #2213 from Yidadaa/bugfix-0701
feat: mac os arm64 bundle
2023-07-01 18:31:56 +08:00
Yidadaa
475158a145 feat: close #2194 add macos arm support 2023-07-01 18:28:47 +08:00
Yidadaa
829df56733 fix: #2195 correct macos icon size 2023-07-01 18:22:17 +08:00
Yidadaa
ee55f8790e fix: #2208 use global settings button dose not work 2023-07-01 18:16:06 +08:00
tdzz1102
6d19fb3909 update japanese translate 2023-07-01 12:28:08 +08:00
Yifei Zhang
9057712c8f Update chat.module.scss 2023-06-30 16:08:45 +08:00
Yifei Zhang
5d6e7de667 Merge pull request #2198 from YBY2020/main
perf : 代码细节优化
2023-06-30 14:35:41 +08:00
兀米
8f6f70879c Merge pull request #1 from YBY2020/YBY2020-patch-1
perf : 代码细节优化
2023-06-30 13:30:13 +08:00
兀米
3120087992 perf : 代码细节优化
大神, 学习代码过程中发现的一个小小的点,887行已定义`const isUser = message.role === "user";`,此处可直接用isUser
2023-06-30 12:00:03 +08:00
Yidadaa
0ec4cc223f fixup 2023-06-30 02:35:40 +08:00
Yidadaa
60c7be31b6 fixup 2023-06-30 02:05:17 +08:00
Yifei Zhang
2388f853c9 Merge pull request #2193 from Yidadaa/bugfix-0629
feat: improve ux & auto updater
2023-06-30 01:17:44 +08:00
Yidadaa
1e8d4763bb fix: tauri auto updater url 2023-06-30 01:15:01 +08:00
Yidadaa
be4834688d feat: close #2190 improve app auto updater 2023-06-30 00:39:54 +08:00
Yidadaa
3937dad6a6 feat: close #2187 improve chat actions ux 2023-06-30 00:39:54 +08:00
Yidadaa
463251dcc1 feat: allow to disable chunk building by setting DISABLE_CHUNK=1 2023-06-30 00:39:19 +08:00
Yifei Zhang
3adca26808 Update chat.module.scss 2023-06-29 11:03:13 +08:00
Yifei Zhang
97a8bb52d6 Update ui-lib.module.scss 2023-06-29 01:40:54 +08:00
Yifei Zhang
7748a980f9 Merge pull request #2185 from Yidadaa/bugfix-0628
feat: many ux improvments
2023-06-29 01:32:57 +08:00
Yidadaa
b044e274aa feat: close #2136 click avatar to edit message 2023-06-29 01:31:27 +08:00
Yidadaa
6c3d4a11cc feat: close #2141 danger zone 2023-06-29 01:09:51 +08:00
Yidadaa
98afd5516b feat: close #1994 add clipboard write api 2023-06-29 00:27:18 +08:00
Yidadaa
ea6926cad3 feat: replace window.prompt with showPrompt 2023-06-29 00:09:56 +08:00
Yidadaa
3298961748 feat: replace window.confirm with showConfirm 2023-06-28 23:40:39 +08:00
Yidadaa
0140f771c6 feat: close #2175 use default api host if endpoint is empty 2023-06-28 23:12:35 +08:00
Yidadaa
64c4f512ce fixup: #2149 chat action button style 2023-06-28 01:15:55 +08:00
Yidadaa
5b1d45c1a9 fix: #2149 try to fix chat action button style 2023-06-28 01:09:25 +08:00
Yifei Zhang
efbd1c15a9 Merge pull request #2174 from Yidadaa/bugfix-0627
fix: #2149 try to fix chat action button style
2023-06-28 01:03:19 +08:00
Yidadaa
bce74890dc fix: #2149 try to fix chat action button style 2023-06-28 01:02:56 +08:00
Yifei Zhang
eb93a43d13 Merge pull request #2173 from Yidadaa/bugfix-0627
feat: improve chat commands
2023-06-28 00:46:38 +08:00
Yidadaa
1dd75b63de feat: [WIP] support webdav 2023-06-28 00:34:01 +08:00
Yidadaa
6caf79121b feat: improve chat commands 2023-06-27 23:39:27 +08:00
Yifei Zhang
5cdd34a388 Merge pull request #2171 from Yidadaa/bugfix-0627
fix: #2149 try to fix ChatAction style on ios mobile
2023-06-27 23:31:04 +08:00
Yidadaa
38c8ee8cd2 feat: improve message item buttons style 2023-06-27 23:29:53 +08:00
Yidadaa
d5c33a1183 fix: #2149 try to fix ChatAction style on ios mobile 2023-06-27 23:07:12 +08:00
Yifei Zhang
058e28911a fix: #2135 disable cmd + arrow to change session 2023-06-26 18:29:46 +08:00
Yifei Zhang
25a3bb351d Merge pull request #2143 from yorunning/fix2
fix: alignment of mobile settings page title.
2023-06-26 13:27:00 +08:00
Yifei Zhang
1607640bed Merge pull request #2145 from Yidadaa/bugfix-0626
feat: #2144 improve input template
2023-06-26 13:21:48 +08:00
Yidadaa
5f0cda829f feat: #2144 improve input template 2023-06-26 13:18:59 +08:00
Yorun
b003a374b8 fix: alignment of mobile settings page title. 2023-06-26 04:16:06 +00:00
Yifei Zhang
5f7c262759 Merge pull request #2134 from Yidadaa/bugfix-0625
fix: input-range style
2023-06-26 01:51:55 +08:00
Yidadaa
f3ab6b27c9 fix: input-range style 2023-06-26 01:51:35 +08:00
Yifei Zhang
d62eb56886 Merge pull request #2133 from quzard/fix_pin_bug
Fix Pin Message
2023-06-26 01:43:20 +08:00
quzard
c2724c6aad Fix Pin Message: when userMessageIndex === 0, pinning a message was not possible. 2023-06-26 01:36:27 +08:00
Yifei Zhang
0cde1683b6 Update window.scss 2023-06-26 01:35:38 +08:00
Yifei Zhang
128f596f93 Update tauri.conf.json 2023-06-26 01:31:01 +08:00
Yifei Zhang
27fae8eed0 Merge pull request #2132 from Yidadaa/bugfix-0625
feat: align chat page title center on mobile screen
2023-06-26 01:28:56 +08:00
Yidadaa
82ec4474c2 feat: align chat page title center on mobile screen 2023-06-26 01:27:36 +08:00
Yifei Zhang
0d85dae239 Merge pull request #2131 from Yidadaa/bugfix-0625
feat: close #1615 pin messages to contextual prompts
2023-06-26 00:40:07 +08:00
Yidadaa
7893693706 feat: close #1615 pin messages to contextual prompts 2023-06-26 00:21:58 +08:00
Yidadaa
25ce6af36e feat: new chat message actions style 2023-06-25 23:53:22 +08:00
Yifei Zhang
c0269254e8 Merge pull request #2129 from Constaline/main
feat: add "Hide_Balance_Query" environment variable
2023-06-25 22:01:27 +08:00
Constaline
2f2aefd48e fix: typo 2023-06-25 21:33:22 +08:00
Constaline
c05de45d99 feat: add "Hide_Balance_Query" environment variable 2023-06-25 20:20:36 +08:00
Yifei Zhang
8f66da1128 Merge pull request #2119 from Yidadaa/bugfix-0624
chore: delete chat button position
2023-06-24 23:54:14 +08:00
Yidadaa
b5eaa8272b chore: delete chat button position 2023-06-24 23:53:37 +08:00
Yifei Zhang
7ee062e1de Merge pull request #2118 from Yidadaa/bugfix-0624
feat: #2013 #628 switch model button & chat commands
2023-06-24 23:40:54 +08:00
Yidadaa
8915af9b6d fixup 2023-06-24 23:39:38 +08:00
Yidadaa
ae1ef3215b feat: close #628 add chat commands 2023-06-24 23:38:11 +08:00
Yidadaa
5d06fa217c feat: close #2013 add switch model button to chat actions 2023-06-24 22:41:13 +08:00
Yidadaa
50b1f7db12 feat: right-click to copy message to user input 2023-06-24 22:32:46 +08:00
Yifei Zhang
fb82806956 Merge pull request #2117 from JunJD/优化useScrollToBottom
refactor: 优化 useScrollToBottom
2023-06-24 22:25:45 +08:00
Yifei Zhang
0658a93962 Merge pull request #2116 from Zizwar/main
Added support for Arabic language and right-to-left direction
2023-06-24 18:37:52 +08:00
Yifei Zhang
f33be37070 Update constant.ts 2023-06-24 16:50:05 +08:00
dingjunjie
9278d7f851 refactor: 优化 useScrollToBottom 2023-06-24 16:37:29 +08:00
Yifei Zhang
35ba898c97 Merge pull request #2112 from songxiaocheng/PR/history-message-count
fix: #2111 make "Attached Messages Count" working
2023-06-24 14:34:48 +08:00
Zizwar
15f8d13d81 Set markdown-body RTL for Arabic text 2023-06-24 05:53:58 +00:00
Zizwar
946e508e8f Support left to right in language arabic 2023-06-24 05:10:34 +00:00
Zizwar
35b4125b98 Added support Arabic language 2023-06-24 04:30:52 +00:00
Xiaocheng Song
91d8f9d73e fix: #2111 make "Attached Messages Count" working 2023-06-24 05:53:39 +08:00
Yifei Zhang
c636993989 Merge pull request #2110 from Yidadaa/bugfix-0623
doc: #695 how to use a proxy with password in docker
2023-06-24 00:30:17 +08:00
Yidadaa
65a24c16bc doc: #695 how to use a proxy with password in docker 2023-06-24 00:29:47 +08:00
Yifei Zhang
15ce114440 Merge pull request #2109 from Yidadaa/bugfix-0623
feat: close #1789 add user input template
2023-06-24 00:20:05 +08:00
Yidadaa
1722f75dcb fixup 2023-06-24 00:18:27 +08:00
Yidadaa
be597a551d feat: #close 1789 add user input template 2023-06-24 00:07:46 +08:00
Yifei Zhang
56bc945335 Merge pull request #2107 from Yidadaa/bugfix-0623
fix: #2087 chat box font size use global config
2023-06-23 20:58:09 +08:00
Yidadaa
fa9ceb5875 fix: #2087 chat box font size use global config 2023-06-23 20:57:43 +08:00
Yifei Zhang
0ccf5a16c7 Merge pull request #2106 from Yidadaa/bugfix-0623
feat: close #2025 just use a smaller to-bottom threshold
2023-06-23 20:53:28 +08:00
Yidadaa
93c666b03d feat: close #2025 just use a smaller to-bottom threshold 2023-06-23 20:53:08 +08:00
Yifei Zhang
d5c28b506c Update README.md 2023-06-23 20:21:41 +08:00
Yifei Zhang
3318f01aa1 Merge pull request #2099 from johan456789/patch-1
add explanation and link to FAQ
2023-06-23 13:20:02 +08:00
Tsung-Han Yu
1641019091 add explanation and link to FAQ 2023-06-23 05:54:39 +08:00
Yifei Zhang
3572969866 Merge pull request #2096 from Yidadaa/dependabot/cargo/src-tauri/openssl-0.10.55
chore(deps): bump openssl from 0.10.52 to 0.10.55 in /src-tauri
2023-06-22 17:43:41 +08:00
Yifei Zhang
ad1fe8762b Merge pull request #2092 from PaRaD1SE98/main
fix: migrate modelConfig state
2023-06-22 11:31:46 +08:00
dependabot[bot]
c9c3e32a78 chore(deps): bump openssl from 0.10.52 to 0.10.55 in /src-tauri
Bumps [openssl](https://github.com/sfackler/rust-openssl) from 0.10.52 to 0.10.55.
- [Release notes](https://github.com/sfackler/rust-openssl/releases)
- [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.52...openssl-v0.10.55)

---
updated-dependencies:
- dependency-name: openssl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-21 22:57:40 +00:00
PaRaD1SE98
f4c99c9cf7 fix: migrate modelConfig state 2023-06-22 02:11:07 +09:00
Yifei Zhang
57447c24c8 Update tauri.conf.json 2023-06-21 15:36:17 +08:00
Yifei Zhang
514ed03378 Merge pull request #2065 from macvip/window-state
restore last window state (position and size) on startup
2023-06-21 02:30:25 +08:00
Yifei Zhang
078b039cf1 Merge pull request #2064 from mushan0x0/patch-1
fix: #2061 RequestInit TypeError
2023-06-20 18:04:24 +08:00
macvip
a120670785 restore last window state (position and size) on startup 2023-06-20 13:06:54 +08:00
mushan0x0
9c05d136f5 fix: #2061 RequestInit TypeError 2023-06-20 11:57:31 +08:00
Yifei Zhang
daeb8a67a3 Merge pull request #2048 from ilizhu/main
feat: i18n in message exporter
2023-06-20 02:45:48 +08:00
Yifei Zhang
c64888cccd Merge pull request #2051 from Yidadaa/dependabot/npm_and_yarn/next-13.4.6
chore(deps): bump next from 13.4.3 to 13.4.6
2023-06-20 02:40:23 +08:00
Yifei Zhang
6fc2637870 Merge pull request #2050 from Yidadaa/dependabot/npm_and_yarn/zustand-4.3.8
chore(deps): bump zustand from 4.3.6 to 4.3.8
2023-06-20 02:40:14 +08:00
Yifei Zhang
fc4df23075 Merge pull request #2053 from Yidadaa/dependabot/npm_and_yarn/lint-staged-13.2.2
chore(deps-dev): bump lint-staged from 13.2.0 to 13.2.2
2023-06-20 02:40:00 +08:00
Yifei Zhang
3ae502986b Merge pull request #2054 from Yidadaa/dependabot/npm_and_yarn/types/react-18.2.12
chore(deps-dev): bump @types/react from 18.0.31 to 18.2.12
2023-06-20 02:39:50 +08:00
Yifei Zhang
67f23d202a Merge pull request #2056 from Yidadaa/bugfix-0620
fix: #2055 should render mermaid completely
2023-06-20 02:38:59 +08:00
Yidadaa
3c38b9c93b fix: #2055 should render mermaid completely 2023-06-20 02:37:38 +08:00
dependabot[bot]
152a00baf5 chore(deps-dev): bump @types/react from 18.0.31 to 18.2.12
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.0.31 to 18.2.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 11:10:39 +00:00
dependabot[bot]
405d02023c chore(deps-dev): bump lint-staged from 13.2.0 to 13.2.2
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 13.2.0 to 13.2.2.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v13.2.0...v13.2.2)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 11:10:15 +00:00
dependabot[bot]
4ba2d0d7e0 chore(deps): bump next from 13.4.3 to 13.4.6
Bumps [next](https://github.com/vercel/next.js) from 13.4.3 to 13.4.6.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.3...v13.4.6)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 11:09:00 +00:00
dependabot[bot]
38ce230cf5 chore(deps): bump zustand from 4.3.6 to 4.3.8
Bumps [zustand](https://github.com/pmndrs/zustand) from 4.3.6 to 4.3.8.
- [Release notes](https://github.com/pmndrs/zustand/releases)
- [Commits](https://github.com/pmndrs/zustand/compare/v4.3.6...v4.3.8)

---
updated-dependencies:
- dependency-name: zustand
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-19 11:06:59 +00:00
zhuge
c6696313bf 聊天记录导出格式为image时预览聊天信息项使用设置的语言 chat information items using the language in the settings when exporting chat records in image format 2023-06-19 16:16:09 +08:00
Yifei Zhang
89717c9b15 Update issue-translator.yml 2023-06-18 18:28:31 +08:00
Yifei Zhang
3aca699ad9 Create issue-translator.yml 2023-06-18 18:27:18 +08:00
Yifei Zhang
f3ec9f02eb Merge pull request #1807 from AngelLiang/feat/frequency_penalty
feat: Add frequency_penalty request parameter
2023-06-18 14:36:55 +08:00
Yifei Zhang
1c7f3e6057 Merge pull request #2028 from Yidadaa/bugfix-0618
fix: #1771 should not lose chat context when sumindex > n - count
2023-06-18 00:52:41 +08:00
Yidadaa
db63a5a670 fix: #1771 should not lose chat context when sumindex > n - count 2023-06-18 00:51:16 +08:00
Yifei Zhang
0a2cccfd6e Merge pull request #2018 from Yidadaa/bugfix-0617
feat: add nynorsk option
2023-06-17 01:20:06 +08:00
Yidadaa
113bd24796 feat: add nynorsk option 2023-06-17 01:03:06 +08:00
Yifei Zhang
374a27a72e Merge pull request #1065 from perandre/norwegian-translation
Add Norwegian translation
2023-06-17 00:33:26 +08:00
Xavier Barbosa
567eff9ef4 Remove deprecated keys 2023-06-16 16:00:57 +01:00
Xavier Barbosa
bfe33af03d Merge branch 'main' into norwegian-translation 2023-06-16 16:00:17 +01:00
Yifei Zhang
7ebc5eb70f Merge pull request #2002 from yeenbean/CVE-2023-2251
Remediation of CVE-2023-2251
2023-06-16 18:11:09 +08:00
Yifei Zhang
52287fd01d Merge pull request #2009 from taro-ishihara/i18n/japanese-translation
Fix Japanese language file
2023-06-16 18:10:56 +08:00
Yifei Zhang
e0f2bc2725 Merge pull request #2005 from Yidadaa/dependabot/npm_and_yarn/use-debounce-9.0.4
chore(deps): bump use-debounce from 9.0.3 to 9.0.4
2023-06-16 16:51:48 +08:00
Yifei Zhang
4355ec3b44 Merge pull request #2007 from Yidadaa/dependabot/npm_and_yarn/types/node-20.3.1
chore(deps-dev): bump @types/node from 18.15.11 to 20.3.1
2023-06-16 16:51:21 +08:00
Yifei Zhang
0c2f11ffd7 Merge pull request #2008 from Yidadaa/dependabot/npm_and_yarn/react-markdown-8.0.7
chore(deps): bump react-markdown from 8.0.6 to 8.0.7
2023-06-16 16:51:10 +08:00
Yifei Zhang
65e2f38fac Merge pull request #2004 from Yidadaa/dependabot/npm_and_yarn/mermaid-10.2.3
chore(deps): bump mermaid from 10.1.0 to 10.2.3
2023-06-16 16:50:51 +08:00
Taro Ishihara
aeaefbf8a9 Fix Japanese language file 2023-06-16 00:23:56 -07:00
dependabot[bot]
719cb3ae05 chore(deps): bump react-markdown from 8.0.6 to 8.0.7
Bumps [react-markdown](https://github.com/remarkjs/react-markdown) from 8.0.6 to 8.0.7.
- [Release notes](https://github.com/remarkjs/react-markdown/releases)
- [Changelog](https://github.com/remarkjs/react-markdown/blob/main/changelog.md)
- [Commits](https://github.com/remarkjs/react-markdown/compare/8.0.6...8.0.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 07:22:32 +00:00
dependabot[bot]
08816c8f7d chore(deps-dev): bump @types/node from 18.15.11 to 20.3.1
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.15.11 to 20.3.1.
- [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-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 07:22:15 +00:00
dependabot[bot]
1031203e3f chore(deps): bump use-debounce from 9.0.3 to 9.0.4
Bumps [use-debounce](https://github.com/xnimorz/use-debounce) from 9.0.3 to 9.0.4.
- [Release notes](https://github.com/xnimorz/use-debounce/releases)
- [Changelog](https://github.com/xnimorz/use-debounce/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xnimorz/use-debounce/compare/9.0.3...9.0.4)

---
updated-dependencies:
- dependency-name: use-debounce
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 07:21:24 +00:00
dependabot[bot]
63ef6e7d3d chore(deps): bump mermaid from 10.1.0 to 10.2.3
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 10.1.0 to 10.2.3.
- [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.1.0...v10.2.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-16 07:20:57 +00:00
Yifei Zhang
590c9bb978 Create dependabot.yml 2023-06-16 15:12:47 +08:00
Joshua
5055a8c7a3 yarn.lock also required for CVE-2023-2251 🙃 2023-06-15 23:54:53 -07:00
Joshua
284c3f27e7 Resolves found vuln CVE-2023-2251 2023-06-15 23:33:08 -07:00
Yifei Zhang
468f198a6b Update README.md 2023-06-16 01:42:51 +08:00
Yifei Zhang
b0c6f6d4d3 Update tauri.conf.json 2023-06-16 00:53:10 +08:00
Yidadaa
473061b4d5 fixup: add TAURI_KEY_PASSWORD secrets 2023-06-16 00:18:54 +08:00
Yifei Zhang
c4921f3da7 Merge pull request #1990 from Yidadaa/bugfix-0615
fix: #1982 should not fullscreen on standlone build
2023-06-16 00:02:17 +08:00
Yidadaa
91b871ef3b feat: try to add auto updater 2023-06-15 23:55:18 +08:00
Yidadaa
698be6671c feat: enable drag area for tauri apps 2023-06-15 23:20:14 +08:00
Yidadaa
47c546fafa fix: #1982 should not fullscreen on standlone build 2023-06-15 22:34:50 +08:00
Yifei Zhang
9d1a84858c Merge pull request #1973 from Jiacheng787/patch-1
fix: updating the array using push in zustand does not actually trigger component updates
2023-06-15 14:42:33 +08:00
Jiacheng Dong
4dd5bf71ea Update chat.ts 2023-06-15 13:59:38 +08:00
Yidadaa
ea253e3bae fixup: tauri app name 2023-06-15 11:01:37 +08:00
Jiacheng Dong
1d42e955fc fix: updating the array using push in zustand does not actually trigger component updates 2023-06-15 10:55:25 +08:00
Jiacheng Dong
e636d486f5 fix: updating the array using push in zustand does not actually trigger component updates 2023-06-15 10:23:01 +08:00
Yifei Zhang
818cc545eb Merge pull request #1970 from Yidadaa/app
feat: update app release workflow
2023-06-15 03:32:18 +08:00
Yidadaa
e7858495e6 feat: update app release workflow 2023-06-15 03:28:53 +08:00
Yifei Zhang
af3a273a56 Merge pull request #1969 from Yidadaa/app
feat: ready to release desktop app
2023-06-15 02:53:42 +08:00
Yidadaa
6264c02543 feat: add app logo and
release workflow
2023-06-15 02:49:08 +08:00
Yidadaa
80d5bfd7c0 feat: add app dev mode 2023-06-15 01:48:56 +08:00
Yifei Zhang
3fa8b0ad14 Merge pull request #1968 from Yidadaa/bugfix-0614
fix: #1954 lazy render bugs
2023-06-15 01:40:51 +08:00
Yidadaa
184a0b9481 fix: #1954 lazy render bugs 2023-06-15 01:34:21 +08:00
Yifei Zhang
4e80096ee4 Merge pull request #1967 from Yidadaa/bugfix-0614
fix: #1931 try to fix cors issues
2023-06-15 00:31:00 +08:00
Yidadaa
0fb775d71a fix: #1931 try to fix cors issues 2023-06-15 00:28:47 +08:00
Yidadaa
76fdd047e7 feat: new token count function 2023-06-15 00:14:38 +08:00
Yidadaa
8590750e4c feat: close #1960 add gpt-3.5-turbo-16k-0613 2023-06-14 23:22:59 +08:00
Yifei Zhang
590bd8e4bb Merge pull request #1946 from cesaryuan/fix-selection
fix: remove selection range when user blured
2023-06-14 15:42:24 +08:00
Cesaryuan
b4cb8c3d75 chore: translate comment to english 2023-06-14 14:40:51 +08:00
Cesaryuan
21aa015a79 fix: remove slection range when user blured 2023-06-14 14:30:08 +08:00
Yifei Zhang
dd5b9d420b Merge pull request #1928 from samelamin/fix_chat_time
fix date time bug on chat lists
2023-06-14 11:31:00 +08:00
Yifei Zhang
2593de5872 Merge pull request #1929 from suhipek/main
add new models
2023-06-14 10:29:00 +08:00
suhipek
d2ae740d5f add new models 2023-06-14 01:51:37 +08:00
samelamin
c56c6074e9 fix date time bug 2023-06-13 18:32:51 +01:00
Yifei Zhang
426ce7fd35 Merge pull request #1927 from Yidadaa/bugfix-0613
feat: #1000 ready to support client-side only
2023-06-14 00:48:32 +08:00
Yidadaa
2a191aacb7 fixup 2023-06-14 00:46:52 +08:00
Yidadaa
50cd33dbb2 feat: #1000 ready to support client-side only 2023-06-14 00:37:42 +08:00
Yifei Zhang
e6b49a60c0 Merge pull request #1921 from Yidadaa/bugfix-0612
fixup: #1762 optimize style on mobile screen
2023-06-13 03:04:38 +08:00
Yidadaa
a7e9356c16 fixup: #1762 optimize style on mobile screen 2023-06-13 03:04:09 +08:00
Yifei Zhang
58cc1c8482 Merge pull request #1919 from Yidadaa/bugfix-0612
feat: close #1762 add hover text for chat input actions
2023-06-13 02:29:56 +08:00
Yidadaa
88df4a2223 feat: close #1762 add hover text for chat input actions 2023-06-13 02:27:39 +08:00
Yifei Zhang
d046100875 Merge pull request #1917 from Yidadaa/bugfix-0612
feat: white url list for openai security
2023-06-13 00:49:15 +08:00
Yidadaa
0d4611052e feat: white url list for openai security 2023-06-13 00:39:29 +08:00
Yifei Zhang
bdb03e07fc Merge pull request #1868 from OldDream666/fix-auth-Security
修改 auth.tsx 输入框为password
2023-06-11 00:01:05 +08:00
Old Dream
1d790b9e8d 修改 auth.tsx 输入框为password 2023-06-09 12:39:42 +08:00
Yifei Zhang
24bf15af4f Merge pull request #1863 from Yidadaa/bugfix-0608
feat: just disable all ngnix buffering
2023-06-08 23:51:09 +08:00
Yidadaa
6410aa214e feat: just disable all ngnix buffer 2023-06-08 23:49:06 +08:00
Yidadaa
a023308d52 feat: #1000 client-side only and desktop app 2023-06-07 23:47:21 +08:00
Yifei Zhang
2516851056 Merge pull request #1844 from Yidadaa/bugfix-0607
feat: close #741 add auth page
2023-06-07 02:26:05 +08:00
Yidadaa
a3a77006ff fixup: #1815 req.body will be broken in edge runtime 2023-06-07 02:24:45 +08:00
Yidadaa
ebbd0128f1 feat: close #741 add auth page 2023-06-07 02:18:24 +08:00
Yifei Zhang
773641aa16 Merge pull request #1843 from Yidadaa/bugfix-0607
fix: #1685 clear context index should be recoverable
2023-06-07 01:19:46 +08:00
Yidadaa
57514e91b6 fix: #1685 clear context index should be recoverable 2023-06-07 01:18:05 +08:00
Yifei Zhang
a9d336baf5 Merge pull request #1842 from Yidadaa/bugfix-0607
fix: #1815 refuse to serve when disable gpt4
2023-06-07 01:07:17 +08:00
Yidadaa
37da759fd5 fix: #1815 refuse to serve when disable gpt4 2023-06-07 01:02:01 +08:00
Yifei Zhang
2ebc26953d Merge pull request #1823 from johnfelipe/main
translation doc to spanish
2023-06-06 14:53:51 +08:00
Yifei Zhang
c8ba72b185 Merge pull request #1829 from OldDream666/fix-docker-bug
fix docker-compose.yml bug
2023-06-06 14:51:52 +08:00
Old Dream
35fe6e2f54 fix docker-compose.yml bug 2023-06-06 07:01:04 +08:00
johnfelipe
f3d35e0ef3 translation doc to spanish 2023-06-05 08:52:23 -05:00
Yifei Zhang
68327907a9 Merge pull request #1813 from qingfengfenga/main
Fix docker-compose bug
2023-06-05 17:48:13 +08:00
qingfengfenga
44fe85b56d Fix docker-compose bug 2023-06-05 11:10:23 +08:00
AngelLiang
72cbb156ae feat: Add frequency_penalty request parameter 2023-06-04 16:05:35 +08:00
Yifei Zhang
5e274130a6 Update README.md 2023-06-02 11:23:25 +08:00
Yifei Zhang
94be03ec4f Merge pull request #1781 from yanCode/fix/css-top-actions
fix: [CSS] the position of top-action buttons
2023-06-01 10:57:45 +08:00
Yifei Zhang
6502855a70 Merge pull request #1782 from yanCode/fix/json-format
fix: the display format of json error
2023-06-01 10:56:47 +08:00
ShengYan, Zhang
6bbdaf7ab0 fix: the display format of json 2023-05-31 19:39:39 +08:00
ShengYan, Zhang
846e323840 fix: the position of top-action buttons 2023-05-31 15:45:12 +08:00
Yifei Zhang
c198c33778 Merge pull request #1780 from leviding/patch-1
fix: style typo error in home.module.scss
2023-05-31 14:50:13 +08:00
LeviDing
c1cc3d1d1f fix: style typo error in home.module.scss 2023-05-30 18:42:13 +08:00
Yifei Zhang
6b5e5a15d7 Merge pull request #1777 from yanCode/chore/cleanup
chore: remove unused code
2023-05-29 16:07:14 +08:00
ShengYan, Zhang
7ac03b4d89 chore: remove unused code 2023-05-29 09:54:02 +08:00
Yifei Zhang
6bd75fae33 Update README.md 2023-05-27 00:37:13 +08:00
Yifei Zhang
3cdaf62fa1 Update README.md 2023-05-27 00:36:02 +08:00
Yifei Zhang
9aea6c5585 Merge pull request #1754 from popcell/fix-immutable-header
fix: #1746 Can't modify immutable headers.
2023-05-25 18:32:30 +08:00
popcell
d533895637 fix: #1746 Can't modify immutable headers. 2023-05-25 17:50:46 +08:00
Yifei Zhang
aa74a74c5c Merge pull request #1742 from wsw2000/refactor/listitem-undefined-classname
refactor: Fix undefined className in ListItem
2023-05-25 16:40:51 +08:00
Yifei Zhang
46f0a256d7 Merge pull request #1750 from gtoxlili/fix-docker-logic
fix: Logical corrections & syntax errors
2023-05-25 16:36:45 +08:00
gtoxlili
e33ad07e16 fix: Logical corrections & syntax errors 2023-05-25 11:46:31 +08:00
Yifei Zhang
786d62905d Merge pull request #1749 from lunaflora/main
*docker file bug
2023-05-25 11:39:31 +08:00
lunaflora
75594b8fe5 *docker file bug 2023-05-25 11:31:45 +08:00
wsw
96d2f05eb7 refactor: Fix undefined className in ListItem 2023-05-25 01:13:19 +08:00
Yifei Zhang
887f93181c Merge pull request #1741 from Yidadaa/bugfix-0524
feat: share to ShareGPT
2023-05-25 01:08:47 +08:00
Yidadaa
9f4a80f6ae chore: update readme 2023-05-25 01:08:19 +08:00
Yidadaa
3e65ef3bea feat: share to ShareGPT 2023-05-25 01:04:37 +08:00
Yidadaa
4ca34e0436 fix: #1711 input range style in mobile screen 2023-05-24 23:21:18 +08:00
Yifei Zhang
bb3f6ee086 Merge pull request #1738 from gtoxlili/main
fix : specify the default hostname if docker builds with a proxy
2023-05-24 22:15:21 +08:00
Yifei Zhang
89f0a4875c Merge pull request #1736 from popcell/main
fix vercel X-Forwarded-For headers
2023-05-24 22:13:00 +08:00
gtoxlili
aa2be9b96c fix : specify the default hostname if docker builds with a proxy 2023-05-24 21:40:53 +08:00
gtoxlili
707c1a2f7e fix : specify the default hostname if docker builds with a proxy 2023-05-24 21:18:51 +08:00
gtoxlili
ed14a0029a fix : issues #1732 2023-05-24 21:08:32 +08:00
popcell
989661e4df fix vercel X-Forwarded-For headers 2023-05-24 20:12:41 +08:00
Yifei Zhang
8874c687d8 Merge pull request #1728 from yanCode/fix/steps
fix:  bug #1727
2023-05-24 20:04:30 +08:00
Yifei Zhang
58e1997bcb Merge pull request #1731 from jarieshan/main
Add Jailbreak Mask
2023-05-24 20:03:37 +08:00
jarieshan
3e3055d7df Add Jailbreak Mask 2023-05-24 17:33:04 +08:00
jarieshan
b68d6e9d1a Revert "Add Jailbreak Mask"
This reverts commit cf9d200b7c.
2023-05-24 17:23:43 +08:00
jarieshan
cf9d200b7c Add Jailbreak Mask 2023-05-24 17:19:27 +08:00
ShengYan, Zhang
e84da3089a fix state loss after user switches from steps 2023-05-24 14:44:06 +08:00
ShengYan, Zhang
fee38b8d13 fix: a few typos 2023-05-24 14:42:36 +08:00
Yifei Zhang
e0a69a9e57 Update issue templates 2023-05-24 12:01:32 +08:00
Yifei Zhang
0bdc95f9c4 Update README.md 2023-05-24 11:53:27 +08:00
Yifei Zhang
4c9e4e507c Merge pull request #1694 from PaRaD1SE98/PaRaD1SE98-patch-1
clean next.config.mjs
2023-05-23 16:27:52 +08:00
Yifei Zhang
adb50fe64f Merge pull request #1695 from PaRaD1SE98/PaRaD1SE98-patch-2
fix: use Select component
2023-05-23 16:27:42 +08:00
Yifei Zhang
e883aefcc3 Merge pull request #1700 from misitebao/main
chore: optimize images
2023-05-23 16:27:31 +08:00
imgbot[bot]
cd7e8bbd3e chore: optimize images (#1)
*Total -- 122.47kb -> 90.64kb (25.98%)

/public/android-chrome-512x512.png -- 23.67kb -> 8.61kb (63.61%)
/app/icons/chatgpt.png -- 9.37kb -> 3.59kb (61.73%)
/app/icons/bot.png -- 7.14kb -> 4.98kb (30.2%)
/app/icons/three-dots.svg -- 1.46kb -> 1.09kb (25.75%)
/public/apple-touch-icon.png -- 11.30kb -> 8.45kb (25.21%)
/public/android-chrome-192x192.png -- 12.39kb -> 9.60kb (22.51%)
/public/favicon-32x32.png -- 1.51kb -> 1.22kb (19.21%)
/app/icons/min.svg -- 2.41kb -> 2.04kb (15.2%)
/app/icons/max.svg -- 2.24kb -> 1.90kb (14.98%)
/app/icons/menu.svg -- 1.17kb -> 1.00kb (14.64%)
/app/icons/share.svg -- 0.72kb -> 0.63kb (12.84%)
/app/icons/add.svg -- 1.25kb -> 1.09kb (12.46%)
/app/icons/github.svg -- 1.97kb -> 1.76kb (10.68%)
/app/icons/brain.svg -- 1.81kb -> 1.64kb (9.42%)
/app/icons/black-bot.svg -- 4.17kb -> 3.89kb (6.66%)
/docs/images/icon.svg -- 4.17kb -> 3.89kb (6.57%)
/app/icons/bottom.svg -- 0.72kb -> 0.69kb (4.08%)
/app/icons/download.svg -- 1.70kb -> 1.64kb (3.96%)
/app/icons/left.svg -- 0.56kb -> 0.54kb (3.49%)
/app/icons/down.svg -- 0.56kb -> 0.54kb (3.48%)
/app/icons/mask.svg -- 2.13kb -> 2.06kb (3.21%)
/app/icons/export.svg -- 1.20kb -> 1.16kb (3.17%)
/app/icons/copy.svg -- 0.99kb -> 0.96kb (2.87%)
/app/icons/prompt.svg -- 1.23kb -> 1.20kb (2.38%)
/app/icons/plugin.svg -- 3.54kb -> 3.48kb (1.6%)
/app/icons/delete.svg -- 23.11kb -> 23.00kb (0.49%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2023-05-23 13:57:26 +08:00
parad1se_jp
99317f759b fix: use Select component 2023-05-23 01:57:45 +09:00
PaRaD1SE98
7ff6a8651a clean next.config.mjs
appDir option no longer needed start from nextjs13.4 https://nextjs.org/docs/app/api-reference/next-config-js/appDir
2023-05-23 01:38:22 +09:00
Yifei Zhang
4e1e1d4fef Merge pull request #1693 from Yidadaa/bugfix-0522
fix: #1668 #1681
2023-05-23 00:00:14 +08:00
Yidadaa
d34676c5b2 fixup 2023-05-22 23:59:11 +08:00
Yidadaa
4cf659c29b chore: wont send system info if user use a mask 2023-05-22 23:52:09 +08:00
Yidadaa
ec61a5b32d fix: #1681 replace svg icons with png icons 2023-05-22 23:49:11 +08:00
Yidadaa
58f726c602 fix: #1668 should not summarize twice 2023-05-22 23:12:26 +08:00
Yifei Zhang
bb0c3835f4 Merge pull request #1689 from Yidadaa/Yidadaa-patch-1
fix: #1688 wrong clear context index
2023-05-22 18:57:21 +08:00
Yifei Zhang
e9642c7505 fix: #1688 wrong clear context index 2023-05-22 18:56:19 +08:00
Yifei Zhang
f0b4ef5917 Merge pull request #1679 from Yidadaa/export
chore: mobile export image style
2023-05-22 01:25:49 +08:00
Yidadaa
1f12753c68 chore: mobile export image style 2023-05-22 01:23:08 +08:00
Yifei Zhang
0439d122a5 Merge pull request #1678 from Yidadaa/export
feat: close #580 export messages as image
2023-05-22 01:02:58 +08:00
Yidadaa
4dad7f2ab6 feat: close #580 export messages as image 2023-05-22 00:59:36 +08:00
Yifei Zhang
ce75dc502b Merge pull request #1665 from yanCode/fix/prompt-select
fix: bug #1662
2023-05-21 12:55:47 +08:00
ShengYan, Zhang
23f6c2e8c9 fix: bug #1662 2023-05-21 11:58:10 +08:00
Yifei Zhang
d3461dd69b Merge pull request #1660 from Yidadaa/bugfix-0520
feat: close #1382 only clear memory btn in chat config
2023-05-21 02:06:01 +08:00
Yidadaa
05b1b8b240 feat: close #1382 only clear memory btn in chat config 2023-05-21 02:04:30 +08:00
Yifei Zhang
3118ba4466 Merge pull request #1659 from Yidadaa/bugfix-0520
feat: close #1415 clear context button
2023-05-21 01:48:38 +08:00
Yidadaa
35cec0f1df fixup: i18n and icon minor changes 2023-05-21 01:44:59 +08:00
Yidadaa
a19d238483 feat: close #1415 clear context button 2023-05-21 01:28:09 +08:00
Yifei Zhang
a57fa2e9ad Merge pull request #1658 from Yidadaa/bugfix-0520
feat: scrollable mask lists in new-chat page
2023-05-21 00:07:42 +08:00
Yidadaa
c2b36cdffa feat: prevent browser to invoke basic auth popup 2023-05-21 00:06:28 +08:00
Yidadaa
600b1814a1 fix: wont show auth popup when receiving a 401 http code 2023-05-20 23:58:36 +08:00
Yidadaa
76e6957a8a fixup 2023-05-20 23:53:39 +08:00
Yifei Zhang
18df79ce00 Merge pull request #1657 from Yidadaa/revert-1648-not-to-detect-user-lang-in-node
Revert "Not to detect user lang when running in Node"
2023-05-20 23:49:24 +08:00
Yidadaa
f14b413b7c feat: scrollable mask lists in new-chat page 2023-05-20 23:49:10 +08:00
Yifei Zhang
d0e73bd6b2 Revert "Not to detect user lang when running in Node" 2023-05-20 23:44:35 +08:00
Yifei Zhang
f27b25a62e Merge pull request #1653 from Yidadaa/bugfix-0520
feat: close #1626 hide context prompts in mask config
2023-05-20 20:20:35 +08:00
Yidadaa
6d8c7ba140 feat: close #1626 hide context prompts in mask config 2023-05-20 20:08:17 +08:00
Yidadaa
af497c96ec fix: #1612 infinite loading 2023-05-20 19:58:12 +08:00
Yifei Zhang
697c7a8dfe Merge pull request #1648 from yaojingguo/not-to-detect-user-lang-in-node
Not to detect user lang when running in Node
2023-05-20 16:07:18 +08:00
Jingguo Yao
3f5a189591 Not to detect user lang when running in Node
Use DEFAULT_LANG with Node. Remove the logging on the server side:

[Lang] failed to detect user lang.
2023-05-20 11:06:54 +08:00
Yifei Zhang
bcb18ff2f4 Merge pull request #1644 from Yidadaa/bugfix0519
feat: close #1478 new chat use global config as default
2023-05-20 00:43:08 +08:00
Yidadaa
b1ba3df989 feat: close #1478 new chat use global config as default 2023-05-20 00:39:52 +08:00
Yidadaa
203ac0970d feat: #1640 support free gpt endpoint 2023-05-19 23:53:27 +08:00
Yifei Zhang
e5329dc28a Merge pull request #1636 from yanCode/fix/css-chat-name
fix: css on display chat names
2023-05-19 22:22:28 +08:00
ShengYan, Zhang
f8ef6278a5 fix: css on display chat names 2023-05-19 20:14:57 +08:00
Yifei Zhang
7f13a8d2bc feat: support fast chatgpt mobile models 2023-05-19 18:34:48 +08:00
Yifei Zhang
b0b078c0fb Merge pull request #1629 from Algustine/dev
fix: get real-ip instead of vercel edge network ip
2023-05-19 18:31:59 +08:00
Illusion
c282433095 fix: get real-ip instead of vercel edge network ip 2023-05-19 16:03:29 +08:00
Yifei Zhang
8d7f3bd215 Merge pull request #1622 from yanCode/fix/single-mask-import
fix: allow to import a single mask
2023-05-19 10:57:37 +08:00
ShengYan, Zhang
f6c268dc1e fix: allow to import a single mask 2023-05-19 10:39:34 +08:00
Yifei Zhang
48f25b0799 Merge pull request #1616 from Yidadaa/bugfix-0519
fix: #1611 show corret message when can not query usage
2023-05-19 01:05:19 +08:00
Yidadaa
50cfbaaab5 feat: partial locale type 2023-05-19 00:59:04 +08:00
Yidadaa
de775511d0 feat: some en masks 2023-05-19 00:38:39 +08:00
Yidadaa
a524a60c46 fix: #1611 show corret message when can not query usage 2023-05-19 00:27:25 +08:00
Yidadaa
6cf2fa02e5 fix: #1612 fill empty message with a placeholder 2023-05-19 00:24:25 +08:00
Yifei Zhang
1a8cb877db Merge pull request #1605 from Allengl/update
add new mask 简历写手
2023-05-18 17:33:22 +08:00
Yifei Zhang
33727aad62 Merge pull request #1604 from ClarenceDan/main
fix:Fix memory leak issue by adding fetch request timeout
2023-05-18 17:21:48 +08:00
Clarence Dan
ac79d810d0 Fix memory leak issue by adding fetch request timeout
This commit resolves a memory leak issue that was occurring due to fetch requests hanging indefinitely. A timeout has been introduced to the `requestOpenai` function which ensures that these requests are aborted after a set period of time (currently 10 minutes). Additionally, error handling has been added to catch and log `AbortError` when a fetch request is aborted. This fix significantly improves the stability and reliability of the application by preventing memory leaks related to unresolved fetch requests.
2023-05-18 16:55:51 +08:00
Clarence Dan
2b912c6834 fix: Fix memory leak issue by adding fetch request timeout
This commit resolves a memory leak issue that was occurring due to fetch requests hanging indefinitely. A timeout has been introduced to the `requestOpenai` function which ensures that these requests are aborted after a set period of time (currently 10 minutes). Additionally, error handling has been added to catch and log `AbortError` when a fetch request is aborted. This fix significantly improves the stability and reliability of the application by preventing memory leaks related to unresolved fetch requests.
2023-05-18 16:52:32 +08:00
guolong
cf2404743d add new mask 简历写手 2023-05-18 16:49:30 +08:00
Yifei Zhang
38bffd423c Merge pull request #1580 from Allengl/update
Add Korean translation 增加国际化语言:韩语
2023-05-18 11:38:54 +08:00
Yifei Zhang
42711d76d6 Merge pull request #1592 from sjnho/main
fix #1590 Render the whole stream response body
2023-05-18 10:38:56 +08:00
kirk.shan
789f3d993c fix content-type = text/event-stream;charset=utf-8 2023-05-18 10:25:06 +08:00
Allen
d5376ab090 Merge branch 'Yidadaa:main' into update 2023-05-18 09:15:27 +08:00
GH Action - Upstream Sync
62ad7e5ad3 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-18 00:58:42 +00:00
Yifei Zhang
f0cb2fc21a Merge pull request #1586 from Yidadaa/bugfix-0518
chore: wont parse finished result
2023-05-18 02:23:48 +08:00
Yidadaa
3fe521421f chore: dynamic server config 2023-05-18 02:15:30 +08:00
Yidadaa
85445c4ef2 chore: wont parse finished result 2023-05-18 02:09:05 +08:00
Yifei Zhang
b4faf3faad Merge pull request #1585 from Yidadaa/bugfix-0518
feat: handle non-stream response
2023-05-18 02:06:22 +08:00
Yidadaa
5f2745c32a feat: handle non-stream response 2023-05-18 02:04:12 +08:00
Yifei Zhang
9ef6713aa1 Merge pull request #1584 from Yidadaa/bugfix-0518
fix: #1571 #1578 handle more error code
2023-05-18 01:28:02 +08:00
Yidadaa
736c66f46a feat: stop all stale messages 2023-05-18 01:24:36 +08:00
Yidadaa
98b699c483 chore: update readme 2023-05-18 00:46:51 +08:00
Yidadaa
93044590cc feat: wont fetch prompts in every building 2023-05-18 00:30:04 +08:00
Yidadaa
30676d118f fix: #1571 #1578 handle more error code 2023-05-18 00:14:29 +08:00
root
e306ac0197 add type LocaleType 2023-05-17 23:20:48 +08:00
root
65c6b4af82 Add Korean translation 2023-05-17 22:59:35 +08:00
Yifei Zhang
a402f646fe Merge pull request #1562 from Gan-Xing/update
Add French Translation -- 增加法语翻译
2023-05-17 20:53:49 +08:00
Yifei Zhang
db29fed99a Merge pull request #1573 from yanCode/fix/tab-change
fix: set openWhenHidden to be true
2023-05-17 18:18:56 +08:00
ShengYan, Zhang
94a2104b55 fix: set openWhenHidden to be true 2023-05-17 17:20:17 +08:00
Gan-Xing
0f886a1ece 更新ReadMe 2023-05-17 12:42:16 +08:00
Gan-Xing
3db024b24d 还原fork仓库内容 2023-05-17 12:33:46 +08:00
Gan-Xing
75bf75d552 法语翻译key值修改 2023-05-17 12:20:48 +08:00
Gan-Xing
5bbe59c9a2 Merge branch 'Yidadaa:main' into main 2023-05-17 12:01:44 +08:00
Yifei Zhang
28e447ea4a Merge pull request #1559 from Yidadaa/revert-1435-update
Revert "feat: close #1433 add french translation"
2023-05-17 11:21:45 +08:00
Yifei Zhang
31a874e24e Revert "feat: close #1433 add french translation" 2023-05-17 11:18:21 +08:00
Yifei Zhang
987412db51 Merge pull request #1435 from Gan-Xing/update
add french translation -增加法语翻译
2023-05-17 10:46:30 +08:00
Yifei Zhang
94ab5c7abf Merge pull request #1554 from yanCode/fix/non-stream-responses
fix: #1533 handle non-stream type
2023-05-17 10:41:32 +08:00
GH Action - Upstream Sync
d358fb256b Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-17 01:01:15 +00:00
ShengYan, Zhang
e00652ce86 fix: #1533 handle non-stream type 2023-05-17 08:50:50 +08:00
Yifei Zhang
086effa6cb Merge pull request #1545 from qianyu-wang/patch-1
fix: add localnet to proxychains config
2023-05-17 01:57:50 +08:00
Qianyu Wang
de000a8b4e fix: add localnet to proxychains config 2023-05-16 18:29:44 +08:00
Yifei Zhang
8f75317820 Merge pull request #1538 from huangyuzhang/main
fix: typo PresencePenlty -> PresencePenalty
2023-05-16 16:24:49 +08:00
Gan-Xing
dbb016c9e4 Merge branch 'Yidadaa:main' into main 2023-05-16 16:12:20 +08:00
Yifei Zhang
2831b91e94 Update README.md 2023-05-16 15:45:08 +08:00
simondby
923b2594df Merge branch 'main' of https://github.com/huangyuzhang/ChatGPT-Next-Web into main 2023-05-16 15:40:48 +08:00
simondby
44874fb5e0 fix: typo PresencePenlty -> PresencePenalty 2023-05-16 15:40:43 +08:00
Yifei Zhang
5b3d7ccb46 Merge pull request #1537 from qingfengfenga/main
Add docker-compose launch
2023-05-16 15:33:47 +08:00
Yifei Zhang
daecd3efa1 Merge pull request #1534 from Quorafind/fix_enter_key_to_select_prompt
Fix: press enter to select prompt when set pressing `enter` key to submit
2023-05-16 15:33:36 +08:00
Yifei Zhang
86e4b58117 Merge pull request #1533 from legionhealth/github-copilot-mask
GitHub Copilot mask (english)
2023-05-16 15:33:27 +08:00
Yifei Zhang
34426d86dc Merge pull request #1529 from PaRaD1SE98/main
fix: typo reqestTimeoutId -> requestTimeoutId
2023-05-16 15:31:39 +08:00
qingfengfenga
50a915b7b6 docker-compose supports more environment variables 2023-05-16 15:02:53 +08:00
qingfengfenga
775ba2596a Add docker-compose launch 2023-05-16 14:34:35 +08:00
Daniel G. Wilson
7141962cce Update en.ts to fix quotes for Copilot mask 2023-05-15 23:11:04 -05:00
Quorafind
d5a4527e9d revert: remove unused lines in gitignore file 2023-05-16 12:08:57 +08:00
Quorafind
cf775e3487 fix: enter key cannot select prompt when using enter key to submit 2023-05-16 12:06:06 +08:00
Daniel G. Wilson
44e1bed57a Update en.ts whoops actually fixed single / double quotes 2023-05-15 22:55:42 -05:00
Daniel G. Wilson
915ba07f86 Update en.ts - fixed single quotes to match original 2023-05-15 22:54:41 -05:00
Daniel G. Wilson
a852c5d0c3 Update en.ts masks to include GitHub Copilot mask (leaked prompt) 2023-05-15 22:52:44 -05:00
PaRaD1SE98
8cde6cd4d7 Merge pull request #1 from PaRaD1SE98/fix_typo
fix: typo reqestTimeoutId -> requestTimeoutId
2023-05-16 10:01:45 +09:00
GH Action - Upstream Sync
a70d59eb45 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-16 00:59:56 +00:00
PaRaD1SE98
06534fa0ae fix: typo reqestTimeoutId -> requestTimeoutId 2023-05-16 09:59:30 +09:00
Gan-Xing
8a548ef252 merge update 2023-05-16 07:41:26 +08:00
Gan-Xing
99c854ce1d merge update 2023-05-16 07:38:06 +08:00
Gan-Xing
46fe3c520c merge update 2023-05-16 07:27:20 +08:00
Gan-Xing
8568fc3544 Merge branch 'Yidadaa:main' into main 2023-05-16 07:13:53 +08:00
Yifei Zhang
5ba0aef799 Merge pull request #1526 from Yidadaa/bugfix-0515
fix: #1498 missing text caused by streaming
2023-05-16 02:02:08 +08:00
Yidadaa
71cbf86b2c fixup: add more error info 2023-05-16 01:58:58 +08:00
Yidadaa
aed6b34950 fix: #1498 missing text caused by streaming 2023-05-16 01:25:16 +08:00
Gan-Xing
e9076c1748 Merge branch 'Yidadaa:main' into main 2023-05-16 00:33:18 +08:00
Yidadaa
8b0cf7d248 fix: #1509 openai url split 2023-05-16 00:22:11 +08:00
Yifei Zhang
9aa794248f Merge pull request #1501 from PaRaD1SE98/main
fix: typo IMPRTANT -> IMPORTANT
2023-05-15 18:57:07 +08:00
PaRaD1SE98
b357e2ecef fix: typo IMPRTANT -> IMPORTANT 2023-05-15 10:03:11 +09:00
GH Action - Upstream Sync
68e27e9513 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-15 01:00:59 +00:00
Gan-Xing
7b4e4c2172 Merge branch 'Yidadaa:main' into main 2023-05-15 07:31:52 +08:00
Yidadaa
9e602eb575 fixup: decode in stream mode 2023-05-15 02:01:50 +08:00
Yifei Zhang
4618c624c8 Merge pull request #1496 from Yidadaa/refactor-api
fixup
2023-05-15 01:56:14 +08:00
Yidadaa
5979bdd48e fixup 2023-05-15 01:55:45 +08:00
Yifei Zhang
2170392bdf Merge pull request #1495 from Yidadaa/refactor-api
refactor: #1000 #1179 api layer for client-side only mode and local models
2023-05-15 01:48:26 +08:00
Yidadaa
e9335d9508 chore: upgrade nextjs to 13.4.2 2023-05-15 01:45:31 +08:00
Yidadaa
5f444c1c82 fix: conflict 2023-05-15 01:36:22 +08:00
Yidadaa
a3de277c43 refactor: #1000 #1179 api layer for client-side only mode and local models 2023-05-15 01:33:46 +08:00
Yifei Zhang
b5b8593e7f Merge pull request #1492 from Yidadaa/bugfix-0514
fix: #1444 async load google fonts
2023-05-14 23:29:01 +08:00
Yidadaa
03163d6a61 fix: #1444 async load google fonts 2023-05-14 23:25:22 +08:00
Yidadaa
bd90caa99d refactor: llm client api 2023-05-14 23:00:17 +08:00
Yifei Zhang
cada0aa70b Merge pull request #1491 from Yidadaa/bugfix-0514
fix: #1423 should not scroll right when dragging side bar items
2023-05-14 22:57:01 +08:00
Gan-Xing
cbde357d94 Merge branch 'Yidadaa:main' into main 2023-05-14 20:25:13 +08:00
Yifei Zhang
75488e4bd5 Merge pull request #1475 from PaRaD1SE98/main
remove error messages in toBeSummarizedMsgs
2023-05-14 14:18:27 +08:00
GH Action - Upstream Sync
d1bdf4a292 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-14 01:02:07 +00:00
Gan-Xing
c73c063ad5 merge 2023-05-14 07:34:15 +08:00
Gan-Xing
9b000ff242 Update README.md
多国语言支持增加法语
2023-05-14 05:30:59 +08:00
Gan-Xing
a282937468 Update README.md
Change My Deployment URL
2023-05-14 05:29:46 +08:00
Yidadaa
6da3aab046 fix: #1423 should not scroll right when dragging side bar items 2023-05-14 02:21:35 +08:00
PaRaD1SE98
ff2589c97f remove error messages for chat title summary 2023-05-14 02:34:32 +09:00
PaRaD1SE98
a9f000e7ef remove error messages in toBeSummarizedMsgs 2023-05-14 01:24:20 +09:00
Yifei Zhang
d9be63e6cb Merge pull request #1452 from PaRaD1SE98/main
fix: typo upater -> updater
2023-05-13 14:52:12 +08:00
Yifei Zhang
1a626a68f0 Merge pull request #1450 from yorunning/fix-1
fix: the theme-color selector
2023-05-13 14:50:54 +08:00
Yifei Zhang
330504b91e Merge pull request #1446 from wsw2000/feat/switchWindowScrollIntoView
feat: scrolling effect when switching chat windows
2023-05-13 14:50:42 +08:00
Yifei Zhang
6bb0166055 Merge pull request #1454 from yanCode/fix/i18n
fix: show Vietnamese in its own language
2023-05-13 14:48:06 +08:00
Yifei Zhang
b22988e6b8 Merge pull request #1459 from InitialXKO/patch-1
增加文生图面具
2023-05-13 14:45:12 +08:00
Yifei Zhang
f7edac961f Merge pull request #1456 from yanCode/fix/doc
docs: add supported languages to README.md
2023-05-13 14:43:52 +08:00
InitialXKO
5b9b120fa6 增加文生图面具 2023-05-13 09:36:04 +08:00
ShengYan, Zhang
f07e4fc87f docs: add supported languages to README.md 2023-05-13 09:02:15 +08:00
GH Action - Upstream Sync
46eb870c46 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-13 00:56:44 +00:00
ShengYan, Zhang
a0e192b6e4 fix: show Vitenamese in it's own language 2023-05-13 07:23:14 +08:00
PaRaD1SE98
dc3fa6c780 Update settings.tsx
fix: typo upater -> updater
2023-05-12 22:46:22 +09:00
Yorun
dd5604f5d9 style: move nextjs supported meta tags to metadata 2023-05-12 19:23:49 +08:00
Yorun
170936a96e fix: the theme-color selector 2023-05-12 18:47:41 +08:00
wsw
93c9974019 feat: scrolling effect when switching chat windows 2023-05-12 17:54:40 +08:00
Yifei Zhang
377579e802 Update README.md 2023-05-12 17:19:40 +08:00
Gan-Xing
881cf082c2 Update README.md
多国语言支持增加法语
2023-05-12 10:57:32 +08:00
GH Action - Upstream Sync
bfc924bc2a Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-12 00:58:55 +00:00
Gan-Xing
dbd92b2db9 Merge branch 'Yidadaa:main' into main 2023-05-12 08:26:45 +08:00
GanXing
505a68093d add french translation -增加法语翻译 2023-05-12 08:17:11 +08:00
599153574@qq.com
f2b81a2f23 add french translation -增加法语翻译 2023-05-12 07:51:04 +08:00
Yifei Zhang
c49dbab127 Merge pull request #1431 from Yidadaa/bugfix-0511
fix: #1401 try to disable zoom
2023-05-12 01:48:37 +08:00
Yidadaa
36adfe87fb fix: #1401 try to disable zoom 2023-05-11 23:21:16 +08:00
Yifei Zhang
cdfcf0f068 Merge pull request #1421 from zxdclyz/hide-hints
Fix: click the prompt button to hide hints when it's already shown
2023-05-11 18:53:59 +08:00
Yifei Zhang
c3676091ee Merge pull request #1417 from yanCode/fix/hotkey-clashes
Fix bug #1413 hotkey clashes
2023-05-11 18:53:44 +08:00
liyuze
ec19b86ade fix: click the prompt button to hide hints when it's already shown 2023-05-11 18:29:25 +08:00
Gan-Xing
ec43f4e6ab Update README.md
Change My Deployment URL
2023-05-11 16:31:42 +08:00
ShengYan, Zhang
7bf74c6a5d fix: bug #1413 cmd/alt/ctrl should be checked for arrowUp events 2023-05-11 16:08:34 +08:00
ShengYan, Zhang
cbb50c14e1 fix: bug #1413 input '/' when clicking icon to open prompt modal 2023-05-11 15:42:32 +08:00
Yifei Zhang
d42622a5c1 Merge pull request #1383 from PaRaD1SE98/main
给所有select元素添加一个右侧下拉按钮
2023-05-11 11:48:59 +08:00
Yifei Zhang
f9bee1485b Merge pull request #1396 from pBrambi/main
Add 🇨🇿 Czech translation
2023-05-11 11:08:49 +08:00
GH Action - Upstream Sync
ff72e8abab Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-11 00:58:37 +00:00
Petr Branberger
c87eee1fda Fixed typo syntax error 2023-05-10 16:33:22 +02:00
PaRaD1SE98
960aa90c32 优化Select component 2023-05-10 14:09:30 +00:00
Per André Rønsen
5a10b2a75f Update no.ts with const no: LocaleType 2023-05-10 14:59:57 +01:00
Per André Rønsen
72b553efe8 Add files for Norwegian translation 2023-05-10 14:59:36 +01:00
Petr Branberger
c4210be3c7 Czech language update 2023-05-10 15:48:36 +02:00
Petr Branberger
28a49827ff Merge branch 'main' of https://github.com/pBrambi/ChatGPT-Next-Web 2023-05-10 15:12:31 +02:00
Petr Branberger
736869454b Fix /cs.ts to include the option 2023-05-10 12:50:12 +02:00
Petr Branberger
db9084b0dc Fix redefined "DE" in /index.ts 2023-05-10 12:39:33 +02:00
Yifei Zhang
c39d75b448 Merge pull request #1381 from crim50n/main
Adding Russian translation
2023-05-10 15:57:07 +08:00
crim50n
71a546dd44 Adding Russian translation
Adding a Russian translation and fixing other language files to make them look the same.
2023-05-10 10:16:40 +03:00
PaRaD1SE98
96e3d3a22c 给所有select元素添加一个右侧下拉按钮 2023-05-10 07:14:49 +00:00
Yifei Zhang
0ad91101a4 Merge pull request #1371 from yanCode/fix/textarea-line
fix: row count logic
2023-05-10 10:47:06 +08:00
Yifei Zhang
e9df58709e Merge pull request #1368 from darth-pika-hu/main
添加Cloudflare Pages的部署说明
2023-05-10 10:38:34 +08:00
ShengYan, Zhang
a80dcaa1c3 fix: row count logic 2023-05-10 09:27:33 +08:00
GH Action - Upstream Sync
d29b7fa1c7 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-10 00:57:53 +00:00
Darth Pika
89551774db Re-format the Chinese version and added an English version
Finally found a computer.
2023-05-09 17:45:01 -07:00
Darth Pika
be58d3afb6 Update cloudflare-pages-cn.md 2023-05-09 17:31:19 -07:00
Darth Pika
652d803739 Update cloudflare-pages-cn.md 2023-05-09 17:25:00 -07:00
Darth Pika
2688914125 Create cloudflare-pages-cn.md
添加Cloudflare Pages的部署说明。这里临时说明,Next-on-Pages有一个bug将很快修复,修复后,Build命令会修改。
2023-05-09 16:47:41 -07:00
Yifei Zhang
1d489cfcea Merge pull request #1364 from Yidadaa/client-side
feat: add model and time info to prompts
2023-05-10 00:52:44 +08:00
Yidadaa
cb55ce084c feat: add model and time info to prompts 2023-05-10 00:51:43 +08:00
Yifei Zhang
dae7da0e4e Merge pull request #1361 from Yidadaa/bugfix-0509
feat: #1055, #444 and fix #1359 hot key to switch chat and allow to disable gpt-4
2023-05-09 23:39:53 +08:00
Yidadaa
e4630e6a0f fixup 2023-05-09 23:38:34 +08:00
Yidadaa
6d9abf11b8 fix: #1363 session index after deleting 2023-05-09 23:36:30 +08:00
Yidadaa
7e8def50aa feat: close #444 use env var to disable gpt-4 2023-05-09 23:20:03 +08:00
Yidadaa
2b7f72deec feat: close #1055 cmd/alt/ctrl + arrow up/down to switch window 2023-05-09 23:01:17 +08:00
Yidadaa
9b1f25140e fix: #1359 empty line wrap count 2023-05-09 22:46:06 +08:00
Yifei Zhang
f4caa0029e Update README.md 2023-05-09 16:51:27 +08:00
Yifei Zhang
15e046b3ce Update README.md 2023-05-09 11:27:57 +08:00
GH Action - Upstream Sync
9d0485fa22 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-09 01:00:01 +00:00
Yifei Zhang
eeef92b068 Merge pull request #1335 from yzy1996/patch-1
改为一天同步一次 Update sync.yml
2023-05-09 01:22:20 +08:00
Crazyang
a1418fe33c Update sync.yml
因为 Vercel 频繁更新会失败,遂建议改为一天同步一次。
2023-05-09 01:03:51 +08:00
Yifei Zhang
8f21e736dd Merge pull request #1333 from Yidadaa/0508-bugfix
feat: #1303 and fix #1294 1307, new modal style
2023-05-09 00:44:53 +08:00
Yidadaa
222301307f feat: close #1301 support message actions 2023-05-09 00:39:00 +08:00
Yidadaa
1b19fdfe11 feat: #1303 improve long text input ux and mobile modal 2023-05-08 22:49:51 +08:00
Yidadaa
1f2ef1cdb7 fix: #1307 empty messages 2023-05-08 22:21:06 +08:00
Yidadaa
c394b21423 fix: #1294 fallback while mermaid render fails 2023-05-08 22:18:19 +08:00
Yifei Zhang
696e84ea88 Merge pull request #1315 from binh234/vi_translation
Add Vietnamese translation
2023-05-08 11:51:28 +08:00
Yifei Zhang
4a17dca7b9 Merge pull request #1323 from yanCode/fix/prompt-list-style
fix: styles on .user-prompt-buttons
2023-05-08 10:43:59 +08:00
GH Action - Upstream Sync
b3f38f3264 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-08 00:57:14 +00:00
ShengYan, Zhang
9bcfe6461d chore: remove an unused css name 2023-05-08 08:48:47 +08:00
ShengYan, Zhang
4a82a91f2d fix: styles on .user-prompt-buttons 2023-05-08 08:27:10 +08:00
Yifei Zhang
24cd905911 Merge pull request #1319 from zhongmeizhi/main
feat: mobile chat overscroll-behavior none
2023-05-07 20:36:46 +08:00
Mokou
6d62ab4257 feat: mobile chat overscroll-behavior none 2023-05-07 18:33:40 +08:00
Binh Le
f6ff32f339 Add Vietnamese translation 2023-05-07 14:54:09 +07:00
Yifei Zhang
bc523d302b Merge pull request #1312 from LaChimere/user/lachimere/fix-typo
Fix typo
2023-05-07 15:38:14 +08:00
LaChimere
1facbb2906 fix typo 2023-05-07 13:52:26 +08:00
GH Action - Upstream Sync
a5eb87e835 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-07 01:02:30 +00:00
Yifei Zhang
8265436437 Merge pull request #1297 from yanCode/enhancement/loading-icon
fix: show Loading Icon when checking repo update
2023-05-07 00:36:40 +08:00
ShengYan, Zhang
96545bd523 fix: show Loading Icon when checking repo update 2023-05-06 20:12:26 +08:00
GH Action - Upstream Sync
dd5ee68808 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-06 00:54:52 +00:00
Yifei Zhang
e773ed2912 Update README.md 2023-05-05 23:43:26 +08:00
Yifei Zhang
9949fc4646 Merge pull request #1276 from Yidadaa/bugfix-0505
feat: #951 mermaid support
2023-05-05 23:38:27 +08:00
Yidadaa
d88da1f6ab feat: close #951 support mermaid 2023-05-05 23:32:35 +08:00
Yidadaa
fe8e3f2bcf fix: #1273 overlap detecting 2023-05-05 22:59:21 +08:00
Yidadaa
4b9d753254 fix: #1251 use google fonts mirror 2023-05-05 22:49:41 +08:00
Yifei Zhang
5ba385b74d Update README.md 2023-05-05 15:42:27 +08:00
GH Action - Upstream Sync
96c0a5c911 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-05 00:54:47 +00:00
Yifei Zhang
ec655f5182 Merge pull request #1248 from wsw2000/main
refactor: determine userInput by trimming
2023-05-04 23:26:02 +08:00
Yifei Zhang
093df395c7 Merge pull request #1235 from xesrc/main
resolve problem about basic auth when the app is behind a proxy server
2023-05-04 23:25:49 +08:00
Yifei Zhang
92d2373d77 Merge pull request #1247 from Yidadaa/proxy-api
fix: #1237 #1233 mask bug and proxy bug
2023-05-04 23:09:24 +08:00
wsw
319959ad6e refactor: determine userInput by trimming 2023-05-04 22:53:22 +08:00
Yidadaa
de1e4b4c6c Merge branch 'main' into proxy-api 2023-05-04 22:50:26 +08:00
Yidadaa
c2e79d22d2 fix: #1233 detect api key with custom prefix 2023-05-04 22:50:07 +08:00
Yidadaa
596c9b1d27 feat: close #887 import masks 2023-05-04 22:33:13 +08:00
Yidadaa
40223e6b3f fix: #1237 can not delete cloned mask 2023-05-04 22:31:10 +08:00
Yidadaa
eec1dd6448 fix: proxy api request 2023-05-04 22:18:03 +08:00
Yifei Zhang
6c1261d28d Merge pull request #1241 from yanCode/main
fix: bug #1240
2023-05-04 19:08:53 +08:00
Yifei Zhang
4697ea1c1a Update README.md 2023-05-04 19:08:10 +08:00
ShengYan, Zhang
6e20031dce fix: bug #1240 2023-05-04 18:52:28 +08:00
xesrc
a5fe9bc6d6 resolve problem about basic auth when the app is behind a proxy server 2023-05-04 16:11:44 +08:00
Yifei Zhang
637660df2a Merge pull request #1232 from sanding0/main
chore: update registry mirror
2023-05-04 14:41:01 +08:00
roller
e1549d109e chore: update registry mirror 2023-05-04 11:26:14 +08:00
GH Action - Upstream Sync
418d270d74 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-04 00:58:20 +00:00
Yifei Zhang
5f8fc3d155 Merge pull request #1222 from Yidadaa/proxy-api
refactor: merge /api/chat-stream to /api/openai
2023-05-04 00:13:25 +08:00
Yidadaa
fce3b3ce7b feat: use commit time as version id 2023-05-04 00:12:00 +08:00
Yidadaa
074bd9f045 feat: close #663 allow disable user api key input 2023-05-03 23:49:33 +08:00
Yidadaa
b1ea26467d refactor: extract client side openai url 2023-05-03 23:25:17 +08:00
Yidadaa
48ebd74859 refactor: merge token and access code 2023-05-03 23:08:37 +08:00
Yidadaa
ef5b7ce853 refactor: merge /api/chat-stream to /api/openai 2023-05-03 17:07:09 +08:00
Yifei Zhang
e0053d57f7 Merge pull request #1220 from Yidadaa/bugfix-0503
feat: #782 select prompt with arrow down / up
2023-05-03 16:26:27 +08:00
Yidadaa
06268543d0 fixup 2023-05-03 16:24:25 +08:00
Yidadaa
58eadd6d7b feat: close #782 select prompt with arrow down / up 2023-05-03 16:22:37 +08:00
Yidadaa
f250594e97 Merge branch 'main' into bugfix-0503 2023-05-03 15:56:02 +08:00
Yidadaa
c1b6828ed4 fix: #1201 wont close prompt list when blur 2023-05-03 15:55:46 +08:00
Yidadaa
328ecd1cfb fix: #1210 change default lang to en 2023-05-03 15:22:44 +08:00
GH Action - Upstream Sync
14b3f300ae Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-03 00:59:21 +00:00
GH Action - Upstream Sync
f54fb177da Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-05-02 01:00:06 +00:00
GH Action - Upstream Sync
a9c6c681ce Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-29 00:57:26 +00:00
GH Action - Upstream Sync
d58545d340 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-28 00:59:42 +00:00
GH Action - Upstream Sync
98de486534 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-27 01:00:31 +00:00
GH Action - Upstream Sync
662b46101c Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-26 00:58:32 +00:00
GH Action - Upstream Sync
23287e9b47 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-25 01:01:30 +00:00
GH Action - Upstream Sync
d5bd77cca1 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-24 01:00:05 +00:00
GH Action - Upstream Sync
bfad3df3b3 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-23 01:03:46 +00:00
GH Action - Upstream Sync
4fcb1498c2 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-22 00:58:24 +00:00
GH Action - Upstream Sync
8a92904287 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-21 00:57:30 +00:00
pBrambi
6a74d62e98 Czech 2023-04-20 19:26:31 +02:00
GH Action - Upstream Sync
78568ef7c0 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-20 00:57:43 +00:00
pBrambi
35ccfb14c2 Create cs.ts
Czech language translation
2023-04-20 01:02:43 +02:00
GH Action - Upstream Sync
735afc64f6 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-19 01:00:23 +00:00
GH Action - Upstream Sync
b178bd6bd6 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-18 00:57:23 +00:00
GH Action - Upstream Sync
b3a0b252ad Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-17 00:59:02 +00:00
GH Action - Upstream Sync
424799191c Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-16 01:00:55 +00:00
GH Action - Upstream Sync
faafb45d83 Merge branch 'main' of https://github.com/Yidadaa/ChatGPT-Next-Web 2023-04-15 00:59:39 +00:00
sjn
75b3561594 Update sync.yml
change schedule with every day
2023-04-14 17:00:46 +08:00
190 changed files with 19288 additions and 3327 deletions

14
.babelrc Normal file
View File

@@ -0,0 +1,14 @@
{
"presets": [
[
"next/babel",
{
"preset-env": {
"targets": {
"browsers": ["> 0.25%, not dead"]
}
}
}
]
]
}

33
.env.template Normal file
View File

@@ -0,0 +1,33 @@
# Your openai api key. (required)
OPENAI_API_KEY=sk-xxxx
# Access passsword, separated by comma. (optional)
CODE=your-password
# You can start service behind a proxy
PROXY_URL=http://localhost:7890
# Override openai api request base url. (optional)
# Default: https://api.openai.com
# Examples: http://your-openai-proxy.com
BASE_URL=
# Specify OpenAI organization ID.(optional)
# Default: Empty
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.
DISABLE_GPT4=
# (optional)
# Default: Empty
# If you do not want users to query balance, set this value to 1.
HIDE_BALANCE_QUERY=

View File

@@ -7,6 +7,10 @@ assignees: ''
---
> 为了提高交流效率,我们设立了官方 QQ 群和 QQ 频道,如果你在使用或者搭建过程中遇到了任何问题,请先第一时间加群或者频道咨询解决,除非是可以稳定复现的 Bug 或者较为有创意的功能建议,否则请不要随意往 Issue 区发送低质无意义帖子。
> [点击加入官方群聊](https://github.com/Yidadaa/ChatGPT-Next-Web/discussions/1724)
**这个功能与现有的问题有关吗?**
如果有关,请在此列出链接或者描述问题。

View File

@@ -7,9 +7,13 @@ 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) 中搜索了此次反馈的问题,没有找到解答;

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

105
.github/workflows/app.yml vendored Normal file
View File

@@ -0,0 +1,105 @@
name: Release App
on:
workflow_dispatch:
release:
types: [published]
jobs:
create-release:
permissions:
contents: write
runs-on: ubuntu-latest
outputs:
release_id: ${{ steps.create-release.outputs.result }}
steps:
- uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
- name: get version
run: echo "PACKAGE_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').package.version")" >> $GITHUB_ENV
- name: create release
id: create-release
uses: actions/github-script@v6
with:
script: |
const { data } = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
})
return data.id
build-tauri:
needs: create-release
permissions:
contents: write
strategy:
fail-fast: false
matrix:
config:
- 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
- os: windows-latest
arch: x86_64
rust_target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.config.os }}
steps:
- uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v3
with:
node-version: 16
- 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 }}
- name: install dependencies (ubuntu only)
if: matrix.config.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: install frontend dependencies
run: yarn install # change this to npm or pnpm depending on which one you use
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
releaseId: ${{ needs.create-release.outputs.release_id }}
publish-release:
permissions:
contents: write
runs-on: ubuntu-latest
needs: [create-release, build-tauri]
steps:
- name: publish release
id: publish-release
uses: actions/github-script@v6
env:
release_id: ${{ needs.create-release.outputs.release_id }}
with:
script: |
github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: process.env.release_id,
draft: false,
prerelease: false
})

15
.github/workflows/issue-translator.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Issue Translator
on:
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
with:
IS_MODIFY_TITLE: false
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically.

View File

@@ -5,7 +5,7 @@ permissions:
on:
schedule:
- cron: "0 * * * *" # every hour
- cron: "0 0 * * *" # every day
workflow_dispatch:
jobs:
@@ -35,6 +35,6 @@ jobs:
- name: Sync check
if: failure()
run: |
echo "::error::由于权限不足,导致同步失败(这是预期的行为),请前往仓库首页手动执行[Sync fork]。"
echo "::error::Due to insufficient permissions, synchronization failed (as expected). Please go to the repository homepage and manually perform [Sync fork]."
echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次详细教程请查看https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/README_CN.md#%E6%89%93%E5%BC%80%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%96%B0"
echo "[Error] Due to a change in the workflow file of the upstream repository, GitHub has automatically suspended the scheduled automatic update. You need to manually sync your fork. Please refer to the detailed tutorial for instructions: https://github.com/Yidadaa/ChatGPT-Next-Web#enable-automatic-updates"
exit 1

10
.gitignore vendored
View File

@@ -36,7 +36,11 @@ yarn-error.log*
next-env.d.ts
dev
public/prompts.json
.vscode
.idea
.idea
# docker-compose env files
.env
*.key
*.key.pub

View File

@@ -8,7 +8,7 @@ WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn config set registry 'https://registry.npm.taobao.org'
RUN yarn config set registry 'https://registry.npmmirror.com/'
RUN yarn install
FROM base AS builder
@@ -41,6 +41,7 @@ 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); \
@@ -50,6 +51,8 @@ CMD if [ -n "$PROXY_URL" ]; then \
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; \

88
LICENSE
View File

@@ -1,75 +1,21 @@
版权所有c<2023><Zhang Yifei>
MIT License
反996许可证版本1.0
Copyright (c) 2023 Zhang Yifei
在符合下列条件的情况下,
特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以下统称为“授权作品”
)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括但不限于使
用、复制,修改,衍生利用、散布,发布和再许可:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不
得自行修改。
2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或
经营地(以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和
标准。如果该司法管辖区没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可
执行,则个人或法人实体必须遵守国际劳工标准的核心公约。
3. 个人或法人不得以任何方式诱导或强迫其全职或兼职员工或其独立承包人以口头或书面形式同
意直接或间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和
标准保护的权利或补救措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该
等个人或法人实体也不得以任何方法限制其雇员或独立承包人向版权持有人或监督许可证合规
情况的有关当局报告或投诉上述违反许可证的行为的权利。
该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用
性和非侵权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均
不承担因本软件或本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。
------------------------- ENGLISH ------------------------------
Copyright (c) <2023> <Zhang Yifei>
Anti 996 License Version 1.0 (Draft)
Permission is hereby granted to any individual or legal entity obtaining a copy
of this licensed work (including the source code, documentation and/or related
items, hereinafter collectively referred to as the "licensed work"), free of
charge, to deal with the licensed work for any purpose, including without
limitation, the rights to use, reproduce, modify, prepare derivative works of,
publish, distribute and sublicense the licensed work, subject to the following
conditions:
1. The individual or the legal entity must conspicuously display, without
modification, this License on each redistributed or derivative copy of the
Licensed Work.
2. The individual or the legal entity must strictly comply with all applicable
laws, regulations, rules and standards of the jurisdiction relating to
labor and employment where the individual is physically located or where
the individual was born or naturalized; or where the legal entity is
registered or is operating (whichever is stricter). In case that the
jurisdiction has no such laws, regulations, rules and standards or its
laws, regulations, rules and standards are unenforceable, the individual
or the legal entity are required to comply with Core International Labor
Standards.
3. The individual or the legal entity shall not induce or force its
employee(s), whether full-time or part-time, or its independent
contractor(s), in any methods, to agree in oral or written form,
to directly or indirectly restrict, weaken or relinquish his or
her rights or remedies under such laws, regulations, rules and
standards relating to labor and employment as mentioned above,
no matter whether such written or oral agreement are enforceable
under the laws of the said jurisdiction, nor shall such individual
or the legal entity limit, in any methods, the rights of its employee(s)
or independent contractor(s) from reporting or complaining to the copyright
holder or relevant authorities monitoring the compliance of the license
about its violation(s) of the said license.
THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION
WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

128
README.md
View File

@@ -3,16 +3,27 @@
<h1 align="center">ChatGPT Next Web</h1>
English / [简体中文](./README_CN.md)
English / [简体中文](./README_CN.md) / [日本語](./README_JA.md)
One-Click to deploy well-designed ChatGPT web UI on Vercel.
One-Click to get well-designed cross-platform ChatGPT web UI.
一键免费部署你的私人 ChatGPT 网页应用。
一键免费部署你的跨平台私人 ChatGPT 应用。
[Demo](https://chat-gpt-next-web.vercel.app/) / [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [Join Discord](https://discord.gg/zrhvHCr79N) / [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
[![Web][Web-image]][web-url]
[![Windows][Windows-image]][download-url]
[![MacOS][MacOS-image]][download-url]
[![Linux][Linux-image]][download-url]
[演示](https://chat-gpt-next-web.vercel.app/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://user-images.githubusercontent.com/16968934/234462588-e8eff256-f5ca-46ef-8f5f-d7db6d28735a.jpg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg)
[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)
[网页版](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)
[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)
@@ -25,43 +36,45 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel.
## Features
- **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
- Markdown support: LaTex, mermaid, code highlight, etc.
- Responsive design, dark mode and PWA
- Fast first screen loading speed (~100kb), support streaming response
- New in v2: create, share and debug your chat tools with prompt templates (mask)
- Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts)
- Automatically compresses chat history to support long conversations while also saving your tokens
- One-click export all chat history with full Markdown support
- I18n supported
- I18n: English, 简体中文, 繁体中文, 日本語, Français, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština, 한국어, Indonesia
## Roadmap
- [x] System Prompt: pin a user defined prompt as system prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)
- [x] User Prompt: user can edit and save custom prompts to prompt list
- [x] Prompt Template: create a new chat with pre-defined in-context prompts [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993)
- [ ] Share as image, share to ShareGPT
- [ ] Desktop App with tauri
- [ ] Self-host Model: support llama, alpaca, ChatGLM, BELLE etc.
- [x] Share as image, share to ShareGPT [#1741](https://github.com/Yidadaa/ChatGPT-Next-Web/pull/1741)
- [x] Desktop App with tauri
- [x] Self-host Model: Fully compatible with [RWKV-Runner](https://github.com/josStorer/RWKV-Runner), as well as server deployment of [LocalAI](https://github.com/go-skynet/LocalAI): llama/gpt4all/rwkv/vicuna/koala/gpt4all-j/cerebras/falcon/dolly etc.
- [ ] Plugins: support network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
### Not in Plan
- User login, accounts, cloud sync
- UI text customize
## 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.8 now we have a client that runs across all platforms!
## 主要功能
- 在 1 分钟内使用 Vercel **免费一键部署**
- 提供体积极小(~5MB的跨平台客户端Linux/Windows/MacOS, [下载地址](https://github.com/Yidadaa/ChatGPT-Next-Web/releases)
- 完整的 Markdown 支持LaTex 公式、Mermaid 流程图、代码高亮等等
- 精心设计的 UI响应式设计支持深色模式支持 PWA
- 极快的首屏加载速度(~100kb支持流式响应
- 隐私安全,所有数据保存在用户浏览器本地
- 预制角色功能(面具),方便地创建、分享和调试你的个性化对话
- 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts)
- 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话
- 一键导出聊天记录,完整的 Markdown 支持
- 多国语言支持English, 简体中文, 繁体中文, 日本語, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština
- 拥有自己的域名?好上加好,绑定后即可在任何地方**无障碍**快速访问
## 开发计划
@@ -69,20 +82,17 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel.
- [x] 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)
- [x] 允许用户自行编辑内置 Prompt 列表
- [x] 预制角色:使用预制角色快速定制新对话 [#993](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/993)
- [ ] 分享为图片,分享到 ShareGPT
- [ ] 使用 tauri 打包桌面应用
- [ ] 支持自部署的大语言模型
- [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-for-open-llm](https://github.com/xusenlinzy/api-for-open-llm)
- [ ] 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165)
### 不会开发的功能
- 界面文字自定义
- 用户登录、账号管理、消息云同步
## 最新动态
- 🚀 v2.0 已经发布,现在你可以使用面具功能快速创建预制对话了! 了解更多: [ChatGPT 提示词高阶技能:零次、一次和少样本提示](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138)。
- 💡 想要更方便地随时随地使用本项目可以试下这款桌面插件https://github.com/mushan0x0/AI0x0.com
- 🚀 v2.7 现在可以将会话分享为图片了,也可以分享到 ShareGPT 的在线链接。
- 🚀 v2.8 发布了横跨 Linux/Windows/MacOS 的体积极小的客户端。
## Get Started
@@ -125,7 +135,7 @@ After forking the project, due to the limitations imposed by GitHub, you need to
If you want to update instantly, you can check out the [GitHub documentation](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork) to learn how to synchronize a forked project with upstream code.
You can star or watch this project or follow author to get release notifictions in time.
You can star or watch this project or follow author to get release notifications in time.
## Access Password
@@ -163,6 +173,28 @@ Override openai api request base url.
Specify OpenAI organization ID.
### `HIDE_USER_API_KEY` (optional)
> Default: Empty
If you do not want users to input their own API key, set this value to 1.
### `DISABLE_GPT4` (optional)
> Default: Empty
If you do not want users to use GPT-4, set this value to 1.
### `HIDE_BALANCE_QUERY` (optional)
> Default: Empty
If you do not want users to query balance, set this value to 1.
## Requirements
NodeJS >= 18, Docker >= 20
## Development
> [简体中文 > 如何进行二次开发](./README_CN.md#开发)
@@ -173,6 +205,9 @@ Before starting development, you must create a new `.env.local` file at project
```
OPENAI_API_KEY=<your api key here>
# if you are not able to access openai service, use this BASE_URL
BASE_URL=https://chatgpt1.nextweb.fun/api/proxy
```
### Local Development
@@ -195,8 +230,8 @@ yarn dev
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY="sk-xxxx" \
-e CODE="your-password" \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
yidadaa/chatgpt-next-web
```
@@ -204,24 +239,44 @@ You can start service behind a proxy:
```shell
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY="sk-xxxx" \
-e CODE="your-password" \
-e PROXY_URL="http://localhost:7890" \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=your-password \
-e PROXY_URL=http://localhost:7890 \
yidadaa/chatgpt-next-web
```
If your proxy needs password, use:
```shell
-e PROXY_URL="http://127.0.0.1:7890 user pass"
```
### Shell
```shell
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
## Documentation
> Please go to the [docs][./docs] directory for more documentation instructions.
- [Deploy with cloudflare (Deprecated)](./docs/cloudflare-pages-en.md)
- [Frequent Ask Questions](./docs/faq-en.md)
- [How to add a new translation](./docs/translation.md)
- [How to use Vercel (No English)](./docs/vercel-cn.md)
- [User Manual (Only Chinese, WIP)](./docs/user-manual-cn.md)
## Screenshots
![Settings](./docs/images/settings.png)
![More](./docs/images/more.png)
## Translation
If you want to add a new translation, read this [document](./docs/translation.md).
## Donation
[Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa)
@@ -248,7 +303,12 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
[@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)
### Contributor
@@ -256,4 +316,4 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s
## LICENSE
[Anti 996 License](https://github.com/kattgu7/Anti-996-License/blob/master/LICENSE_CN_EN)
[MIT](https://opensource.org/license/mit/)

View File

@@ -33,6 +33,7 @@
- 在 Vercel 重新选择并部署,[请查看详细教程](./docs/vercel-cn.md#如何新建项目)。
### 打开自动更新
> 如果你遇到了 Upstream Sync 执行错误,请手动 Sync Fork 一次!
当你 fork 项目之后,由于 Github 的限制,需要手动去你 fork 后的项目的 Actions 页面启用 Workflows并启用 Upstream Sync Action启用之后即可开启每小时定时自动更新
@@ -63,7 +64,7 @@ code1,code2,code3
## 环境变量
> 本项目大多数配置项都通过环境变量来设置。
> 本项目大多数配置项都通过环境变量来设置,教程:[如何修改 Vercel 环境变量](./docs/vercel-cn.md)
### `OPENAI_API_KEY` (必填项)
@@ -89,9 +90,19 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
指定 OpenAI 中的组织 ID。
## 开发
### `HIDE_USER_API_KEY` (可选)
> 强烈不建议在本地进行开发或者部署,由于一些技术原因,很难在本地配置好 OpenAI API 代理,除非你能保证可以直连 OpenAI 服务器
如果你不想让用户自行填入 API Key将此环境变量设置为 1 即可
### `DISABLE_GPT4` (可选)
如果你不想让用户使用 GPT-4将此环境变量设置为 1 即可。
### `HIDE_BALANCE_QUERY` (可选)
如果你不想让用户查询余额,将此环境变量设置为 1 即可。
## 开发
点击下方按钮,开始二次开发:
@@ -101,27 +112,31 @@ OpenAI 接口代理 URL如果你手动配置了 openai 接口代理,请填
```
OPENAI_API_KEY=<your api key here>
# 中国大陆用户,可以使用本项目自带的代理进行开发,你也可以自由选择其他代理地址
BASE_URL=https://nb.nextweb.fun/api/proxy
```
### 本地开发
1. 安装 nodejs 18 和 yarn具体细节请询问 ChatGPT
2. 执行 `yarn install && yarn dev` 即可。⚠️注意:此命令仅用于本地开发,不要用于部署!
3. 如果你想本地部署,请使用 `yarn install && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。
2. 执行 `yarn install && yarn dev` 即可。⚠️ 注意:此命令仅用于本地开发,不要用于部署!
3. 如果你想本地部署,请使用 `yarn install && yarn build && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。
## 部署
### 容器部署 (推荐)
> Docker 版本需要在 20 及其以上,否则会提示找不到镜像。
> 注意docker 版本在大多数时间都会落后最新的版本 1 到 2 天,所以部署后会持续出现“存在更新”的提示,属于正常现象。
> ⚠️ 注意docker 版本在大多数时间都会落后最新的版本 1 到 2 天,所以部署后会持续出现“存在更新”的提示,属于正常现象。
```shell
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY="sk-xxxx" \
-e CODE="页面访问密码" \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=页面访问密码 \
yidadaa/chatgpt-next-web
```
@@ -129,13 +144,19 @@ docker run -d -p 3000:3000 \
```shell
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY="sk-xxxx" \
-e CODE="页面访问密码" \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=页面访问密码 \
--net=host \
-e PROXY_URL="http://127.0.0.1:7890" \
-e PROXY_URL=http://127.0.0.1:7890 \
yidadaa/chatgpt-next-web
```
如果你的本地代理需要账号密码,可以使用:
```shell
-e PROXY_URL="http://127.0.0.1:7890 user password"
```
如果你需要指定其他环境变量,请自行在上述命令中增加 `-e 环境变量=环境变量值` 来指定。
### 本地部署
@@ -146,7 +167,7 @@ docker run -d -p 3000:3000 \
bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh)
```
⚠️注意:如果你安装过程中遇到了问题,请使用 docker 部署。
⚠️ 注意:如果你安装过程中遇到了问题,请使用 docker 部署。
## 鸣谢
@@ -158,8 +179,9 @@ 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): 一站式大模型额度管理平台,支持市面上所有主流大语言模型
## 开源协议
> 反对 996从我开始。
[Anti 996 License](https://github.com/kattgu7/Anti-996-License/blob/master/LICENSE_CN_EN)
[MIT](https://opensource.org/license/mit/)

173
README_ES.md Normal file
View File

@@ -0,0 +1,173 @@
<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/)

275
README_JA.md Normal file
View File

@@ -0,0 +1,275 @@
<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/)

187
README_KO.md Normal file
View File

@@ -0,0 +1,187 @@
<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/)

65
app/api/auth.ts Normal file
View File

@@ -0,0 +1,65 @@
import { NextRequest } from "next/server";
import { getServerSideConfig } from "../config/server";
import md5 from "spark-md5";
import { ACCESS_CODE_PREFIX } from "../constant";
function getIP(req: NextRequest) {
let ip = req.ip ?? req.headers.get("x-real-ip");
const forwardedFor = req.headers.get("x-forwarded-for");
if (!ip && forwardedFor) {
ip = forwardedFor.split(",").at(0) ?? "";
}
return ip;
}
function parseApiKey(bearToken: string) {
const token = bearToken.trim().replaceAll("Bearer ", "").trim();
const isOpenAiKey = !token.startsWith(ACCESS_CODE_PREFIX);
return {
accessCode: isOpenAiKey ? "" : token.slice(ACCESS_CODE_PREFIX.length),
apiKey: isOpenAiKey ? token : "",
};
}
export function auth(req: NextRequest) {
const authToken = req.headers.get("Authorization") ?? "";
// check if it is openai api key or user token
const { accessCode, apiKey: token } = parseApiKey(authToken);
const hashedCode = md5.hash(accessCode ?? "").trim();
const serverConfig = getServerSideConfig();
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
console.log("[Auth] got access code:", accessCode);
console.log("[Auth] hashed access code:", hashedCode);
console.log("[User IP] ", getIP(req));
console.log("[Time] ", new Date().toLocaleString());
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
return {
error: true,
msg: !accessCode ? "empty access code" : "wrong access code",
};
}
// if user does not provide an api key, inject system api key
if (!token) {
const apiKey = serverConfig.apiKey;
if (apiKey) {
console.log("[Auth] use system api key");
req.headers.set("Authorization", `Bearer ${apiKey}`);
} else {
console.log("[Auth] admin did not provide an api key");
}
} else {
console.log("[Auth] use user api key");
}
return {
error: false,
};
}

View File

@@ -1,62 +0,0 @@
import { createParser } from "eventsource-parser";
import { NextRequest } from "next/server";
import { requestOpenai } from "../common";
async function createStream(req: NextRequest) {
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const res = await requestOpenai(req);
const contentType = res.headers.get("Content-Type") ?? "";
if (!contentType.includes("stream")) {
const content = await (
await res.text()
).replace(/provided:.*. You/, "provided: ***. You");
console.log("[Stream] error ", content);
return "```json\n" + content + "```";
}
const stream = new ReadableStream({
async start(controller) {
function onParse(event: any) {
if (event.type === "event") {
const data = event.data;
// https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
if (data === "[DONE]") {
controller.close();
return;
}
try {
const json = JSON.parse(data);
const text = json.choices[0].delta.content;
const queue = encoder.encode(text);
controller.enqueue(queue);
} catch (e) {
controller.error(e);
}
}
}
const parser = createParser(onParse);
for await (const chunk of res.body as any) {
parser.feed(decoder.decode(chunk, { stream: true }));
}
},
});
return stream;
}
export async function POST(req: NextRequest) {
try {
const stream = await createStream(req);
return new Response(stream);
} catch (error) {
console.error("[Chat Stream]", error);
return new Response(
["```json\n", JSON.stringify(error, null, " "), "\n```"].join(""),
);
}
}
export const runtime = "edge";

View File

@@ -1,13 +1,18 @@
import { NextRequest } from "next/server";
import { NextRequest, NextResponse } from "next/server";
const OPENAI_URL = "api.openai.com";
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 PROTOCOL = process.env.PROTOCOL || DEFAULT_PROTOCOL;
const BASE_URL = process.env.BASE_URL || OPENAI_URL;
const DISABLE_GPT4 = !!process.env.DISABLE_GPT4;
export async function requestOpenai(req: NextRequest) {
const apiKey = req.headers.get("token");
const openaiPath = req.headers.get("path");
const controller = new AbortController();
const authValue = req.headers.get("Authorization") ?? "";
const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
"/api/openai/",
"",
);
let baseUrl = BASE_URL;
@@ -15,6 +20,10 @@ export async function requestOpenai(req: NextRequest) {
baseUrl = `${PROTOCOL}://${baseUrl}`;
}
if (baseUrl.endsWith('/')) {
baseUrl = baseUrl.slice(0, -1);
}
console.log("[Proxy] ", openaiPath);
console.log("[Base Url]", baseUrl);
@@ -22,16 +31,68 @@ export async function requestOpenai(req: NextRequest) {
console.log("[Org ID]", process.env.OPENAI_ORG_ID);
}
return fetch(`${baseUrl}/${openaiPath}`, {
const timeoutId = setTimeout(() => {
controller.abort();
}, 10 * 60 * 1000);
const fetchUrl = `${baseUrl}/${openaiPath}`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"Cache-Control": "no-store",
Authorization: authValue,
...(process.env.OPENAI_ORG_ID && {
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
}),
},
cache: "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,
};
// #1815 try to refuse gpt4 request
if (DISABLE_GPT4 && req.body) {
try {
const clonedBody = await req.text();
fetchOptions.body = clonedBody;
const jsonBody = JSON.parse(clonedBody);
if ((jsonBody?.model ?? "").includes("gpt-4")) {
return NextResponse.json(
{
error: true,
message: "you are not allowed to use gpt-4 model",
},
{
status: 403,
},
);
}
} catch (e) {
console.error("[OpenAI] gpt4 filter", e);
}
}
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);
}
}

View File

@@ -1,23 +1,27 @@
import { NextRequest, NextResponse } from "next/server";
import { NextResponse } from "next/server";
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,
};
declare global {
type DangerConfig = typeof DANGER_CONFIG;
}
export async function POST(req: NextRequest) {
return NextResponse.json({
needCode: serverConfig.needCode,
});
async function handle() {
return NextResponse.json(DANGER_CONFIG);
}
export const GET = handle;
export const POST = handle;
export const runtime = "edge";

View File

@@ -0,0 +1,43 @@
import { NextRequest, NextResponse } from "next/server";
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const [protocol, ...subpath] = params.path;
const targetUrl = `${protocol}://${subpath.join("/")}`;
const method = req.headers.get("method") ?? undefined;
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 = "nodejs";

View File

@@ -0,0 +1,77 @@
import { type OpenAIListModelResponse } from "@/app/client/platforms/openai";
import { getServerSideConfig } from "@/app/config/server";
import { OpenaiPath } from "@/app/constant";
import { prettyObject } from "@/app/utils/format";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
import { requestOpenai } from "../../common";
const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
function getModels(remoteModelRes: OpenAIListModelResponse) {
const config = getServerSideConfig();
if (config.disableGPT4) {
remoteModelRes.data = remoteModelRes.data.filter(
(m) => !m.id.startsWith("gpt-4"),
);
}
return remoteModelRes;
}
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[OpenAI Route] params ", params);
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const subpath = params.path.join("/");
if (!ALLOWD_PATH.has(subpath)) {
console.log("[OpenAI Route] forbidden path ", subpath);
return NextResponse.json(
{
error: true,
msg: "you are not allowed to request " + subpath,
},
{
status: 403,
},
);
}
const authResult = auth(req);
if (authResult.error) {
return NextResponse.json(authResult, {
status: 401,
});
}
try {
const response = await requestOpenai(req);
// list models
if (subpath === OpenaiPath.ListModelPath && response.status === 200) {
const resJson = (await response.json()) as OpenAIListModelResponse;
const availableModels = getModels(resJson);
return NextResponse.json(availableModels, {
status: response.status,
});
}
return response;
} catch (e) {
console.error("[OpenAI] ", e);
return NextResponse.json(prettyObject(e));
}
}
export const GET = handle;
export const POST = handle;
export const runtime = "edge";

View File

@@ -1,33 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import { requestOpenai } from "../common";
async function makeRequest(req: NextRequest) {
try {
const api = await requestOpenai(req);
const res = new NextResponse(api.body);
res.headers.set("Content-Type", "application/json");
res.headers.set("Cache-Control", "no-cache");
return res;
} catch (e) {
console.error("[OpenAI] ", req.body, e);
return NextResponse.json(
{
error: true,
msg: JSON.stringify(e),
},
{
status: 500,
},
);
}
}
export async function POST(req: NextRequest) {
return makeRequest(req);
}
export async function GET(req: NextRequest) {
return makeRequest(req);
}
export const runtime = "edge";

View File

@@ -1,9 +0,0 @@
import type {
CreateChatCompletionRequest,
CreateChatCompletionResponse,
} from "openai";
export type ChatRequest = CreateChatCompletionRequest;
export type ChatResponse = CreateChatCompletionResponse;
export type Updater<T> = (updater: (value: T) => void) => void;

151
app/client/api.ts Normal file
View File

@@ -0,0 +1,151 @@
import { getClientConfig } from "../config/client";
import { ACCESS_CODE_PREFIX } from "../constant";
import { ChatMessage, ModelType, useAccessStore } from "../store";
import { ChatGPTApi } from "./platforms/openai";
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 RequestMessage {
role: MessageRole;
content: string;
}
export interface LLMConfig {
model: string;
temperature?: number;
top_p?: number;
stream?: boolean;
presence_penalty?: number;
frequency_penalty?: number;
}
export interface ChatOptions {
messages: RequestMessage[];
config: LLMConfig;
onUpdate?: (message: string, chunk: string) => void;
onFinish: (message: string) => void;
onError?: (err: Error) => void;
onController?: (controller: AbortController) => void;
}
export interface LLMUsage {
used: number;
total: number;
}
export interface LLMModel {
name: string;
available: boolean;
}
export abstract class LLMApi {
abstract chat(options: ChatOptions): Promise<void>;
abstract usage(): Promise<LLMUsage>;
abstract models(): Promise<LLMModel[]>;
}
type ProviderName = "openai" | "azure" | "claude" | "palm";
interface Model {
name: string;
provider: ProviderName;
ctxlen: number;
}
interface ChatProvider {
name: ProviderName;
apiConfig: {
baseUrl: string;
apiKey: string;
summaryModel: Model;
};
models: Model[];
chat: () => void;
usage: () => void;
}
export class ClientApi {
public llm: LLMApi;
constructor() {
this.llm = new ChatGPTApi();
}
config() {}
prompts() {}
masks() {}
async share(messages: ChatMessage[], avatarUrl: string | null = null) {
const msgs = messages
.map((m) => ({
from: m.role === "user" ? "human" : "gpt",
value: m.content,
}))
.concat([
{
from: "human",
value:
"Share from [ChatGPT Next Web]: https://github.com/Yidadaa/ChatGPT-Next-Web",
},
]);
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
// Please do not modify this message
console.log("[Share]", messages, msgs);
const clientConfig = getClientConfig();
const proxyUrl = "/sharegpt";
const rawUrl = "https://sharegpt.com/api/conversations";
const shareUrl = clientConfig?.isApp ? rawUrl : proxyUrl;
const res = await fetch(shareUrl, {
body: JSON.stringify({
avatarUrl,
items: msgs,
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const resJson = await res.json();
console.log("[Share]", resJson);
if (resJson.id) {
return `https://shareg.pt/${resJson.id}`;
}
}
}
export const api = new ClientApi();
export function getHeaders() {
const accessStore = useAccessStore.getState();
let headers: Record<string, string> = {
"Content-Type": "application/json",
"x-requested-with": "XMLHttpRequest",
};
const makeBearer = (token: string) => `Bearer ${token.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,
);
}
return headers;
}

37
app/client/controller.ts Normal file
View File

@@ -0,0 +1,37 @@
// To store message streaming controller
export const ChatControllerPool = {
controllers: {} as Record<string, AbortController>,
addController(
sessionId: string,
messageId: string,
controller: AbortController,
) {
const key = this.key(sessionId, messageId);
this.controllers[key] = controller;
return key;
},
stop(sessionId: string, messageId: string) {
const key = this.key(sessionId, messageId);
const controller = this.controllers[key];
controller?.abort();
},
stopAll() {
Object.values(this.controllers).forEach((v) => v.abort());
},
hasPending() {
return Object.values(this.controllers).length > 0;
},
remove(sessionId: string, messageId: string) {
const key = this.key(sessionId, messageId);
delete this.controllers[key];
},
key(sessionId: string, messageIndex: string) {
return `${sessionId},${messageIndex}`;
},
};

View File

@@ -0,0 +1,281 @@
import {
DEFAULT_API_HOST,
DEFAULT_MODELS,
OpenaiPath,
REQUEST_TIMEOUT_MS,
} from "@/app/constant";
import { useAccessStore, useAppConfig, useChatStore } from "@/app/store";
import { ChatOptions, getHeaders, LLMApi, LLMModel, LLMUsage } from "../api";
import Locale from "../../locales";
import {
EventStreamContentType,
fetchEventSource,
} from "@fortaine/fetch-event-source";
import { prettyObject } from "@/app/utils/format";
import { getClientConfig } from "@/app/config/client";
export interface OpenAIListModelResponse {
object: string;
data: Array<{
id: string;
object: string;
root: string;
}>;
}
export class ChatGPTApi implements LLMApi {
private disableListModels = true;
path(path: string): string {
let openaiUrl = useAccessStore.getState().openaiUrl;
const apiPath = "/api/openai";
if (openaiUrl.length === 0) {
const isApp = !!getClientConfig()?.isApp;
openaiUrl = isApp ? DEFAULT_API_HOST : apiPath;
}
if (openaiUrl.endsWith("/")) {
openaiUrl = openaiUrl.slice(0, openaiUrl.length - 1);
}
if (!openaiUrl.startsWith("http") && !openaiUrl.startsWith(apiPath)) {
openaiUrl = "https://" + openaiUrl;
}
return [openaiUrl, path].join("/");
}
extractMessage(res: any) {
return res.choices?.at(0)?.message?.content ?? "";
}
async chat(options: ChatOptions) {
const messages = options.messages.map((v) => ({
role: v.role,
content: v.content,
}));
const modelConfig = {
...useAppConfig.getState().modelConfig,
...useChatStore.getState().currentSession().mask.modelConfig,
...{
model: options.config.model,
},
};
const requestPayload = {
messages,
stream: options.config.stream,
model: modelConfig.model,
temperature: modelConfig.temperature,
presence_penalty: modelConfig.presence_penalty,
frequency_penalty: modelConfig.frequency_penalty,
top_p: modelConfig.top_p,
};
console.log("[Request] openai payload: ", requestPayload);
const shouldStream = !!options.config.stream;
const controller = new AbortController();
options.onController?.(controller);
try {
const chatPath = this.path(OpenaiPath.ChatPath);
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 finished = false;
const finish = () => {
if (!finished) {
options.onFinish(responseText);
finished = true;
}
};
controller.signal.onabort = finish;
fetchEventSource(chatPath, {
...chatPayload,
async onopen(res) {
clearTimeout(requestTimeoutId);
const contentType = res.headers.get("content-type");
console.log(
"[OpenAI] request response content type: ",
contentType,
);
if (contentType?.startsWith("text/plain")) {
responseText = await res.clone().text();
return finish();
}
if (
!res.ok ||
!res.headers
.get("content-type")
?.startsWith(EventStreamContentType) ||
res.status !== 200
) {
const responseTexts = [responseText];
let extraInfo = await res.clone().text();
try {
const resJson = await res.clone().json();
extraInfo = prettyObject(resJson);
} catch {}
if (res.status === 401) {
responseTexts.push(Locale.Error.Unauthorized);
}
if (extraInfo) {
responseTexts.push(extraInfo);
}
responseText = responseTexts.join("\n\n");
return finish();
}
},
onmessage(msg) {
if (msg.data === "[DONE]" || finished) {
return finish();
}
const text = msg.data;
try {
const json = JSON.parse(text);
const delta = json.choices[0].delta.content;
if (delta) {
responseText += delta;
options.onUpdate?.(responseText, delta);
}
} catch (e) {
console.error("[Request] parse error", text, msg);
}
},
onclose() {
finish();
},
onerror(e) {
options.onError?.(e);
throw e;
},
openWhenHidden: true,
});
} else {
const res = await fetch(chatPath, chatPayload);
clearTimeout(requestTimeoutId);
const resJson = await res.json();
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);
}
}
async usage() {
const formatDate = (d: Date) =>
`${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, "0")}-${d
.getDate()
.toString()
.padStart(2, "0")}`;
const ONE_DAY = 1 * 24 * 60 * 60 * 1000;
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const startDate = formatDate(startOfMonth);
const endDate = formatDate(new Date(Date.now() + ONE_DAY));
const [used, subs] = await Promise.all([
fetch(
this.path(
`${OpenaiPath.UsagePath}?start_date=${startDate}&end_date=${endDate}`,
),
{
method: "GET",
headers: getHeaders(),
},
),
fetch(this.path(OpenaiPath.SubsPath), {
method: "GET",
headers: getHeaders(),
}),
]);
if (used.status === 401) {
throw new Error(Locale.Error.Unauthorized);
}
if (!used.ok || !subs.ok) {
throw new Error("Failed to query usage from openai");
}
const response = (await used.json()) as {
total_usage?: number;
error?: {
type: string;
message: string;
};
};
const total = (await subs.json()) as {
hard_limit_usd?: number;
};
if (response.error && response.error.type) {
throw Error(response.error.message);
}
if (response.total_usage) {
response.total_usage = Math.round(response.total_usage) / 100;
}
if (total.hard_limit_usd) {
total.hard_limit_usd = Math.round(total.hard_limit_usd * 100) / 100;
}
return {
used: response.total_usage,
total: total.hard_limit_usd,
} as LLMUsage;
}
async models(): Promise<LLMModel[]> {
if (this.disableListModels) {
return DEFAULT_MODELS.slice();
}
const res = await fetch(this.path(OpenaiPath.ListModelPath), {
method: "GET",
headers: {
...getHeaders(),
},
});
const resJson = (await res.json()) as OpenAIListModelResponse;
const chatModels = resJson.data?.filter((m) => m.id.startsWith("gpt-"));
console.log("[Models]", chatModels);
if (!chatModels) {
return [];
}
return chatModels.map((m) => ({
name: m.id,
available: true,
}));
}
}
export { OpenaiPath };

75
app/command.ts Normal file
View File

@@ -0,0 +1,75 @@
import { useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import Locale from "./locales";
type Command = (param: string) => void;
interface Commands {
fill?: Command;
submit?: Command;
mask?: Command;
code?: Command;
settings?: Command;
}
export function useCommand(commands: Commands = {}) {
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
let shouldUpdate = false;
searchParams.forEach((param, name) => {
const commandName = name as keyof Commands;
if (typeof commands[commandName] === "function") {
commands[commandName]!(param);
searchParams.delete(name);
shouldUpdate = true;
}
});
if (shouldUpdate) {
setSearchParams(searchParams);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchParams, commands]);
}
interface ChatCommands {
new?: Command;
newm?: Command;
next?: Command;
prev?: Command;
clear?: Command;
del?: Command;
}
export const ChatCommandPrefix = ":";
export function useChatCommand(commands: ChatCommands = {}) {
function extract(userInput: string) {
return (
userInput.startsWith(ChatCommandPrefix) ? userInput.slice(1) : userInput
) as keyof ChatCommands;
}
function search(userInput: string) {
const input = extract(userInput);
const desc = Locale.Chat.Commands;
return Object.keys(commands)
.filter((c) => c.startsWith(input))
.map((c) => ({
title: desc[c as keyof ChatCommands],
content: ChatCommandPrefix + c,
}));
}
function match(userInput: string) {
const command = extract(userInput);
const matched = typeof commands[command] === "function";
return {
matched,
invoke: () => matched && commands[command]!(userInput),
};
}
return { match, search };
}

View File

@@ -0,0 +1,36 @@
.auth-page {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
flex-direction: column;
.auth-logo {
transform: scale(1.4);
}
.auth-title {
font-size: 24px;
font-weight: bold;
line-height: 2;
}
.auth-tips {
font-size: 14px;
}
.auth-input {
margin: 3vh 0;
}
.auth-actions {
display: flex;
justify-content: center;
flex-direction: column;
button:not(:last-child) {
margin-bottom: 10px;
}
}
}

77
app/components/auth.tsx Normal file
View File

@@ -0,0 +1,77 @@
import styles from "./auth.module.scss";
import { IconButton } from "./button";
import { useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { useAccessStore } from "../store";
import Locale from "../locales";
import BotIcon from "../icons/bot.svg";
import { useEffect } from "react";
import { getClientConfig } from "../config/client";
export function AuthPage() {
const navigate = useNavigate();
const access = useAccessStore();
const goHome = () => navigate(Path.Home);
const goChat = () => navigate(Path.Chat);
const resetAccessCode = () => { access.updateCode(""); access.updateToken(""); }; // Reset access code to empty string
useEffect(() => {
if (getClientConfig()?.isApp) {
navigate(Path.Settings);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className={styles["auth-page"]}>
<div className={`no-dark ${styles["auth-logo"]}`}>
<BotIcon />
</div>
<div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
<div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Auth.Input}
value={access.accessCode}
onChange={(e) => {
access.updateCode(e.currentTarget.value);
}}
/>
{!access.hideUserApiKey ? (
<>
<div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
<input
className={styles["auth-input"]}
type="password"
placeholder={Locale.Settings.Token.Placeholder}
value={access.token}
onChange={(e) => {
access.updateToken(e.currentTarget.value);
}}
/>
</>
) : null}
<div className={styles["auth-actions"]}>
<IconButton
text={Locale.Auth.Confirm}
type="primary"
onClick={goChat}
/>
<IconButton
text={Locale.Auth.Later}
onClick={() => {
resetAccessCode();
goHome();
}}
/>
</div>
</div>
);
}

View File

@@ -27,6 +27,26 @@
fill: white !important;
}
}
&.danger {
color: rgba($color: red, $alpha: 0.8);
border-color: rgba($color: red, $alpha: 0.5);
background-color: rgba($color: red, $alpha: 0.05);
&:hover {
border-color: red;
background-color: rgba($color: red, $alpha: 0.1);
}
path {
fill: red !important;
}
}
&:hover,
&:focus {
border-color: var(--primary);
}
}
.shadow {
@@ -37,10 +57,6 @@
border: var(--border-in-light);
}
.icon-button:hover {
border-color: var(--primary);
}
.icon-button-icon {
width: 16px;
height: 16px;
@@ -56,9 +72,12 @@
}
.icon-button-text {
margin-left: 5px;
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&:not(:first-child) {
margin-left: 5px;
}
}

View File

@@ -2,16 +2,20 @@ import * as React from "react";
import styles from "./button.module.scss";
export type ButtonType = "primary" | "danger" | null;
export function IconButton(props: {
onClick?: () => void;
icon?: JSX.Element;
type?: "primary" | "danger";
type?: ButtonType;
text?: string;
bordered?: boolean;
shadow?: boolean;
className?: string;
title?: string;
disabled?: boolean;
tabIndex?: number;
autoFocus?: boolean;
}) {
return (
<button
@@ -25,6 +29,8 @@ export function IconButton(props: {
title={props.title}
disabled={props.disabled}
role="button"
tabIndex={props.tabIndex}
autoFocus={props.autoFocus}
>
{props.icon && (
<div

View File

@@ -16,6 +16,8 @@ import { Link, useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { MaskAvatar } from "./mask";
import { Mask } from "../store/mask";
import { useRef, useEffect } from "react";
import { showConfirm } from "./ui-lib";
export function ChatItem(props: {
onClick?: () => void;
@@ -24,11 +26,19 @@ export function ChatItem(props: {
count: number;
time: string;
selected: boolean;
id: number;
id: string;
index: number;
narrow?: boolean;
mask: Mask;
}) {
const draggableRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (props.selected && draggableRef.current) {
draggableRef.current?.scrollIntoView({
block: "center",
});
}
}, [props.selected]);
return (
<Draggable draggableId={`${props.id}`} index={props.index}>
{(provided) => (
@@ -37,7 +47,10 @@ export function ChatItem(props: {
props.selected && styles["chat-item-selected"]
}`}
onClick={props.onClick}
ref={provided.innerRef}
ref={(ele) => {
draggableRef.current = ele;
provided.innerRef(ele);
}}
{...provided.draggableProps}
{...provided.dragHandleProps}
title={`${props.title}\n${Locale.ChatItem.ChatItemCount(
@@ -60,9 +73,7 @@ export function ChatItem(props: {
<div className={styles["chat-item-count"]}>
{Locale.ChatItem.ChatItemCount(props.count)}
</div>
<div className={styles["chat-item-date"]}>
{new Date(props.time).toLocaleString()}
</div>
<div className={styles["chat-item-date"]}>{props.time}</div>
</div>
</>
)}
@@ -129,8 +140,11 @@ export function ChatList(props: { narrow?: boolean }) {
navigate(Path.Chat);
selectSession(i);
}}
onDelete={() => {
if (!props.narrow || confirm(Locale.Home.DeleteChat)) {
onDelete={async () => {
if (
!props.narrow ||
(await showConfirm(Locale.Home.DeleteChat))
) {
chatStore.deleteSession(i);
}
}}

View File

@@ -14,13 +14,43 @@
padding: 4px 10px;
animation: slide-in ease 0.3s;
box-shadow: var(--card-shadow);
transition: all ease 0.3s;
margin-bottom: 10px;
transition: width ease 0.3s;
align-items: center;
height: 16px;
width: var(--icon-width);
overflow: hidden;
&:not(:last-child) {
margin-right: 5px;
}
.text {
white-space: nowrap;
padding-left: 5px;
opacity: 0;
transform: translateX(-5px);
transition: all ease 0.3s;
pointer-events: none;
}
&:hover {
--delay: 0.5s;
width: var(--full-width);
transition-delay: var(--delay);
.text {
transition-delay: var(--delay);
opacity: 1;
transform: translate(0);
}
}
.text,
.icon {
display: flex;
align-items: center;
justify-content: center;
}
}
}
@@ -68,11 +98,41 @@
}
.context-prompt {
.context-prompt-insert {
display: flex;
justify-content: center;
padding: 4px;
opacity: 0.2;
transition: all ease 0.3s;
background-color: rgba(0, 0, 0, 0);
cursor: pointer;
border-radius: 4px;
margin-top: 4px;
margin-bottom: 4px;
&:hover {
opacity: 1;
background-color: rgba(0, 0, 0, 0.05);
}
}
.context-prompt-row {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 10px;
&:hover {
.context-drag {
opacity: 1;
}
}
.context-drag {
display: flex;
align-items: center;
opacity: 0.5;
transition: all ease 0.3s;
}
.context-role {
margin-right: 10px;
@@ -107,3 +167,352 @@
user-select: text;
}
}
.clear-context {
margin: 20px 0 0 0;
padding: 4px 0;
border-top: var(--border-in-light);
border-bottom: var(--border-in-light);
box-shadow: var(--card-shadow) inset;
display: flex;
justify-content: center;
align-items: center;
color: var(--black);
transition: all ease 0.3s;
cursor: pointer;
overflow: hidden;
position: relative;
font-size: 12px;
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)
);
mask-image: $linear;
@mixin show {
transform: translateY(0);
position: relative;
transition: all ease 0.3s;
opacity: 1;
}
@mixin hide {
transform: translateY(-50%);
position: absolute;
transition: all ease 0.1s;
opacity: 0;
}
&-tips {
@include show;
opacity: 0.5;
}
&-revert-btn {
color: var(--primary);
@include hide;
}
&:hover {
opacity: 1;
border-color: var(--primary);
.clear-context-tips {
@include hide;
}
.clear-context-revert-btn {
@include show;
}
}
}
.chat {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
}
.chat-body {
flex: 1;
overflow: auto;
overflow-x: hidden;
padding: 20px;
padding-bottom: 40px;
position: relative;
overscroll-behavior: none;
}
.chat-body-main-title {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
@media only screen and (max-width: 600px) {
.chat-body-title {
text-align: center;
}
}
.chat-message {
display: flex;
flex-direction: row;
&:last-child {
animation: slide-in ease 0.3s;
}
}
.chat-message-user {
display: flex;
flex-direction: row-reverse;
.chat-message-header {
flex-direction: row-reverse;
}
}
.chat-message-header {
margin-top: 20px;
display: flex;
align-items: center;
.chat-message-actions {
display: flex;
box-sizing: border-box;
font-size: 12px;
align-items: flex-end;
justify-content: space-between;
transition: all ease 0.3s;
transform: scale(0.9) translateY(5px);
margin: 0 10px;
opacity: 0;
pointer-events: none;
.chat-input-actions {
display: flex;
flex-wrap: nowrap;
}
}
}
.chat-message-container {
max-width: var(--message-max-width);
display: flex;
flex-direction: column;
align-items: flex-start;
&:hover {
.chat-message-edit {
opacity: 0.9;
}
.chat-message-actions {
opacity: 1;
pointer-events: all;
transform: scale(1) translateY(0);
}
}
}
.chat-message-user > .chat-message-container {
align-items: flex-end;
}
.chat-message-avatar {
position: relative;
.chat-message-edit {
position: absolute;
height: 100%;
width: 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: all ease 0.3s;
button {
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) {
.chat-message-edit {
top: -8%;
}
}
}
}
.chat-message-status {
font-size: 12px;
color: #aaa;
line-height: 1.5;
margin-top: 5px;
}
.chat-message-item {
box-sizing: border-box;
max-width: 100%;
margin-top: 10px;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.05);
padding: 10px;
font-size: 14px;
user-select: text;
word-break: break-word;
border: var(--border-in-light);
position: relative;
transition: all ease 0.3s;
}
.chat-message-action-date {
font-size: 12px;
opacity: 0.2;
white-space: nowrap;
transition: all ease 0.6s;
color: var(--black);
text-align: right;
width: 100%;
box-sizing: border-box;
padding-right: 10px;
pointer-events: none;
z-index: 1;
}
.chat-message-user > .chat-message-container > .chat-message-item {
background-color: var(--second);
&:hover {
min-width: 0;
}
}
.chat-input-panel {
position: relative;
width: 100%;
padding: 20px;
padding-top: 10px;
box-sizing: border-box;
flex-direction: column;
border-top: var(--border-in-light);
box-shadow: var(--card-shadow);
.chat-input-actions {
.chat-input-action {
margin-bottom: 10px;
}
}
}
@mixin single-line {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.prompt-hints {
min-height: 20px;
width: 100%;
max-height: 50vh;
overflow: auto;
display: flex;
flex-direction: column-reverse;
background-color: var(--white);
border: var(--border-in-light);
border-radius: 10px;
margin-bottom: 10px;
box-shadow: var(--shadow);
.prompt-hint {
color: var(--black);
padding: 6px 10px;
animation: slide-in ease 0.3s;
cursor: pointer;
transition: all ease 0.3s;
border: transparent 1px solid;
margin: 4px;
border-radius: 8px;
&:not(:last-child) {
margin-top: 0;
}
.hint-title {
font-size: 12px;
font-weight: bolder;
@include single-line();
}
.hint-content {
font-size: 12px;
@include single-line();
}
&-selected,
&:hover {
border-color: var(--primary);
}
}
}
.chat-input-panel-inner {
display: flex;
flex: 1;
}
.chat-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: var(--border-in-light);
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03);
background-color: var(--white);
color: var(--black);
font-family: inherit;
padding: 10px 90px 10px 14px;
resize: none;
outline: none;
box-sizing: border-box;
min-height: 68px;
}
.chat-input:focus {
border: 1px solid var(--primary);
}
.chat-input-send {
background-color: var(--primary);
color: white;
position: absolute;
right: 30px;
bottom: 32px;
}
@media only screen and (max-width: 600px) {
.chat-input {
font-size: 16px;
}
.chat-input-send {
bottom: 30px;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +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;
@@ -25,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();
@@ -57,10 +55,11 @@ export class ErrorBoundary extends React.Component<any, IErrorBoundaryState> {
<IconButton
icon={<ResetIcon />}
text="Clear All Data"
onClick={() =>
confirm(Locale.Settings.Actions.ConfirmClearAll) &&
this.clearAndSaveData()
}
onClick={async () => {
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
this.clearAndSaveData();
}
}}
bordered
/>
</div>

View File

@@ -0,0 +1,217 @@
.message-exporter {
&-body {
margin-top: 20px;
}
}
.export-content {
white-space: break-spaces;
padding: 10px !important;
}
.steps {
background-color: var(--gray);
border-radius: 10px;
overflow: hidden;
padding: 5px;
position: relative;
box-shadow: var(--card-shadow) inset;
.steps-progress {
$padding: 5px;
height: calc(100% - 2 * $padding);
width: calc(100% - 2 * $padding);
position: absolute;
top: $padding;
left: $padding;
&-inner {
box-sizing: border-box;
box-shadow: var(--card-shadow);
border: var(--border-in-light);
content: "";
display: inline-block;
width: 0%;
height: 100%;
background-color: var(--white);
transition: all ease 0.3s;
border-radius: 8px;
}
}
.steps-inner {
display: flex;
transform: scale(1);
.step {
flex-grow: 1;
padding: 5px 10px;
font-size: 14px;
color: var(--black);
opacity: 0.5;
transition: all ease 0.3s;
display: flex;
align-items: center;
justify-content: center;
$radius: 8px;
&-finished {
opacity: 0.9;
}
&:hover {
opacity: 0.8;
}
&-current {
color: var(--primary);
}
.step-index {
background-color: var(--gray);
border: var(--border-in-light);
border-radius: 6px;
display: inline-block;
padding: 0px 5px;
font-size: 12px;
margin-right: 8px;
opacity: 0.8;
}
.step-name {
font-size: 12px;
}
}
}
}
.preview-actions {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
button {
flex-grow: 1;
&:not(:last-child) {
margin-right: 10px;
}
}
}
.image-previewer {
.preview-body {
border-radius: 10px;
padding: 20px;
box-shadow: var(--card-shadow) inset;
background-color: var(--gray);
.chat-info {
background-color: var(--second);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: flex-end;
position: relative;
overflow: hidden;
@media screen and (max-width: 600px) {
flex-direction: column;
align-items: flex-start;
.icons {
margin-bottom: 20px;
}
}
.logo {
position: absolute;
top: 0px;
left: 0px;
height: 50%;
transform: scale(1.5);
}
.main-title {
font-size: 20px;
font-weight: bolder;
}
.sub-title {
font-size: 12px;
}
.icons {
margin-top: 10px;
display: flex;
align-items: center;
.icon-space {
font-size: 12px;
margin: 0 10px;
font-weight: bolder;
color: var(--primary);
}
}
.chat-info-item {
font-size: 12px;
color: var(--primary);
padding: 2px 15px;
border-radius: 10px;
background-color: var(--white);
box-shadow: var(--card-shadow);
&:not(:last-child) {
margin-bottom: 5px;
}
}
}
.message {
margin-bottom: 20px;
display: flex;
.avatar {
margin-right: 10px;
}
.body {
border-radius: 10px;
padding: 8px 10px;
max-width: calc(100% - 104px);
box-shadow: var(--card-shadow);
border: var(--border-in-light);
*:not(li) {
overflow: hidden;
}
}
&-assistant {
.body {
background-color: var(--white);
}
}
&-user {
flex-direction: row-reverse;
.avatar {
margin-right: 0;
}
.body {
background-color: var(--second);
margin-right: 10px;
}
}
}
}
.default-theme {
}
}

648
app/components/exporter.tsx Normal file
View File

@@ -0,0 +1,648 @@
/* eslint-disable @next/next/no-img-element */
import { ChatMessage, useAppConfig, useChatStore } from "../store";
import Locale from "../locales";
import styles from "./exporter.module.scss";
import {
List,
ListItem,
Modal,
Select,
showImageModal,
showModal,
showToast,
} from "./ui-lib";
import { IconButton } from "./button";
import { copyToClipboard, downloadAs, useMobileScreen } from "../utils";
import CopyIcon from "../icons/copy.svg";
import LoadingIcon from "../icons/three-dots.svg";
import ChatGptIcon from "../icons/chatgpt.png";
import ShareIcon from "../icons/share.svg";
import BotIcon from "../icons/bot.png";
import DownloadIcon from "../icons/download.svg";
import { useEffect, useMemo, useRef, useState } from "react";
import { MessageSelector, useMessageSelector } from "./message-selector";
import { Avatar } from "./emoji";
import dynamic from "next/dynamic";
import NextImage from "next/image";
import { toBlob, toJpeg, 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 { getClientConfig } from "../config/client";
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () => <LoadingIcon />,
});
export function ExportMessageModal(props: { onClose: () => void }) {
return (
<div className="modal-mask">
<Modal title={Locale.Export.Title} onClose={props.onClose}>
<div style={{ minHeight: "40vh" }}>
<MessageExporter />
</div>
</Modal>
</div>
);
}
function useSteps(
steps: Array<{
name: string;
value: string;
}>,
) {
const stepCount = steps.length;
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const nextStep = () =>
setCurrentStepIndex((currentStepIndex + 1) % stepCount);
const prevStep = () =>
setCurrentStepIndex((currentStepIndex - 1 + stepCount) % stepCount);
return {
currentStepIndex,
setCurrentStepIndex,
nextStep,
prevStep,
currentStep: steps[currentStepIndex],
};
}
function Steps<
T extends {
name: string;
value: string;
}[],
>(props: { steps: T; onStepChange?: (index: number) => void; index: number }) {
const steps = props.steps;
const stepCount = steps.length;
return (
<div className={styles["steps"]}>
<div className={styles["steps-progress"]}>
<div
className={styles["steps-progress-inner"]}
style={{
width: `${((props.index + 1) / stepCount) * 100}%`,
}}
></div>
</div>
<div className={styles["steps-inner"]}>
{steps.map((step, i) => {
return (
<div
key={i}
className={`${styles["step"]} ${
styles[i <= props.index ? "step-finished" : ""]
} ${i === props.index && styles["step-current"]} clickable`}
onClick={() => {
props.onStepChange?.(i);
}}
role="button"
>
<span className={styles["step-index"]}>{i + 1}</span>
<span className={styles["step-name"]}>{step.name}</span>
</div>
);
})}
</div>
</div>
);
}
export function MessageExporter() {
const steps = [
{
name: Locale.Export.Steps.Select,
value: "select",
},
{
name: Locale.Export.Steps.Preview,
value: "preview",
},
];
const { currentStep, setCurrentStepIndex, currentStepIndex } =
useSteps(steps);
const formats = ["text", "image", "json"] as const;
type ExportFormat = (typeof formats)[number];
const [exportConfig, setExportConfig] = useState({
format: "image" as ExportFormat,
includeContext: true,
});
function updateExportConfig(updater: (config: typeof exportConfig) => void) {
const config = { ...exportConfig };
updater(config);
setExportConfig(config);
}
const chatStore = useChatStore();
const session = chatStore.currentSession();
const { selection, updateSelection } = useMessageSelector();
const selectedMessages = useMemo(() => {
const ret: ChatMessage[] = [];
if (exportConfig.includeContext) {
ret.push(...session.mask.context);
}
ret.push(...session.messages.filter((m, i) => selection.has(m.id)));
return ret;
}, [
exportConfig.includeContext,
session.messages,
session.mask.context,
selection,
]);
function preview() {
if (exportConfig.format === "text") {
return (
<MarkdownPreviewer messages={selectedMessages} topic={session.topic} />
);
} else if (exportConfig.format === "json") {
return (
<JsonPreviewer messages={selectedMessages} topic={session.topic} />
);
} else {
return (
<ImagePreviewer messages={selectedMessages} topic={session.topic} />
);
}
}
return (
<>
<Steps
steps={steps}
index={currentStepIndex}
onStepChange={setCurrentStepIndex}
/>
<div
className={styles["message-exporter-body"]}
style={currentStep.value !== "select" ? { display: "none" } : {}}
>
<List>
<ListItem
title={Locale.Export.Format.Title}
subTitle={Locale.Export.Format.SubTitle}
>
<Select
value={exportConfig.format}
onChange={(e) =>
updateExportConfig(
(config) =>
(config.format = e.currentTarget.value as ExportFormat),
)
}
>
{formats.map((f) => (
<option key={f} value={f}>
{f}
</option>
))}
</Select>
</ListItem>
<ListItem
title={Locale.Export.IncludeContext.Title}
subTitle={Locale.Export.IncludeContext.SubTitle}
>
<input
type="checkbox"
checked={exportConfig.includeContext}
onChange={(e) => {
updateExportConfig(
(config) => (config.includeContext = e.currentTarget.checked),
);
}}
></input>
</ListItem>
</List>
<MessageSelector
selection={selection}
updateSelection={updateSelection}
defaultSelectAll
/>
</div>
{currentStep.value === "preview" && (
<div className={styles["message-exporter-body"]}>{preview()}</div>
)}
</>
);
}
export function RenderExport(props: {
messages: ChatMessage[];
onRender: (messages: ChatMessage[]) => void;
}) {
const domRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!domRef.current) return;
const dom = domRef.current;
const messages = Array.from(
dom.getElementsByClassName(EXPORT_MESSAGE_CLASS_NAME),
);
if (messages.length !== props.messages.length) {
return;
}
const renderMsgs = messages.map((v, i) => {
const [role, _] = v.id.split(":");
return {
id: i.toString(),
role: role as any,
content: role === "user" ? v.textContent ?? "" : v.innerHTML,
date: "",
};
});
props.onRender(renderMsgs);
});
return (
<div ref={domRef}>
{props.messages.map((m, i) => (
<div
key={i}
id={`${m.role}:${i}`}
className={EXPORT_MESSAGE_CLASS_NAME}
>
<Markdown content={m.content} defaultShow />
</div>
))}
</div>
);
}
export function PreviewActions(props: {
download: () => void;
copy: () => void;
showCopy?: boolean;
messages?: ChatMessage[];
}) {
const [loading, setLoading] = useState(false);
const [shouldExport, setShouldExport] = useState(false);
const onRenderMsgs = (msgs: ChatMessage[]) => {
setShouldExport(false);
api
.share(msgs)
.then((res) => {
if (!res) return;
showModal({
title: Locale.Export.Share,
children: [
<input
type="text"
value={res}
key="input"
style={{
width: "100%",
maxWidth: "unset",
}}
readOnly
onClick={(e) => e.currentTarget.select()}
></input>,
],
actions: [
<IconButton
icon={<CopyIcon />}
text={Locale.Chat.Actions.Copy}
key="copy"
onClick={() => copyToClipboard(res)}
/>,
],
});
setTimeout(() => {
window.open(res, "_blank");
}, 800);
})
.catch((e) => {
console.error("[Share]", e);
showToast(prettyObject(e));
})
.finally(() => setLoading(false));
};
const share = async () => {
if (props.messages?.length) {
setLoading(true);
setShouldExport(true);
}
};
return (
<>
<div className={styles["preview-actions"]}>
{props.showCopy && (
<IconButton
text={Locale.Export.Copy}
bordered
shadow
icon={<CopyIcon />}
onClick={props.copy}
></IconButton>
)}
<IconButton
text={Locale.Export.Download}
bordered
shadow
icon={<DownloadIcon />}
onClick={props.download}
></IconButton>
<IconButton
text={Locale.Export.Share}
bordered
shadow
icon={loading ? <LoadingIcon /> : <ShareIcon />}
onClick={share}
></IconButton>
</div>
<div
style={{
position: "fixed",
right: "200vw",
pointerEvents: "none",
}}
>
{shouldExport && (
<RenderExport
messages={props.messages ?? []}
onRender={onRenderMsgs}
/>
)}
</div>
</>
);
}
function ExportAvatar(props: { avatar: string }) {
if (props.avatar === DEFAULT_MASK_AVATAR) {
return (
<img
src={BotIcon.src}
width={30}
height={30}
alt="bot"
className="user-avatar"
/>
);
}
return <Avatar avatar={props.avatar} />;
}
export function ImagePreviewer(props: {
messages: ChatMessage[];
topic: string;
}) {
const chatStore = useChatStore();
const session = chatStore.currentSession();
const mask = session.mask;
const config = useAppConfig();
const previewRef = useRef<HTMLDivElement>(null);
const copy = () => {
showToast(Locale.Export.Image.Toast);
const dom = previewRef.current;
if (!dom) return;
toBlob(dom).then((blob) => {
if (!blob) return;
try {
navigator.clipboard
.write([
new ClipboardItem({
"image/png": blob,
}),
])
.then(() => {
showToast(Locale.Copy.Success);
refreshPreview();
});
} catch (e) {
console.error("[Copy Image] ", e);
showToast(Locale.Copy.Failed);
}
});
};
const isMobile = useMobileScreen();
const download = async () => {
showToast(Locale.Export.Image.Toast);
const dom = previewRef.current;
if (!dom) return;
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 {
showImageModal(blob);
}
} 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 = () => {
const dom = previewRef.current;
if (dom) {
dom.innerHTML = dom.innerHTML; // Refresh the content of the preview by resetting its HTML for fix a bug glitching
}
};
return (
<div className={styles["image-previewer"]}>
<PreviewActions
copy={copy}
download={download}
showCopy={!isMobile}
messages={props.messages}
/>
<div
className={`${styles["preview-body"]} ${styles["default-theme"]}`}
ref={previewRef}
>
<div className={styles["chat-info"]}>
<div className={styles["logo"] + " no-dark"}>
<NextImage
src={ChatGptIcon.src}
alt="logo"
width={50}
height={50}
/>
</div>
<div>
<div className={styles["main-title"]}>ChatGPT Next Web</div>
<div className={styles["sub-title"]}>
github.com/Yidadaa/ChatGPT-Next-Web
</div>
<div className={styles["icons"]}>
<ExportAvatar avatar={config.avatar} />
<span className={styles["icon-space"]}>&</span>
<ExportAvatar avatar={mask.avatar} />
</div>
</div>
<div>
<div className={styles["chat-info-item"]}>
{Locale.Exporter.Model}: {mask.modelConfig.model}
</div>
<div className={styles["chat-info-item"]}>
{Locale.Exporter.Messages}: {props.messages.length}
</div>
<div className={styles["chat-info-item"]}>
{Locale.Exporter.Topic}: {session.topic}
</div>
<div className={styles["chat-info-item"]}>
{Locale.Exporter.Time}:{" "}
{new Date(
props.messages.at(-1)?.date ?? Date.now(),
).toLocaleString()}
</div>
</div>
</div>
{props.messages.map((m, i) => {
return (
<div
className={styles["message"] + " " + styles["message-" + m.role]}
key={i}
>
<div className={styles["avatar"]}>
<ExportAvatar
avatar={m.role === "user" ? config.avatar : mask.avatar}
/>
</div>
<div className={styles["body"]}>
<Markdown
content={m.content}
fontSize={config.fontSize}
defaultShow
/>
</div>
</div>
);
})}
</div>
</div>
);
}
export function MarkdownPreviewer(props: {
messages: ChatMessage[];
topic: string;
}) {
const mdText =
`# ${props.topic}\n\n` +
props.messages
.map((m) => {
return m.role === "user"
? `## ${Locale.Export.MessageFromYou}:\n${m.content}`
: `## ${Locale.Export.MessageFromChatGPT}:\n${m.content.trim()}`;
})
.join("\n\n");
const copy = () => {
copyToClipboard(mdText);
};
const download = () => {
downloadAs(mdText, `${props.topic}.md`);
};
return (
<>
<PreviewActions
copy={copy}
download={download}
showCopy={true}
messages={props.messages}
/>
<div className="markdown-body">
<pre className={styles["export-content"]}>{mdText}</pre>
</div>
</>
);
}
// modified by BackTrackZ now it's looks better
export function JsonPreviewer(props: {
messages: ChatMessage[];
topic: string;
}) {
const msgs = {
messages: [
{
role: "system",
content: `${Locale.FineTuned.Sysmessage} ${props.topic}`,
},
...props.messages.map((m) => ({
role: m.role,
content: m.content,
})),
],
};
const mdText = "```json\n" + JSON.stringify(msgs, null, 2) + "\n```";
const minifiedJson = JSON.stringify(msgs);
const copy = () => {
copyToClipboard(minifiedJson);
};
const download = () => {
downloadAs(JSON.stringify(msgs), `${props.topic}.json`);
};
return (
<>
<PreviewActions
copy={copy}
download={download}
showCopy={false}
messages={props.messages}
/>
<div className="markdown-body" onClick={copy}>
<Markdown content={mdText} />
</div>
</>
);
}

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;
@@ -61,24 +61,36 @@
}
}
}
&:hover,
&:active {
.sidebar-drag {
background-color: rgba($color: #000000, $alpha: 0.01);
svg {
opacity: 0.2;
}
}
}
}
.sidebar-drag {
$width: 10px;
$width: 14px;
position: absolute;
top: 0;
right: 0;
height: 100%;
width: $width;
background-color: var(--black);
background-color: rgba($color: #000000, $alpha: 0);
cursor: ew-resize;
opacity: 0;
transition: all ease 0.3s;
display: flex;
align-items: center;
&:hover,
&:active {
opacity: 0.2;
svg {
opacity: 0;
margin-left: -2px;
}
}
@@ -141,7 +153,7 @@
.sidebar-sub-title {
font-size: 12px;
font-weight: 400px;
font-weight: 400;
animation: slide-in ease 0.3s;
}
@@ -162,6 +174,7 @@
user-select: none;
border: 2px solid transparent;
position: relative;
content-visibility: auto;
}
.chat-item:hover {
@@ -176,7 +189,7 @@
font-size: 14px;
font-weight: bolder;
display: block;
width: 200px;
width: calc(100% - 15px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -185,8 +198,8 @@
.chat-item-delete {
position: absolute;
top: 10px;
right: -20px;
top: 0;
right: 0;
transition: all ease 0.3s;
opacity: 0;
cursor: pointer;
@@ -194,7 +207,7 @@
.chat-item:hover > .chat-item-delete {
opacity: 0.5;
right: 10px;
transform: translateX(-4px);
}
.chat-item:hover > .chat-item-delete:hover {
@@ -283,15 +296,6 @@
}
}
.chat-item-delete {
top: 15px;
}
.chat-item:hover > .chat-item-delete {
opacity: 0.5;
right: 5px;
}
.sidebar-tail {
flex-direction: column-reverse;
align-items: center;
@@ -322,246 +326,6 @@
margin-right: 15px;
}
.chat {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
}
.chat-body {
flex: 1;
overflow: auto;
padding: 20px;
padding-bottom: 40px;
position: relative;
}
.chat-body-title {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.chat-message {
display: flex;
flex-direction: row;
&:last-child {
animation: slide-in ease 0.3s;
}
}
.chat-message-user {
display: flex;
flex-direction: row-reverse;
}
.chat-message-container {
max-width: var(--message-max-width);
display: flex;
flex-direction: column;
align-items: flex-start;
&:hover {
.chat-message-top-actions {
opacity: 1;
right: 10px;
pointer-events: all;
}
}
}
.chat-message-user > .chat-message-container {
align-items: flex-end;
}
.chat-message-avatar {
margin-top: 20px;
}
.chat-message-status {
font-size: 12px;
color: #aaa;
line-height: 1.5;
margin-top: 5px;
}
.chat-message-item {
box-sizing: border-box;
max-width: 100%;
margin-top: 10px;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.05);
padding: 10px;
font-size: 14px;
user-select: text;
word-break: break-word;
border: var(--border-in-light);
position: relative;
}
.chat-message-top-actions {
font-size: 12px;
position: absolute;
right: 20px;
top: -26px;
left: 100px;
transition: all ease 0.3s;
opacity: 0;
pointer-events: none;
display: flex;
flex-direction: row-reverse;
.chat-message-top-action {
opacity: 0.5;
color: var(--black);
white-space: nowrap;
cursor: pointer;
&:hover {
opacity: 1;
}
&:not(:first-child) {
margin-right: 10px;
}
}
}
.chat-message-user > .chat-message-container > .chat-message-item {
background-color: var(--second);
}
.chat-message-actions {
display: flex;
flex-direction: row-reverse;
width: 100%;
padding-top: 5px;
box-sizing: border-box;
font-size: 12px;
}
.chat-message-action-date {
color: #aaa;
}
.chat-input-panel {
position: relative;
width: 100%;
padding: 20px;
padding-top: 10px;
box-sizing: border-box;
flex-direction: column;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
border-top: var(--border-in-light);
box-shadow: var(--card-shadow);
}
@mixin single-line {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.prompt-hints {
min-height: 20px;
width: 100%;
max-height: 50vh;
overflow: auto;
display: flex;
flex-direction: column-reverse;
background-color: var(--white);
border: var(--border-in-light);
border-radius: 10px;
margin-bottom: 10px;
box-shadow: var(--shadow);
.prompt-hint {
color: var(--black);
padding: 6px 10px;
animation: slide-in ease 0.3s;
cursor: pointer;
transition: all ease 0.3s;
border: transparent 1px solid;
margin: 4px;
border-radius: 8px;
&:not(:last-child) {
margin-top: 0;
}
.hint-title {
font-size: 12px;
font-weight: bolder;
@include single-line();
}
.hint-content {
font-size: 12px;
@include single-line();
}
&-selected,
&:hover {
border-color: var(--primary);
}
}
}
.chat-input-panel-inner {
display: flex;
flex: 1;
}
.chat-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: var(--border-in-light);
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03);
background-color: var(--white);
color: var(--black);
font-family: inherit;
padding: 10px 90px 10px 14px;
resize: none;
outline: none;
}
.chat-input:focus {
border: 1px solid var(--primary);
}
.chat-input-send {
background-color: var(--primary);
color: white;
position: absolute;
right: 30px;
bottom: 32px;
}
@media only screen and (max-width: 600px) {
.chat-input {
font-size: 16px;
}
.chat-input-send {
bottom: 30px;
}
}
.export-content {
white-space: break-spaces;
padding: 10px !important;
}
.loading-content {
display: flex;
flex-direction: column;
@@ -570,3 +334,7 @@
height: 100%;
width: 100%;
}
.rtl-screen {
direction: rtl;
}

View File

@@ -15,6 +15,8 @@ import dynamic from "next/dynamic";
import { Path, SlotID } from "../constant";
import { ErrorBoundary } from "./error";
import { getISOLang, getLang } from "../locales";
import {
HashRouter as Router,
Routes,
@@ -23,6 +25,10 @@ import {
} from "react-router-dom";
import { SideBar } from "./sidebar";
import { useAppConfig } from "../store/config";
import { AuthPage } from "./auth";
import { getClientConfig } from "../config/client";
import { api } from "../client/api";
import { useAccessStore } from "../store";
export function Loading(props: { noLogo?: boolean }) {
return (
@@ -63,23 +69,34 @@ export function useSwitchTheme() {
}
const metaDescriptionDark = document.querySelector(
'meta[name="theme-color"][media]',
'meta[name="theme-color"][media*="dark"]',
);
const metaDescriptionLight = document.querySelector(
'meta[name="theme-color"]:not([media])',
'meta[name="theme-color"][media*="light"]',
);
if (config.theme === "auto") {
metaDescriptionDark?.setAttribute("content", "#151515");
metaDescriptionLight?.setAttribute("content", "#fafafa");
} else {
const themeColor = getCSSVar("--themeColor");
const themeColor = getCSSVar("--theme-color");
metaDescriptionDark?.setAttribute("content", themeColor);
metaDescriptionLight?.setAttribute("content", themeColor);
}
}, [config.theme]);
}
function useHtmlLang() {
useEffect(() => {
const lang = getISOLang();
const htmlLang = document.documentElement.lang;
if (lang !== htmlLang) {
document.documentElement.lang = lang;
}
}, []);
}
const useHasHydrated = () => {
const [hasHydrated, setHasHydrated] = useState<boolean>(false);
@@ -90,40 +107,87 @@ const useHasHydrated = () => {
return hasHydrated;
};
const loadAsyncGoogleFont = () => {
const linkEl = document.createElement("link");
const proxyFontUrl = "/google-fonts";
const remoteFontUrl = "https://fonts.googleapis.com";
const googleFontUrl =
getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl;
linkEl.rel = "stylesheet";
linkEl.href =
googleFontUrl +
"/css2?family=" +
encodeURIComponent("Noto Sans:wght@300;400;700;900") +
"&display=swap";
document.head.appendChild(linkEl);
};
function Screen() {
const config = useAppConfig();
const location = useLocation();
const isHome = location.pathname === Path.Home;
const isAuth = location.pathname === Path.Auth;
const isMobileScreen = useMobileScreen();
const shouldTightBorder =
config.tightBorder && !isMobileScreen && !getClientConfig()?.isApp;
useEffect(() => {
loadAsyncGoogleFont();
}, []);
return (
<div
className={
styles.container +
` ${
config.tightBorder && !isMobileScreen
? styles["tight-container"]
: styles.container
` ${shouldTightBorder ? styles["tight-container"] : styles.container} ${
getLang() === "ar" ? styles["rtl-screen"] : ""
}`
}
>
<SideBar className={isHome ? styles["sidebar-show"] : ""} />
{isAuth ? (
<>
<AuthPage />
</>
) : (
<>
<SideBar className={isHome ? styles["sidebar-show"] : ""} />
<div className={styles["window-content"]} id={SlotID.AppBody}>
<Routes>
<Route path={Path.Home} element={<Chat />} />
<Route path={Path.NewChat} element={<NewChat />} />
<Route path={Path.Masks} element={<MaskPage />} />
<Route path={Path.Chat} element={<Chat />} />
<Route path={Path.Settings} element={<Settings />} />
</Routes>
</div>
<div className={styles["window-content"]} id={SlotID.AppBody}>
<Routes>
<Route path={Path.Home} element={<Chat />} />
<Route path={Path.NewChat} element={<NewChat />} />
<Route path={Path.Masks} element={<MaskPage />} />
<Route path={Path.Chat} element={<Chat />} />
<Route path={Path.Settings} element={<Settings />} />
</Routes>
</div>
</>
)}
</div>
);
}
export function useLoadData() {
const config = useAppConfig();
useEffect(() => {
(async () => {
const models = await api.llm.models();
config.mergeModels(models);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
}
export function Home() {
useSwitchTheme();
useLoadData();
useHtmlLang();
useEffect(() => {
console.log("[Config] got config from build time", getClientConfig());
useAccessStore.getState().fetch();
}, []);
if (!useHasHydrated()) {
return <Loading />;

View File

@@ -1,7 +1,13 @@
.input-range {
border: var(--border-in-light);
border-radius: 10px;
padding: 5px 15px 5px 10px;
padding: 5px 10px 5px 10px;
font-size: 12px;
display: flex;
justify-content: space-between;
max-width: 40%;
input[type="range"] {
max-width: calc(100% - 34px);
}
}

View File

@@ -7,26 +7,95 @@ import RemarkGfm from "remark-gfm";
import RehypeHighlight from "rehype-highlight";
import { useRef, useState, RefObject, useEffect } 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 { showImageModal } from "./ui-lib";
export function Mermaid(props: { code: string }) {
const ref = useRef<HTMLDivElement>(null);
const [hasError, setHasError] = useState(false);
useEffect(() => {
if (props.code && ref.current) {
mermaid
.run({
nodes: [ref.current],
suppressErrors: true,
})
.catch((e) => {
setHasError(true);
console.error("[Mermaid] ", e.message);
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.code]);
function viewSvgInNewWindow() {
const svg = ref.current?.querySelector("svg");
if (!svg) return;
const text = new XMLSerializer().serializeToString(svg);
const blob = new Blob([text], { type: "image/svg+xml" });
showImageModal(URL.createObjectURL(blob));
}
if (hasError) {
return null;
}
return (
<div
className="no-dark mermaid"
style={{
cursor: "pointer",
overflow: "auto",
}}
ref={ref}
onClick={() => viewSvgInNewWindow()}
>
{props.code}
</div>
);
}
export function PreCode(props: { children: any }) {
const ref = useRef<HTMLPreElement>(null);
const refText = ref.current?.innerText;
const [mermaidCode, setMermaidCode] = useState("");
const renderMermaid = useDebouncedCallback(() => {
if (!ref.current) return;
const mermaidDom = ref.current.querySelector("code.language-mermaid");
if (mermaidDom) {
setMermaidCode((mermaidDom as HTMLElement).innerText);
}
}, 600);
useEffect(() => {
setTimeout(renderMermaid, 1);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refText]);
return (
<pre ref={ref}>
<span
className="copy-code-button"
onClick={() => {
if (ref.current) {
const code = ref.current.innerText;
copyToClipboard(code);
}
}}
></span>
{props.children}
</pre>
<>
{mermaidCode.length > 0 && (
<Mermaid code={mermaidCode} key={mermaidCode} />
)}
<pre ref={ref}>
<span
className="copy-code-button"
onClick={() => {
if (ref.current) {
const code = ref.current.innerText;
copyToClipboard(code);
}
}}
></span>
{props.children}
</pre>
</>
);
}
@@ -46,6 +115,7 @@ function _MarkDownContent(props: { content: string }) {
]}
components={{
pre: PreCode,
p: (pProps) => <p {...pProps} dir="auto" />,
a: (aProps) => {
const href = aProps.href || "";
const isInternal = /^\/#/i.test(href);
@@ -66,58 +136,28 @@ export function Markdown(
content: string;
loading?: boolean;
fontSize?: number;
parentRef: RefObject<HTMLDivElement>;
parentRef?: RefObject<HTMLDivElement>;
defaultShow?: boolean;
} & React.DOMAttributes<HTMLDivElement>,
) {
const mdRef = useRef<HTMLDivElement>(null);
const renderedHeight = useRef(0);
const inView = useRef(!!props.defaultShow);
const parent = props.parentRef.current;
const md = mdRef.current;
const checkInView = () => {
if (parent && md) {
const parentBounds = parent.getBoundingClientRect();
const twoScreenHeight = Math.max(500, parentBounds.height * 2);
const mdBounds = md.getBoundingClientRect();
const isInRange = (x: number) =>
x <= parentBounds.bottom + twoScreenHeight &&
x >= parentBounds.top - twoScreenHeight;
inView.current = isInRange(mdBounds.top) || isInRange(mdBounds.bottom);
}
if (inView.current && md) {
renderedHeight.current = Math.max(
renderedHeight.current,
md.getBoundingClientRect().height,
);
}
};
checkInView();
return (
<div
className="markdown-body"
style={{
fontSize: `${props.fontSize ?? 14}px`,
height:
!inView.current && renderedHeight.current > 0
? renderedHeight.current
: "auto",
}}
ref={mdRef}
onContextMenu={props.onContextMenu}
onDoubleClickCapture={props.onDoubleClickCapture}
dir="auto"
>
{inView.current &&
(props.loading ? (
<LoadingIcon />
) : (
<MarkdownContent content={props.content} />
))}
{props.loading ? (
<LoadingIcon />
) : (
<MarkdownContent content={props.content} />
)}
</div>
);
}

View File

@@ -11,21 +11,52 @@ import CloseIcon from "../icons/close.svg";
import DeleteIcon from "../icons/delete.svg";
import EyeIcon from "../icons/eye.svg";
import CopyIcon from "../icons/copy.svg";
import DragIcon from "../icons/drag.svg";
import { DEFAULT_MASK_AVATAR, Mask, useMaskStore } from "../store/mask";
import { Message, ModelConfig, ROLES, useChatStore } from "../store";
import { Input, List, ListItem, Modal, Popover, showToast } from "./ui-lib";
import {
ChatMessage,
createMessage,
ModelConfig,
useAppConfig,
useChatStore,
} from "../store";
import { ROLES } from "../client/api";
import {
Input,
List,
ListItem,
Modal,
Popover,
Select,
showConfirm,
} from "./ui-lib";
import { Avatar, AvatarPicker } from "./emoji";
import Locale, { AllLangs, Lang } from "../locales";
import Locale, { AllLangs, ALL_LANG_OPTIONS, Lang } from "../locales";
import { useNavigate } from "react-router-dom";
import chatStyle from "./chat.module.scss";
import { useEffect, useState } from "react";
import { downloadAs } from "../utils";
import { Updater } from "../api/openai/typing";
import { copyToClipboard, downloadAs, readFromFile } from "../utils";
import { Updater } from "../typing";
import { ModelConfigList } from "./model-config";
import { FileName, Path } from "../constant";
import { BUILTIN_MASK_STORE } from "../masks";
import { nanoid } from "nanoid";
import {
DragDropContext,
Droppable,
Draggable,
OnDragEndResponder,
} from "@hello-pangea/dnd";
// drag and drop helper function
function reorder<T>(list: T[], startIndex: number, endIndex: number): T[] {
const result = [...list];
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
}
export function MaskAvatar(props: { mask: Mask }) {
return props.mask.avatar !== DEFAULT_MASK_AVATAR ? (
@@ -40,6 +71,7 @@ export function MaskConfig(props: {
updateMask: Updater<Mask>;
extraListItems?: JSX.Element;
readonly?: boolean;
shouldSyncFromGlobal?: boolean;
}) {
const [showPicker, setShowPicker] = useState(false);
@@ -48,9 +80,20 @@ export function MaskConfig(props: {
const config = { ...props.mask.modelConfig };
updater(config);
props.updateMask((mask) => (mask.modelConfig = config));
props.updateMask((mask) => {
mask.modelConfig = config;
// if user changed current session mask, it will disable auto sync
mask.syncGlobalConfig = false;
});
};
const copyMaskLink = () => {
const maskLink = `${location.protocol}//${location.host}/#${Path.NewChat}?mask=${props.mask.id}`;
copyToClipboard(maskLink);
};
const globalConfig = useAppConfig();
return (
<>
<ContextPrompts
@@ -89,10 +132,67 @@ export function MaskConfig(props: {
type="text"
value={props.mask.name}
onInput={(e) =>
props.updateMask((mask) => (mask.name = e.currentTarget.value))
props.updateMask((mask) => {
mask.name = e.currentTarget.value;
})
}
></input>
</ListItem>
<ListItem
title={Locale.Mask.Config.HideContext.Title}
subTitle={Locale.Mask.Config.HideContext.SubTitle}
>
<input
type="checkbox"
checked={props.mask.hideContext}
onChange={(e) => {
props.updateMask((mask) => {
mask.hideContext = e.currentTarget.checked;
});
}}
></input>
</ListItem>
{!props.shouldSyncFromGlobal ? (
<ListItem
title={Locale.Mask.Config.Share.Title}
subTitle={Locale.Mask.Config.Share.SubTitle}
>
<IconButton
icon={<CopyIcon />}
text={Locale.Mask.Config.Share.Action}
onClick={copyMaskLink}
/>
</ListItem>
) : null}
{props.shouldSyncFromGlobal ? (
<ListItem
title={Locale.Mask.Config.Sync.Title}
subTitle={Locale.Mask.Config.Sync.SubTitle}
>
<input
type="checkbox"
checked={props.mask.syncGlobalConfig}
onChange={async (e) => {
const checked = e.currentTarget.checked;
if (
checked &&
(await showConfirm(Locale.Mask.Config.Sync.Confirm))
) {
props.updateMask((mask) => {
mask.syncGlobalConfig = checked;
mask.modelConfig = { ...globalConfig.modelConfig };
});
} else if (!checked) {
props.updateMask((mask) => {
mask.syncGlobalConfig = checked;
});
}
}}
></input>
</ListItem>
) : null}
</List>
<List>
@@ -106,81 +206,172 @@ export function MaskConfig(props: {
);
}
function ContextPromptItem(props: {
index: number;
prompt: ChatMessage;
update: (prompt: ChatMessage) => void;
remove: () => void;
}) {
const [focusingInput, setFocusingInput] = useState(false);
return (
<div className={chatStyle["context-prompt-row"]}>
{!focusingInput && (
<>
<div className={chatStyle["context-drag"]}>
<DragIcon />
</div>
<Select
value={props.prompt.role}
className={chatStyle["context-role"]}
onChange={(e) =>
props.update({
...props.prompt,
role: e.target.value as any,
})
}
>
{ROLES.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</Select>
</>
)}
<Input
value={props.prompt.content}
type="text"
className={chatStyle["context-content"]}
rows={focusingInput ? 5 : 1}
onFocus={() => setFocusingInput(true)}
onBlur={() => {
setFocusingInput(false);
// If the selection is not removed when the user loses focus, some
// extensions like "Translate" will always display a floating bar
window?.getSelection()?.removeAllRanges();
}}
onInput={(e) =>
props.update({
...props.prompt,
content: e.currentTarget.value as any,
})
}
/>
{!focusingInput && (
<IconButton
icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]}
onClick={() => props.remove()}
bordered
/>
)}
</div>
);
}
export function ContextPrompts(props: {
context: Message[];
updateContext: (updater: (context: Message[]) => void) => void;
context: ChatMessage[];
updateContext: (updater: (context: ChatMessage[]) => void) => void;
}) {
const context = props.context;
const addContextPrompt = (prompt: Message) => {
props.updateContext((context) => context.push(prompt));
const addContextPrompt = (prompt: ChatMessage, i: number) => {
props.updateContext((context) => context.splice(i, 0, prompt));
};
const removeContextPrompt = (i: number) => {
props.updateContext((context) => context.splice(i, 1));
};
const updateContextPrompt = (i: number, prompt: Message) => {
const updateContextPrompt = (i: number, prompt: ChatMessage) => {
props.updateContext((context) => (context[i] = prompt));
};
const onDragEnd: OnDragEndResponder = (result) => {
if (!result.destination) {
return;
}
const newContext = reorder(
context,
result.source.index,
result.destination.index,
);
props.updateContext((context) => {
context.splice(0, context.length, ...newContext);
});
};
return (
<>
<div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}>
{context.map((c, i) => (
<div className={chatStyle["context-prompt-row"]} key={i}>
<select
value={c.role}
className={chatStyle["context-role"]}
onChange={(e) =>
updateContextPrompt(i, {
...c,
role: e.target.value as any,
})
}
>
{ROLES.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</select>
<Input
value={c.content}
type="text"
className={chatStyle["context-content"]}
rows={1}
onInput={(e) =>
updateContextPrompt(i, {
...c,
content: e.currentTarget.value as any,
})
}
/>
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId="context-prompt-list">
{(provided) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
{context.map((c, i) => (
<Draggable
draggableId={c.id || i.toString()}
index={i}
key={c.id}
>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<ContextPromptItem
index={i}
prompt={c}
update={(prompt) => updateContextPrompt(i, prompt)}
remove={() => removeContextPrompt(i)}
/>
<div
className={chatStyle["context-prompt-insert"]}
onClick={() => {
addContextPrompt(
createMessage({
role: "user",
content: "",
date: new Date().toLocaleString(),
}),
i + 1,
);
}}
>
<AddIcon />
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
{props.context.length === 0 && (
<div className={chatStyle["context-prompt-row"]}>
<IconButton
icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]}
onClick={() => removeContextPrompt(i)}
icon={<AddIcon />}
text={Locale.Context.Add}
bordered
className={chatStyle["context-prompt-button"]}
onClick={() =>
addContextPrompt(
createMessage({
role: "user",
content: "",
date: "",
}),
props.context.length,
)
}
/>
</div>
))}
<div className={chatStyle["context-prompt-row"]}>
<IconButton
icon={<AddIcon />}
text={Locale.Context.Add}
bordered
className={chatStyle["context-prompt-button"]}
onClick={() =>
addContextPrompt({
role: "system",
content: "",
date: "",
})
}
/>
</div>
)}
</div>
</>
);
@@ -213,13 +404,33 @@ export function MaskPage() {
}
};
const [editingMaskId, setEditingMaskId] = useState<number | undefined>();
const [editingMaskId, setEditingMaskId] = useState<string | undefined>();
const editingMask =
maskStore.get(editingMaskId) ?? BUILTIN_MASK_STORE.get(editingMaskId);
const closeMaskModal = () => setEditingMaskId(undefined);
const downloadAll = () => {
downloadAs(JSON.stringify(masks), FileName.Masks);
downloadAs(JSON.stringify(masks.filter((v) => !v.builtin)), FileName.Masks);
};
const importFromFile = () => {
readFromFile().then((content) => {
try {
const importMasks = JSON.parse(content);
if (Array.isArray(importMasks)) {
for (const mask of importMasks) {
if (mask.name) {
maskStore.create(mask);
}
}
return;
}
//if the content is a single mask.
if (importMasks.name) {
maskStore.create(importMasks);
}
} catch {}
});
};
return (
@@ -241,13 +452,15 @@ 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={() => showToast(Locale.WIP)}
onClick={() => importFromFile()}
/>
</div>
<div className="window-action-button">
@@ -269,7 +482,7 @@ export function MaskPage() {
autoFocus
onInput={(e) => onSearch(e.currentTarget.value)}
/>
<select
<Select
className={styles["mask-filter-lang"]}
value={filterLang ?? Locale.Settings.Lang.All}
onChange={(e) => {
@@ -286,10 +499,10 @@ export function MaskPage() {
</option>
{AllLangs.map((lang) => (
<option value={lang} key={lang}>
{Locale.Settings.Lang.Options[lang]}
{ALL_LANG_OPTIONS[lang]}
</option>
))}
</select>
</Select>
<IconButton
className={styles["mask-create"]}
@@ -314,7 +527,7 @@ export function MaskPage() {
<div className={styles["mask-name"]}>{m.name}</div>
<div className={styles["mask-info"] + " one-line"}>
{`${Locale.Mask.Item.Info(m.context.length)} / ${
Locale.Settings.Lang.Options[m.lang]
ALL_LANG_OPTIONS[m.lang]
} / ${m.modelConfig.model}`}
</div>
</div>
@@ -345,8 +558,8 @@ export function MaskPage() {
<IconButton
icon={<DeleteIcon />}
text={Locale.Mask.Item.Delete}
onClick={() => {
if (confirm(Locale.Mask.Item.DeleteConfirm)) {
onClick={async () => {
if (await showConfirm(Locale.Mask.Item.DeleteConfirm)) {
maskStore.delete(m.id);
}
}}
@@ -371,7 +584,10 @@ export function MaskPage() {
key="export"
bordered
onClick={() =>
downloadAs(JSON.stringify(editingMask), "mask.json")
downloadAs(
JSON.stringify(editingMask),
`${editingMask.name}.json`,
)
}
/>,
<IconButton
@@ -390,7 +606,7 @@ export function MaskPage() {
<MaskConfig
mask={editingMask}
updateMask={(updater) =>
maskStore.update(editingMaskId!, updater)
maskStore.updateMask(editingMaskId!, updater)
}
readonly={editingMask.builtin}
/>

View File

@@ -0,0 +1,76 @@
.message-selector {
.message-filter {
display: flex;
.search-bar {
max-width: unset;
flex-grow: 1;
margin-right: 10px;
}
.actions {
display: flex;
button:not(:last-child) {
margin-right: 10px;
}
}
@media screen and (max-width: 600px) {
flex-direction: column;
.search-bar {
margin-right: 0;
}
.actions {
margin-top: 20px;
button {
flex-grow: 1;
}
}
}
}
.messages {
margin-top: 20px;
border-radius: 10px;
border: var(--border-in-light);
overflow: hidden;
.message {
display: flex;
align-items: center;
padding: 8px 10px;
cursor: pointer;
&-selected {
background-color: var(--second);
}
&:not(:last-child) {
border-bottom: var(--border-in-light);
}
.avatar {
margin-right: 10px;
}
.body {
flex-grow: 1;
max-width: calc(100% - 40px);
.date {
font-size: 12px;
line-height: 1.2;
opacity: 0.5;
}
.content {
font-size: 12px;
}
}
}
}
}

View File

@@ -0,0 +1,215 @@
import { useEffect, useState } from "react";
import { ChatMessage, useAppConfig, useChatStore } from "../store";
import { Updater } from "../typing";
import { IconButton } from "./button";
import { Avatar } from "./emoji";
import { MaskAvatar } from "./mask";
import Locale from "../locales";
import styles from "./message-selector.module.scss";
function useShiftRange() {
const [startIndex, setStartIndex] = useState<number>();
const [endIndex, setEndIndex] = useState<number>();
const [shiftDown, setShiftDown] = useState(false);
const onClickIndex = (index: number) => {
if (shiftDown && startIndex !== undefined) {
setEndIndex(index);
} else {
setStartIndex(index);
setEndIndex(undefined);
}
};
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.key !== "Shift") return;
setShiftDown(true);
};
const onKeyUp = (e: KeyboardEvent) => {
if (e.key !== "Shift") return;
setShiftDown(false);
setStartIndex(undefined);
setEndIndex(undefined);
};
window.addEventListener("keyup", onKeyUp);
window.addEventListener("keydown", onKeyDown);
return () => {
window.removeEventListener("keyup", onKeyUp);
window.removeEventListener("keydown", onKeyDown);
};
}, []);
return {
onClickIndex,
startIndex,
endIndex,
};
}
export function useMessageSelector() {
const [selection, setSelection] = useState(new Set<string>());
const updateSelection: Updater<Set<string>> = (updater) => {
const newSelection = new Set<string>(selection);
updater(newSelection);
setSelection(newSelection);
};
return {
selection,
updateSelection,
};
}
export function MessageSelector(props: {
selection: Set<string>;
updateSelection: Updater<Set<string>>;
defaultSelectAll?: boolean;
onSelected?: (messages: ChatMessage[]) => void;
}) {
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 messageCount = messages.length;
const config = useAppConfig();
const [searchInput, setSearchInput] = useState("");
const [searchIds, setSearchIds] = useState(new Set<string>());
const isInSearchResult = (id: string) => {
return searchInput.length === 0 || searchIds.has(id);
};
const doSearch = (text: string) => {
const searchResults = new Set<string>();
if (text.length > 0) {
messages.forEach((m) =>
m.content.includes(text) ? searchResults.add(m.id!) : null,
);
}
setSearchIds(searchResults);
};
// for range selection
const { startIndex, endIndex, onClickIndex } = useShiftRange();
const selectAll = () => {
props.updateSelection((selection) =>
messages.forEach((m) => selection.add(m.id!)),
);
};
useEffect(() => {
if (props.defaultSelectAll) {
selectAll();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (startIndex === undefined || endIndex === undefined) {
return;
}
const [start, end] = [startIndex, endIndex].sort((a, b) => a - b);
props.updateSelection((selection) => {
for (let i = start; i <= end; i += 1) {
selection.add(messages[i].id ?? i);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [startIndex, endIndex]);
const LATEST_COUNT = 4;
return (
<div className={styles["message-selector"]}>
<div className={styles["message-filter"]}>
<input
type="text"
placeholder={Locale.Select.Search}
className={styles["filter-item"] + " " + styles["search-bar"]}
value={searchInput}
onInput={(e) => {
setSearchInput(e.currentTarget.value);
doSearch(e.currentTarget.value);
}}
></input>
<div className={styles["actions"]}>
<IconButton
text={Locale.Select.All}
bordered
className={styles["filter-item"]}
onClick={selectAll}
/>
<IconButton
text={Locale.Select.Latest}
bordered
className={styles["filter-item"]}
onClick={() =>
props.updateSelection((selection) => {
selection.clear();
messages
.slice(messageCount - LATEST_COUNT)
.forEach((m) => selection.add(m.id!));
})
}
/>
<IconButton
text={Locale.Select.Clear}
bordered
className={styles["filter-item"]}
onClick={() =>
props.updateSelection((selection) => selection.clear())
}
/>
</div>
</div>
<div className={styles["messages"]}>
{messages.map((m, i) => {
if (!isInSearchResult(m.id!)) return null;
return (
<div
className={`${styles["message"]} ${
props.selection.has(m.id!) && styles["message-selected"]
}`}
key={i}
onClick={() => {
props.updateSelection((selection) => {
const id = m.id ?? i;
selection.has(id) ? selection.delete(id) : selection.add(id);
});
onClickIndex(i);
}}
>
<div className={styles["avatar"]}>
{m.role === "user" ? (
<Avatar avatar={config.avatar}></Avatar>
) : (
<MaskAvatar mask={session.mask} />
)}
</div>
<div className={styles["body"]}>
<div className={styles["date"]}>
{new Date(m.date).toLocaleString()}
</div>
<div className={`${styles["content"]} one-line`}>
{m.content}
</div>
</div>
</div>
);
})}
</div>
</div>
);
}

View File

@@ -1,18 +1,19 @@
import styles from "./settings.module.scss";
import { ALL_MODELS, ModalConfigValidator, ModelConfig } from "../store";
import { ModalConfigValidator, ModelConfig, useAppConfig } from "../store";
import Locale from "../locales";
import { InputRange } from "./input-range";
import { List, ListItem } from "./ui-lib";
import { ListItem, Select } from "./ui-lib";
export function ModelConfigList(props: {
modelConfig: ModelConfig;
updateConfig: (updater: (config: ModelConfig) => void) => void;
}) {
const config = useAppConfig();
return (
<>
<ListItem title={Locale.Settings.Model}>
<select
<Select
value={props.modelConfig.model}
onChange={(e) => {
props.updateConfig(
@@ -23,12 +24,12 @@ export function ModelConfigList(props: {
);
}}
>
{ALL_MODELS.map((v) => (
<option value={v.name} key={v.name} disabled={!v.available}>
{config.allModels().map((v, i) => (
<option value={v.name} key={i} disabled={!v.available}>
{v.name}
</option>
))}
</select>
</Select>
</ListItem>
<ListItem
title={Locale.Settings.Temperature.Title}
@@ -49,6 +50,25 @@ export function ModelConfigList(props: {
}}
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.TopP.Title}
subTitle={Locale.Settings.TopP.SubTitle}
>
<InputRange
value={(props.modelConfig.top_p ?? 1).toFixed(1)}
min="0"
max="1"
step="0.1"
onChange={(e) => {
props.updateConfig(
(config) =>
(config.top_p = ModalConfigValidator.top_p(
e.currentTarget.valueAsNumber,
)),
);
}}
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.MaxTokens.Title}
subTitle={Locale.Settings.MaxTokens.SubTitle}
@@ -56,7 +76,7 @@ export function ModelConfigList(props: {
<input
type="number"
min={100}
max={32000}
max={100000}
value={props.modelConfig.max_tokens}
onChange={(e) =>
props.updateConfig(
@@ -69,8 +89,8 @@ export function ModelConfigList(props: {
></input>
</ListItem>
<ListItem
title={Locale.Settings.PresencePenlty.Title}
subTitle={Locale.Settings.PresencePenlty.SubTitle}
title={Locale.Settings.PresencePenalty.Title}
subTitle={Locale.Settings.PresencePenalty.SubTitle}
>
<InputRange
value={props.modelConfig.presence_penalty?.toFixed(1)}
@@ -89,6 +109,58 @@ export function ModelConfigList(props: {
></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>
<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}
@@ -97,7 +169,7 @@ export function ModelConfigList(props: {
title={props.modelConfig.historyMessageCount.toString()}
value={props.modelConfig.historyMessageCount}
min="0"
max="32"
max="64"
step="1"
onChange={(e) =>
props.updateConfig(

View File

@@ -54,13 +54,13 @@
.actions {
margin-top: 5vh;
margin-bottom: 5vh;
margin-bottom: 2vh;
animation: slide-in ease 0.45s;
display: flex;
justify-content: center;
font-size: 12px;
.more {
font-size: 12px;
.skip {
margin-left: 10px;
}
}
@@ -68,16 +68,26 @@
.masks {
flex-grow: 1;
width: 100%;
overflow: hidden;
overflow: auto;
align-items: center;
padding-top: 20px;
$linear: linear-gradient(
to bottom,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 1),
rgba(0, 0, 0, 0)
);
-webkit-mask-image: $linear;
mask-image: $linear;
animation: slide-in ease 0.5s;
.mask-row {
margin-bottom: 10px;
display: flex;
justify-content: center;
// justify-content: center;
margin-bottom: 10px;
@for $i from 1 to 10 {
&:nth-child(#{$i * 2}) {

View File

@@ -13,6 +13,9 @@ import { Mask, useMaskStore } from "../store/mask";
import Locale from "../locales";
import { useAppConfig, useChatStore } from "../store";
import { MaskAvatar } from "./mask";
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);
@@ -26,32 +29,8 @@ function getIntersectionArea(aRect: DOMRect, bRect: DOMRect) {
}
function MaskItem(props: { mask: Mask; onClick?: () => void }) {
const domRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const changeOpacity = () => {
const dom = domRef.current;
const parent = document.getElementById(SlotID.AppBody);
if (!parent || !dom) return;
const domRect = dom.getBoundingClientRect();
const parentRect = parent.getBoundingClientRect();
const intersectionArea = getIntersectionArea(domRect, parentRect);
const domArea = domRect.width * domRect.height;
const ratio = intersectionArea / domArea;
const opacity = ratio > 0.9 ? 1 : 0.4;
dom.style.opacity = opacity.toString();
};
setTimeout(changeOpacity, 30);
window.addEventListener("resize", changeOpacity);
return () => window.removeEventListener("resize", changeOpacity);
}, [domRef]);
return (
<div className={styles["mask"]} ref={domRef} onClick={props.onClick}>
<div className={styles["mask"]} onClick={props.onClick}>
<MaskAvatar mask={props.mask} />
<div className={styles["mask-name"] + " one-line"}>{props.mask.name}</div>
</div>
@@ -62,32 +41,38 @@ function useMaskGroup(masks: Mask[]) {
const [groups, setGroups] = useState<Mask[][]>([]);
useEffect(() => {
const appBody = document.getElementById(SlotID.AppBody);
if (!appBody || masks.length === 0) return;
const computeGroup = () => {
const appBody = document.getElementById(SlotID.AppBody);
if (!appBody || masks.length === 0) return;
const rect = appBody.getBoundingClientRect();
const maxWidth = rect.width;
const maxHeight = rect.height * 0.6;
const maskItemWidth = 120;
const maskItemHeight = 50;
const rect = appBody.getBoundingClientRect();
const maxWidth = rect.width;
const maxHeight = rect.height * 0.6;
const maskItemWidth = 120;
const maskItemHeight = 50;
const randomMask = () => masks[Math.floor(Math.random() * masks.length)];
let maskIndex = 0;
const nextMask = () => masks[maskIndex++ % masks.length];
const randomMask = () => masks[Math.floor(Math.random() * masks.length)];
let maskIndex = 0;
const nextMask = () => masks[maskIndex++ % masks.length];
const rows = Math.ceil(maxHeight / maskItemHeight);
const cols = Math.ceil(maxWidth / maskItemWidth);
const rows = Math.ceil(maxHeight / maskItemHeight);
const cols = Math.ceil(maxWidth / maskItemWidth);
const newGroups = new Array(rows)
.fill(0)
.map((_, _i) =>
new Array(cols)
.fill(0)
.map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())),
);
const newGroups = new Array(rows)
.fill(0)
.map((_, _i) =>
new Array(cols)
.fill(0)
.map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())),
);
setGroups(newGroups);
setGroups(newGroups);
};
computeGroup();
window.addEventListener("resize", computeGroup);
return () => window.removeEventListener("resize", computeGroup);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -104,13 +89,35 @@ export function NewChat() {
const navigate = useNavigate();
const config = useAppConfig();
const maskRef = useRef<HTMLDivElement>(null);
const { state } = useLocation();
const startChat = (mask?: Mask) => {
chatStore.newSession(mask);
navigate(Path.Chat);
setTimeout(() => {
chatStore.newSession(mask);
navigate(Path.Chat);
}, 10);
};
useCommand({
mask: (id) => {
try {
const mask = maskStore.get(id) ?? BUILTIN_MASK_STORE.get(id);
startChat(mask ?? undefined);
} catch {
console.error("[New Chat] failed to create chat from mask id=", id);
}
},
});
useEffect(() => {
if (maskRef.current) {
maskRef.current.scrollLeft =
(maskRef.current.scrollWidth - maskRef.current.clientWidth) / 2;
}
}, [groups]);
return (
<div className={styles["new-chat"]}>
<div className={styles["mask-header"]}>
@@ -122,8 +129,8 @@ export function NewChat() {
{!state?.fromHome && (
<IconButton
text={Locale.NewChat.NotShow}
onClick={() => {
if (confirm(Locale.NewChat.ConfirmNoShow)) {
onClick={async () => {
if (await showConfirm(Locale.NewChat.ConfirmNoShow)) {
startChat();
config.update(
(config) => (config.dontShowMaskSplashScreen = true),
@@ -150,24 +157,24 @@ export function NewChat() {
<div className={styles["actions"]}>
<IconButton
text={Locale.NewChat.Skip}
onClick={() => startChat()}
icon={<LightningIcon />}
type="primary"
shadow
/>
<IconButton
className={styles["more"]}
text={Locale.NewChat.More}
onClick={() => navigate(Path.Masks)}
icon={<EyeIcon />}
bordered
shadow
/>
<IconButton
text={Locale.NewChat.Skip}
onClick={() => startChat()}
icon={<LightningIcon />}
type="primary"
shadow
className={styles["skip"]}
/>
</div>
<div className={styles["masks"]}>
<div className={styles["masks"]} ref={maskRef}>
{groups.map((masks, i) => (
<div key={i} className={styles["mask-row"]}>
{masks.map((mask, index) => (

View File

@@ -60,18 +60,13 @@
.user-prompt-buttons {
display: flex;
align-items: center;
column-gap: 2px;
.user-prompt-button {
height: 100%;
&:not(:last-child) {
margin-right: 5px;
}
//height: 100%;
padding: 7px;
}
}
}
}
.user-prompt-actions {
}
}

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useMemo, HTMLProps, useRef } from "react";
import { useState, useEffect, useMemo } from "react";
import styles from "./settings.module.scss";
@@ -7,9 +7,29 @@ import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg";
import CopyIcon from "../icons/copy.svg";
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 { Input, List, ListItem, Modal, PasswordInput, Popover } from "./ui-lib";
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,
ListItem,
Modal,
PasswordInput,
Popover,
Select,
showConfirm,
showToast,
} from "./ui-lib";
import { ModelConfigList } from "./model-config";
import { IconButton } from "./button";
@@ -22,17 +42,27 @@ import {
useAppConfig,
} from "../store";
import Locale, { AllLangs, changeLang, getLang } from "../locales";
import Locale, {
AllLangs,
ALL_LANG_OPTIONS,
changeLang,
getLang,
} from "../locales";
import { copyToClipboard } from "../utils";
import Link from "next/link";
import { Path, UPDATE_URL } from "../constant";
import { Path, RELEASE_URL, STORAGE_KEY, UPDATE_URL } from "../constant";
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
import { ErrorBoundary } from "./error";
import { InputRange } from "./input-range";
import { useNavigate } from "react-router-dom";
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: number; onClose: () => void }) {
function EditPromptModal(props: { id: string; onClose: () => void }) {
const promptStore = usePromptStore();
const prompt = promptStore.get(props.id);
@@ -57,7 +87,7 @@ function EditPromptModal(props: { id: number; onClose: () => void }) {
readOnly={!prompt.isUser}
className={styles["edit-prompt-title"]}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.title = e.currentTarget.value),
)
@@ -69,7 +99,7 @@ function EditPromptModal(props: { id: number; onClose: () => void }) {
className={styles["edit-prompt-content"]}
rows={10}
onInput={(e) =>
promptStore.update(
promptStore.updatePrompt(
props.id,
(prompt) => (prompt.content = e.currentTarget.value),
)
@@ -90,7 +120,7 @@ function UserPromptModal(props: { onClose?: () => void }) {
const [searchPrompts, setSearchPrompts] = useState<Prompt[]>([]);
const prompts = searchInput.length > 0 ? searchPrompts : allPrompts;
const [editingPromptId, setEditingPromptId] = useState<number>();
const [editingPromptId, setEditingPromptId] = useState<string>();
useEffect(() => {
if (searchInput.length > 0) {
@@ -109,12 +139,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}
@@ -183,25 +216,359 @@ function UserPromptModal(props: { onClose?: () => void }) {
);
}
function DangerItems() {
const chatStore = useChatStore();
const appConfig = useAppConfig();
return (
<List>
<ListItem
title={Locale.Settings.Danger.Reset.Title}
subTitle={Locale.Settings.Danger.Reset.SubTitle}
>
<IconButton
text={Locale.Settings.Danger.Reset.Action}
onClick={async () => {
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
appConfig.reset();
}
}}
type="danger"
/>
</ListItem>
<ListItem
title={Locale.Settings.Danger.Clear.Title}
subTitle={Locale.Settings.Danger.Clear.SubTitle}
>
<IconButton
text={Locale.Settings.Danger.Clear.Action}
onClick={async () => {
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
chatStore.clearAllData();
}
}}
type="danger"
/>
</ListItem>
</List>
);
}
function CheckButton() {
const syncStore = useSyncStore();
const couldCheck = useMemo(() => {
return syncStore.coundSync();
}, [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 (
<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}
/>,
]}
>
<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={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>
{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={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>
</>
)}
{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.coundSync();
}, [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)} />
)}
</>
);
}
export function Settings() {
const navigate = useNavigate();
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const config = useAppConfig();
const updateConfig = config.update;
const resetConfig = config.reset;
const chatStore = useChatStore();
const updateStore = useUpdateStore();
const [checkingUpdate, setCheckingUpdate] = useState(false);
const currentVersion = updateStore.version;
const remoteId = updateStore.remoteVersion;
const currentVersion = updateStore.formatVersion(updateStore.version);
const remoteId = updateStore.formatVersion(updateStore.remoteVersion);
const hasNewVersion = currentVersion !== remoteId;
const updateUrl = getClientConfig()?.isApp ? RELEASE_URL : UPDATE_URL;
function checkUpdate(force = false) {
setCheckingUpdate(true);
updateStore.getLatestVersion(force).then(() => {
setCheckingUpdate(false);
});
console.log("[Update] local version ", updateStore.version);
console.log("[Update] remote version ", updateStore.remoteVersion);
}
const usage = {
@@ -210,6 +577,10 @@ export function Settings() {
};
const [loadingUsage, setLoadingUsage] = useState(false);
function checkUsage(force = false) {
if (accessStore.hideBalanceQuery) {
return;
}
setLoadingUsage(true);
updateStore.updateUsage(force).finally(() => {
setLoadingUsage(false);
@@ -249,9 +620,12 @@ export function Settings() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const clientConfig = useMemo(() => getClientConfig(), []);
const showAccessCode = enabledAccessControl && !clientConfig?.isApp;
return (
<ErrorBoundary>
<div className="window-header">
<div className="window-header" data-tauri-drag-region>
<div className="window-header-title">
<div className="window-header-main-title">
{Locale.Settings.Title}
@@ -261,36 +635,13 @@ export function Settings() {
</div>
</div>
<div className="window-actions">
<div className="window-action-button">
<IconButton
icon={<ClearIcon />}
onClick={() => {
if (confirm(Locale.Settings.Actions.ConfirmClearAll)) {
chatStore.clearAllData();
}
}}
bordered
title={Locale.Settings.Actions.ClearAll}
/>
</div>
<div className="window-action-button">
<IconButton
icon={<ResetIcon />}
onClick={() => {
if (confirm(Locale.Settings.Actions.ConfirmResetAll)) {
resetConfig();
}
}}
bordered
title={Locale.Settings.Actions.ResetAll}
/>
</div>
<div className="window-action-button"></div>
<div className="window-action-button"></div>
<div className="window-action-button">
<IconButton
icon={<CloseIcon />}
onClick={() => navigate(Path.Home)}
bordered
title={Locale.Settings.Actions.Close}
/>
</div>
</div>
@@ -330,9 +681,9 @@ export function Settings() {
}
>
{checkingUpdate ? (
<div />
<LoadingIcon />
) : hasNewVersion ? (
<Link href={UPDATE_URL} target="_blank" className="link">
<Link href={updateUrl} target="_blank" className="link">
{Locale.Settings.Update.GoToUpdate}
</Link>
) : (
@@ -345,7 +696,7 @@ export function Settings() {
</ListItem>
<ListItem title={Locale.Settings.SendKey}>
<select
<Select
value={config.submitKey}
onChange={(e) => {
updateConfig(
@@ -359,11 +710,11 @@ export function Settings() {
{v}
</option>
))}
</select>
</Select>
</ListItem>
<ListItem title={Locale.Settings.Theme}>
<select
<Select
value={config.theme}
onChange={(e) => {
updateConfig(
@@ -376,11 +727,11 @@ export function Settings() {
{v}
</option>
))}
</select>
</Select>
</ListItem>
<ListItem title={Locale.Settings.Lang.Name}>
<select
<Select
value={getLang()}
onChange={(e) => {
changeLang(e.target.value as any);
@@ -388,10 +739,10 @@ export function Settings() {
>
{AllLangs.map((lang) => (
<option value={lang} key={lang}>
{Locale.Settings.Lang.Options[lang]}
{ALL_LANG_OPTIONS[lang]}
</option>
))}
</select>
</Select>
</ListItem>
<ListItem
@@ -413,6 +764,22 @@ export function Settings() {
></InputRange>
</ListItem>
<ListItem
title={Locale.Settings.AutoGenerateTitle.Title}
subTitle={Locale.Settings.AutoGenerateTitle.SubTitle}
>
<input
type="checkbox"
checked={config.enableAutoGenerateTitle}
onChange={(e) =>
updateConfig(
(config) =>
(config.enableAutoGenerateTitle = e.currentTarget.checked),
)
}
></input>
</ListItem>
<ListItem
title={Locale.Settings.SendPreviewBubble.Title}
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
@@ -428,10 +795,14 @@ export function Settings() {
}
></input>
</ListItem>
</List>
<SyncItems />
<List>
<ListItem
title={Locale.Settings.Mask.Title}
subTitle={Locale.Settings.Mask.SubTitle}
title={Locale.Settings.Mask.Splash.Title}
subTitle={Locale.Settings.Mask.Splash.SubTitle}
>
<input
type="checkbox"
@@ -445,63 +816,21 @@ export function Settings() {
}
></input>
</ListItem>
</List>
<List>
{enabledAccessControl ? (
<ListItem
title={Locale.Settings.AccessCode.Title}
subTitle={Locale.Settings.AccessCode.SubTitle}
>
<PasswordInput
value={accessStore.accessCode}
type="text"
placeholder={Locale.Settings.AccessCode.Placeholder}
onChange={(e) => {
accessStore.updateCode(e.currentTarget.value);
}}
/>
</ListItem>
) : (
<></>
)}
<ListItem
title={Locale.Settings.Token.Title}
subTitle={Locale.Settings.Token.SubTitle}
title={Locale.Settings.Mask.Builtin.Title}
subTitle={Locale.Settings.Mask.Builtin.SubTitle}
>
<PasswordInput
value={accessStore.token}
type="text"
placeholder={Locale.Settings.Token.Placeholder}
onChange={(e) => {
accessStore.updateToken(e.currentTarget.value);
}}
/>
</ListItem>
<ListItem
title={Locale.Settings.Usage.Title}
subTitle={
showUsage
? loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(
usage?.used ?? "[?]",
usage?.subscription ?? "[?]",
)
: Locale.Settings.Usage.NoAccess
}
>
{!showUsage || loadingUsage ? (
<div />
) : (
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Usage.Check}
onClick={() => checkUsage(true)}
/>
)}
<input
type="checkbox"
checked={config.hideBuiltinMasks}
onChange={(e) =>
updateConfig(
(config) =>
(config.hideBuiltinMasks = e.currentTarget.checked),
)
}
></input>
</ListItem>
</List>
@@ -537,12 +866,105 @@ export function Settings() {
</ListItem>
</List>
<List>
{showAccessCode ? (
<ListItem
title={Locale.Settings.AccessCode.Title}
subTitle={Locale.Settings.AccessCode.SubTitle}
>
<PasswordInput
value={accessStore.accessCode}
type="text"
placeholder={Locale.Settings.AccessCode.Placeholder}
onChange={(e) => {
accessStore.updateCode(e.currentTarget.value);
}}
/>
</ListItem>
) : (
<></>
)}
{!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}
{!accessStore.hideBalanceQuery ? (
<ListItem
title={Locale.Settings.Usage.Title}
subTitle={
showUsage
? loadingUsage
? Locale.Settings.Usage.IsChecking
: Locale.Settings.Usage.SubTitle(
usage?.used ?? "[?]",
usage?.subscription ?? "[?]",
)
: Locale.Settings.Usage.NoAccess
}
>
{!showUsage || loadingUsage ? (
<div />
) : (
<IconButton
icon={<ResetIcon></ResetIcon>}
text={Locale.Settings.Usage.Check}
onClick={() => checkUsage(true)}
/>
)}
</ListItem>
) : null}
<ListItem
title={Locale.Settings.CustomModel.Title}
subTitle={Locale.Settings.CustomModel.SubTitle}
>
<input
type="text"
value={config.customModels}
placeholder="model1,model2,model3"
onChange={(e) =>
config.update(
(config) => (config.customModels = e.currentTarget.value),
)
}
></input>
</ListItem>
</List>
<List>
<ModelConfigList
modelConfig={config.modelConfig}
updateConfig={(upater) => {
updateConfig={(updater) => {
const modelConfig = { ...config.modelConfig };
upater(modelConfig);
updater(modelConfig);
config.update((config) => (config.modelConfig = modelConfig));
}}
/>
@@ -551,6 +973,8 @@ export function Settings() {
{shouldShowPromptModal && (
<UserPromptModal onClose={() => setShowPromptModal(false)} />
)}
<DangerItems />
</div>
</ErrorBoundary>
);

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react";
import { useEffect, useRef, useCallback } from "react";
import styles from "./home.module.scss";
@@ -10,12 +10,14 @@ import AddIcon from "../icons/add.svg";
import CloseIcon from "../icons/close.svg";
import MaskIcon from "../icons/mask.svg";
import PluginIcon from "../icons/plugin.svg";
import DragIcon from "../icons/drag.svg";
import Locale from "../locales";
import { useAppConfig, useChatStore } from "../store";
import {
DEFAULT_SIDEBAR_WIDTH,
MAX_SIDEBAR_WIDTH,
MIN_SIDEBAR_WIDTH,
NARROW_SIDEBAR_WIDTH,
@@ -26,42 +28,87 @@ import {
import { Link, useNavigate } from "react-router-dom";
import { useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
import { showToast } from "./ui-lib";
import { showConfirm, showToast } from "./ui-lib";
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
loading: () => null,
});
function useHotKey() {
const chatStore = useChatStore();
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.altKey || e.ctrlKey) {
if (e.key === "ArrowUp") {
chatStore.nextSession(-1);
} else if (e.key === "ArrowDown") {
chatStore.nextSession(1);
}
}
};
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
});
}
function useDragSideBar() {
const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x);
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;
@@ -69,13 +116,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,
};
}
@@ -84,19 +131,22 @@ 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();
useHotKey();
return (
<div
className={`${styles.sidebar} ${props.className} ${
shouldNarrow && styles["narrow-sidebar"]
}`}
>
<div className={styles["sidebar-header"]}>
<div className={styles["sidebar-title"]}>ChatGPT Next</div>
<div className={styles["sidebar-header"]} data-tauri-drag-region>
<div className={styles["sidebar-title"]} data-tauri-drag-region>
ChatGPT Next
</div>
<div className={styles["sidebar-sub-title"]}>
Build your own AI assistant.
</div>
@@ -110,7 +160,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
@@ -138,8 +194,8 @@ export function SideBar(props: { className?: string }) {
<div className={styles["sidebar-action"] + " " + styles.mobile}>
<IconButton
icon={<CloseIcon />}
onClick={() => {
if (confirm(Locale.Home.DeleteChat)) {
onClick={async () => {
if (await showConfirm(Locale.Home.DeleteChat)) {
chatStore.deleteSession(chatStore.currentSessionIndex);
}
}}
@@ -151,7 +207,7 @@ export function SideBar(props: { className?: string }) {
</Link>
</div>
<div className={styles["sidebar-action"]}>
<a href={REPO_URL} target="_blank">
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
<IconButton icon={<GithubIcon />} shadow />
</a>
</div>
@@ -163,6 +219,7 @@ export function SideBar(props: { className?: string }) {
onClick={() => {
if (config.dontShowMaskSplashScreen) {
chatStore.newSession();
navigate(Path.Chat);
} else {
navigate(Path.NewChat);
}
@@ -174,8 +231,10 @@ export function SideBar(props: { className?: string }) {
<div
className={styles["sidebar-drag"]}
onMouseDown={(e) => onDragMouseDown(e as any)}
></div>
onPointerDown={(e) => onDragStart(e as any)}
>
<DragIcon />
</div>
</div>
);
}

View File

@@ -62,6 +62,7 @@
box-shadow: var(--card-shadow);
margin-bottom: 20px;
animation: slide-in ease 0.3s;
background: var(--white);
}
.list .list-item:last-child {
@@ -72,11 +73,26 @@
box-shadow: var(--card-shadow);
background-color: var(--white);
border-radius: 12px;
width: 60vw;
width: 80vw;
max-width: 900px;
min-width: 300px;
animation: slide-in ease 0.3s;
--modal-padding: 20px;
&-max {
width: 95vw;
max-width: unset;
height: 95vh;
display: flex;
flex-direction: column;
.modal-content {
max-height: unset !important;
flex-grow: 1;
}
}
.modal-header {
padding: var(--modal-padding);
display: flex;
@@ -89,11 +105,19 @@
font-size: 16px;
}
.modal-close-btn {
cursor: pointer;
.modal-header-actions {
display: flex;
&:hover {
filter: brightness(1.2);
.modal-header-action {
cursor: pointer;
&:not(:last-child) {
margin-right: 20px;
}
&:hover {
filter: brightness(1.2);
}
}
}
}
@@ -124,6 +148,18 @@
}
}
@media screen and (max-width: 600px) {
.modal-container {
width: 100vw;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
.modal-content {
max-height: 50vh;
}
}
}
.show {
opacity: 1;
transition: all ease 0.3s;
@@ -192,12 +228,80 @@
min-width: 50px;
}
@media only screen and (max-width: 600px) {
.modal-container {
width: 90vw;
.select-with-icon {
position: relative;
max-width: fit-content;
.modal-content {
max-height: 50vh;
.select-with-icon-select {
height: 100%;
border: var(--border-in-light);
padding: 10px 25px 10px 10px;
border-radius: 10px;
appearance: none;
cursor: pointer;
background-color: var(--white);
color: var(--black);
text-align: center;
}
.select-with-icon-icon {
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
pointer-events: none;
}
}
.modal-input {
height: 100%;
width: 100%;
border-radius: 10px;
border: var(--border-in-light);
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.03);
background-color: var(--white);
color: var(--black);
font-family: inherit;
padding: 10px;
resize: none;
outline: none;
box-sizing: border-box;
&:focus {
border: 1px solid var(--primary);
}
}
.selector {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
&-content {
.list {
max-height: 90vh;
overflow-x: hidden;
overflow-y: auto;
.list-item {
cursor: pointer;
background-color: var(--white);
&:hover {
filter: brightness(0.95);
}
&:active {
filter: brightness(0.9);
}
}
}
}
}

View File

@@ -1,8 +1,16 @@
/* eslint-disable @next/next/no-img-element */
import styles from "./ui-lib.module.scss";
import LoadingIcon from "../icons/three-dots.svg";
import CloseIcon from "../icons/close.svg";
import EyeIcon from "../icons/eye.svg";
import EyeOffIcon from "../icons/eye-off.svg";
import DownIcon from "../icons/down.svg";
import ConfirmIcon from "../icons/confirm.svg";
import CancelIcon from "../icons/cancel.svg";
import MaxIcon from "../icons/max.svg";
import MinIcon from "../icons/min.svg";
import Locale from "../locales";
import { createRoot } from "react-dom/client";
import React, { HTMLProps, useEffect, useState } from "react";
@@ -39,9 +47,13 @@ export function ListItem(props: {
children?: JSX.Element | JSX.Element[];
icon?: JSX.Element;
className?: string;
onClick?: () => void;
}) {
return (
<div className={styles["list-item"] + ` ${props.className}`}>
<div
className={styles["list-item"] + ` ${props.className || ""}`}
onClick={props.onClick}
>
<div className={styles["list-header"]}>
{props.icon && <div className={styles["list-icon"]}>{props.icon}</div>}
<div className={styles["list-item-title"]}>
@@ -86,8 +98,9 @@ export function Loading() {
interface ModalProps {
title: string;
children?: JSX.Element | JSX.Element[];
children?: any;
actions?: JSX.Element[];
defaultMax?: boolean;
onClose?: () => void;
}
export function Modal(props: ModalProps) {
@@ -106,13 +119,30 @@ export function Modal(props: ModalProps) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const [isMax, setMax] = useState(!!props.defaultMax);
return (
<div className={styles["modal-container"]}>
<div
className={
styles["modal-container"] + ` ${isMax && styles["modal-container-max"]}`
}
>
<div className={styles["modal-header"]}>
<div className={styles["modal-title"]}>{props.title}</div>
<div className={styles["modal-close-btn"]} onClick={props.onClose}>
<CloseIcon />
<div className={styles["modal-header-actions"]}>
<div
className={styles["modal-header-action"]}
onClick={() => setMax(!isMax)}
>
{isMax ? <MinIcon /> : <MaxIcon />}
</div>
<div
className={styles["modal-header-action"]}
onClick={props.onClose}
>
<CloseIcon />
</div>
</div>
</div>
@@ -244,3 +274,214 @@ export function PasswordInput(props: HTMLProps<HTMLInputElement>) {
</div>
);
}
export function Select(
props: React.DetailedHTMLProps<
React.SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
>,
) {
const { className, children, ...otherProps } = props;
return (
<div className={`${styles["select-with-icon"]} ${className}`}>
<select className={styles["select-with-icon-select"]} {...otherProps}>
{children}
</select>
<DownIcon className={styles["select-with-icon-icon"]} />
</div>
);
}
export function showConfirm(content: any) {
const div = document.createElement("div");
div.className = "modal-mask";
document.body.appendChild(div);
const root = createRoot(div);
const closeModal = () => {
root.unmount();
div.remove();
};
return new Promise<boolean>((resolve) => {
root.render(
<Modal
title={Locale.UI.Confirm}
actions={[
<IconButton
key="cancel"
text={Locale.UI.Cancel}
onClick={() => {
resolve(false);
closeModal();
}}
icon={<CancelIcon />}
tabIndex={0}
bordered
shadow
></IconButton>,
<IconButton
key="confirm"
text={Locale.UI.Confirm}
type="primary"
onClick={() => {
resolve(true);
closeModal();
}}
icon={<ConfirmIcon />}
tabIndex={0}
autoFocus
bordered
shadow
></IconButton>,
]}
onClose={closeModal}
>
{content}
</Modal>,
);
});
}
function PromptInput(props: {
value: string;
onChange: (value: string) => void;
rows?: number;
}) {
const [input, setInput] = useState(props.value);
const onInput = (value: string) => {
props.onChange(value);
setInput(value);
};
return (
<textarea
className={styles["modal-input"]}
autoFocus
value={input}
onInput={(e) => onInput(e.currentTarget.value)}
rows={props.rows ?? 3}
></textarea>
);
}
export function showPrompt(content: any, value = "", rows = 3) {
const div = document.createElement("div");
div.className = "modal-mask";
document.body.appendChild(div);
const root = createRoot(div);
const closeModal = () => {
root.unmount();
div.remove();
};
return new Promise<string>((resolve) => {
let userInput = value;
root.render(
<Modal
title={content}
actions={[
<IconButton
key="cancel"
text={Locale.UI.Cancel}
onClick={() => {
closeModal();
}}
icon={<CancelIcon />}
bordered
shadow
tabIndex={0}
></IconButton>,
<IconButton
key="confirm"
text={Locale.UI.Confirm}
type="primary"
onClick={() => {
resolve(userInput);
closeModal();
}}
icon={<ConfirmIcon />}
bordered
shadow
tabIndex={0}
></IconButton>,
]}
onClose={closeModal}
>
<PromptInput
onChange={(val) => (userInput = val)}
value={value}
rows={rows}
></PromptInput>
</Modal>,
);
});
}
export function showImageModal(img: string) {
showModal({
title: Locale.Export.Image.Modal,
children: (
<div>
<img
src={img}
alt="preview"
style={{
maxWidth: "100%",
}}
></img>
</div>
),
});
}
export function Selector<T>(props: {
items: Array<{
title: string;
subTitle?: string;
value: T;
}>;
defaultSelectedValue?: T;
onSelection?: (selection: T[]) => void;
onClose?: () => void;
multiple?: boolean;
}) {
return (
<div className={styles["selector"]} onClick={() => props.onClose?.()}>
<div className={styles["selector-content"]}>
<List>
{props.items.map((item, i) => {
const selected = props.defaultSelectedValue === item.value;
return (
<ListItem
className={styles["selector-item"]}
key={i}
title={item.title}
subTitle={item.subTitle}
onClick={() => {
props.onSelection?.([item.value]);
props.onClose?.();
}}
>
{selected ? (
<div
style={{
height: 10,
width: 10,
backgroundColor: "var(--primary)",
borderRadius: 10,
}}
></div>
) : (
<></>
)}
</ListItem>
);
})}
</List>
</div>
</div>
);
}

View File

@@ -1,18 +1,4 @@
const COMMIT_ID: string = (() => {
try {
const childProcess = require("child_process");
return (
childProcess
// .execSync("git describe --tags --abbrev=0")
.execSync("git rev-parse --short HEAD")
.toString()
.trim()
);
} catch (e) {
console.error("[Build Config] No git or not from git repo.");
return "unknown";
}
})();
import tauriConfig from "../../src-tauri/tauri.conf.json";
export const getBuildConfig = () => {
if (typeof process === "undefined") {
@@ -21,7 +7,38 @@ export const getBuildConfig = () => {
);
}
const buildMode = process.env.BUILD_MODE ?? "standalone";
const isApp = !!process.env.BUILD_APP;
const version = "v" + tauriConfig.package.version;
const commitInfo = (() => {
try {
const childProcess = require("child_process");
const commitDate: string = childProcess
.execSync('git log -1 --format="%at000" --date=unix')
.toString()
.trim();
const commitHash: string = childProcess
.execSync('git log --pretty=format:"%H" -n 1')
.toString()
.trim();
return { commitDate, commitHash };
} catch (e) {
console.error("[Build Config] No git or not from git repo.");
return {
commitDate: "unknown",
commitHash: "unknown",
};
}
})();
return {
commitId: COMMIT_ID,
version,
...commitInfo,
buildMode,
isApp,
};
};
export type BuildConfig = ReturnType<typeof getBuildConfig>;

27
app/config/client.ts Normal file
View File

@@ -0,0 +1,27 @@
import { BuildConfig, getBuildConfig } from "./build";
export function getClientConfig() {
if (typeof document !== "undefined") {
// client side
return JSON.parse(queryMeta("config")) as BuildConfig;
}
if (typeof process !== "undefined") {
// server side
return getBuildConfig();
}
}
function queryMeta(key: string, defaultValue?: string): string {
let ret: string;
if (document) {
const meta = document.head.querySelector(
`meta[name='${key}']`,
) as HTMLMetaElement;
ret = meta?.content ?? "";
} else {
ret = defaultValue ?? "";
}
return ret;
}

View File

@@ -5,8 +5,14 @@ declare global {
interface ProcessEnv {
OPENAI_API_KEY?: string;
CODE?: string;
BASE_URL?: string;
PROXY_URL?: string;
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
}
}
}
@@ -36,7 +42,11 @@ export const getServerSideConfig = () => {
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,
};
};

View File

@@ -3,16 +3,25 @@ export const REPO = "ChatGPT-Next-Web";
export const REPO_URL = `https://github.com/${OWNER}/${REPO}`;
export const ISSUE_URL = `https://github.com/${OWNER}/${REPO}/issues`;
export const UPDATE_URL = `${REPO_URL}#keep-updated`;
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_CORS_HOST = "https://nb.nextweb.fun";
export const DEFAULT_API_HOST = `${DEFAULT_CORS_HOST}/api/proxy`;
export enum Path {
Home = "/",
Chat = "/chat",
Settings = "/settings",
NewChat = "/new-chat",
Masks = "/masks",
Auth = "/auth",
}
export enum ApiPath {
Cors = "/api/cors",
}
export enum SlotID {
@@ -31,8 +40,87 @@ export enum StoreKey {
Mask = "mask-store",
Prompt = "prompt-store",
Update = "chat-update",
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;
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 const OpenaiPath = {
ChatPath: "v1/chat/completions",
UsagePath: "dashboard/billing/usage",
SubsPath: "dashboard/billing/subscription",
ListModelPath: "v1/models",
};
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
Current model: {{model}}
Current time: {{time}}`;
export const SUMMARIZE_MODEL = "gpt-3.5-turbo";
export const DEFAULT_MODELS = [
{
name: "gpt-4",
available: true,
},
{
name: "gpt-4-0314",
available: true,
},
{
name: "gpt-4-0613",
available: true,
},
{
name: "gpt-4-32k",
available: true,
},
{
name: "gpt-4-32k-0314",
available: true,
},
{
name: "gpt-4-32k-0613",
available: true,
},
{
name: "gpt-3.5-turbo",
available: true,
},
{
name: "gpt-3.5-turbo-0301",
available: true,
},
{
name: "gpt-3.5-turbo-0613",
available: true,
},
{
name: "gpt-3.5-turbo-16k",
available: true,
},
{
name: "gpt-3.5-turbo-16k-0613",
available: true,
},
] as const;
export const CHAT_PAGE_SIZE = 15;
export const MAX_RENDER_MSG_COUNT = 45;

18
app/global.d.ts vendored
View File

@@ -9,3 +9,21 @@ declare module "*.scss" {
}
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>;
};
notification:{
requestPermission(): Promise<Permission>;
isPermissionGranted(): Promise<boolean>;
sendNotification(options: string | Options): void;
};
};
}

View File

@@ -1,23 +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(8 5.333333333333333) rotate(0 0 2.6666666666666665)" d="M0,0L0,5.33 " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(5.333333333333333 8) rotate(0 2.6666666666666665 0)" d="M0,0L5.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="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,5.33" transform="translate(8 5.333333333333333) rotate(0 0 2.6666666666666665)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L5.33,0" transform="translate(5.333333333333333 8) rotate(0 2.6666666666666665 0)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.1 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:#333333"
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:#333" 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

BIN
app/icons/bot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 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(4 4) rotate(0 4 2)" d="M8,0L4,4L0,0 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4 8) rotate(0 4 2)" d="M8,0L4,4L0,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="M8,0L4,4L0,0" transform="translate(4 4) rotate(0 4 2)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M8,0L4,4L0,0" transform="translate(4 8) rotate(0 4 2)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 736 B

After

Width:  |  Height:  |  Size: 706 B

View File

@@ -1,25 +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.3333323286384866 1.3334133333333331) rotate(0 6.66666716901409 6.66666)"
d="M5.01,13.33C4.69,12.27 4.19,11.47 3.53,10.95C2.55,10.17 0.97,10.65 0.39,9.84C-0.19,9.04 0.8,7.55 1.15,6.67C1.49,5.79 -0.18,5.48 0.02,5.23C0.15,5.07 0.99,4.59 2.55,3.79C3,1.26 4.63,0 7.47,0C11.71,0 13.33,3.6 13.33,5.89C13.33,8.18 11.37,10.65 8.58,11.18C8.33,11.55 8.69,12.26 9.66,13.33 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(6.374029736345404 3.9567867125879106) rotate(0 2.8215982497276006 2.4327734241007346)"
d="M2.1,3.33C1.91,4.42 2.14,4.93 2.79,4.86C3.44,4.79 3.84,4.52 3.97,4.05C4.99,4.33 5.54,4.09 5.63,3.33C5.75,2.18 5.13,1.26 4.88,1.26C4.63,1.26 3.97,1.23 3.97,0.88C3.97,0.52 3.2,0.33 2.5,0.33C1.81,0.33 2.23,-0.14 1.27,0.04C0.64,0.17 0.26,0.44 0.13,0.88C-0.09,1.72 -0.03,2.31 0.32,2.66C0.67,3 1.26,3.22 2.1,3.33Z " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(8.193033333333332 8.500066666666665) rotate(0 0.9868499999999998 1.1846833333333333)"
d="M1.97,0C1.63,0.21 1.17,0.56 0.97,0.83C0.48,1.52 0.09,1.93 0,2.37 " />
</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="M5.01,13.33C4.69,12.27 4.19,11.47 3.53,10.95C2.55,10.17 0.97,10.65 0.39,9.84C-0.19,9.04 0.8,7.55 1.15,6.67C1.49,5.79 -0.18,5.48 0.02,5.23C0.15,5.07 0.99,4.59 2.55,3.79C3,1.26 4.63,0 7.47,0C11.71,0 13.33,3.6 13.33,5.89C13.33,8.18 11.37,10.65 8.58,11.18C8.33,11.55 8.69,12.26 9.66,13.33" transform="translate(1.3333323286384866 1.3334133333333331) rotate(0 6.66666716901409 6.66666)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M2.1,3.33C1.91,4.42 2.14,4.93 2.79,4.86C3.44,4.79 3.84,4.52 3.97,4.05C4.99,4.33 5.54,4.09 5.63,3.33C5.75,2.18 5.13,1.26 4.88,1.26C4.63,1.26 3.97,1.23 3.97,0.88C3.97,0.52 3.2,0.33 2.5,0.33C1.81,0.33 2.23,-0.14 1.27,0.04C0.64,0.17 0.26,0.44 0.13,0.88C-0.09,1.72 -0.03,2.31 0.32,2.66C0.67,3 1.26,3.22 2.1,3.33Z" transform="translate(6.374029736345404 3.9567867125879106) rotate(0 2.8215982497276006 2.4327734241007346)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M1.97,0C1.63,0.21 1.17,0.56 0.97,0.83C0.48,1.52 0.09,1.93 0,2.37" transform="translate(8.193033333333332 8.500066666666665) rotate(0 0.9868499999999998 1.1846833333333333)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

1
app/icons/break.svg Normal file
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" 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>

After

Width:  |  Height:  |  Size: 4.5 KiB

1
app/icons/cancel.svg Normal file
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" 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>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
app/icons/chatgpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

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" 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" style="fill:#333333; 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><path id="路径 2" style="fill:#333333; 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"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></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

1
app/icons/confirm.svg Normal file
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" 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>

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(4.333333333333333 1.6666666666666665) rotate(0 5 5)" d="M0,2.48L0,0.94C0,0.42 0.42,0 0.94,0L9.06,0C9.58,0 10,0.42 10,0.94L10,9.06C10,9.58 9.58,10 9.06,10L7.51,10 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.6666666666666665 4.333333333333333) rotate(0 5 5)" d="M0.94,0C0.42,0 0,0.42 0,0.94L0,9.06C0,9.58 0.42,10 0.94,10L9.06,10C9.58,10 10,9.58 10,9.06L10,0.94C10,0.42 9.58,0 9.06,0L0.94,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="M0,2.48L0,0.94C0,0.42 0.42,0 0.94,0L9.06,0C9.58,0 10,0.42 10,0.94L10,9.06C10,9.58 9.58,10 9.06,10L7.51,10" transform="translate(4.333333333333333 1.6666666666666665) rotate(0 5 5)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0.94,0C0.42,0 0,0.42 0,0.94L0,9.06C0,9.58 0.42,10 0.94,10L9.06,10C9.58,10 10,9.58 10,9.06L10,0.94C10,0.42 9.58,0 9.06,0L0.94,0Z" transform="translate(1.6666666666666665 4.333333333333333) rotate(0 5 5)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 981 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

1
app/icons/down.svg Normal file
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"><defs><rect id="path_0" width="16" height="16" x="0" y="0"/></defs><g opacity="1" transform="translate(0 0) rotate(-90 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="M4,8L0,4L4,0" transform="translate(6.333333333333333 4) rotate(0 2 4)"/></g></g></svg>

After

Width:  |  Height:  |  Size: 555 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"><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) rotate(0 6 6)" d="M1,12L11,12C11.55,12 12,11.55 12,11L12,1C12,0.45 11.55,0 11,0L1,0C0.45,0 0,0.45 0,1L0,11C0,11.55 0.45,12 1,12Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.3333333333333333 10.333333333333332) rotate(0 6.666666666666666 0.6666666666666666)" d="M0,0L3.67,0L4.33,1.33L9,1.33L9.67,0L13.33,0 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(14 8.666666666666666) rotate(0 0 1.6666666666666665)" d="M0,3.33L0,0 " /><path id="路径 4" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6 7.333333333333333) rotate(0 2 1)" d="M0,0L2,2L4,0 " /><path id="路径 5" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(8 4) rotate(0 0 2.6666666666666665)" d="M0,5.33L0,0 " /><path id="路径 6" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(2 8.666666666666666) rotate(0 0 1.6666666666666665)" d="M0,3.33L0,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="M1,12L11,12C11.55,12 12,11.55 12,11L12,1C12,0.45 11.55,0 11,0L1,0C0.45,0 0,0.45 0,1L0,11C0,11.55 0.45,12 1,12Z" transform="translate(2 2) rotate(0 6 6)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L3.67,0L4.33,1.33L9,1.33L9.67,0L13.33,0" transform="translate(1.3333333333333333 10.333333333333332) rotate(0 6.666666666666666 0.6666666666666666)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3.33L0,0" transform="translate(14 8.666666666666666) rotate(0 0 1.6666666666666665)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L2,2L4,0" transform="translate(6 7.333333333333333) rotate(0 2 1)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,5.33L0,0" transform="translate(8 4) rotate(0 0 2.6666666666666665)"/><path id="路径 6" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3.33L0,0" transform="translate(2 8.666666666666666) rotate(0 0 1.6666666666666665)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

1
app/icons/drag.svg Normal file
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" 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>

After

Width:  |  Height:  |  Size: 1.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(1.2400716519614834 2.3333321805983163) rotate(0 6.785117896431597 4.552683909700841)" d="M12.27,9.11C13.36,8.34 13.83,6.94 13.43,5.67C13.02,4.39 11.78,3.69 10.44,3.69L9.67,3.69C9.16,1.72 7.5,0.27 5.47,0.03C3.45,-0.2 1.5,0.84 0.56,2.64C-0.38,4.45 -0.11,6.64 1.23,8.17 " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(8 7.666666666666666) rotate(0 0.00140000000000029 3)" d="M0,6L0,0 " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.8786 11.5454) rotate(0 2.1213333333333333 1.0606666666666662)" d="M4.24,0L2.12,2.12L0,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="M12.27,9.11C13.36,8.34 13.83,6.94 13.43,5.67C13.02,4.39 11.78,3.69 10.44,3.69L9.67,3.69C9.16,1.72 7.5,0.27 5.47,0.03C3.45,-0.2 1.5,0.84 0.56,2.64C-0.38,4.45 -0.11,6.64 1.23,8.17" transform="translate(1.2400716519614834 2.3333321805983163) rotate(0 6.785117896431597 4.552683909700841)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,6L0,0" transform="translate(8 7.666666666666666) rotate(0 0.00140000000000029 3)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M4.24,0L2.12,2.12L0,0" transform="translate(5.8786 11.5454) rotate(0 2.1213333333333333 1.0606666666666662)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,29 +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 1.644694921083138) rotate(0 5.333333333333333 4.287969206125098)"
d="M7.11,8.51C7.92,8.35 8.64,8.06 9.21,7.64C10.17,6.91 10.67,5.79 10.67,4.69C10.67,3.91 10.37,3.19 9.86,2.58C9.58,2.24 10.41,-0.31 9.67,0.03C8.94,0.37 7.86,1.13 7.29,0.97C6.68,0.79 6.02,0.69 5.33,0.69C4.73,0.69 4.16,0.76 3.62,0.9C2.83,1.1 2.09,0.36 1.33,0.03C0.58,-0.29 0.99,2.34 0.77,2.62C0.28,3.22 0,3.93 0,4.69C0,5.79 0.6,6.91 1.56,7.64C2.21,8.12 3.01,8.42 3.91,8.58 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(6.000666666666666 10.220633333333332) rotate(0 0.2896166666666667 2.058116666666666)"
d="M0.58,0C0.19,0.43 0,0.83 0,1.21C0,1.59 0,2.56 0,4.12 " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(9.781533333333332 10.158866666666666) rotate(0 0.2744333333333332 2.0890166666666663)"
d="M0,0C0.37,0.48 0.55,0.91 0.55,1.29C0.55,1.68 0.55,2.64 0.55,4.18 " />
<path id="路径 4"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 10.405166666666666) rotate(0 2.0004 1.050416666666667)"
d="M0,0C0.3,0.04 0.52,0.17 0.67,0.41C0.88,0.77 1.69,2.1 2.61,2.1C3.22,2.1 3.68,2.1 4,2.1 " />
</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="M7.11,8.51C7.92,8.35 8.64,8.06 9.21,7.64C10.17,6.91 10.67,5.79 10.67,4.69C10.67,3.91 10.37,3.19 9.86,2.58C9.58,2.24 10.41,-0.31 9.67,0.03C8.94,0.37 7.86,1.13 7.29,0.97C6.68,0.79 6.02,0.69 5.33,0.69C4.73,0.69 4.16,0.76 3.62,0.9C2.83,1.1 2.09,0.36 1.33,0.03C0.58,-0.29 0.99,2.34 0.77,2.62C0.28,3.22 0,3.93 0,4.69C0,5.79 0.6,6.91 1.56,7.64C2.21,8.12 3.01,8.42 3.91,8.58" transform="translate(2.6666666666666665 1.644694921083138) rotate(0 5.333333333333333 4.287969206125098)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0.58,0C0.19,0.43 0,0.83 0,1.21C0,1.59 0,2.56 0,4.12" transform="translate(6.000666666666666 10.220633333333332) rotate(0 0.2896166666666667 2.058116666666666)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0C0.37,0.48 0.55,0.91 0.55,1.29C0.55,1.68 0.55,2.64 0.55,4.18" transform="translate(9.781533333333332 10.158866666666666) rotate(0 0.2744333333333332 2.0890166666666663)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0C0.3,0.04 0.52,0.17 0.67,0.41C0.88,0.77 1.69,2.1 2.61,2.1C3.22,2.1 3.68,2.1 4,2.1" transform="translate(2 10.405166666666666) rotate(0 2.0004 1.050416666666667)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.8 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(6.333333333333333 4) rotate(0 2 4)" d="M4,8L0,4L4,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="M4,8L0,4L4,0" transform="translate(6.333333333333333 4) rotate(0 2 4)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 573 B

After

Width:  |  Height:  |  Size: 553 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"><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 3.333333333333333) rotate(0 6 5.666666666666666)" d="M6,0C2.69,0 0,2.54 0,5.67C0,8.8 2.69,11.33 6,11.33C9.31,11.33 12,8.8 12,5.67C12,2.54 9.31,0 6,0Z " /><path id="路径 2" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(4.64 6.715000010822796) rotate(14.999999999999998 1 1.3333333333333335)" d="M1,0C0.45,0 0,0.6 0,1.33C0,2.07 0.45,2.67 1,2.67C1.55,2.67 2,2.07 2,1.33C2,0.6 1.55,0 1,0Z " /><path id="路径 3" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.31 6.714999802665079) rotate(165.00000507213028 1.000000156118488 1.3333335414913166)" d="M1,0C0.45,0 0,0.6 0,1.33C0,2.07 0.45,2.67 1,2.67C1.55,2.67 2,2.07 2,1.33C2,0.6 1.55,0 1,0Z " /><path id="路径 4" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(9.666599999999999 2.492504620561264) rotate(0 2.4176172657482775 2.2535810230527007)" d="M4,4.51C5.04,3.47 5.15,1.77 4.1,0.73C3.06,-0.32 1.04,-0.2 0,0.84 " /><path id="路径 5" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.492667974925419 2.4926141635940393) rotate(0 2.4203326792039572 2.253609584869647)" d="M0.84,4.51C-0.2,3.47 -0.32,1.77 0.73,0.73C1.77,-0.32 3.8,-0.2 4.84,0.84 " /><path id="路径 6" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(6.5 11.67) rotate(0 1.6666666666666665 0.33333029691911636)" d="M0,0C0.17,0.43 0.73,1.09 1.67,0.29C2.6,1.09 3.17,0.43 3.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="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,0C2.69,0 0,2.54 0,5.67C0,8.8 2.69,11.33 6,11.33C9.31,11.33 12,8.8 12,5.67C12,2.54 9.31,0 6,0Z" transform="translate(2 3.333333333333333) rotate(0 6 5.666666666666666)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M1,0C0.45,0 0,0.6 0,1.33C0,2.07 0.45,2.67 1,2.67C1.55,2.67 2,2.07 2,1.33C2,0.6 1.55,0 1,0Z" transform="translate(4.64 6.715000010822796) rotate(14.999999999999998 1 1.3333333333333335)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M1,0C0.45,0 0,0.6 0,1.33C0,2.07 0.45,2.67 1,2.67C1.55,2.67 2,2.07 2,1.33C2,0.6 1.55,0 1,0Z" transform="translate(9.31 6.714999802665079) rotate(165.00000507213028 1.000000156118488 1.3333335414913166)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M4,4.51C5.04,3.47 5.15,1.77 4.1,0.73C3.06,-0.32 1.04,-0.2 0,0.84" transform="translate(9.666599999999999 2.492504620561264) rotate(0 2.4176172657482775 2.2535810230527007)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0.84,4.51C-0.2,3.47 -0.32,1.77 0.73,0.73C1.77,-0.32 3.8,-0.2 4.84,0.84" transform="translate(1.492667974925419 2.4926141635940393) rotate(0 2.4203326792039572 2.253609584869647)"/><path id="路径 6" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0C0.17,0.43 0.73,1.09 1.67,0.29C2.6,1.09 3.17,0.43 3.33,0" transform="translate(6.5 11.67) rotate(0 1.6666666666666665 0.33333029691911636)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,41 +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) rotate(0 1.6666666666666665 1.6499166666666665)"
d="M0,0L3.33,3.3 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 10.666666666666666) rotate(0 1.6666666666666665 1.6499166666666671)"
d="M0,3.3L3.33,0 " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.700199999999999 10.666666666666666) rotate(0 1.6499166666666671 1.6499166666666671)"
d="M3.3,3.3L0,0 " />
<path id="路径 4"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.666666666666666 2) rotate(0 1.6499166666666671 1.6499166666666665)"
d="M3.3,0L0,3.3 " />
<path id="路径 5"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(11 2) rotate(0 1.5 1.5)" d="M0,0L3,0L3,3 " />
<path id="路径 6"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(11 11) rotate(0 1.5 1.5)" d="M3,0L3,3L0,3 " />
<path id="路径 7"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 11) rotate(0 1.5 1.5)" d="M3,3L0,3L0,0 " />
<path id="路径 8"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 2) rotate(0 1.5 1.5)" d="M0,3L0,0L3,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,0L3.33,3.3" transform="translate(2 2) rotate(0 1.6666666666666665 1.6499166666666665)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3.3L3.33,0" transform="translate(2 10.666666666666666) rotate(0 1.6666666666666665 1.6499166666666671)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3.3,3.3L0,0" transform="translate(10.700199999999999 10.666666666666666) rotate(0 1.6499166666666671 1.6499166666666671)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3.3,0L0,3.3" transform="translate(10.666666666666666 2) rotate(0 1.6499166666666671 1.6499166666666665)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L3,0L3,3" transform="translate(11 2) rotate(0 1.5 1.5)"/><path id="路径 6" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3,0L3,3L0,3" transform="translate(11 11) rotate(0 1.5 1.5)"/><path id="路径 7" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3,3L0,3L0,0" transform="translate(2 11) rotate(0 1.5 1.5)"/><path id="路径 8" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3L0,0L3,0" transform="translate(2 2) rotate(0 1.5 1.5)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -1,25 +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.649903333333333 3.983233333333333) rotate(0 5.333331666666666 0)"
d="M0,0L10.67,0 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.649903333333333 7.983233333333333) rotate(0 5.333331666666666 0)"
d="M0,0L10.67,0 " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.649903333333333 11.983233333333333) rotate(0 5.333331666666666 0)"
d="M0,0L10.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,0" transform="translate(2.649903333333333 3.983233333333333) rotate(0 5.333331666666666 0)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L10.67,0" transform="translate(2.649903333333333 7.983233333333333) rotate(0 5.333331666666666 0)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L10.67,0" transform="translate(2.649903333333333 11.983233333333333) rotate(0 5.333331666666666 0)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1020 B

View File

@@ -1,45 +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) rotate(0 1.6666666666666665 1.6499166666666665)"
d="M0,0L3.33,3.3 " />
<path id="路径 2"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2 10.666666666666666) rotate(0 1.6666666666666665 1.6499166666666671)"
d="M0,3.3L3.33,0 " />
<path id="路径 3"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.700199999999999 10.666666666666666) rotate(0 1.6499166666666671 1.6499166666666671)"
d="M3.3,3.3L0,0 " />
<path id="路径 4"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.666666666666666 2) rotate(0 1.6499166666666671 1.6499166666666665)"
d="M3.3,0L0,3.3 " />
<path id="路径 5"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.666666666666666 2.333333333333333) rotate(0 1.5 1.5)"
d="M0,0L0,3L3,3 " />
<path id="路径 6"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.333333333333333 2.333333333333333) rotate(0 1.5 1.5)"
d="M3,0L3,3L0,3 " />
<path id="路径 7"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(2.333333333333333 10.666666666666666) rotate(0 1.5 1.5)"
d="M3,3L3,0L0,0 " />
<path id="路径 8"
style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0"
transform="translate(10.666666666666666 10.666666666666666) rotate(0 1.4832500000000004 1.5)"
d="M0,3L0,0L2.97,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,0L3.33,3.3" transform="translate(2 2) rotate(0 1.6666666666666665 1.6499166666666665)"/><path id="路径 2" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3.3L3.33,0" transform="translate(2 10.666666666666666) rotate(0 1.6666666666666665 1.6499166666666671)"/><path id="路径 3" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3.3,3.3L0,0" transform="translate(10.700199999999999 10.666666666666666) rotate(0 1.6499166666666671 1.6499166666666671)"/><path id="路径 4" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3.3,0L0,3.3" transform="translate(10.666666666666666 2) rotate(0 1.6499166666666671 1.6499166666666665)"/><path id="路径 5" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,0L0,3L3,3" transform="translate(10.666666666666666 2.333333333333333) rotate(0 1.5 1.5)"/><path id="路径 6" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3,0L3,3L0,3" transform="translate(2.333333333333333 2.333333333333333) rotate(0 1.5 1.5)"/><path id="路径 7" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M3,3L3,0L0,0" transform="translate(2.333333333333333 10.666666666666666) rotate(0 1.5 1.5)"/><path id="路径 8" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M0,3L0,0L2.97,0" transform="translate(10.666666666666666 10.666666666666666) rotate(0 1.4832500000000004 1.5)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

1
app/icons/pin.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 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="M6.94515,1.72485c0.26101,0.26101 0.39152,0.57606 0.39152,0.94515v3.33c0,0.36909 -0.13051,0.68414 -0.39152,0.94515c-0.26101,0.26101 -0.57606,0.39152 -0.94515,0.39152h-3.33c-0.36909,0 -0.68414,-0.13051 -0.94515,-0.39152c-0.26101,-0.26101 -0.39152,-0.57606 -0.39152,-0.94515v-3.33c0,-0.36909 0.13051,-0.68414 0.39152,-0.94515c0.26101,-0.26101 0.57606,-0.39152 0.94515,-0.39152h3.33c0.36909,0 0.68414,0.13051 0.94515,0.39152zM2.66667,6c0,0.00222 0.00111,0.00333 0.00333,0.00333h3.33c0.00222,0 0.00333,-0.00111 0.00333,-0.00333v-3.33c0,-0.00222 -0.00111,-0.00333 -0.00333,-0.00333h-3.33c-0.00222,0 -0.00333,0.00111 -0.00333,0.00333z"></path><path id="路径 2" style="fill:#333333; opacity:1;" d="M6.94515,9.05822c0.26101,0.26101 0.39152,0.57607 0.39152,0.94518v3.33c0,0.36907 -0.13051,0.6841 -0.39152,0.9451c-0.26101,0.261 -0.57606,0.3915 -0.94515,0.3915h-3.33c-0.36909,0 -0.68414,-0.1305 -0.94515,-0.3915c-0.26101,-0.261 -0.39152,-0.57603 -0.39152,-0.9451v-3.33c0,-0.36911 0.13051,-0.68417 0.39152,-0.94518c0.26101,-0.26101 0.57606,-0.39151 0.94515,-0.39151h3.33c0.36909,0 0.68414,0.1305 0.94515,0.39151zM2.66667,13.3334c0,0.0022 0.00111,0.0033 0.00333,0.0033h3.33c0.00222,0 0.00333,-0.0011 0.00333,-0.0033v-3.33c0,-0.00227 -0.00111,-0.0034 -0.00333,-0.0034h-3.33c-0.00222,0 -0.00333,0.00113 -0.00333,0.0034z"></path><path id="路径 3" style="fill:#333333; opacity:1;" d="M13.7885,6.45515c-0.27727,0.27728 -0.59567,0.4921 -0.9552,0.64445c-0.37287,0.15805 -0.76283,0.23707 -1.1699,0.23707c-0.40727,0 -0.797,-0.07913 -1.1692,-0.23738c-0.35875,-0.15255 -0.67621,-0.36773 -0.95237,-0.64553c-0.27567,-0.2773 -0.48912,-0.59581 -0.64036,-0.95553c-0.15651,-0.37226 -0.23476,-0.76167 -0.23476,-1.16823c0,-0.40677 0.07836,-0.7959 0.23507,-1.1674c0.15143,-0.35898 0.36525,-0.67656 0.64144,-0.95275c0.27619,-0.27619 0.59379,-0.49001 0.95278,-0.64144c0.37147,-0.15672 0.7606,-0.23508 1.1674,-0.23508c0.40653,0 0.79593,0.07826 1.1682,0.23477c0.35973,0.15124 0.67823,0.36469 0.9555,0.64035c0.2778,0.27617 0.493,0.59363 0.6456,0.9524c0.1582,0.37217 0.2373,0.76189 0.2373,1.16915c0,0.40705 -0.079,0.79704 -0.237,1.16997c-0.1524,0.35951 -0.36723,0.67791 -0.6445,0.95518zM11.6634,2.66667c-0.46093,0 -0.8534,0.16199 -1.1774,0.48598c-0.324,0.32399 -0.486,0.71644 -0.486,1.17735c0,0.46231 0.16247,0.85689 0.4874,1.18374c0.32447,0.32639 0.71647,0.48959 1.176,0.48959c0.46087,0 0.85497,-0.16366 1.1823,-0.49098c0.32733,-0.32733 0.491,-0.72144 0.491,-1.18235c0,-0.4595 -0.1632,-0.85148 -0.4896,-1.17595c-0.32687,-0.32492 -0.72143,-0.48738 -1.1837,-0.48738z"></path><path id="路径 4" style="fill:#333333; opacity:1;" d="M14.2785,9.05822c0.261,0.26101 0.3915,0.57607 0.3915,0.94518v3.33c0,0.36907 -0.1305,0.6841 -0.3915,0.9451c-0.261,0.261 -0.57603,0.3915 -0.9451,0.3915h-3.33c-0.36911,0 -0.68417,-0.1305 -0.94518,-0.3915c-0.26101,-0.261 -0.39151,-0.57603 -0.39151,-0.9451v-3.33c0,-0.36911 0.1305,-0.68417 0.39151,-0.94518c0.26101,-0.26101 0.57607,-0.39151 0.94518,-0.39151h3.33c0.36907,0 0.6841,0.1305 0.9451,0.39151zM10,13.3334c0,0.0022 0.00113,0.0033 0.0034,0.0033h3.33c0.0022,0 0.0033,-0.0011 0.0033,-0.0033v-3.33c0,-0.00227 -0.0011,-0.0034 -0.0033,-0.0034h-3.33c-0.00227,0 -0.0034,0.00113 -0.0034,0.0034z"></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="M6.94515,1.72485c0.26101,0.26101 0.39152,0.57606 0.39152,0.94515v3.33c0,0.36909 -0.13051,0.68414 -0.39152,0.94515c-0.26101,0.26101 -0.57606,0.39152 -0.94515,0.39152h-3.33c-0.36909,0 -0.68414,-0.13051 -0.94515,-0.39152c-0.26101,-0.26101 -0.39152,-0.57606 -0.39152,-0.94515v-3.33c0,-0.36909 0.13051,-0.68414 0.39152,-0.94515c0.26101,-0.26101 0.57606,-0.39152 0.94515,-0.39152h3.33c0.36909,0 0.68414,0.13051 0.94515,0.39152zM2.66667,6c0,0.00222 0.00111,0.00333 0.00333,0.00333h3.33c0.00222,0 0.00333,-0.00111 0.00333,-0.00333v-3.33c0,-0.00222 -0.00111,-0.00333 -0.00333,-0.00333h-3.33c-0.00222,0 -0.00333,0.00111 -0.00333,0.00333z"/><path id="路径 2" style="fill:#333;opacity:1" d="M6.94515,9.05822c0.26101,0.26101 0.39152,0.57607 0.39152,0.94518v3.33c0,0.36907 -0.13051,0.6841 -0.39152,0.9451c-0.26101,0.261 -0.57606,0.3915 -0.94515,0.3915h-3.33c-0.36909,0 -0.68414,-0.1305 -0.94515,-0.3915c-0.26101,-0.261 -0.39152,-0.57603 -0.39152,-0.9451v-3.33c0,-0.36911 0.13051,-0.68417 0.39152,-0.94518c0.26101,-0.26101 0.57606,-0.39151 0.94515,-0.39151h3.33c0.36909,0 0.68414,0.1305 0.94515,0.39151zM2.66667,13.3334c0,0.0022 0.00111,0.0033 0.00333,0.0033h3.33c0.00222,0 0.00333,-0.0011 0.00333,-0.0033v-3.33c0,-0.00227 -0.00111,-0.0034 -0.00333,-0.0034h-3.33c-0.00222,0 -0.00333,0.00113 -0.00333,0.0034z"/><path id="路径 3" style="fill:#333;opacity:1" d="M13.7885,6.45515c-0.27727,0.27728 -0.59567,0.4921 -0.9552,0.64445c-0.37287,0.15805 -0.76283,0.23707 -1.1699,0.23707c-0.40727,0 -0.797,-0.07913 -1.1692,-0.23738c-0.35875,-0.15255 -0.67621,-0.36773 -0.95237,-0.64553c-0.27567,-0.2773 -0.48912,-0.59581 -0.64036,-0.95553c-0.15651,-0.37226 -0.23476,-0.76167 -0.23476,-1.16823c0,-0.40677 0.07836,-0.7959 0.23507,-1.1674c0.15143,-0.35898 0.36525,-0.67656 0.64144,-0.95275c0.27619,-0.27619 0.59379,-0.49001 0.95278,-0.64144c0.37147,-0.15672 0.7606,-0.23508 1.1674,-0.23508c0.40653,0 0.79593,0.07826 1.1682,0.23477c0.35973,0.15124 0.67823,0.36469 0.9555,0.64035c0.2778,0.27617 0.493,0.59363 0.6456,0.9524c0.1582,0.37217 0.2373,0.76189 0.2373,1.16915c0,0.40705 -0.079,0.79704 -0.237,1.16997c-0.1524,0.35951 -0.36723,0.67791 -0.6445,0.95518zM11.6634,2.66667c-0.46093,0 -0.8534,0.16199 -1.1774,0.48598c-0.324,0.32399 -0.486,0.71644 -0.486,1.17735c0,0.46231 0.16247,0.85689 0.4874,1.18374c0.32447,0.32639 0.71647,0.48959 1.176,0.48959c0.46087,0 0.85497,-0.16366 1.1823,-0.49098c0.32733,-0.32733 0.491,-0.72144 0.491,-1.18235c0,-0.4595 -0.1632,-0.85148 -0.4896,-1.17595c-0.32687,-0.32492 -0.72143,-0.48738 -1.1837,-0.48738z"/><path id="路径 4" style="fill:#333;opacity:1" d="M14.2785,9.05822c0.261,0.26101 0.3915,0.57607 0.3915,0.94518v3.33c0,0.36907 -0.1305,0.6841 -0.3915,0.9451c-0.261,0.261 -0.57603,0.3915 -0.9451,0.3915h-3.33c-0.36911,0 -0.68417,-0.1305 -0.94518,-0.3915c-0.26101,-0.261 -0.39151,-0.57603 -0.39151,-0.9451v-3.33c0,-0.36911 0.1305,-0.68417 0.39151,-0.94518c0.26101,-0.26101 0.57607,-0.39151 0.94518,-0.39151h3.33c0.36907,0 0.6841,0.1305 0.9451,0.39151zM10,13.3334c0,0.0022 0.00113,0.0033 0.0034,0.0033h3.33c0.0022,0 0.0033,-0.0011 0.0033,-0.0033v-3.33c0,-0.00227 -0.0011,-0.0034 -0.0033,-0.0034h-3.33c-0.00227,0 -0.0034,0.00113 -0.0034,0.0034z"/></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

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.3; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(5.333333333333333 1.3333333333333333) rotate(0 4.666666666666666 4.666666666666666)" d="M1.36683 1.36683L2.77683 2.77683 M4.66667 0L4.66667 2 M4.66667 2L4.66667 0 M7.9623 1.36683L6.5523 2.77683 M6.5523 2.77683L7.9623 1.36683 M9.33333 4.66667L7.33333 4.66667 M7.33333 4.66667L9.33333 4.66667 M7.9623 7.9623L6.5523 6.5523 M6.5523 6.5523L7.9623 7.9623 M4.66667 9.33333L4.66667 7.33333 M4.66667 7.33333L4.66667 9.33333 M1.36683 7.9623L2.77683 6.5523 M2.77683 6.5523L1.36683 7.9623 M0 4.66667L2 4.66667 M2 4.66667L0 4.66667 " /><path id="路径 9" style="stroke:#333333; stroke-width:1.3333333333333333; stroke-opacity:1; stroke-dasharray:0 0" transform="translate(1.847983333333333 6.1381) rotate(0 4.006941666666666 4.006933333333333)" d="M8.01,0L0,8.01 " /></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.3;stroke-opacity:1;stroke-dasharray:0 0" d="M1.36683 1.36683L2.77683 2.77683 M4.66667 0L4.66667 2 M4.66667 2L4.66667 0 M7.9623 1.36683L6.5523 2.77683 M6.5523 2.77683L7.9623 1.36683 M9.33333 4.66667L7.33333 4.66667 M7.33333 4.66667L9.33333 4.66667 M7.9623 7.9623L6.5523 6.5523 M6.5523 6.5523L7.9623 7.9623 M4.66667 9.33333L4.66667 7.33333 M4.66667 7.33333L4.66667 9.33333 M1.36683 7.9623L2.77683 6.5523 M2.77683 6.5523L1.36683 7.9623 M0 4.66667L2 4.66667 M2 4.66667L0 4.66667" transform="translate(5.333333333333333 1.3333333333333333) rotate(0 4.666666666666666 4.666666666666666)"/><path id="路径 9" style="stroke:#333;stroke-width:1.3333333333333333;stroke-opacity:1;stroke-dasharray:0 0" d="M8.01,0L0,8.01" transform="translate(1.847983333333333 6.1381) rotate(0 4.006941666666666 4.006933333333333)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

1
app/icons/robot.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,17 +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 1.3333333333333333) rotate(0 6.333333333333333 6.5)"
d="M6.67,3.67C1.67,3.67 0,7.33 0,13C0,13 2,8 6.67,8L6.67,11.67L12.67,6L6.67,0L6.67,3.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="M6.67,3.67C1.67,3.67 0,7.33 0,13C0,13 2,8 6.67,8L6.67,11.67L12.67,6L6.67,0L6.67,3.67Z" transform="translate(2 1.3333333333333333) rotate(0 6.333333333333333 6.5)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 645 B

View File

@@ -1,33 +1 @@
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
<svg width="30" height="14" viewBox="0 0 120 30" xmlns="http://www.w3.org/2000/svg" fill="#fff">
<circle cx="15" cy="15" r="15" fill="var(--primary, red)">
<animate attributeName="r" from="15" to="15"
begin="0s" dur="0.8s"
values="15;9;15" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="1" to="1"
begin="0s" dur="0.8s"
values="1;.5;1" calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="60" cy="15" r="9" fill-opacity="0.3" fill="var(--primary, red)">
<animate attributeName="r" from="9" to="9"
begin="0s" dur="0.8s"
values="9;15;9" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="0.5" to="0.5"
begin="0s" dur="0.8s"
values=".5;1;.5" calcMode="linear"
repeatCount="indefinite" />
</circle>
<circle cx="105" cy="15" r="15" fill="var(--primary, red)">
<animate attributeName="r" from="15" to="15"
begin="0s" dur="0.8s"
values="15;9;15" calcMode="linear"
repeatCount="indefinite" />
<animate attributeName="fill-opacity" from="1" to="1"
begin="0s" dur="0.8s"
values="1;.5;1" calcMode="linear"
repeatCount="indefinite" />
</circle>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="14" fill="#fff" viewBox="0 0 120 30"><circle cx="15" cy="15" r="15" fill="var(--primary, red)"><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="15" repeatCount="indefinite" to="15" values="15;9;15"/><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from="1" repeatCount="indefinite" to="1" values="1;.5;1"/></circle><circle cx="60" cy="15" r="9" fill="var(--primary, red)" fill-opacity=".3"><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="9" repeatCount="indefinite" to="9" values="9;15;9"/><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from=".5" repeatCount="indefinite" to=".5" values=".5;1;.5"/></circle><circle cx="105" cy="15" r="15" fill="var(--primary, red)"><animate attributeName="r" begin="0s" calcMode="linear" dur="0.8s" from="15" repeatCount="indefinite" to="15" values="15;9;15"/><animate attributeName="fill-opacity" begin="0s" calcMode="linear" dur="0.8s" from="1" repeatCount="indefinite" to="1" values="1;.5;1"/></circle></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -2,18 +2,25 @@
import "./styles/globals.scss";
import "./styles/markdown.scss";
import "./styles/highlight.scss";
import { getBuildConfig } from "./config/build";
import { getClientConfig } from "./config/client";
import { type Metadata } from "next";
const buildConfig = getBuildConfig();
export const metadata = {
export const metadata: Metadata = {
title: "ChatGPT Next Web",
description: "Your personal ChatGPT Chat Bot.",
viewport: {
width: "device-width",
initialScale: 1,
maximumScale: 1,
},
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#fafafa" },
{ media: "(prefers-color-scheme: dark)", color: "#151515" },
],
appleWebApp: {
title: "ChatGPT Next Web",
statusBarStyle: "default",
},
themeColor: "#fafafa",
};
export default function RootLayout({
@@ -24,23 +31,8 @@ export default function RootLayout({
return (
<html lang="en">
<head>
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta
name="theme-color"
content="#151515"
media="(prefers-color-scheme: dark)"
/>
<meta name="version" content={buildConfig.commitId} />
<meta name="config" content={JSON.stringify(getClientConfig())} />
<link rel="manifest" href="/site.webmanifest"></link>
<link rel="preconnect" href="https://fonts.googleapis.com"></link>
<link rel="preconnect" href="https://fonts.gstatic.com"></link>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;700;900&display=swap"
rel="stylesheet"
></link>
<script src="/serviceWorkerRegister.js" defer></script>
</head>
<body>{children}</body>

301
app/locales/ar.ts Normal file
View File

@@ -0,0 +1,301 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
const ar: PartialLocaleType = {
WIP: "قريبًا...",
Error: {
Unauthorized:
"غير مصرح بالوصول، يرجى إدخال رمز الوصول [auth](/#/auth) في صفحة المصادقة.",
},
Auth: {
Title: "تحتاج إلى رمز الوصول",
Tips: "يرجى إدخال رمز الوصول أدناه",
SubTips: "أو أدخل مفتاح واجهة برمجة تطبيقات OpenAI الخاص بك",
Input: "رمز الوصول",
Confirm: "تأكيد",
Later: "لاحقًا",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} رسائل`,
},
Chat: {
SubTitle: (count: number) => ` ${count} رسائل مع ChatGPT`,
Actions: {
ChatList: "الانتقال إلى قائمة الدردشة",
CompressedHistory: "ملخص ضغط ذاكرة التاريخ",
Export: "تصدير جميع الرسائل كـ Markdown",
Copy: "نسخ",
Stop: "توقف",
Retry: "إعادة المحاولة",
Delete: "حذف",
},
InputActions: {
Stop: "توقف",
ToBottom: "إلى آخر",
Theme: {
auto: "تلقائي",
light: "نمط فاتح",
dark: "نمط داكن",
},
Prompt: "الاقتراحات",
Masks: "الأقنعة",
Clear: "مسح السياق",
Settings: "الإعدادات",
},
Rename: "إعادة تسمية الدردشة",
Typing: "كتابة...",
Input: (submitKey: string) => {
var inputHints = ` اضغط على ${submitKey} للإرسال`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += "، Shift + Enter للإنشاء";
}
return inputHints + "، / للبحث في الاقتراحات";
},
Send: "إرسال",
Config: {
Reset: "إعادة التعيين إلى الإعدادات الافتراضية",
SaveAs: "حفظ كأقنعة",
},
},
Export: {
Title: "تصدير الرسائل",
Copy: "نسخ الكل",
Download: "تنزيل",
MessageFromYou: "رسالة منك",
MessageFromChatGPT: "رسالة من ChatGPT",
Share: "مشاركة على ShareGPT",
Format: {
Title: "صيغة التصدير",
SubTitle: "Markdown أو صورة PNG",
},
IncludeContext: {
Title: "تضمين السياق",
SubTitle: "تصدير اقتراحات السياق في الأقنعة أم لا",
},
Steps: {
Select: "تحديد",
Preview: "معاينة",
},
},
Select: {
Search: "بحث",
All: "تحديد الكل",
Latest: "تحديد أحدث",
Clear: "مسح",
},
Memory: {
Title: "اقتراحات الذاكرة",
EmptyContent: "لا شيء حتى الآن.",
Send: "إرسال الذاكرة",
Copy: "نسخ الذاكرة",
Reset: "إعادة التعيين",
ResetConfirm:
"سيؤدي إعادة التعيين إلى مسح سجل المحادثة الحالي والذاكرة التاريخية. هل أنت متأكد أنك تريد الاستمرار؟",
},
Home: {
NewChat: "دردشة جديدة",
DeleteChat: "هل تريد تأكيد حذف المحادثة المحددة؟",
DeleteToast: "تم حذف الدردشة",
Revert: "التراجع",
},
Settings: {
Title: "الإعدادات",
SubTitle: "جميع الإعدادات",
Lang: {
Name: "Language", // تنبيه: إذا كنت ترغب في إضافة ترجمة جديدة، يرجى عدم ترجمة هذه القيمة وتركها "Language"
All: "كل اللغات",
},
Avatar: "الصورة الرمزية",
FontSize: {
Title: "حجم الخط",
SubTitle: "ضبط حجم الخط لمحتوى الدردشة",
},
InjectSystemPrompts: {
Title: "حقن تلميحات النظام",
SubTitle:
"قم بإضافة تلميحة نظام محاكاة ChatGPT إلى بداية قائمة الرسائل المُطلَبة في كل طلب",
},
InputTemplate: {
Title: "نموذج الإدخال",
SubTitle: "سيتم ملء أحدث رسالة في هذا النموذج",
},
Update: {
Version: (x: string) => ` الإصدار: ${x}`,
IsLatest: "أحدث إصدار",
CheckUpdate: "التحقق من التحديث",
IsChecking: "جارٍ التحقق من التحديث...",
FoundUpdate: (x: string) => ` تم العثور على إصدار جديد: ${x}`,
GoToUpdate: "التحديث",
},
SendKey: "مفتاح الإرسال",
Theme: "السمة",
TightBorder: "حدود ضيقة",
SendPreviewBubble: {
Title: "عرض معاينة الـ Send",
SubTitle: "معاينة Markdown في فقاعة",
},
Mask: {
Splash: {
Title: "شاشة تظهر الأقنعة",
SubTitle: "عرض شاشة تظهر الأقنعة قبل بدء الدردشة الجديدة",
},
},
Prompt: {
Disable: {
Title: "تعطيل الاكتمال التلقائي",
SubTitle: "اكتب / لتشغيل الاكتمال التلقائي",
},
List: "قائمة الاقتراحات",
ListCount: (builtin: number, custom: number) => `
${builtin} مدمجة، ${custom} تم تعريفها من قبل المستخدم`,
Edit: "تعديل",
Modal: {
Title: "قائمة الاقتراحات",
Add: "إضافة واحدة",
Search: "البحث في الاقتراحات",
},
EditModal: {
Title: "تحرير الاقتراح",
},
},
HistoryCount: {
Title: "عدد الرسائل المرفقة",
SubTitle: "عدد الرسائل المرسلة المرفقة في كل طلب",
},
CompressThreshold: {
Title: "حد الضغط للتاريخ",
SubTitle: "سيتم الضغط إذا تجاوزت طول الرسائل غير المضغوطة الحد المحدد",
},
Token: {
Title: "مفتاح API",
SubTitle: "استخدم مفتاحك لتجاوز حد رمز الوصول",
Placeholder: "مفتاح OpenAI API",
},
Usage: {
Title: "رصيد الحساب",
SubTitle(used: any, total: any) {
return `تم استخدام $${used} من هذا الشهر، الاشتراك ${total}`;
},
IsChecking: "جارٍ التحقق...",
Check: "التحقق",
NoAccess: "أدخل مفتاح API للتحقق من الرصيد",
},
AccessCode: {
Title: "رمز الوصول",
SubTitle: "تم تمكين التحكم في الوصول",
Placeholder: "رمز الوصول المطلوب",
},
Endpoint: {
Title: "نقطة النهاية",
SubTitle: "يجب أن تبدأ نقطة النهاية المخصصة بـ http(s)://",
},
Model: "النموذج",
Temperature: {
Title: "الحرارة",
SubTitle: "قيمة أكبر تجعل الإخراج أكثر عشوائية",
},
MaxTokens: {
Title: "الحد الأقصى للرموز",
SubTitle: "الحد الأقصى لعدد الرموز المدخلة والرموز المُنشأة",
},
PresencePenalty: {
Title: "تأثير الوجود",
SubTitle: "قيمة أكبر تزيد من احتمالية التحدث عن مواضيع جديدة",
},
FrequencyPenalty: {
Title: "تأثير التكرار",
SubTitle: "قيمة أكبر تقلل من احتمالية تكرار نفس السطر",
},
},
Store: {
DefaultTopic: "محادثة جديدة",
BotHello: "مرحبًا! كيف يمكنني مساعدتك اليوم؟",
Error: "حدث خطأ ما، يرجى المحاولة مرة أخرى في وقت لاحق.",
Prompt: {
History: (content: string) => "هذا ملخص لسجل الدردشة كمراجعة: " + content,
Topic:
"يرجى إنشاء عنوان يتكون من أربع إلى خمس كلمات يلخص محادثتنا دون أي مقدمة أو ترقيم أو علامات ترقيم أو نقاط أو رموز إضافية. قم بإزالة علامات التنصيص المحيطة.",
Summarize:
"قم بتلخيص النقاش بشكل موجز في 200 كلمة أو أقل لاستخدامه كاقتراح للسياق في المستقبل.",
},
},
Copy: {
Success: "تم النسخ إلى الحافظة",
Failed: "فشلت عملية النسخ، يرجى منح الإذن للوصول إلى الحافظة",
},
Context: {
Toast: (x: any) => `مع ${x} اقتراحًا ذا سياق`,
Edit: "الاقتراحات السياقية والذاكرة",
Add: "إضافة اقتراح",
Clear: "مسح السياق",
Revert: "التراجع",
},
Plugin: {
Name: "المكوّن الإضافي",
},
FineTuned: {
Sysmessage: "أنت مساعد ي",
},
Mask: {
Name: "الأقنعة",
Page: {
Title: "قالب الاقتراح",
SubTitle: (count: number) => `${count} قوالب الاقتراح`,
Search: "البحث في القوالب",
Create: "إنشاء",
},
Item: {
Info: (count: number) => `${count} اقتراحات`,
Chat: "الدردشة",
View: "عرض",
Edit: "تعديل",
Delete: "حذف",
DeleteConfirm: "تأكيد الحذف؟",
},
EditModal: {
Title: (readonly: boolean) => `
تعديل قالب الاقتراح ${readonly ? "(للقراءة فقط)" : ""}`,
Download: "تنزيل",
Clone: "استنساخ",
},
Config: {
Avatar: "صورة الروبوت",
Name: "اسم الروبوت",
Sync: {
Title: "استخدام الإعدادات العامة",
SubTitle: "استخدام الإعدادات العامة في هذه الدردشة",
Confirm: "تأكيد الاستبدال بالإعدادات المخصصة بالإعدادات العامة؟",
},
HideContext: {
Title: "إخفاء اقتراحات السياق",
SubTitle: "عدم عرض اقتراحات السياق في الدردشة",
},
},
},
NewChat: {
Return: "العودة",
Skip: "ابدأ فقط",
Title: "اختيار قناع",
SubTitle: "دردشة مع الروح وراء القناع",
More: "المزيد",
NotShow: "عدم العرض مرة أخرى",
ConfirmNoShow: "تأكيد تعطيله؟ يمكنك تمكينه في الإعدادات لاحقًا.",
},
UI: {
Confirm: "تأكيد",
Cancel: "إلغاء",
Close: "إغلاق",
Create: "إنشاء",
Edit: "تعديل",
},
Exporter: {
Model: "النموذج",
Messages: "الرسائل",
Topic: "الموضوع",
Time: "الوقت",
},
};
export default ar;

346
app/locales/bn.ts Normal file
View File

@@ -0,0 +1,346 @@
import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "./index";
const bn: PartialLocaleType = {
WIP: "শীঘ্রই আসছে...",
Error: {
Unauthorized:
"অননুমোদিত অ্যাক্সেস, অনুগ্রহ করে [অথোরাইজশন](/#/auth) পৃষ্ঠায় অ্যাক্সেস কোড ইনপুট করুন।",
},
Auth: {
Title: "একটি অ্যাক্সেস কোড প্রয়োজন",
Tips: "নীচে অ্যাক্সেস কোড ইনপুট করুন",
SubTips: "অথবা আপনার OpenAI API কী প্রবেশ করুন",
Input: "অ্যাক্সেস কোড",
Confirm: "নিশ্চিত করুন",
Later: "পরে",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} টি বার্তা`,
},
Chat: {
SubTitle: (count: number) => `${count} টি বার্তা`,
Actions: {
ChatList: "চ্যাট তালিকায় যান",
CompressedHistory: "সংক্ষিপ্ত ইতিহাস মেমোরি প্রম্পট",
Export: "সমস্ত বার্তা মার্কডাউন হিসাবে রপ্তানি করুন",
Copy: "কপি",
Stop: "বন্ধ করুন",
Retry: "পুনরায় চেষ্টা করুন",
Pin: "পিন করুন",
PinToastContent: "পিন করা হয়েছে ২টি বার্তা প্রম্পটে",
PinToastAction: "দেখুন",
Delete: "মুছে ফেলুন",
Edit: "সম্পাদন করুন",
},
Commands: {
new: "নতুন চ্যাট শুরু করুন",
newm: "মাস্ক সহ নতুন চ্যাট শুরু করুন",
next: "পরবর্তী চ্যাট",
prev: "পূর্ববর্তী চ্যাট",
clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
del: "চ্যাট মুছুন",
},
InputActions: {
Stop: "বন্ধ করুন",
ToBottom: "সর্বশেষতম দিকে",
Theme: {
auto: "অটো",
light: "হালকা থিম",
dark: "ডার্ক থিম",
},
Prompt: "প্রম্পটগুলিতে",
Masks: "মাস্কগুলি",
Clear: "সংশ্লিষ্টতাবদ্ধকরণ পরিষ্কার করুন",
Settings: "সেটিংস",
},
Rename: "চ্যাট পুনঃনামকরণ করুন",
Typing: "টাইপিং...",
Input: (submitKey: string) => {
var inputHints = `${submitKey} to send`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter to wrap";
}
return inputHints + ", / to search prompts, : to use commands";
},
Send: "প্রেরণ করুন",
Config: {
Reset: "ডিফল্টে রিসেট করুন",
SaveAs: "মাস্ক হিসাবে সংরক্ষণ করুন",
},
},
Export: {
Title: "বার্তা রপ্তানিকরণ",
Copy: "সমস্তটি কপি করুন",
Download: "ডাউনলোড করুন",
MessageFromYou: "আপনার বার্তা",
MessageFromChatGPT: "চ্যাটজিপিটির বার্তা",
Share: "শেয়ার করুন শেয়ারজিপিটি তে",
Format: {
Title: "রপ্তানি ফরম্যাট",
SubTitle: "মার্কডাউন বা পিএনজি চিত্র",
},
IncludeContext: {
Title: "মাস্ক অন্তর্ভুক্ত করুন",
SubTitle: "মাস্কগুলি সংরক্ষণ করবেন না কি",
},
Steps: {
Select: "নির্বাচন করুন",
Preview: "প্রিভিউ করুন",
},
},
Select: {
Search: "অনুসন্ধান করুন",
All: "সমস্তটি নির্বাচন করুন",
Latest: "সর্বশেষতমটি নির্বাচন করুন",
Clear: "পরিষ্কার করুন",
},
Memory: {
Title: "মেমোরি প্রম্পট",
EmptyContent: "এখনও কিছুই নেই।",
Send: "মেমোরি প্রেরণ করুন",
Copy: "মেমোরি কপি করুন",
Reset: "পুনরায় নিশ্চিত করুন",
ResetConfirm:
"রিসেট করলে বর্তমান চ্যাট ইতিহাস এবং ঐতিহাসিক মেমোরি মুছে যাবে। পুনরায় নির্দিষ্ট করতে চান তা নিশ্চিত করতে চান?",
},
Home: {
NewChat: "নতুন চ্যাট",
DeleteChat: "নির্বাচিত সংলাপটি মুছতে নিশ্চিত করুন?",
DeleteToast: "চ্যাটটি মুছেছেন",
Revert: "পুনরায়",
},
Settings: {
Title: "সেটিংস",
SubTitle: "সমস্ত সেটিংস",
Danger: {
Reset: {
Title: "সমস্ত সেটিংস পুনঃনির্দেশ দিন",
SubTitle: "সকল সেটিংস ডিফল্টে পুনঃনির্দেশ দিতে",
Action: "পুনঃনির্দেশ দিন",
Confirm: "সমস্ত সেটিংস ডিফল্টে পুনঃনির্দেশ করতে নিশ্চিত করতে?",
},
Clear: {
Title: "সমস্ত তথ্য মুছুন",
SubTitle: "সমস্ত বার্তা এবং সেটিংস মুছুন",
Action: "মুছুন",
Confirm: "সমস্ত বার্তা এবং সেটিংস মুছে ফেলতে নিশ্চিত করতে?",
},
},
Lang: {
Name: "বাংলা", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "সমস্ত ভাষা",
},
Avatar: "অবতার",
FontSize: {
Title: "ফন্ট সাইজ",
SubTitle: "চ্যাট সামগ্রীর ফন্ট সাইজ সংশোধন করুন",
},
InjectSystemPrompts: {
Title: "حقن تلميحات النظام",
SubTitle:
"قم بإضافة تلميحة نظام محاكاة ChatGPT إلى بداية قائمة الرسائل المُطلَبة في كل طلب",
},
InputTemplate: {
Title: "ইনপুট টেমপ্লেট",
SubTitle: "নতুনতম বার্তা এই টেমপ্লেটে পূরণ হবে",
},
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Latest version",
CheckUpdate: "Check Update",
IsChecking: "Checking update...",
FoundUpdate: (x: string) => `Found new version: ${x}`,
GoToUpdate: "Update",
},
SendKey: "প্রেরণ চাবি",
Theme: "থিম",
TightBorder: "সঙ্গতি সীমা",
SendPreviewBubble: {
Title: "প্রিভিউ বুলবুল প্রেরণ করুন",
SubTitle: "বুলবুলে মার্কডাউন প্রিভিউ করুন",
},
Mask: {
Splash: {
Title: "মাস্ক স্প্ল্যাশ স্ক্রিন",
SubTitle:
"নতুন চ্যাট শুরু করার আগে মাস্ক স্প্ল্যাশ স্ক্রিন প্রদর্শন করুন",
},
Builtin: {
Title: "মূলত মাস্ক গোপন করুন",
SubTitle: "মাস্ক তালিকা থেকে মূলত মাস্কগুলি লুকান",
},
},
Prompt: {
Disable: {
Title: "অটো-সম্পূর্ণতা নিষ্ক্রিয় করুন",
SubTitle: "অটো-সম্পূর্ণতা চালু করতে / ইনপুট করুন",
},
List: "প্রম্পট তালিকা",
ListCount: (builtin: number, custom: number) =>
`${builtin} built-in, ${custom} user-defined`,
Edit: "সম্পাদন করুন",
Modal: {
Title: "প্রম্পট তালিকা",
Add: "একটি যোগ করুন",
Search: "সন্ধান প্রম্পট",
},
EditModal: {
Title: "সম্পাদন করুন প্রম্পট",
},
},
HistoryCount: {
Title: "সংযুক্ত বার্তা সংখ্যা",
SubTitle: "প্রতি অনুরোধে প্রেরণ করা গেলে প্রেরণ করা হবে",
},
CompressThreshold: {
Title: "ইতিহাস সঙ্কুচিত করার সীমা",
SubTitle:
"নকুল বার্তা দৈর্ঘ্য সীমা অতিক্রান্ত হলে ঐ বার্তাটি সঙ্কুচিত হবে",
},
Token: {
Title: "অ্যাপি কী",
SubTitle: "অ্যাক্সেস কোড সীমা উপেক্ষা করতে আপনার কীটি ব্যবহার করুন",
Placeholder: "OpenAI API কী",
},
Usage: {
Title: "একাউন্ট ব্যালেন্স",
SubTitle(used: any, total: any) {
return `এই মাসে ব্যবহৃত $${used}, সাবস্ক্রিপশন $${total}`;
},
IsChecking: "চেক করা হচ্ছে...",
Check: "চেক",
NoAccess: "ব্যালেন্স চেক করতে অ্যাপি কী ইনপুট করুন",
},
AccessCode: {
Title: "অ্যাক্সেস কোড",
SubTitle: "অ্যাক্সেস নিয়ন্ত্রণ সক্রিয়",
Placeholder: "অ্যাক্সেস কোড প্রয়োজন",
},
Endpoint: {
Title: "ইনটারপয়েন্ট",
SubTitle: "কাস্টম এন্ডপয়েন্টটি হতে হবে http(s):// দিয়ে শুরু হতে হবে",
},
Model: "মডেল",
Temperature: {
Title: "তাপমাত্রা",
SubTitle: "আরতি মান বেশি করলে বেশি এলোমেলো আউটপুট হবে",
},
TopP: {
Title: "শীর্ষ পি",
SubTitle: "তাপমাত্রা সঙ্গে এই মান পরিবর্তন করবেন না",
},
MaxTokens: {
Title: "সর্বাধিক টোকেন",
SubTitle: "ইনপুট টোকেন এবং উৎপাদিত টোকেনের সর্বাধিক দৈর্ঘ্য",
},
PresencePenalty: {
Title: "উপস্থিতির জরিমানা",
SubTitle: "আরতি মান বেশি করলে নতুন বিষয়গুলি সম্ভাব্যতা বাড়াতে পারে",
},
FrequencyPenalty: {
Title: "ফ্রিকুয়েন্সি জরিমানা",
SubTitle:
"আরতি মান বাড়ালে একই লাইন পুনরায় ব্যাবহার করার সম্ভাবনা হ্রাস পায়",
},
},
Store: {
DefaultTopic: "নতুন সংলাপ",
BotHello: "হ্যালো! আজকে আপনাকে কিভাবে সাহায্য করতে পারি?",
Error: "কিছু নিয়ে ভুল হয়েছে, পরে আবার চেষ্টা করুন।",
Prompt: {
History: (content: string) =>
"এটি চ্যাট ইতিহাসের সংক্ষিপ্ত সংকলনের মতো: " + content,
Topic:
"আমাদের সংলাপটির চার থেকে পাঁচ শব্দের একটি শিরোনাম তৈরি করুন যা আমাদের আলাপের সংক্ষিপ্তসার হিসাবে যোগ হবে না, যেমন অভিবৃত্তি, বিন্যাস, উদ্ধৃতি, পূর্বচালক চিহ্ন, পূর্বরোবক্তির যেকোনো চিহ্ন বা অতিরিক্ত পাঠ। মেয়াদশেষ উদ্ধৃতি চেষ্টা করুন।",
Summarize:
"২০০ শব্দের লম্বা হয়ে মুহূর্তে আলোচনা সংক্ষেপের রপ্তানি করুন, যেটি ভবিষ্যতের প্রম্পট হিসাবে ব্যবহার করবেন।",
},
},
Copy: {
Success: "ক্লিপবোর্ডে কপি করা হয়েছে",
Failed: "কপি ব্যর্থ, অনুমতি প্রদান করার জন্য অনুমতি প্রদান করুন",
},
Context: {
Toast: (x: any) => `With ${x} contextual prompts`,
Edit: "বর্তমান চ্যাট সেটিংস",
Add: "একটি প্রম্পট যোগ করুন",
Clear: "সঙ্গতি পরিস্কার করুন",
Revert: "পূর্ববর্তী অবস্থানে ফিরে যান",
},
Plugin: {
Name: "প্লাগইন",
},
FineTuned: {
Sysmessage: "আপনি একটি সহকারী যা",
},
Mask: {
Name: "মাস্ক",
Page: {
Title: "প্রম্পট টেমপ্লেট",
SubTitle: (count: number) => `${count} টি প্রম্পট টেমপ্লেট`,
Search: "টেমপ্লেট অনুসন্ধান করুন",
Create: "তৈরি করুন",
},
Item: {
Info: (count: number) => `${count} প্রম্পট`,
Chat: "চ্যাট",
View: "দেখুন",
Edit: "সম্পাদন করুন",
Delete: "মুছে ফেলুন",
DeleteConfirm: "মুছে ফেলতে নিশ্চিত করুন?",
},
EditModal: {
Title: (readonly: boolean) =>
`প্রম্পট টেমপ্লেট সম্পাদন করুন ${readonly ? "(readonly)" : ""}`,
Download: "ডাউনলোড করুন",
Clone: "ক্লোন করুন",
},
Config: {
Avatar: "বট অবতার",
Name: "বটের নাম",
Sync: {
Title: "গ্লোবাল কনফিগ ব্যবহার করুন",
SubTitle: "এই চ্যাটে গ্লোবাল কনফিগ ব্যবহার করুন",
Confirm:
"গ্লোবাল কনফিগ দ্বারা কাস্টম কনফিগ ওভাররাইড করতে নিশ্চিত করতে?",
},
HideContext: {
Title: "সংশ্লিষ্টতা প্রম্পটগুলি লুকান",
SubTitle: "চ্যাটে সংশ্লিষ্টতা প্রম্পটগুলি দেখাবেন না",
},
Share: {
Title: "এই মাস্কটি শেয়ার করুন",
SubTitle: "এই মাস্কের একটি লিঙ্ক তৈরি করুন",
Action: "লিঙ্ক কপি করুন",
},
},
},
NewChat: {
Return: "ফিরে যান",
Skip: "শুরু করুন",
Title: "মাস্ক নির্বাচন করুন",
SubTitle: "মাস্কের পিছনে আত্মার সঙ্গে চ্যাট করুন",
More: "আরো খুঁজুন",
NotShow: "এখনও দেখাবেন না",
ConfirmNoShow:
"নিষ্ক্রিয় করতে নিশ্চিত করুন? পরে আপনি এটি সেটিংসে সক্ষম করতে পারবেন।",
},
UI: {
Confirm: "নিশ্চিত করুন",
Cancel: "বাতিল করুন",
Close: "বন্ধ করুন",
Create: "তৈরি করুন",
Edit: "সম্পাদন করুন",
},
Exporter: {
Model: "মডেল",
Messages: "বার্তা",
Topic: "টপিক",
Time: "সময়",
},
};
export default bn;

View File

@@ -1,16 +1,35 @@
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
const isApp = !!getClientConfig()?.isApp;
const cn = {
WIP: "该功能仍在开发中……",
Error: {
Unauthorized:
"现在是未授权状态,请点击左下角[设置](/#/settings)按钮输入访问密码。",
Unauthorized: isApp
? "检测到无效 API Key请前往[设置](/#/settings)页检查 API Key 是否配置正确。"
: "访问密码不正确或为空,请前往[登录](/#/auth)页输入正确的访问密码,或者在[设置](/#/settings)页填入你自己的 OpenAI API Key。",
},
Auth: {
Title: "需要密码",
Tips: "管理员开启了密码验证,请在下方填入访问码",
SubTips: "或者输入你的 OpenAI API 密钥",
Input: "在此处填写访问码",
Confirm: "确认",
Later: "稍后再说",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} 条对话`,
},
Chat: {
SubTitle: (count: number) => `与 ChatGPT 的 ${count} 条对话`,
SubTitle: (count: number) => ` ${count} 条对话`,
EditMessage: {
Title: "编辑消息记录",
Topic: {
Title: "聊天主题",
SubTitle: "更改当前聊天主题",
},
},
Actions: {
ChatList: "查看消息列表",
CompressedHistory: "查看压缩后的历史 Prompt",
@@ -18,7 +37,32 @@ const cn = {
Copy: "复制",
Stop: "停止",
Retry: "重试",
Pin: "固定",
PinToastContent: "已将 1 条对话固定至预设提示词",
PinToastAction: "查看",
Delete: "删除",
Edit: "编辑",
},
Commands: {
new: "新建聊天",
newm: "从面具新建聊天",
next: "下一个聊天",
prev: "上一个聊天",
clear: "清除上下文",
del: "删除聊天",
},
InputActions: {
Stop: "停止响应",
ToBottom: "滚到最新",
Theme: {
auto: "自动主题",
light: "亮色模式",
dark: "深色模式",
},
Prompt: "快捷指令",
Masks: "所有面具",
Clear: "清除聊天",
Settings: "对话设置",
},
Rename: "重命名对话",
Typing: "正在输入…",
@@ -27,28 +71,52 @@ const cn = {
if (submitKey === String(SubmitKey.Enter)) {
inputHints += "Shift + Enter 换行";
}
return inputHints + "/ 触发补全";
return inputHints + "/ 触发补全: 触发命令";
},
Send: "发送",
Config: {
Reset: "重置默认",
SaveAs: "存为面具",
Reset: "清除记忆",
SaveAs: "存为面具",
},
IsContext: "预设提示词",
},
Export: {
Title: "导出聊天记录为 Markdown",
Title: "分享聊天记录",
Copy: "全部复制",
Download: "下载文件",
Share: "分享到 ShareGPT",
MessageFromYou: "来自你的消息",
MessageFromChatGPT: "来自 ChatGPT 的消息",
Format: {
Title: "导出格式",
SubTitle: "可以导出 Markdown 文本或者 PNG 图片",
},
IncludeContext: {
Title: "包含面具上下文",
SubTitle: "是否在消息中展示面具上下文",
},
Steps: {
Select: "选取",
Preview: "预览",
},
Image: {
Toast: "正在生成截图",
Modal: "长按或右键保存图片",
},
},
Select: {
Search: "搜索消息",
All: "选取全部",
Latest: "最近几条",
Clear: "清除选中",
},
Memory: {
Title: "历史摘要",
EmptyContent: "对话内容过短,无需总结",
Send: "自动压缩聊天记录并作为上下文发送",
Copy: "复制摘要",
Reset: "重置对话",
ResetConfirm: "重置后将清空当前对话记录以及历史摘要,确认重置",
Reset: "[unused]",
ResetConfirm: "确认清空历史摘要",
},
Home: {
NewChat: "新的聊天",
@@ -58,33 +126,39 @@ const cn = {
},
Settings: {
Title: "设置",
SubTitle: "设置选项",
Actions: {
ClearAll: "清除所有数据",
ResetAll: "重置所有选项",
Close: "关闭",
ConfirmResetAll: "确认重置所有配置?",
ConfirmClearAll: "确认清除所有数据?",
SubTitle: "所有设置选项",
Danger: {
Reset: {
Title: "重置所有设置",
SubTitle: "重置所有设置项回默认值",
Action: "立即重置",
Confirm: "确认重置所有设置?",
},
Clear: {
Title: "清除所有数据",
SubTitle: "清除所有聊天、设置数据",
Action: "立即清除",
Confirm: "确认清除所有聊天、设置数据?",
},
},
Lang: {
Name: "Language",
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "所有语言",
Options: {
cn: "简体中文",
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
tr: "Türkçe",
jp: "日本語",
de: "Deutsch",
},
},
Avatar: "头像",
FontSize: {
Title: "字体大小",
SubTitle: "聊天内容的字体大小",
},
InjectSystemPrompts: {
Title: "注入系统级提示信息",
SubTitle: "强制给每次请求的消息列表开头添加一个模拟 ChatGPT 的系统提示",
},
InputTemplate: {
Title: "用户输入预处理",
SubTitle: "用户最新的一条消息会填充到此模板",
},
Update: {
Version: (x: string) => `当前版本:${x}`,
@@ -101,9 +175,62 @@ const cn = {
Title: "预览气泡",
SubTitle: "在预览气泡中预览 Markdown 内容",
},
AutoGenerateTitle: {
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: {
Title: "面具启动页",
SubTitle: "新建聊天时,展示面具启动页",
Splash: {
Title: "面具启动页",
SubTitle: "新建聊天时,展示面具启动页",
},
Builtin: {
Title: "隐藏内置面具",
SubTitle: "在所有面具列表中隐藏内置面具",
},
},
Prompt: {
Disable: {
@@ -148,48 +275,72 @@ const cn = {
},
AccessCode: {
Title: "访问密码",
SubTitle: "已开启加密访问",
SubTitle: "管理员已开启加密访问",
Placeholder: "请输入访问密码",
},
Endpoint: {
Title: "接口地址",
SubTitle: "除默认地址外,必须包含 http(s)://",
},
CustomModel: {
Title: "自定义模型名",
SubTitle: "增加自定义模型可选项,使用英文逗号隔开",
},
Model: "模型 (model)",
Temperature: {
Title: "随机性 (temperature)",
SubTitle: "值越大,回复越随机",
},
TopP: {
Title: "核采样 (top_p)",
SubTitle: "与随机性类似,但不要和随机性一起更改",
},
MaxTokens: {
Title: "单次回复限制 (max_tokens)",
SubTitle: "单次交互所用的最大 Token 数",
},
PresencePenlty: {
PresencePenalty: {
Title: "话题新鲜度 (presence_penalty)",
SubTitle: "值越大,越有可能扩展到新话题",
},
FrequencyPenalty: {
Title: "频率惩罚度 (frequency_penalty)",
SubTitle: "值越大,越有可能降低重复字词",
},
},
Store: {
DefaultTopic: "新的聊天",
BotHello: "有什么可以帮你的吗",
Error: "出错了,稍后重试吧",
Prompt: {
History: (content: string) =>
"这是 ai 和用户的历史聊天总结作为前情提要:" + content,
History: (content: string) => "这是历史聊天总结作为前情提要:" + content,
Topic:
"使用四到五个字直接返回这句话的简要主题,不要解释、不要标点、不要语气词、不要多余文本,如果没有主题,请直接返回“闲聊”",
Summarize:
"简要总结一下你和用户的对话,用作后续的上下文提示 prompt控制在 200 字以内",
"简要总结一下对话内容,用作后续的上下文提示 prompt控制在 200 字以内",
},
},
Copy: {
Success: "已写入剪切板",
Failed: "复制失败,请赋予剪切板权限",
},
Download: {
Success: "内容已下载到您的目录。",
Failed: "下载失败。",
},
Context: {
Toast: (x: any) => `已设置 ${x}前置上下文`,
Toast: (x: any) => `包含 ${x}预设提示词`,
Edit: "当前对话设置",
Add: "新增预设对话",
Add: "新增一条对话",
Clear: "上下文已清除",
Revert: "恢复上下文",
},
Plugin: {
Name: "插件",
},
FineTuned: {
Sysmessage: "你是一个助手",
},
Mask: {
Name: "面具",
Page: {
@@ -215,6 +366,20 @@ const cn = {
Config: {
Avatar: "角色头像",
Name: "角色名称",
Sync: {
Title: "使用全局设置",
SubTitle: "当前对话是否使用全局模型设置",
Confirm: "当前对话的自定义设置将会被自动覆盖,确认启用全局设置?",
},
HideContext: {
Title: "隐藏预设对话",
SubTitle: "隐藏后预设对话不会出现在聊天界面",
},
Share: {
Title: "分享此面具",
SubTitle: "生成此面具的直达链接",
Action: "复制链接",
},
},
},
NewChat: {
@@ -227,15 +392,37 @@ const cn = {
More: "查看全部",
},
URLCommand: {
Code: "检测到链接中已经包含访问码,是否自动填入?",
Settings: "检测到链接中包含了预制设置,是否自动填入?",
},
UI: {
Confirm: "确认",
Cancel: "取消",
Close: "关闭",
Create: "新建",
Edit: "编辑",
Export: "导出",
Import: "导入",
Sync: "同步",
Config: "配置",
},
Exporter: {
Model: "模型",
Messages: "消息",
Topic: "主题",
Time: "时间",
},
};
type DeepPartial<T> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;
export type LocaleType = typeof cn;
export type PartialLocaleType = DeepPartial<typeof cn>;
export default cn;

246
app/locales/cs.ts Normal file
View File

@@ -0,0 +1,246 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
const cs: PartialLocaleType = {
WIP: "V přípravě...",
Error: {
Unauthorized:
"Neoprávněný přístup, zadejte přístupový kód na [stránce](/#/auth) nastavení.",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} zpráv`,
},
Chat: {
SubTitle: (count: number) => `${count} zpráv s ChatGPT`,
Actions: {
ChatList: "Přejít na seznam chatů",
CompressedHistory: "Pokyn z komprimované paměti historie",
Export: "Exportovat všechny zprávy jako Markdown",
Copy: "Kopírovat",
Stop: "Zastavit",
Retry: "Zopakovat",
Delete: "Smazat",
},
Rename: "Přejmenovat chat",
Typing: "Píše...",
Input: (submitKey: string) => {
var inputHints = `${submitKey} pro odeslání`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter pro řádkování";
}
return inputHints + ", / pro vyhledávání pokynů";
},
Send: "Odeslat",
Config: {
Reset: "Obnovit výchozí",
SaveAs: "Uložit jako Masku",
},
},
Export: {
Title: "Všechny zprávy",
Copy: "Kopírovat vše",
Download: "Stáhnout",
MessageFromYou: "Zpráva od vás",
MessageFromChatGPT: "Zpráva z ChatGPT",
},
Memory: {
Title: "Pokyn z paměti",
EmptyContent: "Zatím nic.",
Send: "Odeslat paměť",
Copy: "Kopírovat paměť",
Reset: "Obnovit relaci",
ResetConfirm:
"Resetováním se vymaže historie aktuálních konverzací i paměť historie pokynů. Opravdu chcete provést obnovu?",
},
Home: {
NewChat: "Nový chat",
DeleteChat: "Potvrzujete smazání vybrané konverzace?",
DeleteToast: "Chat smazán",
Revert: "Zvrátit",
},
Settings: {
Title: "Nastavení",
SubTitle: "Všechna nastavení",
Lang: {
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "Všechny jazyky",
},
Avatar: "Avatar",
FontSize: {
Title: "Velikost písma",
SubTitle: "Nastavení velikosti písma obsahu chatu",
},
InjectSystemPrompts: {
Title: "Vložit systémové prompty",
SubTitle:
"Vynutit přidání simulovaného systémového promptu ChatGPT na začátek seznamu zpráv každého požadavku",
},
Update: {
Version: (x: string) => `Verze: ${x}`,
IsLatest: "Aktuální verze",
CheckUpdate: "Zkontrolovat aktualizace",
IsChecking: "Kontrola aktualizace...",
FoundUpdate: (x: string) => `Nalezena nová verze: ${x}`,
GoToUpdate: "Aktualizovat",
},
SendKey: "Odeslat klíč",
Theme: "Téma",
TightBorder: "Těsné ohraničení",
SendPreviewBubble: {
Title: "Odesílat chatovací bublinu s náhledem",
SubTitle: "Zobrazit v náhledu bubliny",
},
Mask: {
Splash: {
Title: "Úvodní obrazovka Masek",
SubTitle: "Před zahájením nového chatu zobrazte úvodní obrazovku Masek",
},
},
Prompt: {
Disable: {
Title: "Deaktivovat automatické dokončování",
SubTitle: "Zadejte / pro spuštění automatického dokončování",
},
List: "Seznam pokynů",
ListCount: (builtin: number, custom: number) =>
`${builtin} vestavěných, ${custom} uživatelských`,
Edit: "Upravit",
Modal: {
Title: "Seznam pokynů",
Add: "Přidat pokyn",
Search: "Hledat pokyny",
},
EditModal: {
Title: "Editovat pokyn",
},
},
HistoryCount: {
Title: "Počet připojených zpráv",
SubTitle: "Počet odeslaných připojených zpráv na žádost",
},
CompressThreshold: {
Title: "Práh pro kompresi historie",
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) {
return `Použito tento měsíc $${used}, předplaceno $${total}`;
},
IsChecking: "Kontroluji...",
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",
SubTitle: "Větší hodnota činí výstup náhodnějším",
},
MaxTokens: {
Title: "Max. počet tokenů",
SubTitle: "Maximální délka vstupního tokenu a generovaných tokenů",
},
PresencePenalty: {
Title: "Přítomnostní korekce",
SubTitle: "Větší hodnota zvyšuje pravděpodobnost nových témat.",
},
FrequencyPenalty: {
Title: "Frekvenční penalizace",
SubTitle:
"Větší hodnota snižující pravděpodobnost opakování stejného řádku",
},
},
Store: {
DefaultTopic: "Nová konverzace",
BotHello: "Ahoj! Jak mohu dnes pomoci?",
Error: "Něco se pokazilo, zkuste to prosím později.",
Prompt: {
History: (content: string) =>
"Toto je shrnutí historie chatu mezi umělou inteligencí a uživatelem v podobě rekapitulace: " +
content,
Topic:
"Vytvořte prosím název o čtyřech až pěti slovech vystihující průběh našeho rozhovoru bez jakýchkoli úvodních slov, interpunkčních znamének, uvozovek, teček, symbolů nebo dalšího textu. Odstraňte uvozovky.",
Summarize:
"Krátce shrň naši diskusi v rozsahu do 200 slov a použij ji jako podnět pro budoucí kontext.",
},
},
Copy: {
Success: "Zkopírováno do schránky",
Failed: "Kopírování selhalo, prosím, povolte přístup ke schránce",
},
Context: {
Toast: (x: any) => `Použití ${x} kontextových pokynů`,
Edit: "Kontextové a paměťové pokyny",
Add: "Přidat pokyn",
},
Plugin: {
Name: "Plugin",
},
FineTuned: {
Sysmessage: "Jste asistent, který",
},
Mask: {
Name: "Maska",
Page: {
Title: "Šablona pokynu",
SubTitle: (count: number) => `${count} šablon pokynů`,
Search: "Hledat v šablonách",
Create: "Vytvořit",
},
Item: {
Info: (count: number) => `${count} pokynů`,
Chat: "Chat",
View: "Zobrazit",
Edit: "Upravit",
Delete: "Smazat",
DeleteConfirm: "Potvrdit smazání?",
},
EditModal: {
Title: (readonly: boolean) =>
`Editovat šablonu pokynu ${readonly ? "(pouze ke čtení)" : ""}`,
Download: "Stáhnout",
Clone: "Duplikovat",
},
Config: {
Avatar: "Avatar Bota",
Name: "Jméno Bota",
},
},
NewChat: {
Return: "Zpět",
Skip: "Přeskočit",
Title: "Vyberte Masku",
SubTitle: "Chatovat s duší za Maskou",
More: "Najít více",
NotShow: "Nezobrazovat znovu",
ConfirmNoShow: "Potvrdit zakázáníMůžete jej povolit později v nastavení.",
},
UI: {
Confirm: "Potvrdit",
Cancel: "Zrušit",
Close: "Zavřít",
Create: "Vytvořit",
Edit: "Upravit",
},
Exporter: {
Model: "Model",
Messages: "Zprávy",
Topic: "Téma",
Time: "Čas",
},
};
export default cs;

View File

@@ -1,11 +1,11 @@
import { SubmitKey } from "../store/config";
import type { LocaleType } from "./index";
import type { PartialLocaleType } from "./index";
const de: LocaleType = {
const de: PartialLocaleType = {
WIP: "In Bearbeitung...",
Error: {
Unauthorized:
"Unbefugter Zugriff, bitte geben Sie den Zugangscode auf der Einstellungsseite ein.",
"Unbefugter Zugriff, bitte geben Sie den Zugangscode auf der [Einstellungsseite](/#/auth) ein.",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} Nachrichten`,
@@ -61,33 +61,21 @@ const de: LocaleType = {
Settings: {
Title: "Einstellungen",
SubTitle: "Alle Einstellungen",
Actions: {
ClearAll: "Alle Daten löschen",
ResetAll: "Alle Einstellungen zurücksetzen",
Close: "Schließen",
ConfirmResetAll:
"Möchten Sie wirklich alle Konfigurationen zurücksetzen?",
ConfirmClearAll: "Möchten Sie wirklich alle Chats zurücksetzen?",
},
Lang: {
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "All Languages",
Options: {
cn: "简体中文",
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
tr: "Türkçe",
jp: "日本語",
de: "Deutsch",
},
All: "Alle Sprachen",
},
Avatar: "Avatar",
FontSize: {
Title: "Schriftgröße",
SubTitle: "Schriftgröße des Chat-Inhalts anpassen",
},
InjectSystemPrompts: {
Title: "System-Prompts einfügen",
SubTitle:
"Erzwingt das Hinzufügen eines simulierten systemweiten Prompts von ChatGPT am Anfang der Nachrichtenliste bei jeder Anfrage",
},
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Neueste Version",
@@ -104,8 +92,10 @@ const de: LocaleType = {
SubTitle: "Preview markdown in bubble",
},
Mask: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
Splash: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
},
},
Prompt: {
Disable: {
@@ -163,11 +153,16 @@ const de: LocaleType = {
Title: "Max Tokens", //Maximale Token
SubTitle: "Maximale Anzahl der Anfrage- plus Antwort-Token",
},
PresencePenlty: {
PresencePenalty: {
Title: "Presence Penalty", //Anwesenheitsstrafe
SubTitle:
"Ein größerer Wert erhöht die Wahrscheinlichkeit, dass über neue Themen gesprochen wird",
},
FrequencyPenalty: {
Title: "Frequency Penalty", // HäufigkeitStrafe
SubTitle:
"Ein größerer Wert, der die Wahrscheinlichkeit verringert, dass dieselbe Zeile wiederholt wird",
},
},
Store: {
DefaultTopic: "Neues Gespräch",
@@ -197,6 +192,9 @@ const de: LocaleType = {
Plugin: {
Name: "Plugin",
},
FineTuned: {
Sysmessage: "Du bist ein Assistent, der",
},
Mask: {
Name: "Mask",
Page: {
@@ -241,6 +239,12 @@ const de: LocaleType = {
Create: "Create",
Edit: "Edit",
},
Exporter: {
Model: "Modell",
Messages: "Nachrichten",
Topic: "Thema",
Time: "Zeit",
},
};
export default de;

View File

@@ -1,17 +1,37 @@
import { getClientConfig } from "../config/client";
import { SubmitKey } from "../store/config";
import type { LocaleType } from "./index";
import { LocaleType } from "./index";
// if you are adding a new translation, please use PartialLocaleType instead of LocaleType
const isApp = !!getClientConfig()?.isApp;
const en: LocaleType = {
WIP: "Coming Soon...",
Error: {
Unauthorized:
"Unauthorized access, please enter access code in settings page.",
Unauthorized: isApp
? "Invalid API Key, please check it in [Settings](/#/settings) page."
: "Unauthorized access, please enter access code in [auth](/#/auth) page, or enter your OpenAI API Key.",
},
Auth: {
Title: "Need Access Code",
Tips: "Please enter access code below",
SubTips: "Or enter your OpenAI API Key",
Input: "access code",
Confirm: "Confirm",
Later: "Later",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} messages`,
},
Chat: {
SubTitle: (count: number) => `${count} messages with ChatGPT`,
SubTitle: (count: number) => `${count} messages`,
EditMessage: {
Title: "Edit All Messages",
Topic: {
Title: "Topic",
SubTitle: "Change the current topic",
},
},
Actions: {
ChatList: "Go To Chat List",
CompressedHistory: "Compressed History Memory Prompt",
@@ -19,7 +39,32 @@ const en: LocaleType = {
Copy: "Copy",
Stop: "Stop",
Retry: "Retry",
Pin: "Pin",
PinToastContent: "Pinned 1 messages to contextual prompts",
PinToastAction: "View",
Delete: "Delete",
Edit: "Edit",
},
Commands: {
new: "Start a new chat",
newm: "Start a new chat with mask",
next: "Next Chat",
prev: "Previous Chat",
clear: "Clear Context",
del: "Delete Chat",
},
InputActions: {
Stop: "Stop",
ToBottom: "To Latest",
Theme: {
auto: "Auto",
light: "Light Theme",
dark: "Dark Theme",
},
Prompt: "Prompts",
Masks: "Masks",
Clear: "Clear Context",
Settings: "Settings",
},
Rename: "Rename Chat",
Typing: "Typing…",
@@ -28,20 +73,44 @@ const en: LocaleType = {
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter to wrap";
}
return inputHints + ", / to search prompts";
return inputHints + ", / to search prompts, : to use commands";
},
Send: "Send",
Config: {
Reset: "Reset to Default",
SaveAs: "Save as Mask",
},
IsContext: "Contextual Prompt",
},
Export: {
Title: "All Messages",
Title: "Export Messages",
Copy: "Copy All",
Download: "Download",
MessageFromYou: "Message From You",
MessageFromChatGPT: "Message From ChatGPT",
Share: "Share to ShareGPT",
Format: {
Title: "Export Format",
SubTitle: "Markdown or PNG Image",
},
IncludeContext: {
Title: "Including Context",
SubTitle: "Export context prompts in mask or not",
},
Steps: {
Select: "Select",
Preview: "Preview",
},
Image: {
Toast: "Capturing Image...",
Modal: "Long press or right click to save image",
},
},
Select: {
Search: "Search",
All: "Select All",
Latest: "Select Latest",
Clear: "Clear",
},
Memory: {
Title: "Memory Prompt",
@@ -61,32 +130,38 @@ const en: LocaleType = {
Settings: {
Title: "Settings",
SubTitle: "All Settings",
Actions: {
ClearAll: "Clear All Data",
ResetAll: "Reset All Settings",
Close: "Close",
ConfirmResetAll: "Are you sure you want to reset all configurations?",
ConfirmClearAll: "Are you sure you want to reset all data?",
Danger: {
Reset: {
Title: "Reset All Settings",
SubTitle: "Reset all setting items to default",
Action: "Reset",
Confirm: "Confirm to reset all settings to default?",
},
Clear: {
Title: "Clear All Data",
SubTitle: "Clear all messages and settings",
Action: "Clear",
Confirm: "Confirm to clear all messages and settings?",
},
},
Lang: {
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "All Languages",
Options: {
cn: "简体中文",
en: "English",
tw: "繁體中文",
es: "Español",
it: "Italiano",
tr: "Türkçe",
jp: "日本語",
de: "Deutsch",
},
},
Avatar: "Avatar",
FontSize: {
Title: "Font Size",
SubTitle: "Adjust font size of chat content",
},
InjectSystemPrompts: {
Title: "Inject System Prompts",
SubTitle: "Inject a global system prompt for every request",
},
InputTemplate: {
Title: "Input Template",
SubTitle: "Newest message will be filled to this template",
},
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Latest version",
@@ -102,9 +177,63 @@ const en: LocaleType = {
Title: "Send Preview Bubble",
SubTitle: "Preview markdown in bubble",
},
AutoGenerateTitle: {
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: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
Splash: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
},
Builtin: {
Title: "Hide Builtin Masks",
SubTitle: "Hide builtin masks in mask list",
},
},
Prompt: {
Disable: {
@@ -152,20 +281,37 @@ const en: LocaleType = {
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",
},
Model: "Model",
Temperature: {
Title: "Temperature",
SubTitle: "A larger value makes the more random output",
},
TopP: {
Title: "Top P",
SubTitle: "Do not alter this value together with temperature",
},
MaxTokens: {
Title: "Max Tokens",
SubTitle: "Maximum length of input tokens and generated tokens",
},
PresencePenlty: {
PresencePenalty: {
Title: "Presence Penalty",
SubTitle:
"A larger value increases the likelihood to talk about new topics",
},
FrequencyPenalty: {
Title: "Frequency Penalty",
SubTitle:
"A larger value decreasing the likelihood to repeat the same line",
},
},
Store: {
DefaultTopic: "New Conversation",
@@ -173,26 +319,34 @@ const en: LocaleType = {
Error: "Something went wrong, please try again later.",
Prompt: {
History: (content: string) =>
"This is a summary of the chat history between the AI and the user as a recap: " +
content,
"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.",
Summarize:
"Summarize our discussion briefly in 200 words or less to use as a prompt for future context.",
"Summarize the discussion briefly in 200 words or less to use as a prompt for future context.",
},
},
Copy: {
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: "Contextual and Memory Prompts",
Edit: "Current Chat Settings",
Add: "Add a Prompt",
Clear: "Context Cleared",
Revert: "Revert",
},
Plugin: {
Name: "Plugin",
},
FineTuned: {
Sysmessage: "You are an assistant that",
},
Mask: {
Name: "Mask",
Page: {
@@ -218,15 +372,29 @@ const en: LocaleType = {
Config: {
Avatar: "Bot Avatar",
Name: "Bot Name",
Sync: {
Title: "Use Global Config",
SubTitle: "Use global config in this chat",
Confirm: "Confirm to override custom config with global config?",
},
HideContext: {
Title: "Hide Context Prompts",
SubTitle: "Do not show in-context prompts in chat",
},
Share: {
Title: "Share This Mask",
SubTitle: "Generate a link to this mask",
Action: "Copy Link",
},
},
},
NewChat: {
Return: "Return",
Skip: "Skip",
Skip: "Just Start",
Title: "Pick a Mask",
SubTitle: "Chat with the Soul behind the Mask",
More: "Find More",
NotShow: "Not Show Again",
NotShow: "Never Show Again",
ConfirmNoShow: "Confirm to disableYou can enable it in settings later.",
},
@@ -236,6 +404,21 @@ const en: LocaleType = {
Close: "Close",
Create: "Create",
Edit: "Edit",
Export: "Export",
Import: "Import",
Sync: "Sync",
Config: "Config",
},
Exporter: {
Model: "Model",
Messages: "Messages",
Topic: "Topic",
Time: "Time",
},
URLCommand: {
Code: "Detected access code from url, confirm to apply? ",
Settings: "Detected settings from url, confirm to apply?",
},
};

View File

@@ -1,11 +1,11 @@
import { SubmitKey } from "../store/config";
import type { LocaleType } from "./index";
import type { PartialLocaleType } from "./index";
const es: LocaleType = {
const es: PartialLocaleType = {
WIP: "En construcción...",
Error: {
Unauthorized:
"Acceso no autorizado, por favor ingrese el código de acceso en la página de configuración.",
"Acceso no autorizado, por favor ingrese el código de acceso en la [página](/#/auth) de configuración.",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} mensajes`,
@@ -61,32 +61,21 @@ const es: LocaleType = {
Settings: {
Title: "Configuración",
SubTitle: "Todas las configuraciones",
Actions: {
ClearAll: "Borrar todos los datos",
ResetAll: "Restablecer todas las configuraciones",
Close: "Cerrar",
ConfirmResetAll: "Are you sure you want to reset all configurations?",
ConfirmClearAll: "Are you sure you want to reset all chat?",
},
Lang: {
Name: "Language",
All: "All Languages",
Options: {
cn: "简体中文",
en: "Inglés",
tw: "繁體中文",
es: "Español",
it: "Italiano",
tr: "Türkçe",
jp: "日本語",
de: "Deutsch",
},
Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language`
All: "Todos los idiomas",
},
Avatar: "Avatar",
FontSize: {
Title: "Tamaño de fuente",
SubTitle: "Ajustar el tamaño de fuente del contenido del chat",
},
InjectSystemPrompts: {
Title: "Inyectar Prompts del Sistema",
SubTitle:
"Agregar forzosamente un prompt de sistema simulado de ChatGPT al comienzo de la lista de mensajes en cada solicitud",
},
Update: {
Version: (x: string) => `Versión: ${x}`,
IsLatest: "Última versión",
@@ -103,8 +92,10 @@ const es: LocaleType = {
SubTitle: "Preview markdown in bubble",
},
Mask: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
Splash: {
Title: "Mask Splash Screen",
SubTitle: "Show a mask splash screen before starting new chat",
},
},
Prompt: {
Disable: {
@@ -161,11 +152,16 @@ const es: LocaleType = {
Title: "Máximo de tokens",
SubTitle: "Longitud máxima de tokens de entrada y tokens generados",
},
PresencePenlty: {
PresencePenalty: {
Title: "Penalización de presencia",
SubTitle:
"Un valor mayor aumenta la probabilidad de hablar sobre nuevos temas",
},
FrequencyPenalty: {
Title: "Penalización de frecuencia",
SubTitle:
"Un valor mayor que disminuye la probabilidad de repetir la misma línea",
},
},
Store: {
DefaultTopic: "Nueva conversación",
@@ -194,6 +190,9 @@ const es: LocaleType = {
Plugin: {
Name: "Plugin",
},
FineTuned: {
Sysmessage: "Eres un asistente que",
},
Mask: {
Name: "Mask",
Page: {
@@ -238,6 +237,12 @@ const es: LocaleType = {
Create: "Create",
Edit: "Edit",
},
Exporter: {
Model: "Modelo",
Messages: "Mensajes",
Topic: "Tema",
Time: "Time",
},
};
export default es;

317
app/locales/fr.ts Normal file
View File

@@ -0,0 +1,317 @@
import { SubmitKey } from "../store/config";
import type { PartialLocaleType } from "./index";
const fr: PartialLocaleType = {
WIP: "Prochainement...",
Error: {
Unauthorized:
"Accès non autorisé, veuillez saisir le code d'accès dans la [page](/#/auth) des paramètres.",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} messages en total`,
},
Chat: {
SubTitle: (count: number) => `${count} messages échangés avec ChatGPT`,
Actions: {
ChatList: "Aller à la liste de discussion",
CompressedHistory: "Mémoire d'historique compressée Prompt",
Export: "Exporter tous les messages en tant que Markdown",
Copy: "Copier",
Stop: "Arrêter",
Retry: "Réessayer",
Delete: "Supprimer",
Pin: "Épingler",
PinToastContent: "Épingler 2 messages à des messages contextuels",
PinToastAction: "Voir",
Edit: "Modifier",
},
Commands: {
new: "Commencer une nouvelle conversation",
newm: "Démarrer une nouvelle conversation avec un assistant",
next: "Conversation suivante",
prev: "Conversation précédente",
clear: "Effacer le contexte",
del: "Supprimer la Conversation",
},
InputActions: {
Stop: "Stop",
ToBottom: "Au dernier",
Theme: {
auto: "Auto",
light: "Thème clair",
dark: "Thème sombre",
},
Prompt: "Instructions",
Masks: "Assistants",
Clear: "Effacer le contexte",
Settings: "Réglages",
},
Rename: "Renommer la conversation",
Typing: "En train d'écrire…",
Input: (submitKey: string) => {
var inputHints = `Appuyez sur ${submitKey} pour envoyer`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter pour insérer un saut de ligne";
}
return inputHints + ", / pour rechercher des prompts";
},
Send: "Envoyer",
Config: {
Reset: "Restaurer les paramètres par défaut",
SaveAs: "Enregistrer en tant que masque",
},
},
Export: {
Title: "Tous les messages",
Copy: "Tout sélectionner",
Download: "Télécharger",
MessageFromYou: "Message de votre part",
MessageFromChatGPT: "Message de ChatGPT",
},
Memory: {
Title: "Prompt mémoire",
EmptyContent: "Rien encore.",
Send: "Envoyer la mémoire",
Copy: "Copier la mémoire",
Reset: "Réinitialiser la session",
ResetConfirm:
"La réinitialisation supprimera l'historique de la conversation actuelle ainsi que la mémoire de l'historique. Êtes-vous sûr de vouloir procéder à la réinitialisation?",
},
Home: {
NewChat: "Nouvelle discussion",
DeleteChat: "Confirmer la suppression de la conversation sélectionnée ?",
DeleteToast: "Conversation supprimée",
Revert: "Revenir en arrière",
},
Settings: {
Title: "Paramètres",
SubTitle: "Toutes les configurations",
Danger: {
Reset: {
Title: "Restaurer les paramètres",
SubTitle: "Restaurer les paramètres par défaut",
Action: "Reinitialiser",
Confirm: "Confirmer la réinitialisation des paramètres?",
},
Clear: {
Title: "Supprimer toutes les données",
SubTitle:
"Effacer toutes les données, y compris les conversations et les paramètres",
Action: "Supprimer",
Confirm: "Confirmer la suppression de toutes les données?",
},
},
Lang: {
Name: "Language", // ATTENTION : si vous souhaitez ajouter une nouvelle traduction, ne traduisez pas cette valeur, laissez-la sous forme de `Language`
All: "Toutes les langues",
},
Avatar: "Avatar",
FontSize: {
Title: "Taille des polices",
SubTitle: "Ajuste la taille de police du contenu de la conversation",
},
InjectSystemPrompts: {
Title: "Injecter des invites système",
SubTitle:
"Ajoute de force une invite système simulée de ChatGPT au début de la liste des messages pour chaque demande",
},
InputTemplate: {
Title: "Template",
SubTitle: "Le message le plus récent sera ajouté à ce template.",
},
Update: {
Version: (x: string) => `Version : ${x}`,
IsLatest: "Dernière version",
CheckUpdate: "Vérifier la mise à jour",
IsChecking: "Vérification de la mise à jour...",
FoundUpdate: (x: string) => `Nouvelle version disponible : ${x}`,
GoToUpdate: "Mise à jour",
},
SendKey: "Clé d'envoi",
Theme: "Thème",
TightBorder: "Bordure serrée",
SendPreviewBubble: {
Title: "Aperçu de l'envoi dans une bulle",
SubTitle: "Aperçu du Markdown dans une bulle",
},
Mask: {
Splash: {
Title: "Écran de masque",
SubTitle:
"Afficher un écran de masque avant de démarrer une nouvelle discussion",
},
Builtin: {
Title: "Masquer Les Assistants Intégrés",
SubTitle: "Masquer les assistants intégrés par défaut",
},
},
Prompt: {
Disable: {
Title: "Désactiver la saisie semi-automatique",
SubTitle: "Appuyez sur / pour activer la saisie semi-automatique",
},
List: "Liste de prompts",
ListCount: (builtin: number, custom: number) =>
`${builtin} intégré, ${custom} personnalisé`,
Edit: "Modifier",
Modal: {
Title: "Liste de prompts",
Add: "Ajouter un élément",
Search: "Rechercher des prompts",
},
EditModal: {
Title: "Modifier le prompt",
},
},
HistoryCount: {
Title: "Nombre de messages joints",
SubTitle: "Nombre de messages envoyés attachés par demande",
},
CompressThreshold: {
Title: "Seuil de compression de l'historique",
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) {
return `Épuisé ce mois-ci $${used}, abonnement $${total}`;
},
IsChecking: "Vérification...",
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",
SubTitle: "Une valeur plus élevée rendra les réponses plus aléatoires",
},
TopP: {
Title: "Top P",
SubTitle:
"Ne modifiez pas à moins que vous ne sachiez ce que vous faites",
},
MaxTokens: {
Title: "Limite de Tokens",
SubTitle: "Longueur maximale des tokens d'entrée et des tokens générés",
},
PresencePenalty: {
Title: "Pénalité de présence",
SubTitle:
"Une valeur plus élevée augmentera la probabilité d'introduire de nouveaux sujets",
},
FrequencyPenalty: {
Title: "Pénalité de fréquence",
SubTitle:
"Une valeur plus élevée diminuant la probabilité de répéter la même ligne",
},
},
Store: {
DefaultTopic: "Nouvelle conversation",
BotHello: "Bonjour ! Comment puis-je vous aider aujourd'hui ?",
Error: "Quelque chose s'est mal passé, veuillez réessayer plus tard.",
Prompt: {
History: (content: string) =>
"Ceci est un résumé de l'historique des discussions entre l'IA et l'utilisateur : " +
content,
Topic:
"Veuillez générer un titre de quatre à cinq mots résumant notre conversation sans introduction, ponctuation, guillemets, points, symboles ou texte supplémentaire. Supprimez les guillemets inclus.",
Summarize:
"Résumez brièvement nos discussions en 200 mots ou moins pour les utiliser comme prompt de contexte futur.",
},
},
Copy: {
Success: "Copié dans le presse-papiers",
Failed:
"La copie a échoué, veuillez accorder l'autorisation d'accès au presse-papiers",
},
Context: {
Toast: (x: any) => `Avec ${x} contextes de prompts`,
Edit: "Contextes et mémoires de prompts",
Add: "Ajouter un prompt",
},
Plugin: {
Name: "Extension",
},
FineTuned: {
Sysmessage: "Eres un asistente que",
},
Mask: {
Name: "Masque",
Page: {
Title: "Modèle de prompt",
SubTitle: (count: number) => `${count} modèles de prompts`,
Search: "Rechercher des modèles",
Create: "Créer",
},
Item: {
Info: (count: number) => `${count} prompts`,
Chat: "Discussion",
View: "Vue",
Edit: "Modifier",
Delete: "Supprimer",
DeleteConfirm: "Confirmer la suppression?",
},
EditModal: {
Title: (readonly: boolean) =>
`Modifier le modèle de prompt ${readonly ? "(en lecture seule)" : ""}`,
Download: "Télécharger",
Clone: "Dupliquer",
},
Config: {
Avatar: "Avatar de lassistant",
Name: "Nom de lassistant",
Sync: {
Title: "Utiliser la configuration globale",
SubTitle: "Utiliser la configuration globale dans cette conversation",
Confirm: "Voulez-vous definir votre configuration personnalisée ?",
},
HideContext: {
Title: "Masquer les invites contextuelles",
SubTitle: "Ne pas afficher les instructions contextuelles dans le chat",
},
Share: {
Title: "Partager ce masque",
SubTitle: "Générer un lien vers ce masque",
Action: "Copier le lien",
},
},
},
NewChat: {
Return: "Retour",
Skip: "Passer",
Title: "Choisir un assitant",
SubTitle: "Discutez avec l'âme derrière le masque",
More: "En savoir plus",
NotShow: "Ne pas afficher à nouveau",
ConfirmNoShow:
"Confirmez-vous vouloir désactiver cela? Vous pouvez le réactiver plus tard dans les paramètres.",
},
UI: {
Confirm: "Confirmer",
Cancel: "Annuler",
Close: "Fermer",
Create: "Créer",
Edit: "Éditer",
},
Exporter: {
Model: "Modèle",
Messages: "Messages",
Topic: "Sujet",
Time: "Temps",
},
};
export default fr;

393
app/locales/id.ts Normal file
View File

@@ -0,0 +1,393 @@
import { SubmitKey } from "../store/config";
import { PartialLocaleType } from "./index";
const id: PartialLocaleType = {
WIP: "Coming Soon...",
Error: {
Unauthorized: "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",
},
ChatItem: {
ChatItemCount: (count: number) => `${count} pesan`,
},
Chat: {
SubTitle: (count: number) => `${count} pesan`,
Actions: {
ChatList: "Buka Daftar Chat",
CompressedHistory: "Ekspor Riwayat Terkompresi",
Export: "Ekspor Semua Pesan sebagai Markdown",
Copy: "Salin",
Stop: "Berhenti",
Retry: "Coba Lagi",
Pin: "Pin",
PinToastContent: "2 pesan telah ditandai",
PinToastAction: "Lihat",
Delete: "Hapus",
Edit: "Edit",
},
Commands: {
new: "Mulai Chat Baru",
newm: "Mulai Chat Baru dengan Masks",
next: "Chat Selanjutnya",
prev: "Chat Sebelumnya",
clear: "Bersihkan Percakapan",
del: "Hapus Chat",
},
InputActions: {
Stop: "Berhenti",
ToBottom: "Ke Bagian Bawah",
Theme: {
auto: "Otomatis",
light: "Tema Terang",
dark: "Tema Gelap",
},
Prompt: "Prompts",
Masks: "Masks",
Clear: "Bersihkan Percakapan",
Settings: "Pengaturan",
},
Rename: "Ubah Nama Chat",
Typing: "Mengetik...",
Input: (submitKey: string) => {
var inputHints = `${submitKey} untuk mengirim`;
if (submitKey === String(SubmitKey.Enter)) {
inputHints += ", Shift + Enter untuk membalut";
}
return (
inputHints + ", / untuk mencari prompt, : untuk menggunakan perintah"
);
},
Send: "Kirim",
Config: {
Reset: "Reset ke Default",
SaveAs: "Simpan sebagai Masks",
},
},
Export: {
Title: "Ekspor Pesan",
Copy: "Salin Semua",
Download: "Unduh",
MessageFromYou: "Pesan dari Anda",
MessageFromChatGPT: "Pesan dari ChatGPT",
Share: "Bagikan ke ShareGPT",
Format: {
Title: "Format Ekspor",
SubTitle: "Markdown atau Gambar PNG",
},
IncludeContext: {
Title: "Sertakan Konteks",
SubTitle: "Apakah akan menyertakan masks",
},
Steps: {
Select: "Pilih",
Preview: "Pratinjau",
},
},
Select: {
Search: "Cari",
All: "Pilih Semua",
Latest: "Pilih Terbaru",
Clear: "Bersihkan",
},
Memory: {
Title: "Prompt Memori",
EmptyContent: "Belum ada yang tersedia.",
Send: "Kirim Memori",
Copy: "Salin Memori",
Reset: "Reset",
ResetConfirm:
"Jika Anda mereset, riwayat obrolan saat ini dan memori historis akan dihapus. Apakah Anda yakin ingin melakukan reset?",
},
Home: {
NewChat: "Obrolan Baru",
DeleteChat: "Anda yakin ingin menghapus percakapan yang dipilih?",
DeleteToast: "Percakapan telah dihapus",
Revert: "Kembali",
},
Settings: {
Title: "Pengaturan",
SubTitle: "Semua Pengaturan",
Danger: {
Reset: {
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?",
},
Clear: {
Title: "Hapus Semua Data",
SubTitle: "Semua data yang tersimpan secara lokal akan dihapus",
Action: "Hapus",
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",
},
Update: {
Version: (x: string) => `Version: ${x}`,
IsLatest: "Versi terbaru",
CheckUpdate: "Periksa Pembaruan",
IsChecking: "Memeriksa pembaruan...",
FoundUpdate: (x: string) => `Versi terbaru ditemukan: ${x}`,
GoToUpdate: "Perbarui Sekarang",
},
AutoGenerateTitle: {
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",
SendPreviewBubble: {
Title: "Pratinjau Obrolan",
SubTitle: "Pratinjau Obrolan dengan markdown",
},
Mask: {
Splash: {
Title: "Layar Pembuka Masks",
SubTitle:
"Tampilkan layar pembuka masks sebelum memulai percakapan baru",
},
Builtin: {
Title: "Sembunyikan Masks Bawaan",
SubTitle: "Sembunyikan Masks bawaan dari daftar masks",
},
},
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",
},
},
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",
},
Token: {
Title: "Kunci API",
SubTitle: "Gunakan kunci Anda untuk melewati batas kode akses",
Placeholder: "Kunci API OpenAI",
},
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",
},
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",
},
},
Store: {
DefaultTopic: "Percakapan Baru",
BotHello: "Halo! Bagaimana saya bisa membantu Anda hari ini?",
Error: "Terjadi kesalahan, silakan coba lagi nanti.",
Prompt: {
History: (content: string) =>
"Ini adalah ringkasan singkat dari riwayat percakapan: " + content,
Topic:
"Buat judul berisi empat hingga lima kata untuk percakapan kita yang tidak akan disertakan dalam ringkasan percakapan, seperti instruksi, format, kutipan, tanda baca awal, tanda kutip pendahuluan, atau karakter tambahan. Silakan coba dengan kutipan berakhir.",
Summarize:
"Buat ringkasan percakapan dalam 200 kata yang akan digunakan sebagai promp di masa depan.",
},
},
Copy: {
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`,
Edit: "Pengaturan Obrolan Saat Ini",
Add: "Tambahkan Promp",
Clear: "Bersihkan Konteks",
Revert: "Kembali ke Posisi Sebelumnya",
},
Plugin: {
Name: "Plugin",
},
FineTuned: {
Sysmessage: "Anda adalah asisten yang",
},
Mask: {
Name: "Masks",
Page: {
Title: "Template Promp",
SubTitle: (count: number) => `${count} template prompt`,
Search: "Cari template",
Create: "Buat",
},
Item: {
Info: (count: number) => `${count} prompt`,
Chat: "Obrolan",
View: "Lihat",
Edit: "Edit",
Delete: "Hapus",
DeleteConfirm: "Anda yakin ingin menghapus?",
},
EditModal: {
Title: (readonly: boolean) =>
`Edit Template Prompt ${readonly ? "(hanya baca)" : ""}`,
Download: "Unduh",
Clone: "Duplikat",
},
Config: {
Avatar: "Avatar Bot",
Name: "Nama Bot",
Sync: {
Title: "Gunakan Konfigurasi Global",
SubTitle: "Gunakan konfigurasi global dalam percakapan ini",
Confirm:
"Pastikan untuk mengganti konfigurasi kustom dengan konfigurasi global?",
},
HideContext: {
Title: "Sembunyikan Prompt Konteks",
SubTitle: "Tidak menampilkan prompt konteks dalam obrolan",
},
Share: {
Title: "Bagikan Masks Ini",
SubTitle: "Buat tautan untuk masks ini",
Action: "Salin Tautan",
},
},
},
NewChat: {
Return: "Kembali",
Skip: "Lewati",
Title: "Pilih Masks",
SubTitle: "Berkonversasilah dengan diri Anda di balik masks",
More: "Lebih Lanjut",
NotShow: "Jangan Tampilkan Sekarang",
ConfirmNoShow:
"Pastikan untuk menonaktifkannya? Anda dapat mengaktifkannya nanti melalui pengaturan.",
},
UI: {
Confirm: "Konfirmasi",
Cancel: "Batal",
Close: "Tutup",
Create: "Buat",
Edit: "Edit",
},
Exporter: {
Model: "Model",
Messages: "Pesan",
Topic: "Topik",
Time: "Tanggal & Waktu",
},
URLCommand: {
Code: "Kode akses terdeteksi dari url, konfirmasi untuk mendaftar ? ",
Settings: "Pengaturan terdeteksi dari url, konfirmasi untuk diterapkan ?",
},
};
export default id;

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