From bd69c8f5dd90bef9290c20a321a638a949b929b5 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 16 Apr 2023 17:20:33 +0800 Subject: [PATCH 01/50] feat: close #813 log user ip time --- middleware.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/middleware.ts b/middleware.ts index c2e077706..d16a812d9 100644 --- a/middleware.ts +++ b/middleware.ts @@ -8,6 +8,17 @@ export const config = { const serverConfig = getServerSideConfig(); +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; +} + export function middleware(req: NextRequest) { const accessCode = req.headers.get("access-code"); const token = req.headers.get("token"); @@ -16,6 +27,8 @@ export function middleware(req: NextRequest) { 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 NextResponse.json( From dc3883ed1aa8bc4c7b25216f52774a4a860623e4 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 16 Apr 2023 18:07:43 +0800 Subject: [PATCH 02/50] feat: close #118 add stop all button --- app/components/chat.tsx | 16 ++++++++++++++-- app/icons/bottom.svg | 2 +- app/icons/pause.svg | 1 + app/requests.ts | 15 +++++++++++---- app/store/app.ts | 2 +- package.json | 3 ++- scripts/.gitignore | 1 + scripts/init-proxy.sh | 5 +++++ scripts/proxychains.template.conf | 12 ++++++++++++ 9 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 app/icons/pause.svg create mode 100644 scripts/.gitignore create mode 100644 scripts/init-proxy.sh create mode 100644 scripts/proxychains.template.conf diff --git a/app/components/chat.tsx b/app/components/chat.tsx index f7b67b293..9e4a2bd9a 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -19,6 +19,7 @@ import LightIcon from "../icons/light.svg"; import DarkIcon from "../icons/dark.svg"; import AutoIcon from "../icons/auto.svg"; import BottomIcon from "../icons/bottom.svg"; +import StopIcon from "../icons/pause.svg"; import { Message, @@ -38,7 +39,6 @@ import { isMobileScreen, selectOrCopy, autoGrowTextArea, - getCSSVar, } from "../utils"; import dynamic from "next/dynamic"; @@ -355,8 +355,8 @@ export function ChatActions(props: { }) { const chatStore = useChatStore(); + // switch themes const theme = chatStore.config.theme; - function nextTheme() { const themes = [Theme.Auto, Theme.Light, Theme.Dark]; const themeIndex = themes.indexOf(theme); @@ -365,8 +365,20 @@ export function ChatActions(props: { chatStore.updateConfig((config) => (config.theme = nextTheme)); } + // stop all responses + const couldStop = ControllerPool.hasPending(); + const stopAll = () => ControllerPool.stopAll(); + return (
+ {couldStop && ( +
+ +
+ )} {!props.hitBottom && (
+ diff --git a/app/icons/pause.svg b/app/icons/pause.svg new file mode 100644 index 000000000..382f7a939 --- /dev/null +++ b/app/icons/pause.svg @@ -0,0 +1 @@ + diff --git a/app/requests.ts b/app/requests.ts index c48ef2817..db91f6fc3 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -2,7 +2,7 @@ import type { ChatRequest, ChatResponse } from "./api/openai/typing"; import { Message, ModelConfig, useAccessStore, useChatStore } from "./store"; import { showToast } from "./components/ui-lib"; -const TIME_OUT_MS = 30000; +const TIME_OUT_MS = 60000; const makeRequestParam = ( messages: Message[], @@ -167,15 +167,14 @@ export async function requestChatStream( options?.onController?.(controller); while (true) { - // handle time out, will stop if no response in 10 secs const resTimeoutId = setTimeout(() => finish(), TIME_OUT_MS); const content = await reader?.read(); clearTimeout(resTimeoutId); - + if (!content || !content.value) { break; } - + const text = decoder.decode(content.value, { stream: true }); responseText += text; @@ -235,6 +234,14 @@ export const ControllerPool = { controller?.abort(); }, + stopAll() { + Object.values(this.controllers).forEach((v) => v.abort()); + }, + + hasPending() { + return Object.values(this.controllers).length > 0; + }, + remove(sessionIndex: number, messageId: number) { const key = this.key(sessionIndex, messageId); delete this.controllers[key]; diff --git a/app/store/app.ts b/app/store/app.ts index 813668905..c4c1c3411 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -421,7 +421,7 @@ export const useChatStore = create()( onError(error, statusCode) { if (statusCode === 401) { botMessage.content = Locale.Error.Unauthorized; - } else { + } else if (!error.message.includes("aborted")) { botMessage.content += "\n\n" + Locale.Store.Error; } botMessage.streaming = false; diff --git a/package.json b/package.json index 9fcb74e77..9a3e6d41c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "start": "next start", "lint": "next lint", "fetch": "node ./scripts/fetch-prompts.mjs", - "prepare": "husky install" + "prepare": "husky install", + "proxy-dev": "sh ./scripts/init-proxy.sh && proxychains -f ./scripts/proxychains.conf yarn dev" }, "dependencies": { "@hello-pangea/dnd": "^16.2.0", diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 000000000..80fe56c37 --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +proxychains.conf diff --git a/scripts/init-proxy.sh b/scripts/init-proxy.sh new file mode 100644 index 000000000..acba064f4 --- /dev/null +++ b/scripts/init-proxy.sh @@ -0,0 +1,5 @@ +dir="$(dirname "$0")" +config=$dir/proxychains.conf +host_ip=$(grep nameserver /etc/resolv.conf | sed 's/nameserver //') +cp $dir/proxychains.template.conf $config +sed -i "\$s/.*/http $host_ip 7890/" $config diff --git a/scripts/proxychains.template.conf b/scripts/proxychains.template.conf new file mode 100644 index 000000000..e78b96a68 --- /dev/null +++ b/scripts/proxychains.template.conf @@ -0,0 +1,12 @@ +strict_chain +proxy_dns + +remote_dns_subnet 224 + +tcp_read_time_out 15000 +tcp_connect_time_out 8000 + +localnet 127.0.0.0/255.0.0.0 + +[ProxyList] +socks4 127.0.0.1 9050 From ea3e8a7459db28ca201aada341e54137e43cebb4 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 16 Apr 2023 18:11:09 +0800 Subject: [PATCH 03/50] fix: #829 filter empty prompt --- app/store/prompt.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/store/prompt.ts b/app/store/prompt.ts index d0dd454ac..648b38145 100644 --- a/app/store/prompt.ts +++ b/app/store/prompt.ts @@ -116,10 +116,9 @@ export const usePromptStore = create()( }) .concat([...(state?.prompts?.values() ?? [])]); - const allPromptsForSearch = builtinPrompts.reduce( - (pre, cur) => pre.concat(cur), - [], - ); + const allPromptsForSearch = builtinPrompts + .reduce((pre, cur) => pre.concat(cur), []) + .filter((v) => !!v.title && !!v.content); SearchService.count.builtin = res.en.length + res.cn.length; SearchService.init(allPromptsForSearch); }); From 124938ecc9d0e015ed1a0cd3185395fec34de08d Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 16 Apr 2023 18:17:46 +0800 Subject: [PATCH 04/50] fix: #832 update nextjs version to 13.3.0 --- package.json | 2 +- yarn.lock | 145 ++++++++++++++++++++++++--------------------------- 2 files changed, 68 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 9a3e6d41c..acca33ce2 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "emoji-picker-react": "^4.4.7", "eventsource-parser": "^0.1.0", "fuse.js": "^6.6.2", - "next": "^13.2.3", + "next": "^13.3.0", "node-fetch": "^3.3.1", "openai": "^3.2.1", "react": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index 88aa59823..f1f5bf2f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1099,10 +1099,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.4.tgz#8b763700262b2445140a44a8c8d088cef676dbae" - integrity sha512-+Mq3TtpkeeKFZanPturjcXt+KHfKYnLlX6jMLyCrmpq6OOs4i1GqBOAauSkii9QeKCMTYzGppar21JU57b/GEA== +"@next/env@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" + integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== "@next/eslint-plugin-next@13.2.3": version "13.2.3" @@ -1111,70 +1111,50 @@ dependencies: glob "7.1.7" -"@next/swc-android-arm-eabi@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.4.tgz#758d0403771e549f9cee71cbabc0cb16a6c947c0" - integrity sha512-DWlalTSkLjDU11MY11jg17O1gGQzpRccM9Oes2yTqj2DpHndajrXHGxj9HGtJ+idq2k7ImUdJVWS2h2l/EDJOw== +"@next/swc-darwin-arm64@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" + integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== -"@next/swc-android-arm64@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.4.tgz#834d586523045110d5602e0c8aae9028835ac427" - integrity sha512-sRavmUImUCf332Gy+PjIfLkMhiRX1Ez4SI+3vFDRs1N5eXp+uNzjFUK/oLMMOzk6KFSkbiK/3Wt8+dHQR/flNg== +"@next/swc-darwin-x64@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" + integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== -"@next/swc-darwin-arm64@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.4.tgz#5006fca179a36ef3a24d293abadec7438dbb48c6" - integrity sha512-S6vBl+OrInP47TM3LlYx65betocKUUlTZDDKzTiRDbsRESeyIkBtZ6Qi5uT2zQs4imqllJznVjFd1bXLx3Aa6A== +"@next/swc-linux-arm64-gnu@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" + integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== -"@next/swc-darwin-x64@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.4.tgz#6549c7c04322766acc3264ccdb3e1b43fcaf7946" - integrity sha512-a6LBuoYGcFOPGd4o8TPo7wmv5FnMr+Prz+vYHopEDuhDoMSHOnC+v+Ab4D7F0NMZkvQjEJQdJS3rqgFhlZmKlw== +"@next/swc-linux-arm64-musl@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" + integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== -"@next/swc-freebsd-x64@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.4.tgz#0bbe28979e3e868debc2cc06e45e186ce195b7f4" - integrity sha512-kkbzKVZGPaXRBPisoAQkh3xh22r+TD+5HwoC5bOkALraJ0dsOQgSMAvzMXKsN3tMzJUPS0tjtRf1cTzrQ0I5vQ== +"@next/swc-linux-x64-gnu@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" + integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== -"@next/swc-linux-arm-gnueabihf@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.4.tgz#1d28d2203f5a7427d6e7119d7bcb5fc40959fb3e" - integrity sha512-7qA1++UY0fjprqtjBZaOA6cas/7GekpjVsZn/0uHvquuITFCdKGFCsKNBx3S0Rpxmx6WYo0GcmhNRM9ru08BGg== +"@next/swc-linux-x64-musl@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" + integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== -"@next/swc-linux-arm64-gnu@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.4.tgz#eb26448190948cdf4c44b8f34110a3ecea32f1d0" - integrity sha512-xzYZdAeq883MwXgcwc72hqo/F/dwUxCukpDOkx/j1HTq/J0wJthMGjinN9wH5bPR98Mfeh1MZJ91WWPnZOedOg== +"@next/swc-win32-arm64-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" + integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== -"@next/swc-linux-arm64-musl@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.4.tgz#c4227c0acd94a420bb14924820710e6284d234d3" - integrity sha512-8rXr3WfmqSiYkb71qzuDP6I6R2T2tpkmf83elDN8z783N9nvTJf2E7eLx86wu2OJCi4T05nuxCsh4IOU3LQ5xw== +"@next/swc-win32-ia32-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" + integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== -"@next/swc-linux-x64-gnu@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.4.tgz#6bcb540944ee9b0209b33bfc23b240c2044dfc3e" - integrity sha512-Ngxh51zGSlYJ4EfpKG4LI6WfquulNdtmHg1yuOYlaAr33KyPJp4HeN/tivBnAHcZkoNy0hh/SbwDyCnz5PFJQQ== - -"@next/swc-linux-x64-musl@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.4.tgz#ce21e43251eaf09a09df39372b2c3e38028c30ff" - integrity sha512-gOvwIYoSxd+j14LOcvJr+ekd9fwYT1RyMAHOp7znA10+l40wkFiMONPLWiZuHxfRk+Dy7YdNdDh3ImumvL6VwA== - -"@next/swc-win32-arm64-msvc@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.4.tgz#68220063d8e5e082f5465498675640dedb670ff1" - integrity sha512-q3NJzcfClgBm4HvdcnoEncmztxrA5GXqKeiZ/hADvC56pwNALt3ngDC6t6qr1YW9V/EPDxCYeaX4zYxHciW4Dw== - -"@next/swc-win32-ia32-msvc@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.4.tgz#7c120ab54a081be9566df310bed834f168252990" - integrity sha512-/eZ5ncmHUYtD2fc6EUmAIZlAJnVT2YmxDsKs1Ourx0ttTtvtma/WKlMV5NoUsyOez0f9ExLyOpeCoz5aj+MPXw== - -"@next/swc-win32-x64-msvc@13.2.4": - version "13.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.4.tgz#5abda92fe12b9829bf7951c4a221282c56041144" - integrity sha512-0MffFmyv7tBLlji01qc0IaPP/LVExzvj7/R5x1Jph1bTAIj4Vu81yFQWHHQAP6r4ff9Ukj1mBK6MDNVXm7Tcvw== +"@next/swc-win32-x64-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" + integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1730,6 +1710,13 @@ browserslist@^4.21.3, browserslist@^4.21.5: node-releases "^2.0.8" update-browserslist-db "^1.0.10" +busboy@1.6.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3937,30 +3924,27 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^13.2.3: - version "13.2.4" - resolved "https://registry.yarnpkg.com/next/-/next-13.2.4.tgz#2363330392b0f7da02ab41301f60857ffa7f67d6" - integrity sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw== +next@^13.3.0: + version "13.3.0" + resolved "https://registry.npmmirror.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" + integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== dependencies: - "@next/env" "13.2.4" + "@next/env" "13.3.0" "@swc/helpers" "0.4.14" + busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm-eabi" "13.2.4" - "@next/swc-android-arm64" "13.2.4" - "@next/swc-darwin-arm64" "13.2.4" - "@next/swc-darwin-x64" "13.2.4" - "@next/swc-freebsd-x64" "13.2.4" - "@next/swc-linux-arm-gnueabihf" "13.2.4" - "@next/swc-linux-arm64-gnu" "13.2.4" - "@next/swc-linux-arm64-musl" "13.2.4" - "@next/swc-linux-x64-gnu" "13.2.4" - "@next/swc-linux-x64-musl" "13.2.4" - "@next/swc-win32-arm64-msvc" "13.2.4" - "@next/swc-win32-ia32-msvc" "13.2.4" - "@next/swc-win32-x64-msvc" "13.2.4" + "@next/swc-darwin-arm64" "13.3.0" + "@next/swc-darwin-x64" "13.3.0" + "@next/swc-linux-arm64-gnu" "13.3.0" + "@next/swc-linux-arm64-musl" "13.3.0" + "@next/swc-linux-x64-gnu" "13.3.0" + "@next/swc-linux-x64-musl" "13.3.0" + "@next/swc-win32-arm64-msvc" "13.3.0" + "@next/swc-win32-ia32-msvc" "13.3.0" + "@next/swc-win32-x64-msvc" "13.3.0" node-domexception@^1.0.0: version "1.0.0" @@ -4668,6 +4652,11 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-argv@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" From 12d4081311f22ee2b9de30292b1be8aa5c69e6dd Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 16 Apr 2023 18:55:29 +0800 Subject: [PATCH 05/50] feat: close #539 add delete message button --- app/components/chat.tsx | 68 ++++++++++++++++++++++++++++++----------- app/locales/cn.ts | 1 + app/locales/de.ts | 1 + app/locales/en.ts | 1 + app/locales/es.ts | 1 + app/locales/index.ts | 24 +++++---------- app/locales/it.ts | 1 + app/locales/jp.ts | 3 +- app/locales/tr.ts | 1 + app/locales/tw.ts | 1 + app/store/app.ts | 1 + 11 files changed, 66 insertions(+), 37 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 9e4a2bd9a..991ef312e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -536,21 +536,45 @@ export function Chat(props: { } }; - const onResend = (botIndex: number) => { + const findLastUesrIndex = (messageId: number) => { // find last user input message and resend - for (let i = botIndex; i >= 0; i -= 1) { - if (messages[i].role === "user") { - setIsLoading(true); - chatStore - .onUserInput(messages[i].content) - .then(() => setIsLoading(false)); - chatStore.updateCurrentSession((session) => - session.messages.splice(i, 2), - ); - inputRef.current?.focus(); - return; + let lastUserMessageIndex: number | null = null; + for (let i = 0; i < session.messages.length; i += 1) { + const message = session.messages[i]; + if (message.id === messageId) { + break; + } + if (message.role === "user") { + lastUserMessageIndex = i; } } + + return lastUserMessageIndex; + }; + + const deleteMessage = (userIndex: number) => { + chatStore.updateCurrentSession((session) => + session.messages.splice(userIndex, 2), + ); + }; + + const onDelete = (botMessageId: number) => { + const userIndex = findLastUesrIndex(botMessageId); + if (userIndex === null) return; + deleteMessage(userIndex); + }; + + const onResend = (botMessageId: number) => { + // find last user input message and resend + const userIndex = findLastUesrIndex(botMessageId); + if (userIndex === null) return; + + setIsLoading(true); + chatStore + .onUserInput(session.messages[userIndex].content) + .then(() => setIsLoading(false)); + deleteMessage(userIndex); + inputRef.current?.focus(); }; const config = useChatStore((state) => state.config); @@ -722,12 +746,20 @@ export function Chat(props: { {Locale.Chat.Actions.Stop}
) : ( -
onResend(i)} - > - {Locale.Chat.Actions.Retry} -
+ <> +
onDelete(message.id ?? i)} + > + {Locale.Chat.Actions.Delete} +
+
onResend(message.id ?? i)} + > + {Locale.Chat.Actions.Retry} +
+ )}
()( const botMessage: Message = createMessage({ role: "assistant", streaming: true, + id: userMessage.id! + 1, }); // get recent messages From 2979df1d82a86046c07283b83b071df3db60d6e0 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 17 Apr 2023 11:17:05 +0800 Subject: [PATCH 06/50] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a6288798a..53706900d 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,9 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s [@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) ### Contributor From cc053b148d6487c83a2dd647059e0cfa7314fe16 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 17 Apr 2023 11:27:31 +0800 Subject: [PATCH 07/50] fix: #853 fetch duplex errors --- app/api/common.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/common.ts b/app/api/common.ts index 53ab18ed6..089dbf02a 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -25,5 +25,6 @@ export async function requestOpenai(req: NextRequest) { }, method: req.method, body: req.body, + duplex: !!req.body, }); } From 76ef5ef9a9872e1d3f30e9d201d9ac2546010156 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 17 Apr 2023 11:34:33 +0800 Subject: [PATCH 08/50] fixup --- app/api/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/api/common.ts b/app/api/common.ts index 089dbf02a..65870ee91 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -25,6 +25,6 @@ export async function requestOpenai(req: NextRequest) { }, method: req.method, body: req.body, - duplex: !!req.body, + duplex: !!req.body ? "half" : "", }); } From b79845fcaa5fce0da272b88564bf02d8252570c0 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Mon, 17 Apr 2023 11:36:32 +0800 Subject: [PATCH 09/50] fixup --- app/api/common.ts | 1 - app/api/openai/route.ts | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/api/common.ts b/app/api/common.ts index 65870ee91..53ab18ed6 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -25,6 +25,5 @@ export async function requestOpenai(req: NextRequest) { }, method: req.method, body: req.body, - duplex: !!req.body ? "half" : "", }); } diff --git a/app/api/openai/route.ts b/app/api/openai/route.ts index 261c20a85..0ac94bdd5 100644 --- a/app/api/openai/route.ts +++ b/app/api/openai/route.ts @@ -17,7 +17,7 @@ async function makeRequest(req: NextRequest) { }, { status: 500, - }, + } ); } } @@ -29,3 +29,7 @@ export async function POST(req: NextRequest) { export async function GET(req: NextRequest) { return makeRequest(req); } + +export const config = { + runtime: "edge", +}; From 64e78329ec4650300d49d12641880f43a8226e98 Mon Sep 17 00:00:00 2001 From: Ma Yuke Date: Mon, 17 Apr 2023 11:51:51 +0800 Subject: [PATCH 10/50] Update app/requests.ts fix: displaying the number of subscription beyond two decimal places. --- app/requests.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/requests.ts b/app/requests.ts index db91f6fc3..af718f234 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -113,6 +113,10 @@ export async function requestUsage() { 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, From 3038dfdb278c3fa4bdc5e1aa6ce12aa7f1e8eae8 Mon Sep 17 00:00:00 2001 From: jaw <2135326728@qq.com> Date: Mon, 17 Apr 2023 15:37:03 +0800 Subject: [PATCH 11/50] fix: error in windows --- package.json | 2 +- yarn.lock | 108 +++++++++++++++++++++++++-------------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index acca33ce2..19047ad11 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "emoji-picker-react": "^4.4.7", "eventsource-parser": "^0.1.0", "fuse.js": "^6.6.2", - "next": "^13.3.0", + "next": "^13.3.1-canary.8", "node-fetch": "^3.3.1", "openai": "^3.2.1", "react": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index f1f5bf2f7..342ea4a44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1099,10 +1099,10 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" - integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== +"@next/env@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.1-canary.8.tgz#9f5cf57999e4f4b59ef6407924803a247cc4e451" + integrity sha512-xZfNu7yq3OfiC4rkGuGMcqb25se+ZHRqajSdny8dp+nZzkNSK1SHuNT3W8faI+KGk6dqzO/zAdHR9YrqnQlCAg== "@next/eslint-plugin-next@13.2.3": version "13.2.3" @@ -1111,50 +1111,50 @@ dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" - integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== +"@next/swc-darwin-arm64@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.1-canary.8.tgz#66786ba76d37c210c184739624c6f84eaf2dc52b" + integrity sha512-BLbvhcaSzwuXbREOmJiqAdXVD7Jl9830hDY5ZTTNg7hXqEZgoMg2LxAEmtaaBMVZRfDQjd5bH3QPBV8fbG4UKg== -"@next/swc-darwin-x64@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" - integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== +"@next/swc-darwin-x64@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.1-canary.8.tgz#289296bd3cc55db7fef42037eb89ce4a6260ba31" + integrity sha512-n4tJKPIvFTZshS1TVWrsqaW7h9VW+BmguO/AlZ3Q3NJ9hWxC5L4lxn2T6CTQ4M30Gf+t5u+dPzYLQ5IDtJFnFQ== -"@next/swc-linux-arm64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" - integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== +"@next/swc-linux-arm64-gnu@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.1-canary.8.tgz#dc79e8005849b6482241b460abdce9334665c766" + integrity sha512-AxnsgZ56whwVAeejyEZMk8xc8Vapwzb3Zn0YdZzPCR42WKfkcSkM+AWfq33zUOZnjvCmQBDyfHIo4CURVweR6g== -"@next/swc-linux-arm64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" - integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== +"@next/swc-linux-arm64-musl@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.1-canary.8.tgz#f70873add4aad7ced36f760d1640adc008b7dc03" + integrity sha512-zc7rzhtrHMWZ/phvjCNplHGo+ZLembjtluI5J8Xl4iwQQCyZwAtnmQhs37/zkdi6dHZou+wcFBZWRz14awRDBw== -"@next/swc-linux-x64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" - integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== +"@next/swc-linux-x64-gnu@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.1-canary.8.tgz#fe81b8033628c6cf74e154f2db8c8c7f1593008f" + integrity sha512-vNbFDiuZ9fWmcznlilDbflZLb04evWPUQlyDT7Tqjd964PlSIaaX3tr64pdYjJOljDaqTr2Kbx0YW74mWF/PEw== -"@next/swc-linux-x64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" - integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== +"@next/swc-linux-x64-musl@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.1-canary.8.tgz#ada4585046a7937f96f2d39fc4aaca12826dde5f" + integrity sha512-/FVBPJEBDZYCNraocRWtd5ObAgNi9VFnzJYGYDYIj4jKkFRWWm/CaWu9A7toQACC/JDy262uPyDPathXT9BAqQ== -"@next/swc-win32-arm64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" - integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== +"@next/swc-win32-arm64-msvc@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.1-canary.8.tgz#21b4f6c4be61845759753df9313bd9bcbb241969" + integrity sha512-8jMwRCeI26yVZLPwG0AjOi4b1yqSeqYmbHA7r+dqiV0OgFdYjnbyHU1FmiKDaC5SnnJN6LWV2Qjer9GDD0Kcuw== -"@next/swc-win32-ia32-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" - integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== +"@next/swc-win32-ia32-msvc@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.1-canary.8.tgz#e23192e1d1b1a32b0eb805363b02360c5b523a77" + integrity sha512-kcYB9iSEikFhv0I9uQDdgQ2lm8i3O8LA+GhnED9e5VtURBwOSwED7c6ZpaRQBYSPgnEA9/xiJVChICE/I7Ig1g== -"@next/swc-win32-x64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.npmmirror.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" - integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== +"@next/swc-win32-x64-msvc@13.3.1-canary.8": + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.1-canary.8.tgz#a3f29404955cba2193de5e74fd5d9fcfdcb0ab51" + integrity sha512-UKrGHonKVWBNg+HI4J8pXE6Jjjl8GwjhygFau71s8M0+jSy99y5Y+nGH9EmMNWKNvrObukyYvrs6OsAusKdCqw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -3924,27 +3924,27 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^13.3.0: - version "13.3.0" - resolved "https://registry.npmmirror.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" - integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== +next@^13.3.1-canary.8: + version "13.3.1-canary.8" + resolved "https://registry.yarnpkg.com/next/-/next-13.3.1-canary.8.tgz#f0846e5eada1491884326786a0749d5adc04c24d" + integrity sha512-z4QUgyAN+hSWSEqb4pvGvC3iRktE6NH2DVLU4AvfqNYpzP+prePiJC8HN/cJpFhGW9YbhyRLi5FliDC631OOag== dependencies: - "@next/env" "13.3.0" + "@next/env" "13.3.1-canary.8" "@swc/helpers" "0.4.14" busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "13.3.0" - "@next/swc-darwin-x64" "13.3.0" - "@next/swc-linux-arm64-gnu" "13.3.0" - "@next/swc-linux-arm64-musl" "13.3.0" - "@next/swc-linux-x64-gnu" "13.3.0" - "@next/swc-linux-x64-musl" "13.3.0" - "@next/swc-win32-arm64-msvc" "13.3.0" - "@next/swc-win32-ia32-msvc" "13.3.0" - "@next/swc-win32-x64-msvc" "13.3.0" + "@next/swc-darwin-arm64" "13.3.1-canary.8" + "@next/swc-darwin-x64" "13.3.1-canary.8" + "@next/swc-linux-arm64-gnu" "13.3.1-canary.8" + "@next/swc-linux-arm64-musl" "13.3.1-canary.8" + "@next/swc-linux-x64-gnu" "13.3.1-canary.8" + "@next/swc-linux-x64-musl" "13.3.1-canary.8" + "@next/swc-win32-arm64-msvc" "13.3.1-canary.8" + "@next/swc-win32-ia32-msvc" "13.3.1-canary.8" + "@next/swc-win32-x64-msvc" "13.3.1-canary.8" node-domexception@^1.0.0: version "1.0.0" From 525a2ff9a7e9b1be79a15972f138d092b71bf4de Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 17 Apr 2023 22:51:14 +0800 Subject: [PATCH 12/50] fix: #866 remove unused retry messages --- app/components/chat.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 991ef312e..3c66627f6 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -570,10 +570,9 @@ export function Chat(props: { if (userIndex === null) return; setIsLoading(true); - chatStore - .onUserInput(session.messages[userIndex].content) - .then(() => setIsLoading(false)); + const content = session.messages[userIndex].content; deleteMessage(userIndex); + chatStore.onUserInput(content).then(() => setIsLoading(false)); inputRef.current?.focus(); }; From fdc8278b90cdcacc8859df4740752a58d8829d8b Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Mon, 17 Apr 2023 23:12:27 +0800 Subject: [PATCH 13/50] feat: check usage throttle --- app/components/settings.module.scss | 11 ++++++++++ app/components/settings.tsx | 28 +++++++++++++++--------- app/requests.ts | 2 +- app/store/update.ts | 34 +++++++++++++++++++++-------- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index 830e1baeb..0628c6e3f 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -32,3 +32,14 @@ min-width: 80%; } } + +.user-prompt-modal { + .user-prompt-search { + } + + .user-prompt-list { + } + + .user-prompt-actions { + } +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index e418d4843..d5f26c3e6 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useMemo, HTMLProps } from "react"; +import { useState, useEffect, useMemo, HTMLProps, useRef } from "react"; import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react"; @@ -34,6 +34,15 @@ import { requestUsage } from "../requests"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; +function UserPromptModal() { + const promptStore = usePromptStore(); + const prompts = Array.from(promptStore.prompts.values()).sort( + (a, b) => a.id ?? 0 - (b.id ?? 0), + ); + + return <>; +} + function SettingItem(props: { title: string; subTitle?: string; @@ -99,18 +108,16 @@ export function Settings(props: { closeSettings: () => void }) { }); } - const [usage, setUsage] = useState<{ - used?: number; - subscription?: number; - }>(); + const usage = { + used: updateStore.used, + subscription: updateStore.subscription, + }; const [loadingUsage, setLoadingUsage] = useState(false); function checkUsage() { setLoadingUsage(true); - requestUsage() - .then((res) => setUsage(res)) - .finally(() => { - setLoadingUsage(false); - }); + updateStore.updateUsage().finally(() => { + setLoadingUsage(false); + }); } const accessStore = useAccessStore(); @@ -126,6 +133,7 @@ export function Settings(props: { closeSettings: () => void }) { const showUsage = accessStore.isAuthorized(); useEffect(() => { + // checks per minutes checkUpdate(); showUsage && checkUsage(); // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/app/requests.ts b/app/requests.ts index af718f234..3cb838e63 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -113,7 +113,7 @@ export async function requestUsage() { 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; } diff --git a/app/store/update.ts b/app/store/update.ts index efcdc8a7b..d49c246d1 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -1,13 +1,19 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { FETCH_COMMIT_URL, FETCH_TAG_URL } from "../constant"; +import { requestUsage } from "../requests"; export interface UpdateStore { lastUpdate: number; remoteVersion: string; + used?: number; + subscription?: number; + lastUpdateUsage: number; + version: string; - getLatestVersion: (force: boolean) => Promise; + getLatestVersion: (force?: boolean) => Promise; + updateUsage: (force?: boolean) => Promise; } export const UPDATE_KEY = "chat-update"; @@ -26,22 +32,23 @@ function queryMeta(key: string, defaultValue?: string): string { return ret; } +const ONE_MINUTE = 60 * 1000; + export const useUpdateStore = create()( persist( (set, get) => ({ lastUpdate: 0, remoteVersion: "", + lastUpdateUsage: 0, + version: "unknown", async getLatestVersion(force = false) { - set(() => ({ version: queryMeta("version") })); + set(() => ({ version: queryMeta("version") ?? "unknown" })); - const overTenMins = Date.now() - get().lastUpdate > 10 * 60 * 1000; - const shouldFetch = force || overTenMins; - if (!shouldFetch) { - return get().version ?? "unknown"; - } + const overTenMins = Date.now() - get().lastUpdate > 10 * ONE_MINUTE; + if (!force && !overTenMins) return; try { // const data = await (await fetch(FETCH_TAG_URL)).json(); @@ -53,10 +60,19 @@ export const useUpdateStore = create()( remoteVersion: remoteId, })); console.log("[Got Upstream] ", remoteId); - return remoteId; } catch (error) { console.error("[Fetch Upstream Commit Id]", error); - return get().version ?? ""; + } + }, + + async updateUsage(force = false) { + const overOneMinute = Date.now() - get().lastUpdateUsage >= ONE_MINUTE; + if (!overOneMinute && !force) return; + + const usage = await requestUsage(); + + if (usage) { + set(() => usage); } }, }), From 789a77977525eb06be52c329a7a65ad47e6babfc Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 18 Apr 2023 01:34:12 +0800 Subject: [PATCH 14/50] feat: user prompts --- app/components/settings.module.scss | 49 ++++++++++++ app/components/settings.tsx | 119 +++++++++++++++++++++++++--- app/components/ui-lib.module.scss | 2 +- app/locales/cn.ts | 9 ++- app/locales/de.ts | 5 ++ app/locales/en.ts | 7 +- app/locales/es.ts | 5 ++ app/locales/it.ts | 5 ++ app/locales/jp.ts | 5 ++ app/locales/tr.ts | 5 ++ app/locales/tw.ts | 5 ++ app/store/prompt.ts | 79 +++++++++++++----- app/store/update.ts | 9 ++- app/styles/globals.scss | 2 + 14 files changed, 269 insertions(+), 37 deletions(-) diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index 0628c6e3f..b7f095580 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -34,10 +34,59 @@ } .user-prompt-modal { + min-height: 40vh; + .user-prompt-search { + width: 100%; + max-width: 100%; + margin-bottom: 10px; + background-color: var(--gray); } .user-prompt-list { + padding: 10px 0; + + .user-prompt-item { + margin-bottom: 10px; + widows: 100%; + + .user-prompt-header { + display: flex; + widows: 100%; + margin-bottom: 5px; + + .user-prompt-title { + flex-grow: 1; + max-width: 100%; + margin-right: 5px; + padding: 5px; + font-size: 12px; + text-align: left; + } + + .user-prompt-buttons { + display: flex; + align-items: center; + + .user-prompt-button { + height: 100%; + + &:not(:last-child) { + margin-right: 5px; + } + } + } + } + + .user-prompt-content { + width: 100%; + box-sizing: border-box; + padding: 5px; + margin-right: 10px; + font-size: 12px; + flex-grow: 1; + } + } } .user-prompt-actions { diff --git a/app/components/settings.tsx b/app/components/settings.tsx index d5f26c3e6..d81b5b358 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -6,12 +6,13 @@ import styles from "./settings.module.scss"; import ResetIcon from "../icons/reload.svg"; import CloseIcon from "../icons/close.svg"; +import CopyIcon from "../icons/copy.svg"; import ClearIcon from "../icons/clear.svg"; import EditIcon from "../icons/edit.svg"; import EyeIcon from "../icons/eye.svg"; import EyeOffIcon from "../icons/eye-off.svg"; -import { List, ListItem, Popover, showToast } from "./ui-lib"; +import { Input, List, ListItem, Modal, Popover } from "./ui-lib"; import { IconButton } from "./button"; import { @@ -26,21 +27,112 @@ import { import { Avatar } from "./chat"; import Locale, { AllLangs, changeLang, getLang } from "../locales"; -import { getEmojiUrl } from "../utils"; +import { copyToClipboard, getEmojiUrl } from "../utils"; import Link from "next/link"; import { UPDATE_URL } from "../constant"; -import { SearchService, usePromptStore } from "../store/prompt"; -import { requestUsage } from "../requests"; +import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; -function UserPromptModal() { +function UserPromptModal(props: { onClose?: () => void }) { const promptStore = usePromptStore(); - const prompts = Array.from(promptStore.prompts.values()).sort( - (a, b) => a.id ?? 0 - (b.id ?? 0), - ); + const userPrompts = promptStore.getUserPrompts(); + const builtinPrompts = SearchService.builtinPrompts; + const allPrompts = userPrompts.concat(builtinPrompts); + const [searchInput, setSearchInput] = useState(""); + const [searchPrompts, setSearchPrompts] = useState([]); + const prompts = searchInput.length > 0 ? searchPrompts : allPrompts; - return <>; + useEffect(() => { + if (searchInput.length > 0) { + const searchResult = SearchService.search(searchInput); + setSearchPrompts(searchResult); + } else { + setSearchPrompts([]); + } + }, [searchInput]); + + return ( +
+ props.onClose?.()} + actions={[ + promptStore.add({ title: "", content: "" })} + icon={} + bordered + text={Locale.Settings.Prompt.Modal.Add} + />, + ]} + > +
+ setSearchInput(e.currentTarget.value)} + > + +
+ {prompts.map((v, _) => ( +
+
+ { + if (v.isUser) { + promptStore.updateUserPrompts( + v.id!, + (prompt) => (prompt.title = e.currentTarget.value), + ); + } + }} + > + +
+ {v.isUser && ( + } + bordered + className={styles["user-prompt-button"]} + onClick={() => promptStore.remove(v.id!)} + /> + )} + } + bordered + className={styles["user-prompt-button"]} + onClick={() => copyToClipboard(v.content)} + /> +
+
+ { + if (v.isUser) { + promptStore.updateUserPrompts( + v.id!, + (prompt) => (prompt.content = e.currentTarget.value), + ); + } + }} + /> +
+ ))} +
+
+
+
+ ); } function SettingItem(props: { @@ -129,7 +221,8 @@ export function Settings(props: { closeSettings: () => void }) { const promptStore = usePromptStore(); const builtinCount = SearchService.count.builtin; - const customCount = promptStore.prompts.size ?? 0; + const customCount = promptStore.getUserPrompts().length ?? 0; + const [shouldShowPromptModal, setShowPromptModal] = useState(false); const showUsage = accessStore.isAuthorized(); useEffect(() => { @@ -477,7 +570,7 @@ export function Settings(props: { closeSettings: () => void }) { } text={Locale.Settings.Prompt.Edit} - onClick={() => showToast(Locale.WIP)} + onClick={() => setShowPromptModal(true)} /> @@ -563,6 +656,10 @@ export function Settings(props: { closeSettings: () => void }) { > + + {shouldShowPromptModal && ( + setShowPromptModal(false)} /> + )}
); diff --git a/app/components/ui-lib.module.scss b/app/components/ui-lib.module.scss index 457c55049..8965c06a0 100644 --- a/app/components/ui-lib.module.scss +++ b/app/components/ui-lib.module.scss @@ -53,7 +53,7 @@ box-shadow: var(--card-shadow); background-color: var(--white); border-radius: 12px; - width: 50vw; + width: 60vw; animation: slide-in ease 0.3s; --modal-padding: 20px; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 391ade2ab..d0ab27ca2 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -59,10 +59,10 @@ const cn = { ResetAll: "重置所有选项", Close: "关闭", ConfirmResetAll: { - Confirm: "Are you sure you want to reset all configurations?", + Confirm: "确认清除所有配置?", }, ConfirmClearAll: { - Confirm: "Are you sure you want to reset all chat?", + Confirm: "确认清除所有聊天记录?", }, }, Lang: { @@ -105,6 +105,11 @@ const cn = { ListCount: (builtin: number, custom: number) => `内置 ${builtin} 条,用户定义 ${custom} 条`, Edit: "编辑", + Modal: { + Title: "提示词列表", + Add: "增加一条", + Search: "搜尋提示詞", + }, }, HistoryCount: { Title: "附带历史消息数", diff --git a/app/locales/de.ts b/app/locales/de.ts index d373cba50..e71abfaf7 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -107,6 +107,11 @@ const de: LocaleType = { ListCount: (builtin: number, custom: number) => `${builtin} integriert, ${custom} benutzerdefiniert`, Edit: "Bearbeiten", + Modal: { + Title: "Prompt List", + Add: "Add One", + Search: "Search Prompts", + }, }, HistoryCount: { Title: "Anzahl der angehängten Nachrichten", diff --git a/app/locales/en.ts b/app/locales/en.ts index 213b02d3e..20e569606 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -107,6 +107,11 @@ const en: LocaleType = { ListCount: (builtin: number, custom: number) => `${builtin} built-in, ${custom} user-defined`, Edit: "Edit", + Modal: { + Title: "Prompt List", + Add: "Add One", + Search: "Search Prompts", + }, }, HistoryCount: { Title: "Attached Messages Count", @@ -128,7 +133,7 @@ const en: LocaleType = { return `Used this month $${used}, subscription $${total}`; }, IsChecking: "Checking...", - Check: "Check Again", + Check: "Check", NoAccess: "Enter API Key to check balance", }, AccessCode: { diff --git a/app/locales/es.ts b/app/locales/es.ts index 5d6eca17a..e2a9eb211 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -107,6 +107,11 @@ const es: LocaleType = { ListCount: (builtin: number, custom: number) => `${builtin} incorporado, ${custom} definido por el usuario`, Edit: "Editar", + Modal: { + Title: "Prompt List", + Add: "Add One", + Search: "Search Prompts", + }, }, HistoryCount: { Title: "Cantidad de mensajes adjuntos", diff --git a/app/locales/it.ts b/app/locales/it.ts index 225238888..f0453b5c3 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -107,6 +107,11 @@ const it: LocaleType = { ListCount: (builtin: number, custom: number) => `${builtin} built-in, ${custom} user-defined`, Edit: "Modifica", + Modal: { + Title: "Prompt List", + Add: "Add One", + Search: "Search Prompts", + }, }, HistoryCount: { Title: "Conteggio dei messaggi allegati", diff --git a/app/locales/jp.ts b/app/locales/jp.ts index f4ad741c9..a793b5fe0 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -108,6 +108,11 @@ const jp = { ListCount: (builtin: number, custom: number) => `組み込み ${builtin} 件、ユーザー定義 ${custom} 件`, Edit: "編集", + Modal: { + Title: "提示词列表", + Add: "增加一条", + Search: "搜尋提示詞", + }, }, HistoryCount: { Title: "履歴メッセージ数を添付", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index a7ede1926..04a846245 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -107,6 +107,11 @@ const tr: LocaleType = { ListCount: (builtin: number, custom: number) => `${builtin} yerleşik, ${custom} kullanıcı tanımlı`, Edit: "Düzenle", + Modal: { + Title: "Prompt List", + Add: "Add One", + Search: "Search Prompts", + }, }, HistoryCount: { Title: "Ekli Mesaj Sayısı", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 1397bb098..2fbb2e477 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -105,6 +105,11 @@ const tw: LocaleType = { ListCount: (builtin: number, custom: number) => `內置 ${builtin} 條,用戶定義 ${custom} 條`, Edit: "編輯", + Modal: { + Title: "提示詞列表", + Add: "增加一條", + Search: "搜索提示词", + }, }, HistoryCount: { Title: "附帶歷史訊息數", diff --git a/app/store/prompt.ts b/app/store/prompt.ts index 648b38145..8d754ff5d 100644 --- a/app/store/prompt.ts +++ b/app/store/prompt.ts @@ -5,62 +5,74 @@ import { getLang } from "../locales"; export interface Prompt { id?: number; + isUser?: boolean; title: string; content: string; } export interface PromptStore { + counter: number; latestId: number; - prompts: Map; + prompts: Record; add: (prompt: Prompt) => number; remove: (id: number) => void; search: (text: string) => Prompt[]; + + getUserPrompts: () => Prompt[]; + updateUserPrompts: (id: number, updater: (prompt: Prompt) => void) => void; } export const PROMPT_KEY = "prompt-store"; export const SearchService = { ready: false, - engine: new Fuse([], { keys: ["title"] }), + builtinEngine: new Fuse([], { keys: ["title"] }), + userEngine: new Fuse([], { keys: ["title"] }), count: { builtin: 0, }, - allBuiltInPrompts: [] as Prompt[], + allPrompts: [] as Prompt[], + builtinPrompts: [] as Prompt[], - init(prompts: Prompt[]) { + init(builtinPrompts: Prompt[], userPrompts: Prompt[]) { if (this.ready) { return; } - this.allBuiltInPrompts = prompts; - this.engine.setCollection(prompts); + this.allPrompts = userPrompts.concat(builtinPrompts); + this.builtinPrompts = builtinPrompts.slice(); + this.builtinEngine.setCollection(builtinPrompts); + this.userEngine.setCollection(userPrompts); this.ready = true; }, remove(id: number) { - this.engine.remove((doc) => doc.id === id); + this.userEngine.remove((doc) => doc.id === id); }, add(prompt: Prompt) { - this.engine.add(prompt); + this.userEngine.add(prompt); }, search(text: string) { - const results = this.engine.search(text); - return results.map((v) => v.item); + const userResults = this.userEngine.search(text); + const builtinResults = this.builtinEngine.search(text); + return userResults.concat(builtinResults).map((v) => v.item); }, }; export const usePromptStore = create()( persist( (set, get) => ({ + counter: 0, latestId: 0, - prompts: new Map(), + prompts: {}, add(prompt) { const prompts = get().prompts; prompt.id = get().latestId + 1; - prompts.set(prompt.id, prompt); + prompt.isUser = true; + prompts[prompt.id] = prompt; set(() => ({ latestId: prompt.id!, @@ -72,19 +84,40 @@ export const usePromptStore = create()( remove(id) { const prompts = get().prompts; - prompts.delete(id); + delete prompts[id]; SearchService.remove(id); set(() => ({ prompts, + counter: get().counter + 1, })); }, + getUserPrompts() { + const userPrompts = Object.values(get().prompts ?? {}); + userPrompts.sort((a, b) => (b.id && a.id ? b.id - a.id : 0)); + return userPrompts; + }, + + updateUserPrompts(id: number, updater) { + const prompt = get().prompts[id] ?? { + title: "", + content: "", + id, + }; + + SearchService.remove(id); + updater(prompt); + const prompts = get().prompts; + prompts[id] = prompt; + set(() => ({ prompts })); + SearchService.add(prompt); + }, + search(text) { if (text.length === 0) { - // return all prompts - const userPrompts = get().prompts?.values?.() ?? []; - return SearchService.allBuiltInPrompts.concat([...userPrompts]); + // return all rompts + return SearchService.allPrompts.concat([...get().getUserPrompts()]); } return SearchService.search(text) as Prompt[]; }, @@ -104,23 +137,27 @@ export const usePromptStore = create()( if (getLang() === "cn") { fetchPrompts = fetchPrompts.reverse(); } - const builtinPrompts = fetchPrompts - .map((promptList: PromptList) => { + const builtinPrompts = fetchPrompts.map( + (promptList: PromptList) => { return promptList.map( ([title, content]) => ({ + id: Math.random(), title, content, } as Prompt), ); - }) - .concat([...(state?.prompts?.values() ?? [])]); + }, + ); + + const userPrompts = + usePromptStore.getState().getUserPrompts() ?? []; const allPromptsForSearch = builtinPrompts .reduce((pre, cur) => pre.concat(cur), []) .filter((v) => !!v.title && !!v.content); SearchService.count.builtin = res.en.length + res.cn.length; - SearchService.init(allPromptsForSearch); + SearchService.init(allPromptsForSearch, userPrompts); }); }, }, diff --git a/app/store/update.ts b/app/store/update.ts index d49c246d1..47b190b88 100644 --- a/app/store/update.ts +++ b/app/store/update.ts @@ -50,13 +50,16 @@ export const useUpdateStore = create()( const overTenMins = Date.now() - get().lastUpdate > 10 * ONE_MINUTE; if (!force && !overTenMins) return; + set(() => ({ + lastUpdate: Date.now(), + })); + try { // const data = await (await fetch(FETCH_TAG_URL)).json(); // const remoteId = data[0].name as string; const data = await (await fetch(FETCH_COMMIT_URL)).json(); const remoteId = (data[0].sha as string).substring(0, 7); set(() => ({ - lastUpdate: Date.now(), remoteVersion: remoteId, })); console.log("[Got Upstream] ", remoteId); @@ -69,6 +72,10 @@ export const useUpdateStore = create()( const overOneMinute = Date.now() - get().lastUpdateUsage >= ONE_MINUTE; if (!overOneMinute && !force) return; + set(() => ({ + lastUpdateUsage: Date.now(), + })); + const usage = await requestUsage(); if (usage) { diff --git a/app/styles/globals.scss b/app/styles/globals.scss index cf36ee92b..37c662288 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -140,6 +140,7 @@ label { input { text-align: center; + font-family: inherit; } input[type="checkbox"] { @@ -224,6 +225,7 @@ input[type="password"] { color: var(--black); padding: 0 10px; max-width: 50%; + font-family: inherit; } div.math { From c03b4adec0c0178dbcb7de85c0b6a3488171e93d Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 18 Apr 2023 01:48:07 +0800 Subject: [PATCH 15/50] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 53706900d..3b6308be9 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## Roadmap - [x] System Prompt: pin a user defined prompt as system prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) -- [ ] User Prompt: user can edit and save custom prompts to prompt list +- [x] User Prompt: user can edit and save custom prompts to prompt list +- [ ] Prompt Template: create a new chat with pre-defined in-context prompts +- [ ] Share as image, share to ShareGPT - [ ] Desktop App with tauri - [ ] Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. - [ ] Plugins: support network search, caculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) @@ -59,7 +61,9 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. ## 开发计划 - [x] 为每个对话设置系统 Prompt [#138](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/138) -- [ ] 允许用户自行编辑内置 Prompt 列表 +- [x] 允许用户自行编辑内置 Prompt 列表 +- [ ] 提示词模板:使用预制上下文快速定制新对话 +- [ ] 分享为图片,分享到 ShareGPT - [ ] 使用 tauri 打包桌面应用 - [ ] 支持自部署的大语言模型 - [ ] 插件机制,支持联网搜索、计算器、调用其他平台 api [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) From bd85d9a36a4ce22ea708d393b634997edaec558a Mon Sep 17 00:00:00 2001 From: yesRabbit Date: Tue, 18 Apr 2023 08:55:24 +0800 Subject: [PATCH 16/50] fix: typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b6308be9..ab902dd39 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. - [ ] Share as image, share to ShareGPT - [ ] Desktop App with tauri - [ ] Self-host Model: support llama, alpaca, ChatGLM, BELLE etc. -- [ ] Plugins: support network search, caculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) +- [ ] Plugins: support network search, calculator, any other apis etc. [#165](https://github.com/Yidadaa/ChatGPT-Next-Web/issues/165) ### Not in Plan From e68207be2cd9faa72ead10cf1f512dc357ac6f05 Mon Sep 17 00:00:00 2001 From: L1468999760 <1468999760@qq.com> Date: Tue, 18 Apr 2023 11:34:33 +0800 Subject: [PATCH 17/50] fix:delete user.svg --- app/icons/user.svg | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 app/icons/user.svg diff --git a/app/icons/user.svg b/app/icons/user.svg deleted file mode 100644 index 7f91de4dc..000000000 --- a/app/icons/user.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 🤣 - - - - - \ No newline at end of file From 53173d90534616ea30aacd163033b12b8586b816 Mon Sep 17 00:00:00 2001 From: jtung4 <126780548+jtung4@users.noreply.github.com> Date: Tue, 18 Apr 2023 11:38:26 +0800 Subject: [PATCH 18/50] Optimize topic and summary prompts in tw.ts --- app/locales/tw.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 2fbb2e477..8d79e74fa 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -160,9 +160,9 @@ const tw: LocaleType = { History: (content: string) => "這是 AI 與用戶的歷史聊天總結,作為前情提要:" + content, Topic: - "Summarise the conversation in a short and concise eye-catching title that instantly conveys the main topic. Use as few words as possible. Use the language used in the enquiry, e.g. use English for English enquiry, use zh-hant for traditional chinese enquiry. Don't use quotation marks at the beginning and the end.", + "Use the language used by the user (e.g. en for english conversation, zh-hant for chinese conversation, etc.) to generate a title (at most 6 words) summarizing our conversation without any lead-in, quotation marks, preamble like 'Title:', direct text copies, single-word replies, quotation marks, translations, or brackets. Remove enclosing quotation marks. The title should make third-party grasp the essence of the conversation in first sight.", Summarize: - "Summarise the conversation in at most 250 tokens for continuing the conversation in future. Use the language used in the conversation, e.g. use English for English conversation, use zh-hant for traditional chinese conversation.", + "Use the language used by the user (e.g. en-us for english conversation, zh-hant for chinese conversation, etc.) to summarise the conversation in at most 200 words. The summary will be used as prompt for you to continue the conversation in the future.", }, ConfirmClearAll: "確認清除所有對話、設定數據?", }, From d75b7d49b83362583a09884654bbbcd81f0f08ce Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 18 Apr 2023 11:42:08 +0800 Subject: [PATCH 19/50] feat: close #864 improve long term history --- app/locales/cn.ts | 10 ++++---- app/store/app.ts | 64 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index d0ab27ca2..7d011c7f1 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -38,12 +38,12 @@ const cn = { MessageFromChatGPT: "来自 ChatGPT 的消息", }, Memory: { - Title: "历史记忆", - EmptyContent: "尚未记忆", - Send: "发送记忆", - Copy: "复制记忆", + Title: "历史摘要", + EmptyContent: "尚未总结", + Send: "启用总结并发送摘要", + Copy: "复制摘要", Reset: "重置对话", - ResetConfirm: "重置后将清空当前对话记录以及历史记忆,确认重置?", + ResetConfirm: "重置后将清空当前对话记录以及历史摘要,确认重置?", }, Home: { NewChat: "新的聊天", diff --git a/app/store/app.ts b/app/store/app.ts index 39df9ef79..b3dfbd687 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -102,7 +102,7 @@ export function limitNumber( x: number, min: number, max: number, - defaultValue: number, + defaultValue: number ) { if (typeof x !== "number" || isNaN(x)) { return defaultValue; @@ -217,7 +217,7 @@ interface ChatStore { updateMessage: ( sessionIndex: number, messageIndex: number, - updater: (message?: Message) => void, + updater: (message?: Message) => void ) => void; resetSession: () => void; getMessagesWithMemory: () => Message[]; @@ -345,12 +345,12 @@ export const useChatStore = create()( .slice(0, index) .concat([deletedSession]) .concat( - state.sessions.slice(index + Number(isLastSession)), + state.sessions.slice(index + Number(isLastSession)) ), })); }, }, - 5000, + 5000 ); } }, @@ -412,7 +412,7 @@ export const useChatStore = create()( get().onNewMessage(botMessage); ControllerPool.remove( sessionIndex, - botMessage.id ?? messageIndex, + botMessage.id ?? messageIndex ); } else { botMessage.content = content; @@ -436,7 +436,7 @@ export const useChatStore = create()( ControllerPool.addController( sessionIndex, botMessage.id ?? messageIndex, - controller, + controller ); }, filterBot: !get().config.sendBotMessages, @@ -462,6 +462,7 @@ export const useChatStore = create()( const context = session.context.slice(); + // long term memory if ( session.sendMemory && session.memoryPrompt && @@ -471,9 +472,33 @@ export const useChatStore = create()( context.push(memoryPrompt); } - const recentMessages = context.concat( - messages.slice(Math.max(0, n - config.historyMessageCount)), + // get short term and unmemoried long term memory + const shortTermMemoryMessageIndex = Math.max( + 0, + n - config.historyMessageCount ); + const longTermMemoryMessageIndex = config.lastSummarizeIndex; + const oldestIndex = Math.min( + shortTermMemoryMessageIndex, + longTermMemoryMessageIndex + ); + const threshold = config.compressMessageLengthThreshold; + + // get recent messages as many as possible + const reversedRecentMessages = []; + for ( + let i = n - 1, count = 0; + i >= oldestIndex && count < threshold; + i -= 1 + ) { + const msg = messages[i]; + if (!msg || msg.isError) continue; + count += msg.content.length; + reversedRecentMessages.push(msg); + } + + // concat + const recentMessages = context.concat(reversedRecentMessages.reverse()); return recentMessages; }, @@ -481,7 +506,7 @@ export const useChatStore = create()( updateMessage( sessionIndex: number, messageIndex: number, - updater: (message?: Message) => void, + updater: (message?: Message) => void ) { const sessions = get().sessions; const session = sessions.at(sessionIndex); @@ -510,15 +535,15 @@ export const useChatStore = create()( (res) => { get().updateCurrentSession( (session) => - (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC), + (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC) ); - }, + } ); } const config = get().config; let toBeSummarizedMsgs = session.messages.slice( - session.lastSummarizeIndex, + session.lastSummarizeIndex ); const historyMsgLength = countMessages(toBeSummarizedMsgs); @@ -526,7 +551,7 @@ export const useChatStore = create()( if (historyMsgLength > get().config?.modelConfig?.max_tokens ?? 4000) { const n = toBeSummarizedMsgs.length; toBeSummarizedMsgs = toBeSummarizedMsgs.slice( - Math.max(0, n - config.historyMessageCount), + Math.max(0, n - config.historyMessageCount) ); } @@ -539,10 +564,13 @@ export const useChatStore = create()( "[Chat History] ", toBeSummarizedMsgs, historyMsgLength, - config.compressMessageLengthThreshold, + config.compressMessageLengthThreshold ); - if (historyMsgLength > config.compressMessageLengthThreshold) { + if ( + historyMsgLength > config.compressMessageLengthThreshold && + session.sendMemory + ) { requestChatStream( toBeSummarizedMsgs.concat({ role: "system", @@ -561,7 +589,7 @@ export const useChatStore = create()( onError(error) { console.error("[Summarize] ", error); }, - }, + } ); } }, @@ -603,6 +631,6 @@ export const useChatStore = create()( return state; }, - }, - ), + } + ) ); From ad1c8ffe21f16d86367101e5ecfaca66860f7254 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Tue, 18 Apr 2023 11:44:15 +0800 Subject: [PATCH 20/50] fixup --- app/store/app.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/store/app.ts b/app/store/app.ts index b3dfbd687..da0e886ff 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -102,7 +102,7 @@ export function limitNumber( x: number, min: number, max: number, - defaultValue: number + defaultValue: number, ) { if (typeof x !== "number" || isNaN(x)) { return defaultValue; @@ -217,7 +217,7 @@ interface ChatStore { updateMessage: ( sessionIndex: number, messageIndex: number, - updater: (message?: Message) => void + updater: (message?: Message) => void, ) => void; resetSession: () => void; getMessagesWithMemory: () => Message[]; @@ -345,12 +345,12 @@ export const useChatStore = create()( .slice(0, index) .concat([deletedSession]) .concat( - state.sessions.slice(index + Number(isLastSession)) + state.sessions.slice(index + Number(isLastSession)), ), })); }, }, - 5000 + 5000, ); } }, @@ -412,7 +412,7 @@ export const useChatStore = create()( get().onNewMessage(botMessage); ControllerPool.remove( sessionIndex, - botMessage.id ?? messageIndex + botMessage.id ?? messageIndex, ); } else { botMessage.content = content; @@ -436,7 +436,7 @@ export const useChatStore = create()( ControllerPool.addController( sessionIndex, botMessage.id ?? messageIndex, - controller + controller, ); }, filterBot: !get().config.sendBotMessages, @@ -475,12 +475,12 @@ export const useChatStore = create()( // get short term and unmemoried long term memory const shortTermMemoryMessageIndex = Math.max( 0, - n - config.historyMessageCount + n - config.historyMessageCount, ); - const longTermMemoryMessageIndex = config.lastSummarizeIndex; + const longTermMemoryMessageIndex = session.lastSummarizeIndex; const oldestIndex = Math.min( shortTermMemoryMessageIndex, - longTermMemoryMessageIndex + longTermMemoryMessageIndex, ); const threshold = config.compressMessageLengthThreshold; @@ -506,7 +506,7 @@ export const useChatStore = create()( updateMessage( sessionIndex: number, messageIndex: number, - updater: (message?: Message) => void + updater: (message?: Message) => void, ) { const sessions = get().sessions; const session = sessions.at(sessionIndex); @@ -535,15 +535,15 @@ export const useChatStore = create()( (res) => { get().updateCurrentSession( (session) => - (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC) + (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC), ); - } + }, ); } const config = get().config; let toBeSummarizedMsgs = session.messages.slice( - session.lastSummarizeIndex + session.lastSummarizeIndex, ); const historyMsgLength = countMessages(toBeSummarizedMsgs); @@ -551,7 +551,7 @@ export const useChatStore = create()( if (historyMsgLength > get().config?.modelConfig?.max_tokens ?? 4000) { const n = toBeSummarizedMsgs.length; toBeSummarizedMsgs = toBeSummarizedMsgs.slice( - Math.max(0, n - config.historyMessageCount) + Math.max(0, n - config.historyMessageCount), ); } @@ -564,7 +564,7 @@ export const useChatStore = create()( "[Chat History] ", toBeSummarizedMsgs, historyMsgLength, - config.compressMessageLengthThreshold + config.compressMessageLengthThreshold, ); if ( @@ -589,7 +589,7 @@ export const useChatStore = create()( onError(error) { console.error("[Summarize] ", error); }, - } + }, ); } }, @@ -631,6 +631,6 @@ export const useChatStore = create()( return state; }, - } - ) + }, + ), ); From d1653ded9a92b746fca908d6ebee422aa72493ab Mon Sep 17 00:00:00 2001 From: aooyoo Date: Tue, 18 Apr 2023 08:13:32 +0000 Subject: [PATCH 21/50] fix user prompt translation --- app/locales/cn.ts | 2 +- app/locales/jp.ts | 6 +++--- app/locales/tw.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 7d011c7f1..1c198195e 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -108,7 +108,7 @@ const cn = { Modal: { Title: "提示词列表", Add: "增加一条", - Search: "搜尋提示詞", + Search: "搜索提示词", }, }, HistoryCount: { diff --git a/app/locales/jp.ts b/app/locales/jp.ts index a793b5fe0..2818820b5 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -109,9 +109,9 @@ const jp = { `組み込み ${builtin} 件、ユーザー定義 ${custom} 件`, Edit: "編集", Modal: { - Title: "提示词列表", - Add: "增加一条", - Search: "搜尋提示詞", + Title: "プロンプトリスト", + Add: "新規追加", + Search: "プロンプトワード検索", }, }, HistoryCount: { diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 8d79e74fa..44c07898d 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -108,7 +108,7 @@ const tw: LocaleType = { Modal: { Title: "提示詞列表", Add: "增加一條", - Search: "搜索提示词", + Search: "搜尋提示詞", }, }, HistoryCount: { From dfcae92ae0914004484fc273de3a3ea84f0dd940 Mon Sep 17 00:00:00 2001 From: Ma Yuke Date: Tue, 18 Apr 2023 18:01:09 +0800 Subject: [PATCH 22/50] Update app/components/chat.tsx fix: chat-body flickers when click-and-hold the scroll bar using mouse --- app/components/chat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3c66627f6..5dce8fd61 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -708,6 +708,7 @@ export function Chat(props: { className={styles["chat-body"]} ref={scrollRef} onScroll={(e) => onChatBodyScroll(e.currentTarget)} + onMouseDown={() => inputRef.current?.blur()} onWheel={(e) => setAutoScroll(hitBottom && e.deltaY > 0)} onTouchStart={() => { inputRef.current?.blur(); From 072a35b4ee1940fb23264731038403c563638150 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 19 Apr 2023 11:20:07 +0800 Subject: [PATCH 23/50] fix: #915 allow send 0 history messages --- app/store/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/app.ts b/app/store/app.ts index da0e886ff..8d875fee5 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -478,7 +478,7 @@ export const useChatStore = create()( n - config.historyMessageCount, ); const longTermMemoryMessageIndex = session.lastSummarizeIndex; - const oldestIndex = Math.min( + const oldestIndex = Math.max( shortTermMemoryMessageIndex, longTermMemoryMessageIndex, ); From beb04d81811d260358411a74e61ef3f530f092a1 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Wed, 19 Apr 2023 15:36:10 +0800 Subject: [PATCH 24/50] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab902dd39..1764f2dbe 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ One-Click to deploy well-designed ChatGPT web UI on Vercel. [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) -[演示](https://chat-gpt-next-web.vercel.app/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://user-images.githubusercontent.com/16968934/231789746-41f34d05-6ef9-43f3-a1d1-ff109d4c3c14.jpg) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) +[演示](https://chat-gpt-next-web.vercel.app/) / [反馈](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) / [QQ 群](https://user-images.githubusercontent.com/16968934/233002565-139daa1a-eb3a-4a12-ac37-6418e7a15d36.png) / [打赏开发者](https://user-images.githubusercontent.com/16968934/227772541-5bcd52d8-61b7-488c-a203-0330d8006e2b.jpg) [![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) From 21da781350e88edde1842b74097c7908bd5c93be Mon Sep 17 00:00:00 2001 From: Yin Min Date: Wed, 19 Apr 2023 19:28:33 +0800 Subject: [PATCH 25/50] Update common.ts Add OrgID --- app/api/common.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/api/common.ts b/app/api/common.ts index 53ab18ed6..22e71884f 100644 --- a/app/api/common.ts +++ b/app/api/common.ts @@ -18,10 +18,15 @@ export async function requestOpenai(req: NextRequest) { console.log("[Proxy] ", openaiPath); console.log("[Base Url]", baseUrl); + if (process.env.OPENAI_ORG_ID) { + console.log("[Org ID]", process.env.OPENAI_ORG_ID); + } + return fetch(`${baseUrl}/${openaiPath}`, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, + ...(process.env.OPENAI_ORG_ID && { "OpenAI-Organization": process.env.OPENAI_ORG_ID }), }, method: req.method, body: req.body, From 7e8973c9ffba853b46ea9d795b1a05e81828ed37 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 20 Apr 2023 22:52:14 +0800 Subject: [PATCH 26/50] feat: close #291 gpt-4 model uses black icon --- app/components/chat.tsx | 12 +++++++++--- app/icons/black-bot.svg | 28 ++++++++++++++++++++++++++++ app/store/app.ts | 8 ++++++-- 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 app/icons/black-bot.svg diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 5dce8fd61..d27c138e7 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -10,6 +10,7 @@ import CopyIcon from "../icons/copy.svg"; import DownloadIcon from "../icons/download.svg"; import LoadingIcon from "../icons/three-dots.svg"; import BotIcon from "../icons/bot.svg"; +import BlackBotIcon from "../icons/black-bot.svg"; import AddIcon from "../icons/add.svg"; import DeleteIcon from "../icons/delete.svg"; import MaxIcon from "../icons/max.svg"; @@ -30,6 +31,7 @@ import { createMessage, useAccessStore, Theme, + ModelType, } from "../store"; import { @@ -64,13 +66,17 @@ const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, { loading: () => , }); -export function Avatar(props: { role: Message["role"] }) { +export function Avatar(props: { role: Message["role"]; model?: ModelType }) { const config = useChatStore((state) => state.config); if (props.role !== "user") { return (
- + {props.model?.startsWith("gpt-4") ? ( + + ) : ( + + )}
); } @@ -727,7 +733,7 @@ export function Chat(props: { >
- +
{(message.preview || message.streaming) && (
diff --git a/app/icons/black-bot.svg b/app/icons/black-bot.svg new file mode 100644 index 000000000..3aad2adde --- /dev/null +++ b/app/icons/black-bot.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/store/app.ts b/app/store/app.ts index 8d875fee5..5af4374c2 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -17,6 +17,7 @@ export type Message = ChatCompletionResponseMessage & { streaming?: boolean; isError?: boolean; id?: number; + model?: ModelType; }; export function createMessage(override: Partial): Message { @@ -58,7 +59,7 @@ export interface ChatConfig { disablePromptHint: boolean; modelConfig: { - model: string; + model: ModelType; temperature: number; max_tokens: number; presence_penalty: number; @@ -96,7 +97,9 @@ export const ALL_MODELS = [ name: "gpt-3.5-turbo-0301", available: true, }, -]; +] as const; + +export type ModelType = (typeof ALL_MODELS)[number]["name"]; export function limitNumber( x: number, @@ -387,6 +390,7 @@ export const useChatStore = create()( role: "assistant", streaming: true, id: userMessage.id! + 1, + model: get().config.modelConfig.model, }); // get recent messages From e3d2dd72794aa3d2b63c477231d54b0df62111e6 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 20 Apr 2023 22:55:14 +0800 Subject: [PATCH 27/50] feat: close #427 add OPENAI_ORG_ID --- README.md | 4 ++++ README_CN.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 1764f2dbe..e9ec7e68e 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,10 @@ Access passsword, separated by comma. Override openai api request base url. +### `OPENAI_ORG_ID` (optional) + +Specify OpenAI organization ID. + ## Development > [简体中文 > 如何进行二次开发](./README_CN.md#开发) diff --git a/README_CN.md b/README_CN.md index d2d64aa00..03ec2a101 100644 --- a/README_CN.md +++ b/README_CN.md @@ -94,6 +94,10 @@ OpenAI 接口代理 URL,如果你手动配置了 openai 接口代理,请填 > 如果遇到 ssl 证书问题,请将 `BASE_URL` 的协议设置为 http。 +### `OPENAI_ORG_ID` (可选) + +指定 OpenAI 中的组织 ID。 + ## 开发 > 强烈不建议在本地进行开发或者部署,由于一些技术原因,很难在本地配置好 OpenAI API 代理,除非你能保证可以直连 OpenAI 服务器。 From 2e9e69d66c56a0d0a2468b6456477d156980126b Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 20 Apr 2023 22:58:19 +0800 Subject: [PATCH 28/50] fixup --- app/store/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/app.ts b/app/store/app.ts index 5af4374c2..0a3ff8656 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -122,7 +122,7 @@ export function limitModel(name: string) { export const ModalConfigValidator = { model(x: string) { - return limitModel(x); + return limitModel(x) as ModelType; }, max_tokens(x: number) { return limitNumber(x, 0, 32000, 2000); From 06d503152bcba1ad9175441709d7e5c3044eea0a Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 20 Apr 2023 23:04:58 +0800 Subject: [PATCH 29/50] feat: close #928 summarize with gpt-3.5 --- app/requests.ts | 38 ++++++++++++++++++++++++++++++++------ app/store/app.ts | 16 ++++++++-------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/requests.ts b/app/requests.ts index 3cb838e63..9159f1cf5 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -1,5 +1,11 @@ import type { ChatRequest, ChatResponse } from "./api/openai/typing"; -import { Message, ModelConfig, useAccessStore, useChatStore } from "./store"; +import { + Message, + ModelConfig, + ModelType, + useAccessStore, + useChatStore, +} from "./store"; import { showToast } from "./components/ui-lib"; const TIME_OUT_MS = 60000; @@ -9,6 +15,7 @@ const makeRequestParam = ( options?: { filterBot?: boolean; stream?: boolean; + model?: ModelType; }, ): ChatRequest => { let sendMessages = messages.map((v) => ({ @@ -26,6 +33,11 @@ const makeRequestParam = ( // @ts-expect-error delete modelConfig.max_tokens; + // override model config + if (options?.model) { + modelConfig.model = options.model; + } + return { messages: sendMessages, stream: options?.stream, @@ -50,7 +62,7 @@ function getHeaders() { export function requestOpenaiClient(path: string) { return (body: any, method = "POST") => - fetch("/api/openai?_vercel_no_cache=1", { + fetch("/api/openai", { method, headers: { "Content-Type": "application/json", @@ -61,8 +73,16 @@ export function requestOpenaiClient(path: string) { }); } -export async function requestChat(messages: Message[]) { - const req: ChatRequest = makeRequestParam(messages, { filterBot: true }); +export async function requestChat( + messages: Message[], + options?: { + model?: ModelType; + }, +) { + const req: ChatRequest = makeRequestParam(messages, { + filterBot: true, + model: options?.model, + }); const res = await requestOpenaiClient("v1/chat/completions")(req); @@ -204,7 +224,13 @@ export async function requestChatStream( } } -export async function requestWithPrompt(messages: Message[], prompt: string) { +export async function requestWithPrompt( + messages: Message[], + prompt: string, + options?: { + model?: ModelType; + }, +) { messages = messages.concat([ { role: "user", @@ -213,7 +239,7 @@ export async function requestWithPrompt(messages: Message[], prompt: string) { }, ]); - const res = await requestChat(messages); + const res = await requestChat(messages, options); return res?.choices?.at(0)?.message?.content ?? ""; } diff --git a/app/store/app.ts b/app/store/app.ts index 0a3ff8656..fe2a07da7 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -535,14 +535,14 @@ export const useChatStore = create()( session.topic === DEFAULT_TOPIC && countMessages(session.messages) >= SUMMARIZE_MIN_LEN ) { - requestWithPrompt(session.messages, Locale.Store.Prompt.Topic).then( - (res) => { - get().updateCurrentSession( - (session) => - (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC), - ); - }, - ); + requestWithPrompt(session.messages, Locale.Store.Prompt.Topic, { + model: "gpt-3.5-turbo", + }).then((res) => { + get().updateCurrentSession( + (session) => + (session.topic = res ? trimTopic(res) : DEFAULT_TOPIC), + ); + }); } const config = get().config; From 2390da11651c80bd3e0fd3935063614a5694aa02 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Thu, 20 Apr 2023 23:09:42 +0800 Subject: [PATCH 30/50] fix: #930 wont show delete for first message --- app/components/chat.tsx | 66 ++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index d27c138e7..02f461416 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -723,6 +723,11 @@ export function Chat(props: { > {messages.map((message, i) => { const isUser = message.role === "user"; + const showActions = + !isUser && + i > 0 && + !(message.preview || message.content.length === 0); + const showTyping = message.preview || message.streaming; return (
- {(message.preview || message.streaming) && ( + {showTyping && (
{Locale.Chat.Typing}
)}
- {!isUser && - !(message.preview || message.content.length === 0) && ( -
- {message.streaming ? ( -
onUserStop(message.id ?? i)} - > - {Locale.Chat.Actions.Stop} -
- ) : ( - <> -
onDelete(message.id ?? i)} - > - {Locale.Chat.Actions.Delete} -
-
onResend(message.id ?? i)} - > - {Locale.Chat.Actions.Retry} -
- - )} - + {showActions && ( +
+ {message.streaming ? (
copyToClipboard(message.content)} + onClick={() => onUserStop(message.id ?? i)} > - {Locale.Chat.Actions.Copy} + {Locale.Chat.Actions.Stop}
+ ) : ( + <> +
onDelete(message.id ?? i)} + > + {Locale.Chat.Actions.Delete} +
+
onResend(message.id ?? i)} + > + {Locale.Chat.Actions.Retry} +
+ + )} + +
copyToClipboard(message.content)} + > + {Locale.Chat.Actions.Copy}
- )} +
+ )} Date: Thu, 20 Apr 2023 23:20:25 +0800 Subject: [PATCH 31/50] feat: reactive isMobileScreen --- app/components/chat-list.tsx | 1 - app/components/chat.tsx | 13 +++++++------ app/components/home.tsx | 20 +++++++++----------- app/utils.ts | 18 ++++++++++++++++++ 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index cab8812c3..f0139205b 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -10,7 +10,6 @@ import { import { useChatStore } from "../store"; import Locale from "../locales"; -import { isMobileScreen } from "../utils"; export function ChatItem(props: { onClick?: () => void; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 02f461416..c6bc61ebf 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -38,9 +38,9 @@ import { copyToClipboard, downloadAs, getEmojiUrl, - isMobileScreen, selectOrCopy, autoGrowTextArea, + useMobileScreen, } from "../utils"; import dynamic from "next/dynamic"; @@ -438,6 +438,7 @@ export function Chat(props: { const { submitKey, shouldSubmit } = useSubmitHandler(); const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom(); const [hitBottom, setHitBottom] = useState(false); + const isMobileScreen = useMobileScreen(); const onChatBodyScroll = (e: HTMLElement) => { const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 20; @@ -468,7 +469,7 @@ export function Chat(props: { const rows = inputRef.current ? autoGrowTextArea(inputRef.current) : 1; const inputRows = Math.min( 5, - Math.max(2 + Number(!isMobileScreen()), rows), + Math.max(2 + Number(!isMobileScreen), rows), ); setInputRows(inputRows); }, @@ -508,7 +509,7 @@ export function Chat(props: { setBeforeInput(userInput); setUserInput(""); setPromptHints([]); - if (!isMobileScreen()) inputRef.current?.focus(); + if (!isMobileScreen) inputRef.current?.focus(); setAutoScroll(true); }; @@ -640,7 +641,7 @@ export function Chat(props: { // Auto focus useEffect(() => { - if (props.sideBarShowing && isMobileScreen()) return; + if (props.sideBarShowing && isMobileScreen) return; inputRef.current?.focus(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -688,7 +689,7 @@ export function Chat(props: { }} />
- {!isMobileScreen() && ( + {!isMobileScreen && (
: } @@ -788,7 +789,7 @@ export function Chat(props: { } onContextMenu={(e) => onRightClick(e, message)} onDoubleClickCapture={() => { - if (!isMobileScreen()) return; + if (!isMobileScreen) return; setUserInput(message.content); }} fontSize={fontSize} diff --git a/app/components/home.tsx b/app/components/home.tsx index 828b7576a..ef30243c4 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -17,7 +17,7 @@ import LoadingIcon from "../icons/three-dots.svg"; import CloseIcon from "../icons/close.svg"; import { useChatStore } from "../store"; -import { getCSSVar, isMobileScreen } from "../utils"; +import { getCSSVar, useMobileScreen } from "../utils"; import Locale from "../locales"; import { Chat } from "./chat"; @@ -103,17 +103,14 @@ function useDragSideBar() { window.addEventListener("mousemove", handleMouseMove.current); window.addEventListener("mouseup", handleMouseUp.current); }; + const isMobileScreen = useMobileScreen(); useEffect(() => { - if (isMobileScreen()) { - return; - } - - document.documentElement.style.setProperty( - "--sidebar-width", - `${limit(chatStore.config.sidebarWidth ?? 300)}px`, - ); - }, [chatStore.config.sidebarWidth]); + const sideBarWidth = isMobileScreen + ? "100vw" + : `${limit(chatStore.config.sidebarWidth ?? 300)}px`; + document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); + }, [chatStore.config.sidebarWidth, isMobileScreen]); return { onDragMouseDown, @@ -148,6 +145,7 @@ function _Home() { // drag side bar const { onDragMouseDown } = useDragSideBar(); + const isMobileScreen = useMobileScreen(); useSwitchTheme(); @@ -158,7 +156,7 @@ function _Home() { return (
{ + const onResize = () => { + setIsMobileScreen(isMobileScreen()); + }; + + window.addEventListener("resize", onResize); + + return () => { + window.removeEventListener("resize", onResize); + }; + }, []); + + return isMobileScreen_; +} + export function isMobileScreen() { return window.innerWidth <= 600; } From 693dcf12d6c6ddd610b12bbc85ebab0474e46256 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 01:12:39 +0800 Subject: [PATCH 32/50] refactor: close #643 use react router --- app/components/chat-list.tsx | 8 +- app/components/chat.tsx | 14 +-- app/components/home.tsx | 212 +++++++++-------------------------- app/components/settings.tsx | 10 +- app/components/sidebar.tsx | 135 ++++++++++++++++++++++ app/constant.ts | 6 + app/utils.ts | 2 +- package.json | 1 + yarn.lock | 20 ++++ 9 files changed, 234 insertions(+), 174 deletions(-) create mode 100644 app/components/sidebar.tsx diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index f0139205b..fb0f740c5 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -10,6 +10,8 @@ import { import { useChatStore } from "../store"; import Locale from "../locales"; +import { Link, useNavigate } from "react-router-dom"; +import { Path } from "../constant"; export function ChatItem(props: { onClick?: () => void; @@ -59,6 +61,7 @@ export function ChatList() { state.moveSession, ]); const chatStore = useChatStore(); + const navigate = useNavigate(); const onDragEnd: OnDragEndResponder = (result) => { const { destination, source } = result; @@ -94,7 +97,10 @@ export function ChatList() { id={item.id} index={i} selected={i === selectedIndex} - onClick={() => selectSession(i)} + onClick={() => { + navigate(Path.Chat); + selectSession(i); + }} onDelete={() => chatStore.deleteSession(i)} /> ))} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index c6bc61ebf..bab422983 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -54,6 +54,8 @@ import styles from "./home.module.scss"; import chatStyle from "./chat.module.scss"; import { Input, Modal, showModal } from "./ui-lib"; +import { useNavigate } from "react-router-dom"; +import { Path } from "../constant"; const Markdown = dynamic( async () => memo((await import("./markdown")).Markdown), @@ -418,10 +420,7 @@ export function ChatActions(props: { ); } -export function Chat(props: { - showSideBar?: () => void; - sideBarShowing?: boolean; -}) { +export function Chat() { type RenderMessage = Message & { preview?: boolean }; const chatStore = useChatStore(); @@ -439,6 +438,7 @@ export function Chat(props: { const { scrollRef, setAutoScroll, scrollToBottom } = useScrollToBottom(); const [hitBottom, setHitBottom] = useState(false); const isMobileScreen = useMobileScreen(); + const navigate = useNavigate(); const onChatBodyScroll = (e: HTMLElement) => { const isTouchBottom = e.scrollTop + e.clientHeight >= e.scrollHeight - 20; @@ -641,7 +641,7 @@ export function Chat(props: { // Auto focus useEffect(() => { - if (props.sideBarShowing && isMobileScreen) return; + if (isMobileScreen) return; inputRef.current?.focus(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -666,7 +666,7 @@ export function Chat(props: { icon={} bordered title={Locale.Chat.Actions.ChatList} - onClick={props?.showSideBar} + onClick={() => navigate(Path.Home)} />
@@ -830,7 +830,7 @@ export function Chat(props: { setAutoScroll(false); setTimeout(() => setPromptHints([]), 500); }} - autoFocus={!props?.sideBarShowing} + autoFocus rows={inputRows} /> +
{!props.noLogo && }
@@ -38,7 +38,7 @@ const Settings = dynamic(async () => (await import("./settings")).Settings, { loading: () => , }); -const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { +const SideBar = dynamic(async () => (await import("./sidebar")).SideBar, { loading: () => , }); @@ -73,50 +73,6 @@ function useSwitchTheme() { }, [config.theme]); } -function useDragSideBar() { - const limit = (x: number) => Math.min(500, Math.max(220, x)); - - const chatStore = useChatStore(); - const startX = useRef(0); - const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300); - const lastUpdateTime = useRef(Date.now()); - - const handleMouseMove = useRef((e: MouseEvent) => { - if (Date.now() < lastUpdateTime.current + 100) { - return; - } - lastUpdateTime.current = Date.now(); - const d = e.clientX - startX.current; - const nextWidth = limit(startDragWidth.current + d); - chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth)); - }); - - const handleMouseUp = useRef(() => { - startDragWidth.current = chatStore.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 isMobileScreen = useMobileScreen(); - - useEffect(() => { - const sideBarWidth = isMobileScreen - ? "100vw" - : `${limit(chatStore.config.sidebarWidth ?? 300)}px`; - document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); - }, [chatStore.config.sidebarWidth, isMobileScreen]); - - return { - onDragMouseDown, - }; -} - const useHasHydrated = () => { const [hasHydrated, setHasHydrated] = useState(false); @@ -127,130 +83,64 @@ const useHasHydrated = () => { return hasHydrated; }; -function _Home() { - const [createNewSession, currentIndex, removeSession] = useChatStore( - (state) => [ - state.newSession, - state.currentSessionIndex, - state.removeSession, - ], - ); - const chatStore = useChatStore(); - const loading = !useHasHydrated(); - const [showSideBar, setShowSideBar] = useState(true); - +function WideScreen() { // setting - const [openSettings, setOpenSettings] = useState(false); const config = useChatStore((state) => state.config); - // drag side bar - const { onDragMouseDown } = useDragSideBar(); - const isMobileScreen = useMobileScreen(); - - useSwitchTheme(); - - if (loading) { - return ; - } - return (
-
-
-
ChatGPT Next
-
- Build your own AI assistant. -
-
- -
-
- -
{ - setOpenSettings(false); - setShowSideBar(false); - }} - > - -
- -
-
-
- } - onClick={chatStore.deleteSession} - /> -
-
- } - onClick={() => { - setOpenSettings(true); - setShowSideBar(false); - }} - shadow - /> -
- -
-
- } - text={Locale.Home.NewChat} - onClick={() => { - createNewSession(); - setShowSideBar(false); - }} - shadow - /> -
-
- -
onDragMouseDown(e as any)} - >
+
+
- {openSettings ? ( - { - setOpenSettings(false); - setShowSideBar(true); - }} - /> - ) : ( - setShowSideBar(true)} - sideBarShowing={showSideBar} - /> - )} + + } /> + } /> + } /> + +
+
+ ); +} + +function MobileScreen() { + const location = useLocation(); + const isHome = location.pathname === Path.Home; + + return ( +
+
+ +
+ +
+ + + } /> + } /> +
); } export function Home() { + useSwitchTheme(); + + const isMobileScreen = useMobileScreen(); + + if (!useHasHydrated()) { + return ; + } + return ( - <_Home> + {isMobileScreen ? : } ); } diff --git a/app/components/settings.tsx b/app/components/settings.tsx index d81b5b358..021484927 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -29,10 +29,11 @@ import { Avatar } from "./chat"; import Locale, { AllLangs, changeLang, getLang } from "../locales"; import { copyToClipboard, getEmojiUrl } from "../utils"; import Link from "next/link"; -import { UPDATE_URL } from "../constant"; +import { Path, 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"; function UserPromptModal(props: { onClose?: () => void }) { const promptStore = usePromptStore(); @@ -176,7 +177,8 @@ function PasswordInput(props: HTMLProps) { ); } -export function Settings(props: { closeSettings: () => void }) { +export function Settings() { + const navigate = useNavigate(); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [config, updateConfig, resetConfig, clearAllData, clearSessions] = useChatStore((state) => [ @@ -235,7 +237,7 @@ export function Settings(props: { closeSettings: () => void }) { useEffect(() => { const keydownEvent = (e: KeyboardEvent) => { if (e.key === "Escape") { - props.closeSettings(); + navigate(Path.Home); } }; document.addEventListener("keydown", keydownEvent); @@ -290,7 +292,7 @@ export function Settings(props: { closeSettings: () => void }) {
} - onClick={props.closeSettings} + onClick={() => navigate(Path.Home)} bordered title={Locale.Settings.Actions.Close} /> diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx new file mode 100644 index 000000000..338dec193 --- /dev/null +++ b/app/components/sidebar.tsx @@ -0,0 +1,135 @@ +import { useState, useEffect, useRef } from "react"; + +import styles from "./home.module.scss"; + +import { IconButton } from "./button"; +import SettingsIcon from "../icons/settings.svg"; +import GithubIcon from "../icons/github.svg"; +import ChatGptIcon from "../icons/chatgpt.svg"; +import AddIcon from "../icons/add.svg"; +import CloseIcon from "../icons/close.svg"; +import Locale from "../locales"; + +import { useChatStore } from "../store"; + +import { Path, REPO_URL } from "../constant"; + +import { HashRouter as Router, Link, useNavigate } from "react-router-dom"; +import { useMobileScreen } from "../utils"; +import { ChatList } from "./chat-list"; + +function useDragSideBar() { + const limit = (x: number) => Math.min(500, Math.max(220, x)); + + const chatStore = useChatStore(); + const startX = useRef(0); + const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300); + const lastUpdateTime = useRef(Date.now()); + + const handleMouseMove = useRef((e: MouseEvent) => { + if (Date.now() < lastUpdateTime.current + 100) { + return; + } + lastUpdateTime.current = Date.now(); + const d = e.clientX - startX.current; + const nextWidth = limit(startDragWidth.current + d); + chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth)); + }); + + const handleMouseUp = useRef(() => { + startDragWidth.current = chatStore.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 isMobileScreen = useMobileScreen(); + + useEffect(() => { + const sideBarWidth = isMobileScreen + ? "100vw" + : `${limit(chatStore.config.sidebarWidth ?? 300)}px`; + document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); + }, [chatStore.config.sidebarWidth, isMobileScreen]); + + return { + onDragMouseDown, + }; +} + +export function SideBar(props: { setShowSideBar?: (_: boolean) => void }) { + const chatStore = useChatStore(); + + // drag side bar + const { onDragMouseDown } = useDragSideBar(); + const navigate = useNavigate(); + const isMobileScreen = useMobileScreen(); + + return ( + <> +
+
ChatGPT Next
+
+ Build your own AI assistant. +
+
+ +
+
+ +
{ + if (e.target === e.currentTarget) { + navigate(Path.Home); + } + props.setShowSideBar?.(false); + }} + > + +
+ +
+
+
+ } + onClick={chatStore.deleteSession} + /> +
+
+ + } shadow /> + +
+ +
+
+ } + text={Locale.Home.NewChat} + onClick={() => { + chatStore.newSession(); + props.setShowSideBar?.(false); + }} + shadow + /> +
+
+ +
onDragMouseDown(e as any)} + >
+ + ); +} diff --git a/app/constant.ts b/app/constant.ts index 6f08ad756..687445462 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -6,3 +6,9 @@ export const UPDATE_URL = `${REPO_URL}#keep-updated`; 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 enum Path { + Home = "/", + Chat = "/chat", + Settings = "/settings", +} diff --git a/app/utils.ts b/app/utils.ts index 9d6e90622..0af9fef5b 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -49,7 +49,7 @@ export function isIOS() { } export function useMobileScreen() { - const [isMobileScreen_, setIsMobileScreen] = useState(false); + const [isMobileScreen_, setIsMobileScreen] = useState(isMobileScreen()); useEffect(() => { const onResize = () => { setIsMobileScreen(isMobileScreen()); diff --git a/package.json b/package.json index 19047ad11..785005519 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.5", + "react-router-dom": "^6.10.0", "rehype-highlight": "^6.0.0", "rehype-katex": "^6.0.2", "remark-breaks": "^3.0.2", diff --git a/yarn.lock b/yarn.lock index 342ea4a44..b7d9f8309 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1189,6 +1189,11 @@ tiny-glob "^0.2.9" tslib "^2.4.0" +"@remix-run/router@1.5.0": + version "1.5.0" + resolved "https://registry.npmmirror.com/@remix-run/router/-/router-1.5.0.tgz#57618e57942a5f0131374a9fdb0167e25a117fdc" + integrity sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg== + "@rushstack/eslint-patch@^1.1.3": version "1.2.0" resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" @@ -4296,6 +4301,21 @@ react-redux@^8.0.4: react-is "^18.0.0" use-sync-external-store "^1.0.0" +react-router-dom@^6.10.0: + version "6.10.0" + resolved "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.10.0.tgz#090ddc5c84dc41b583ce08468c4007c84245f61f" + integrity sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg== + dependencies: + "@remix-run/router" "1.5.0" + react-router "6.10.0" + +react-router@6.10.0: + version "6.10.0" + resolved "https://registry.npmmirror.com/react-router/-/react-router-6.10.0.tgz#230f824fde9dd0270781b5cb497912de32c0a971" + integrity sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ== + dependencies: + "@remix-run/router" "1.5.0" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" From 5185166e3b5b1f2b802833c79565c51c766b912e Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 01:18:49 +0800 Subject: [PATCH 33/50] fixup --- app/components/home.tsx | 2 -- app/utils.ts | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index efca5c0b3..8b5e1d74d 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -21,7 +21,6 @@ import { HashRouter as Router, Routes, Route, - useNavigation, useLocation, } from "react-router-dom"; @@ -131,7 +130,6 @@ function MobileScreen() { export function Home() { useSwitchTheme(); - const isMobileScreen = useMobileScreen(); if (!useHasHydrated()) { diff --git a/app/utils.ts b/app/utils.ts index 0af9fef5b..dfec8d3e9 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -66,6 +66,9 @@ export function useMobileScreen() { } export function isMobileScreen() { + if (typeof window === "undefined") { + return false; + } return window.innerWidth <= 600; } From 82ad0573be93b0ee43f9cc52b865ea8878988dfa Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 02:52:53 +0800 Subject: [PATCH 34/50] feat: close #380 collapse side bar --- app/components/chat-list.tsx | 31 ++++++++++----- app/components/home.module.scss | 69 ++++++++++++++++++++++++++++++++- app/components/home.tsx | 13 ++----- app/components/sidebar.tsx | 43 ++++++++++++-------- app/constant.ts | 4 ++ 5 files changed, 125 insertions(+), 35 deletions(-) diff --git a/app/components/chat-list.tsx b/app/components/chat-list.tsx index fb0f740c5..626336afd 100644 --- a/app/components/chat-list.tsx +++ b/app/components/chat-list.tsx @@ -22,6 +22,7 @@ export function ChatItem(props: { selected: boolean; id: number; index: number; + narrow?: boolean; }) { return ( @@ -35,13 +36,20 @@ export function ChatItem(props: { {...provided.draggableProps} {...provided.dragHandleProps} > -
{props.title}
-
-
- {Locale.ChatItem.ChatItemCount(props.count)} -
-
{props.time}
-
+ {props.narrow ? ( +
{props.count}
+ ) : ( + <> +
{props.title}
+
+
+ {Locale.ChatItem.ChatItemCount(props.count)} +
+
{props.time}
+
+ + )} +
@@ -51,7 +59,7 @@ export function ChatItem(props: { ); } -export function ChatList() { +export function ChatList(props: { narrow?: boolean }) { const [sessions, selectedIndex, selectSession, removeSession, moveSession] = useChatStore((state) => [ state.sessions, @@ -101,7 +109,12 @@ export function ChatList() { navigate(Path.Chat); selectSession(i); }} - onDelete={() => chatStore.deleteSession(i)} + onDelete={() => { + if (!props.narrow || confirm(Locale.Home.DeleteChat)) { + chatStore.deleteSession(i); + } + }} + narrow={props.narrow} /> ))} {provided.placeholder} diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 9bf0d5719..38e755bc5 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -50,7 +50,7 @@ flex-direction: column; box-shadow: inset -2px 0px 2px 0px rgb(0, 0, 0, 0.05); position: relative; - transition: width ease 0.1s; + transition: width ease 0.05s; } .sidebar-drag { @@ -126,11 +126,13 @@ .sidebar-title { font-size: 20px; font-weight: bold; + animation: slide-in ease 0.3s; } .sidebar-sub-title { font-size: 12px; font-weight: 400px; + animation: slide-in ease 0.3s; } .sidebar-body { @@ -171,6 +173,7 @@ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + animation: slide-in ease 0.3s; } .chat-item-delete { @@ -197,6 +200,7 @@ color: rgb(166, 166, 166); font-size: 12px; margin-top: 8px; + animation: slide-in ease 0.3s; } .chat-item-count, @@ -206,6 +210,69 @@ white-space: nowrap; } +.narrow-sidebar { + .sidebar-title, + .sidebar-sub-title { + display: none; + } + .sidebar-logo { + position: relative; + display: flex; + justify-content: center; + } + + .chat-item { + padding: 0; + min-height: 50px; + display: flex; + justify-content: center; + align-items: center; + transition: all ease 0.3s; + + &:hover { + .chat-item-narrow { + transform: scale(0.7) translateX(-50%); + } + } + } + + .chat-item-narrow { + font-weight: bolder; + font-size: 24px; + line-height: 0; + font-weight: lighter; + color: var(--black); + transform: translateX(0); + transition: all ease 0.3s; + opacity: 0.1; + padding: 4px; + } + + .chat-item-delete { + top: 15px; + } + + .chat-item:hover > .chat-item-delete { + opacity: 0.5; + right: 5px; + } + + .sidebar-tail { + flex-direction: column; + align-items: center; + + .sidebar-actions { + flex-direction: column; + align-items: center; + + .sidebar-action { + margin-right: 0; + margin-bottom: 15px; + } + } + } +} + .sidebar-tail { display: flex; justify-content: space-between; diff --git a/app/components/home.tsx b/app/components/home.tsx index 8b5e1d74d..123be03a9 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -41,7 +41,7 @@ const SideBar = dynamic(async () => (await import("./sidebar")).SideBar, { loading: () => , }); -function useSwitchTheme() { +export function useSwitchTheme() { const config = useChatStore((state) => state.config); useEffect(() => { @@ -83,7 +83,6 @@ const useHasHydrated = () => { }; function WideScreen() { - // setting const config = useChatStore((state) => state.config); return ( @@ -92,9 +91,7 @@ function WideScreen() { config.tightBorder ? styles["tight-container"] : styles.container }`} > -
- -
+
@@ -113,9 +110,7 @@ function MobileScreen() { return (
-
- -
+
@@ -129,8 +124,8 @@ function MobileScreen() { } export function Home() { - useSwitchTheme(); const isMobileScreen = useMobileScreen(); + useSwitchTheme(); if (!useHasHydrated()) { return ; diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 338dec193..71e75f8ab 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -12,14 +12,20 @@ import Locale from "../locales"; import { useChatStore } from "../store"; -import { Path, REPO_URL } from "../constant"; +import { + MAX_SIDEBAR_WIDTH, + MIN_SIDEBAR_WIDTH, + NARROW_SIDEBAR_WIDTH, + Path, + REPO_URL, +} from "../constant"; import { HashRouter as Router, Link, useNavigate } from "react-router-dom"; import { useMobileScreen } from "../utils"; import { ChatList } from "./chat-list"; function useDragSideBar() { - const limit = (x: number) => Math.min(500, Math.max(220, x)); + const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); const chatStore = useChatStore(); const startX = useRef(0); @@ -27,7 +33,7 @@ function useDragSideBar() { const lastUpdateTime = useRef(Date.now()); const handleMouseMove = useRef((e: MouseEvent) => { - if (Date.now() < lastUpdateTime.current + 100) { + if (Date.now() < lastUpdateTime.current + 50) { return; } lastUpdateTime.current = Date.now(); @@ -49,29 +55,36 @@ function useDragSideBar() { window.addEventListener("mouseup", handleMouseUp.current); }; const isMobileScreen = useMobileScreen(); + const shouldNarrow = + !isMobileScreen && chatStore.config.sidebarWidth < MIN_SIDEBAR_WIDTH; useEffect(() => { - const sideBarWidth = isMobileScreen - ? "100vw" - : `${limit(chatStore.config.sidebarWidth ?? 300)}px`; + const barWidth = shouldNarrow + ? NARROW_SIDEBAR_WIDTH + : limit(chatStore.config.sidebarWidth ?? 300); + const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`; document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); - }, [chatStore.config.sidebarWidth, isMobileScreen]); + }, [chatStore.config.sidebarWidth, isMobileScreen, shouldNarrow]); return { onDragMouseDown, + shouldNarrow, }; } -export function SideBar(props: { setShowSideBar?: (_: boolean) => void }) { +export function SideBar(props: { className?: string }) { const chatStore = useChatStore(); // drag side bar - const { onDragMouseDown } = useDragSideBar(); + const { onDragMouseDown, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); - const isMobileScreen = useMobileScreen(); return ( - <> +
ChatGPT Next
@@ -88,10 +101,9 @@ export function SideBar(props: { setShowSideBar?: (_: boolean) => void }) { if (e.target === e.currentTarget) { navigate(Path.Home); } - props.setShowSideBar?.(false); }} > - +
@@ -116,10 +128,9 @@ export function SideBar(props: { setShowSideBar?: (_: boolean) => void }) {
} - text={Locale.Home.NewChat} + text={shouldNarrow ? undefined : Locale.Home.NewChat} onClick={() => { chatStore.newSession(); - props.setShowSideBar?.(false); }} shadow /> @@ -130,6 +141,6 @@ export function SideBar(props: { setShowSideBar?: (_: boolean) => void }) { className={styles["sidebar-drag"]} onMouseDown={(e) => onDragMouseDown(e as any)} >
- +
); } diff --git a/app/constant.ts b/app/constant.ts index 687445462..43ae4cc68 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -12,3 +12,7 @@ export enum Path { Chat = "/chat", Settings = "/settings", } + +export const MAX_SIDEBAR_WIDTH = 500; +export const MIN_SIDEBAR_WIDTH = 230; +export const NARROW_SIDEBAR_WIDTH = 100; From b6a7104b60b462f79cbdd7be6b5b8f4285196b62 Mon Sep 17 00:00:00 2001 From: Shi Liang <7258605+shih-liang@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:03:02 +0800 Subject: [PATCH 35/50] chat-stream: runtime = "experimental-edge"; --- app/api/chat-stream/route.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/api/chat-stream/route.ts b/app/api/chat-stream/route.ts index 41f135495..22550e39c 100644 --- a/app/api/chat-stream/route.ts +++ b/app/api/chat-stream/route.ts @@ -59,6 +59,4 @@ export async function POST(req: NextRequest) { } } -export const config = { - runtime: "edge", -}; +export const runtime = "experimental-edge"; From 8966fd3b23935e86212840e17577d18d263ccac9 Mon Sep 17 00:00:00 2001 From: Shi Liang <7258605+shih-liang@users.noreply.github.com> Date: Fri, 21 Apr 2023 13:03:38 +0800 Subject: [PATCH 36/50] openai runtime = "experimental-edge"; --- app/api/openai/route.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/api/openai/route.ts b/app/api/openai/route.ts index 0ac94bdd5..bed70d928 100644 --- a/app/api/openai/route.ts +++ b/app/api/openai/route.ts @@ -30,6 +30,4 @@ export async function GET(req: NextRequest) { return makeRequest(req); } -export const config = { - runtime: "edge", -}; +export const runtime = "experimental-edge"; From 596a46846ad675c6a9304bd59700a13a47b5653e Mon Sep 17 00:00:00 2001 From: jzjwonderful Date: Fri, 21 Apr 2023 17:26:40 +0800 Subject: [PATCH 37/50] fix bug 978 --- scripts/setup.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/setup.sh b/scripts/setup.sh index b96533398..751a9ac17 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -29,13 +29,13 @@ esac if ! command -v node >/dev/null || ! command -v git >/dev/null || ! command -v yarn >/dev/null; then case "$(uname -s)" in Linux) - if [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"ubuntu\"" ]]; then + if [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=ubuntu" ]]; then sudo apt-get update sudo apt-get -y install nodejs git yarn - elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"centos\"" ]]; then + elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=centos" ]]; then sudo yum -y install epel-release sudo yum -y install nodejs git yarn - elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=\"arch\"" ]]; then + elif [[ "$(cat /etc/*-release | grep '^ID=')" = "ID=arch" ]]; then sudo pacman -Syu -y sudo pacman -S -y nodejs git yarn else From 4d45c07bf2096e9f12c142c010e3893c905d35f1 Mon Sep 17 00:00:00 2001 From: Zhenyu Zhu Date: Fri, 21 Apr 2023 18:52:32 +0800 Subject: [PATCH 38/50] fix: adjust presence_penalty step 0.1 --- app/components/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 021484927..4b552e289 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -645,7 +645,7 @@ export function Settings() { value={config.modelConfig.presence_penalty?.toFixed(1)} min="-2" max="2" - step="0.5" + step="0.1" onChange={(e) => { updateConfig( (config) => From 209a727fe92d9dac8e33c49a83efef702c661a7e Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 23:22:02 +0800 Subject: [PATCH 39/50] feat: close #928 summarize with gpt3.5 --- app/requests.ts | 2 ++ app/store/app.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/app/requests.ts b/app/requests.ts index 9159f1cf5..ce72bb7cf 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -149,6 +149,7 @@ export async function requestChatStream( options?: { filterBot?: boolean; modelConfig?: ModelConfig; + model?: ModelType; onMessage: (message: string, done: boolean) => void; onError: (error: Error, statusCode?: number) => void; onController?: (controller: AbortController) => void; @@ -157,6 +158,7 @@ export async function requestChatStream( const req = makeRequestParam(messages, { stream: true, filterBot: options?.filterBot, + model: options?.model, }); console.log("[Request] ", req); diff --git a/app/store/app.ts b/app/store/app.ts index fe2a07da7..89995e0bb 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -583,6 +583,7 @@ export const useChatStore = create()( }), { filterBot: false, + model: "gpt-3.5-turbo", onMessage(message, done) { session.memoryPrompt = message; if (done) { From e1ce1f2f4002abbb0cb86cf688957457e92afb90 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 23:28:53 +0800 Subject: [PATCH 40/50] feat: close #976 esc to close modal --- app/components/ui-lib.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/components/ui-lib.tsx b/app/components/ui-lib.tsx index a72aa868f..ffc05cf8e 100644 --- a/app/components/ui-lib.tsx +++ b/app/components/ui-lib.tsx @@ -2,7 +2,7 @@ import styles from "./ui-lib.module.scss"; import LoadingIcon from "../icons/three-dots.svg"; import CloseIcon from "../icons/close.svg"; import { createRoot } from "react-dom/client"; -import React from "react"; +import React, { useEffect } from "react"; export function Popover(props: { children: JSX.Element; @@ -64,6 +64,21 @@ interface ModalProps { onClose?: () => void; } export function Modal(props: ModalProps) { + useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + props.onClose?.(); + } + }; + + window.addEventListener("keydown", onKeyDown); + + return () => { + window.removeEventListener("keydown", onKeyDown); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return (
From ab826363ea4d585becb70d53778d45c0aa312403 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Apr 2023 23:37:25 +0800 Subject: [PATCH 41/50] fix: #965 improve loading animation --- app/components/home.module.scss | 5 ++++- app/components/home.tsx | 7 ++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/components/home.module.scss b/app/components/home.module.scss index 38e755bc5..1c021d884 100644 --- a/app/components/home.module.scss +++ b/app/components/home.module.scss @@ -313,6 +313,10 @@ .chat-message { display: flex; flex-direction: row; + + &:last-child { + animation: slide-in ease 0.3s; + } } .chat-message-user { @@ -325,7 +329,6 @@ display: flex; flex-direction: column; align-items: flex-start; - animation: slide-in ease 0.3s; &:hover { .chat-message-top-actions { diff --git a/app/components/home.tsx b/app/components/home.tsx index 123be03a9..ac3ce90e2 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -2,7 +2,7 @@ require("../polyfill"); -import { useState, useEffect } from "react"; +import { useState, useEffect, StyleHTMLAttributes } from "react"; import styles from "./home.module.scss"; @@ -23,6 +23,7 @@ import { Route, useLocation, } from "react-router-dom"; +import { SideBar } from "./sidebar"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -37,10 +38,6 @@ const Settings = dynamic(async () => (await import("./settings")).Settings, { loading: () => , }); -const SideBar = dynamic(async () => (await import("./sidebar")).SideBar, { - loading: () => , -}); - export function useSwitchTheme() { const config = useChatStore((state) => state.config); From ae479f4a92d1f5a20cfd5265a932bc329a029d58 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sat, 22 Apr 2023 00:12:07 +0800 Subject: [PATCH 42/50] fix: #963 config not work --- app/components/chat.tsx | 22 ++--- app/components/home.tsx | 6 +- app/components/settings.tsx | 16 ++-- app/components/sidebar.tsx | 26 +++--- app/locales/cn.ts | 2 +- app/locales/de.ts | 2 +- app/locales/en.ts | 2 +- app/locales/es.ts | 2 +- app/locales/it.ts | 2 +- app/locales/jp.ts | 2 +- app/locales/tr.ts | 2 +- app/locales/tw.ts | 2 +- app/requests.ts | 3 +- app/store/app.ts | 159 ++---------------------------------- app/store/config.ts | 135 ++++++++++++++++++++++++++++++ app/store/index.ts | 1 + 16 files changed, 190 insertions(+), 194 deletions(-) create mode 100644 app/store/config.ts diff --git a/app/components/chat.tsx b/app/components/chat.tsx index bab422983..c5cc54299 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -32,6 +32,7 @@ import { useAccessStore, Theme, ModelType, + useAppConfig, } from "../store"; import { @@ -69,7 +70,7 @@ const Emoji = dynamic(async () => (await import("emoji-picker-react")).Emoji, { }); export function Avatar(props: { role: Message["role"]; model?: ModelType }) { - const config = useChatStore((state) => state.config); + const config = useAppConfig(); if (props.role !== "user") { return ( @@ -285,7 +286,7 @@ function PromptToast(props: { } function useSubmitHandler() { - const config = useChatStore((state) => state.config); + const config = useAppConfig(); const submitKey = config.submitKey; const shouldSubmit = (e: React.KeyboardEvent) => { @@ -361,16 +362,16 @@ export function ChatActions(props: { scrollToBottom: () => void; hitBottom: boolean; }) { - const chatStore = useChatStore(); + const config = useAppConfig(); // switch themes - const theme = chatStore.config.theme; + const theme = config.theme; function nextTheme() { const themes = [Theme.Auto, Theme.Light, Theme.Dark]; const themeIndex = themes.indexOf(theme); const nextIndex = (themeIndex + 1) % themes.length; const nextTheme = themes[nextIndex]; - chatStore.updateConfig((config) => (config.theme = nextTheme)); + config.update((config) => (config.theme = nextTheme)); } // stop all responses @@ -428,7 +429,8 @@ export function Chat() { state.currentSession(), state.currentSessionIndex, ]); - const fontSize = useChatStore((state) => state.config.fontSize); + const config = useAppConfig(); + const fontSize = config.fontSize; const inputRef = useRef(null); const [userInput, setUserInput] = useState(""); @@ -492,7 +494,7 @@ export function Chat() { // clear search results if (n === 0) { setPromptHints([]); - } else if (!chatStore.config.disablePromptHint && n < SEARCH_TEXT_LIMIT) { + } else if (!config.disablePromptHint && n < SEARCH_TEXT_LIMIT) { // check if need to trigger auto completion if (text.startsWith("/")) { let searchText = text.slice(1); @@ -583,8 +585,6 @@ export function Chat() { inputRef.current?.focus(); }; - const config = useChatStore((state) => state.config); - const context: RenderMessage[] = session.context.slice(); const accessStore = useAccessStore(); @@ -692,10 +692,10 @@ export function Chat() { {!isMobileScreen && (
: } + icon={config.tightBorder ? : } bordered onClick={() => { - chatStore.updateConfig( + config.update( (config) => (config.tightBorder = !config.tightBorder), ); }} diff --git a/app/components/home.tsx b/app/components/home.tsx index ac3ce90e2..32334028f 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -9,7 +9,6 @@ import styles from "./home.module.scss"; import BotIcon from "../icons/bot.svg"; import LoadingIcon from "../icons/three-dots.svg"; -import { useChatStore } from "../store"; import { getCSSVar, useMobileScreen } from "../utils"; import { Chat } from "./chat"; @@ -24,6 +23,7 @@ import { useLocation, } from "react-router-dom"; import { SideBar } from "./sidebar"; +import { useAppConfig } from "../store/config"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -39,7 +39,7 @@ const Settings = dynamic(async () => (await import("./settings")).Settings, { }); export function useSwitchTheme() { - const config = useChatStore((state) => state.config); + const config = useAppConfig(); useEffect(() => { document.body.classList.remove("light"); @@ -80,7 +80,7 @@ const useHasHydrated = () => { }; function WideScreen() { - const config = useChatStore((state) => state.config); + const config = useAppConfig(); return (
) { export function Settings() { const navigate = useNavigate(); const [showEmojiPicker, setShowEmojiPicker] = useState(false); - const [config, updateConfig, resetConfig, clearAllData, clearSessions] = - useChatStore((state) => [ - state.config, - state.updateConfig, - state.resetConfig, - state.clearAllData, - state.clearSessions, - ]); + const config = useAppConfig(); + const updateConfig = config.update; + const resetConfig = config.reset; + const [clearAllData, clearSessions] = useChatStore((state) => [ + state.clearAllData, + state.clearSessions, + ]); const updateStore = useUpdateStore(); const [checkingUpdate, setCheckingUpdate] = useState(false); diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 71e75f8ab..d0c99dd19 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useEffect, useRef } from "react"; import styles from "./home.module.scss"; @@ -10,7 +10,7 @@ import AddIcon from "../icons/add.svg"; import CloseIcon from "../icons/close.svg"; import Locale from "../locales"; -import { useChatStore } from "../store"; +import { useAppConfig, useChatStore } from "../store"; import { MAX_SIDEBAR_WIDTH, @@ -20,16 +20,20 @@ import { REPO_URL, } from "../constant"; -import { HashRouter as Router, Link, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import { useMobileScreen } from "../utils"; -import { ChatList } from "./chat-list"; +import dynamic from "next/dynamic"; + +const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { + loading: () => null, +}); function useDragSideBar() { const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); - const chatStore = useChatStore(); + const config = useAppConfig(); const startX = useRef(0); - const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300); + const startDragWidth = useRef(config.sidebarWidth ?? 300); const lastUpdateTime = useRef(Date.now()); const handleMouseMove = useRef((e: MouseEvent) => { @@ -39,11 +43,11 @@ function useDragSideBar() { lastUpdateTime.current = Date.now(); const d = e.clientX - startX.current; const nextWidth = limit(startDragWidth.current + d); - chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth)); + config.update((config) => (config.sidebarWidth = nextWidth)); }); const handleMouseUp = useRef(() => { - startDragWidth.current = chatStore.config.sidebarWidth ?? 300; + startDragWidth.current = config.sidebarWidth ?? 300; window.removeEventListener("mousemove", handleMouseMove.current); window.removeEventListener("mouseup", handleMouseUp.current); }); @@ -56,15 +60,15 @@ function useDragSideBar() { }; const isMobileScreen = useMobileScreen(); const shouldNarrow = - !isMobileScreen && chatStore.config.sidebarWidth < MIN_SIDEBAR_WIDTH; + !isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH; useEffect(() => { const barWidth = shouldNarrow ? NARROW_SIDEBAR_WIDTH - : limit(chatStore.config.sidebarWidth ?? 300); + : limit(config.sidebarWidth ?? 300); const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`; document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); - }, [chatStore.config.sidebarWidth, isMobileScreen, shouldNarrow]); + }, [config.sidebarWidth, isMobileScreen, shouldNarrow]); return { onDragMouseDown, diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 1c198195e..777cea59b 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; const cn = { WIP: "该功能仍在开发中……", diff --git a/app/locales/de.ts b/app/locales/de.ts index e71abfaf7..42a4c8f68 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const de: LocaleType = { diff --git a/app/locales/en.ts b/app/locales/en.ts index 20e569606..f7af4bfb3 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const en: LocaleType = { diff --git a/app/locales/es.ts b/app/locales/es.ts index e2a9eb211..efecf113b 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const es: LocaleType = { diff --git a/app/locales/it.ts b/app/locales/it.ts index f0453b5c3..b519ef453 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const it: LocaleType = { diff --git a/app/locales/jp.ts b/app/locales/jp.ts index 2818820b5..1c8d66d90 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; const jp = { WIP: "この機能は開発中です……", diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 04a846245..86f1f417c 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const tr: LocaleType = { diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 44c07898d..20e41f47a 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -1,4 +1,4 @@ -import { SubmitKey } from "../store/app"; +import { SubmitKey } from "../store/config"; import type { LocaleType } from "./index"; const tw: LocaleType = { diff --git a/app/requests.ts b/app/requests.ts index ce72bb7cf..0e7570904 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -4,6 +4,7 @@ import { ModelConfig, ModelType, useAccessStore, + useAppConfig, useChatStore, } from "./store"; import { showToast } from "./components/ui-lib"; @@ -27,7 +28,7 @@ const makeRequestParam = ( sendMessages = sendMessages.filter((m) => m.role !== "assistant"); } - const modelConfig = { ...useChatStore.getState().config.modelConfig }; + const modelConfig = { ...useAppConfig.getState().modelConfig }; // @yidadaa: wont send max_tokens, because it is nonsense for Muggles // @ts-expect-error diff --git a/app/store/app.ts b/app/store/app.ts index 89995e0bb..2294130ad 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -11,6 +11,7 @@ import { isMobileScreen, trimTopic } from "../utils"; import Locale from "../locales"; import { showToast } from "../components/ui-lib"; +import { ModelType, useAppConfig } from "./config"; export type Message = ChatCompletionResponseMessage & { date: string; @@ -30,133 +31,8 @@ export function createMessage(override: Partial): Message { }; } -export enum SubmitKey { - Enter = "Enter", - CtrlEnter = "Ctrl + Enter", - ShiftEnter = "Shift + Enter", - AltEnter = "Alt + Enter", - MetaEnter = "Meta + Enter", -} - -export enum Theme { - Auto = "auto", - Dark = "dark", - Light = "light", -} - -export interface ChatConfig { - historyMessageCount: number; // -1 means all - compressMessageLengthThreshold: number; - sendBotMessages: boolean; // send bot's message or not - submitKey: SubmitKey; - avatar: string; - fontSize: number; - theme: Theme; - tightBorder: boolean; - sendPreviewBubble: boolean; - sidebarWidth: number; - - disablePromptHint: boolean; - - modelConfig: { - model: ModelType; - temperature: number; - max_tokens: number; - presence_penalty: number; - }; -} - -export type ModelConfig = ChatConfig["modelConfig"]; - export const ROLES: Message["role"][] = ["system", "user", "assistant"]; -const ENABLE_GPT4 = true; - -export const ALL_MODELS = [ - { - name: "gpt-4", - available: ENABLE_GPT4, - }, - { - name: "gpt-4-0314", - available: ENABLE_GPT4, - }, - { - name: "gpt-4-32k", - available: ENABLE_GPT4, - }, - { - name: "gpt-4-32k-0314", - available: ENABLE_GPT4, - }, - { - name: "gpt-3.5-turbo", - available: true, - }, - { - name: "gpt-3.5-turbo-0301", - available: true, - }, -] as const; - -export type ModelType = (typeof ALL_MODELS)[number]["name"]; - -export function limitNumber( - x: number, - min: number, - max: number, - defaultValue: number, -) { - if (typeof x !== "number" || isNaN(x)) { - return defaultValue; - } - - return Math.min(max, Math.max(min, x)); -} - -export function limitModel(name: string) { - return ALL_MODELS.some((m) => m.name === name && m.available) - ? name - : ALL_MODELS[4].name; -} - -export const ModalConfigValidator = { - model(x: string) { - return limitModel(x) as ModelType; - }, - max_tokens(x: number) { - return limitNumber(x, 0, 32000, 2000); - }, - presence_penalty(x: number) { - return limitNumber(x, -2, 2, 0); - }, - temperature(x: number) { - return limitNumber(x, 0, 2, 1); - }, -}; - -const DEFAULT_CONFIG: ChatConfig = { - historyMessageCount: 4, - compressMessageLengthThreshold: 1000, - sendBotMessages: true as boolean, - submitKey: SubmitKey.CtrlEnter as SubmitKey, - avatar: "1f603", - fontSize: 14, - theme: Theme.Auto as Theme, - tightBorder: false, - sendPreviewBubble: true, - sidebarWidth: 300, - - disablePromptHint: false, - - modelConfig: { - model: "gpt-3.5-turbo", - temperature: 1, - max_tokens: 2000, - presence_penalty: 0, - }, -}; - export interface ChatStat { tokenCount: number; wordCount: number; @@ -202,7 +78,6 @@ function createEmptySession(): ChatSession { } interface ChatStore { - config: ChatConfig; sessions: ChatSession[]; currentSessionIndex: number; clearSessions: () => void; @@ -226,9 +101,6 @@ interface ChatStore { getMessagesWithMemory: () => Message[]; getMemoryPrompt: () => Message; - getConfig: () => ChatConfig; - resetConfig: () => void; - updateConfig: (updater: (config: ChatConfig) => void) => void; clearAllData: () => void; } @@ -243,9 +115,6 @@ export const useChatStore = create()( (set, get) => ({ sessions: [createEmptySession()], currentSessionIndex: 0, - config: { - ...DEFAULT_CONFIG, - }, clearSessions() { set(() => ({ @@ -254,20 +123,6 @@ export const useChatStore = create()( })); }, - resetConfig() { - set(() => ({ config: { ...DEFAULT_CONFIG } })); - }, - - getConfig() { - return get().config; - }, - - updateConfig(updater) { - const config = get().config; - updater(config); - set(() => ({ config })); - }, - selectSession(index: number) { set({ currentSessionIndex: index, @@ -390,7 +245,7 @@ export const useChatStore = create()( role: "assistant", streaming: true, id: userMessage.id! + 1, - model: get().config.modelConfig.model, + model: useAppConfig.getState().modelConfig.model, }); // get recent messages @@ -443,8 +298,8 @@ export const useChatStore = create()( controller, ); }, - filterBot: !get().config.sendBotMessages, - modelConfig: get().config.modelConfig, + filterBot: !useAppConfig.getState().sendBotMessages, + modelConfig: useAppConfig.getState().modelConfig, }); }, @@ -460,7 +315,7 @@ export const useChatStore = create()( getMessagesWithMemory() { const session = get().currentSession(); - const config = get().config; + const config = useAppConfig.getState(); const messages = session.messages.filter((msg) => !msg.isError); const n = messages.length; @@ -545,14 +400,14 @@ export const useChatStore = create()( }); } - const config = get().config; + const config = useAppConfig.getState(); let toBeSummarizedMsgs = session.messages.slice( session.lastSummarizeIndex, ); const historyMsgLength = countMessages(toBeSummarizedMsgs); - if (historyMsgLength > get().config?.modelConfig?.max_tokens ?? 4000) { + if (historyMsgLength > config?.modelConfig?.max_tokens ?? 4000) { const n = toBeSummarizedMsgs.length; toBeSummarizedMsgs = toBeSummarizedMsgs.slice( Math.max(0, n - config.historyMessageCount), diff --git a/app/store/config.ts b/app/store/config.ts new file mode 100644 index 000000000..346f38da2 --- /dev/null +++ b/app/store/config.ts @@ -0,0 +1,135 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +export enum SubmitKey { + Enter = "Enter", + CtrlEnter = "Ctrl + Enter", + ShiftEnter = "Shift + Enter", + AltEnter = "Alt + Enter", + MetaEnter = "Meta + Enter", +} + +export enum Theme { + Auto = "auto", + Dark = "dark", + Light = "light", +} + +const DEFAULT_CONFIG = { + historyMessageCount: 4, + compressMessageLengthThreshold: 1000, + sendBotMessages: true as boolean, + submitKey: SubmitKey.CtrlEnter as SubmitKey, + avatar: "1f603", + fontSize: 14, + theme: Theme.Auto as Theme, + tightBorder: false, + sendPreviewBubble: true, + sidebarWidth: 300, + + disablePromptHint: false, + + modelConfig: { + model: "gpt-3.5-turbo" as ModelType, + temperature: 1, + max_tokens: 2000, + presence_penalty: 0, + }, +}; + +export type ChatConfig = typeof DEFAULT_CONFIG; + +export type ChatConfigStore = ChatConfig & { + reset: () => void; + update: (updater: (config: ChatConfig) => void) => void; +}; + +export type ModelConfig = ChatConfig["modelConfig"]; + +const ENABLE_GPT4 = true; + +export const ALL_MODELS = [ + { + name: "gpt-4", + available: ENABLE_GPT4, + }, + { + name: "gpt-4-0314", + available: ENABLE_GPT4, + }, + { + name: "gpt-4-32k", + available: ENABLE_GPT4, + }, + { + name: "gpt-4-32k-0314", + available: ENABLE_GPT4, + }, + { + name: "gpt-3.5-turbo", + available: true, + }, + { + name: "gpt-3.5-turbo-0301", + available: true, + }, +] as const; + +export type ModelType = (typeof ALL_MODELS)[number]["name"]; + +export function limitNumber( + x: number, + min: number, + max: number, + defaultValue: number, +) { + if (typeof x !== "number" || isNaN(x)) { + return defaultValue; + } + + return Math.min(max, Math.max(min, x)); +} + +export function limitModel(name: string) { + return ALL_MODELS.some((m) => m.name === name && m.available) + ? name + : ALL_MODELS[4].name; +} + +export const ModalConfigValidator = { + model(x: string) { + return limitModel(x) as ModelType; + }, + max_tokens(x: number) { + return limitNumber(x, 0, 32000, 2000); + }, + presence_penalty(x: number) { + return limitNumber(x, -2, 2, 0); + }, + temperature(x: number) { + return limitNumber(x, 0, 2, 1); + }, +}; + +const CONFIG_KEY = "app-config"; + +export const useAppConfig = create()( + persist( + (set, get) => ({ + ...DEFAULT_CONFIG, + + reset() { + set(() => ({ ...DEFAULT_CONFIG })); + }, + + update(updater) { + const config = { ...get() }; + updater(config); + set(() => config); + }, + }), + { + name: CONFIG_KEY, + }, + ), +); diff --git a/app/store/index.ts b/app/store/index.ts index 3bdb58ca2..7b7bbd04d 100644 --- a/app/store/index.ts +++ b/app/store/index.ts @@ -1,3 +1,4 @@ export * from "./app"; export * from "./update"; export * from "./access"; +export * from "./config"; From 79f58f5c6ad61e321c24c039e8e17607bd8d0397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B2=E9=9C=A7?= <123147018+yunwuu@users.noreply.github.com> Date: Sat, 22 Apr 2023 00:47:15 +0800 Subject: [PATCH 43/50] fix: typo --- app/components/chat.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index c5cc54299..b80bf5a18 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -545,7 +545,7 @@ export function Chat() { } }; - const findLastUesrIndex = (messageId: number) => { + const findLastUserIndex = (messageId: number) => { // find last user input message and resend let lastUserMessageIndex: number | null = null; for (let i = 0; i < session.messages.length; i += 1) { @@ -568,14 +568,14 @@ export function Chat() { }; const onDelete = (botMessageId: number) => { - const userIndex = findLastUesrIndex(botMessageId); + const userIndex = findLastUserIndex(botMessageId); if (userIndex === null) return; deleteMessage(userIndex); }; const onResend = (botMessageId: number) => { // find last user input message and resend - const userIndex = findLastUesrIndex(botMessageId); + const userIndex = findLastUserIndex(botMessageId); if (userIndex === null) return; setIsLoading(true); From 5d2fb8791ccaacc6baf873a26c842fd1c47e9427 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sun, 23 Apr 2023 00:07:16 +0900 Subject: [PATCH 44/50] Update README.md Github -> GitHub --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9ec7e68e..a6d1e3d33 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ We recommend that you follow the steps below to re-deploy: ### Enable Automatic Updates -After forking the project, due to the limitations imposed by Github, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour: +After forking the project, due to the limitations imposed by GitHub, you need to manually enable Workflows and Upstream Sync Action on the Actions page of the forked project. Once enabled, automatic updates will be scheduled every hour: ![Automatic Updates](./docs/images/enable-actions.jpg) @@ -110,7 +110,7 @@ After forking the project, due to the limitations imposed by Github, you need to ### Manually Updating Code -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. +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. From 1761289716aba1e6c6745d7e313dd837e463b4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B2=E9=9C=A7?= <123147018+yunwuu@users.noreply.github.com> Date: Sat, 22 Apr 2023 23:53:58 +0800 Subject: [PATCH 45/50] fix: typo --- scripts/fetch-prompts.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetch-prompts.mjs b/scripts/fetch-prompts.mjs index 7f6818d3b..9dc72626f 100644 --- a/scripts/fetch-prompts.mjs +++ b/scripts/fetch-prompts.mjs @@ -30,7 +30,7 @@ async function fetchEN() { .slice(1) .map((v) => v.split('","').map((v) => v.replace('"', ""))); } catch (error) { - console.error("[Fetch] failed to fetch cn prompts", error); + console.error("[Fetch] failed to fetch en prompts", error); return []; } } From 818629e58bdc96b30e83320ed863a41d4118bf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B2=E9=9C=A7?= <123147018+yunwuu@users.noreply.github.com> Date: Sun, 23 Apr 2023 00:17:00 +0800 Subject: [PATCH 46/50] chore: add timeout to prompt download request --- scripts/fetch-prompts.mjs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/fetch-prompts.mjs b/scripts/fetch-prompts.mjs index 9dc72626f..689377ca7 100644 --- a/scripts/fetch-prompts.mjs +++ b/scripts/fetch-prompts.mjs @@ -10,10 +10,20 @@ const RAW_EN_URL = "f/awesome-chatgpt-prompts/main/prompts.csv"; const EN_URL = MIRRORF_FILE_URL + RAW_EN_URL; const FILE = "./public/prompts.json"; +const timeoutPromise = (timeout) => { + return new Promise((resolve, reject) => { + setTimeout(() => { + reject(new Error('Request timeout')); + }, timeout); + }); +}; + async function fetchCN() { console.log("[Fetch] fetching cn prompts..."); try { - const raw = await (await fetch(CN_URL)).json(); + // const raw = await (await fetch(CN_URL)).json(); + const response = await Promise.race([fetch(CN_URL), timeoutPromise(5000)]); + const raw = await response.json(); return raw.map((v) => [v.act, v.prompt]); } catch (error) { console.error("[Fetch] failed to fetch cn prompts", error); @@ -24,7 +34,9 @@ async function fetchCN() { async function fetchEN() { console.log("[Fetch] fetching en prompts..."); try { - const raw = await (await fetch(EN_URL)).text(); + // const raw = await (await fetch(EN_URL)).text(); + const response = await Promise.race([fetch(EN_URL), timeoutPromise(5000)]); + const raw = response.text(); return raw .split("\n") .slice(1) From 725054c7d569f4d7b63def9b3bd3bb1b70cd3bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B2=E9=9C=A7?= <123147018+yunwuu@users.noreply.github.com> Date: Sun, 23 Apr 2023 10:51:34 +0800 Subject: [PATCH 47/50] fix: raw.split is not a function --- scripts/fetch-prompts.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetch-prompts.mjs b/scripts/fetch-prompts.mjs index 689377ca7..02b52a31d 100644 --- a/scripts/fetch-prompts.mjs +++ b/scripts/fetch-prompts.mjs @@ -36,7 +36,7 @@ async function fetchEN() { try { // const raw = await (await fetch(EN_URL)).text(); const response = await Promise.race([fetch(EN_URL), timeoutPromise(5000)]); - const raw = response.text(); + const raw = await response.text(); return raw .split("\n") .slice(1) From e5e2f6c2e1c293efcb0b41b254f4cd12ec374440 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 23 Apr 2023 00:57:55 +0800 Subject: [PATCH 48/50] Improve tw locale --- app/locales/tw.ts | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 20e41f47a..26791c77d 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -12,7 +12,7 @@ const tw: LocaleType = { Chat: { SubTitle: (count: number) => `您已經與 ChatGPT 進行了 ${count} 條對話`, Actions: { - ChatList: "查看消息列表", + ChatList: "查看訊息列表", CompressedHistory: "查看壓縮後的歷史 Prompt", Export: "匯出聊天紀錄", Copy: "複製", @@ -32,10 +32,10 @@ const tw: LocaleType = { Send: "發送", }, Export: { - Title: "匯出聊天記錄為 Markdown", + Title: "將聊天記錄匯出為 Markdown", Copy: "複製全部", Download: "下載檔案", - MessageFromYou: "來自你的訊息", + MessageFromYou: "來自您的訊息", MessageFromChatGPT: "來自 ChatGPT 的訊息", }, Memory: { @@ -43,8 +43,8 @@ const tw: LocaleType = { EmptyContent: "尚未記憶", Copy: "複製全部", Send: "發送記憶", - Reset: "重置對話", - ResetConfirm: "重置後將清空當前對話記錄以及歷史記憶,確認重置?", + Reset: "重設對話", + ResetConfirm: "重設後將清除目前對話記錄以及歷史記憶,確認重設?", }, Home: { NewChat: "新的對話", @@ -56,18 +56,18 @@ const tw: LocaleType = { Title: "設定", SubTitle: "設定選項", Actions: { - ClearAll: "清除所有數據", - ResetAll: "重置所有設定", + ClearAll: "清除所有資料", + ResetAll: "重設所有設定", Close: "關閉", ConfirmResetAll: { - Confirm: "Are you sure you want to reset all configurations?", + Confirm: "您確定要重設所有設定嗎?", }, ConfirmClearAll: { - Confirm: "Are you sure you want to reset all chat?", + Confirm: "您確定要清除所有聊天嗎?", }, }, Lang: { - Name: "Language", + Name: "語言", Options: { cn: "简体中文", en: "English", @@ -98,16 +98,16 @@ const tw: LocaleType = { SendPreviewBubble: "發送預覽氣泡", Prompt: { Disable: { - Title: "停用提示詞自動補全", - SubTitle: "在輸入框開頭輸入 / 即可觸發自動補全", + Title: "停用提示詞自動補齊", + SubTitle: "在輸入框開頭輸入 / 即可觸發自動補齊", }, List: "自定義提示詞列表", ListCount: (builtin: number, custom: number) => - `內置 ${builtin} 條,用戶定義 ${custom} 條`, + `內建 ${builtin} 條,用戶定義 ${custom} 條`, Edit: "編輯", Modal: { Title: "提示詞列表", - Add: "增加一條", + Add: "新增一條", Search: "搜尋提示詞", }, }, @@ -121,13 +121,13 @@ const tw: LocaleType = { }, Token: { Title: "API Key", - SubTitle: "使用自己的 Key 可規避授權訪問限制", + SubTitle: "使用自己的 Key 可規避授權存取限制", Placeholder: "OpenAI API Key", }, Usage: { Title: "帳戶餘額", SubTitle(used: any, total: any) { - return `本月已使用 $${used},订阅总额 $${total}`; + return `本月已使用 $${used},訂閱總額 $${total}`; }, IsChecking: "正在檢查…", Check: "重新檢查", @@ -135,17 +135,17 @@ const tw: LocaleType = { }, AccessCode: { Title: "授權碼", - SubTitle: "現在是未授權訪問狀態", + SubTitle: "目前是未授權存取狀態", Placeholder: "請輸入授權碼", }, Model: "模型 (model)", Temperature: { Title: "隨機性 (temperature)", - SubTitle: "值越大,回復越隨機", + SubTitle: "值越大,回應越隨機", }, MaxTokens: { - Title: "單次回復限制 (max_tokens)", - SubTitle: "單次交互所用的最大 Token 數", + Title: "單次回應限制 (max_tokens)", + SubTitle: "單次互動所用的最大 Token 數", }, PresencePenlty: { Title: "話題新穎度 (presence_penalty)", @@ -164,16 +164,16 @@ const tw: LocaleType = { Summarize: "Use the language used by the user (e.g. en-us for english conversation, zh-hant for chinese conversation, etc.) to summarise the conversation in at most 200 words. The summary will be used as prompt for you to continue the conversation in the future.", }, - ConfirmClearAll: "確認清除所有對話、設定數據?", + ConfirmClearAll: "確認清除所有對話、設定?", }, Copy: { Success: "已複製到剪貼簿中", Failed: "複製失敗,請賦予剪貼簿權限", }, Context: { - Toast: (x: any) => `已設置 ${x} 條前置上下文`, + Toast: (x: any) => `已設定 ${x} 條前置上下文`, Edit: "前置上下文和歷史記憶", - Add: "新增壹條", + Add: "新增一條", }, }; From c4ca05865d87533614de03989788887e3d4cafbf Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 25 Apr 2023 20:12:00 +0800 Subject: [PATCH 49/50] Update README_CN.md --- README_CN.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README_CN.md b/README_CN.md index 03ec2a101..23dfee85a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -114,14 +114,16 @@ OPENAI_API_KEY= ### 本地开发 -1. 安装 nodejs 和 yarn,具体细节请询问 ChatGPT; -2. 执行 `yarn install && yarn dev` 即可。 +1. 安装 nodejs 18 和 yarn,具体细节请询问 ChatGPT; +2. 执行 `yarn install && yarn dev` 即可。⚠️注意:此命令仅用于本地开发,不要用于部署! +3. 如果你想本地部署,请使用 `yarn install && yarn start` 命令,你可以配合 pm2 来守护进程,防止被杀死,详情询问 ChatGPT。 ## 部署 ### 容器部署 (推荐) +> Docker 版本需要在 20 及其以上,否则会提示找不到镜像。 -> 注意:docker 版本在大多数时间都会落后最新的版本 1 到 2 天,所以部署后会持续出现“存在更新”的提示,属于正常现象。 +> ⚠️注意:docker 版本在大多数时间都会落后最新的版本 1 到 2 天,所以部署后会持续出现“存在更新”的提示,属于正常现象。 ```shell docker pull yidadaa/chatgpt-next-web @@ -143,6 +145,8 @@ docker run -d -p 3000:3000 \ yidadaa/chatgpt-next-web ``` +如果你需要指定其他环境变量,请自行在上述命令中增加 `-e 环境变量=环境变量值` 来指定。 + ### 本地部署 在控制台运行下方命令: @@ -151,6 +155,8 @@ docker run -d -p 3000:3000 \ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/scripts/setup.sh) ``` +⚠️注意:如果你安装过程中遇到了问题,请使用 docker 部署。 + ## 鸣谢 ### 捐赠者 From 2e01a93a4a9195db095d0d208bf9c788cacb8294 Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 25 Apr 2023 22:54:21 +0800 Subject: [PATCH 50/50] Update issue templates --- .github/ISSUE_TEMPLATE/反馈问题.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/反馈问题.md b/.github/ISSUE_TEMPLATE/反馈问题.md index ea56aa6fa..b21442f53 100644 --- a/.github/ISSUE_TEMPLATE/反馈问题.md +++ b/.github/ISSUE_TEMPLATE/反馈问题.md @@ -8,6 +8,9 @@ assignees: '' --- **反馈须知** + +⚠️ 注意:不遵循此模板的任何帖子都会被立即关闭。 + > 请在下方中括号内输入 x 来表示你已经知晓相关内容。 - [ ] 我确认已经在 [常见问题](https://github.com/Yidadaa/ChatGPT-Next-Web/blob/main/docs/faq-cn.md) 中搜索了此次反馈的问题,没有找到解答; - [ ] 我确认已经在 [Issues](https://github.com/Yidadaa/ChatGPT-Next-Web/issues) 列表(包括已经 Close 的)中搜索了此次反馈的问题,没有找到解答。