From bb30fdfa1735835b5d51b317d9e3ae0f0d52de30 Mon Sep 17 00:00:00 2001 From: Feifan Zheng <1450849885z@gmail.com> Date: Mon, 3 Apr 2023 12:18:04 +0000 Subject: [PATCH 1/6] feat: optimize usage display --- app/components/settings.tsx | 51 ++++++++++++++++++++++++++++++------- app/icons/eye-off.svg | 4 +++ app/icons/eye.svg | 4 +++ app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/locales/es.ts | 1 + app/locales/it.ts | 1 + app/locales/tw.ts | 1 + app/styles/globals.scss | 3 ++- 9 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 app/icons/eye-off.svg create mode 100644 app/icons/eye.svg diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 4c5adb86b..936588b79 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useMemo } from "react"; +import { useState, useEffect, useMemo, HTMLProps } from "react"; import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react"; @@ -8,6 +8,8 @@ import ResetIcon from "../icons/reload.svg"; import CloseIcon from "../icons/close.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"; @@ -47,6 +49,28 @@ function SettingItem(props: { ); } +function PasswordInput(props: HTMLProps) { + const [visible, setVisible] = useState(false); + + function changeVisibility() { + setVisible(!visible); + } + + return ( + + + : } + onClick={changeVisibility} + /> + + ); +} + export function Settings(props: { closeSettings: () => void }) { const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [config, updateConfig, resetConfig, clearAllData, clearSessions] = @@ -103,6 +127,13 @@ export function Settings(props: { closeSettings: () => void }) { const builtinCount = SearchService.count.builtin; const customCount = promptStore.prompts.size ?? 0; + const showUsage = accessStore.token !== ""; + useEffect(() => { + if (showUsage) { + checkUsage(); + } + }, [showUsage]); + return ( <>
@@ -327,14 +358,14 @@ export function Settings(props: { closeSettings: () => void }) { title={Locale.Settings.AccessCode.Title} subTitle={Locale.Settings.AccessCode.SubTitle} > - { accessStore.updateCode(e.currentTarget.value); }} - > + /> ) : ( <> @@ -344,25 +375,27 @@ export function Settings(props: { closeSettings: () => void }) { title={Locale.Settings.Token.Title} subTitle={Locale.Settings.Token.SubTitle} > - { accessStore.updateToken(e.currentTarget.value); }} - > + /> - {loadingUsage ? ( + {!showUsage || loadingUsage ? (
) : ( + + + \ No newline at end of file diff --git a/app/icons/eye.svg b/app/icons/eye.svg new file mode 100644 index 000000000..b5df29d5b --- /dev/null +++ b/app/icons/eye.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 8f2193b3d..49bcce235 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -109,6 +109,7 @@ const cn = { }, IsChecking: "正在检查…", Check: "重新检查", + NoAccess: "输入API Key查看余额", }, AccessCode: { Title: "访问码", diff --git a/app/locales/en.ts b/app/locales/en.ts index d8e9c615e..ae8e88c2d 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -111,6 +111,7 @@ const en: LocaleType = { }, IsChecking: "Checking...", Check: "Check Again", + NoAccess: "Enter API Key to check balance", }, AccessCode: { Title: "Access Code", diff --git a/app/locales/es.ts b/app/locales/es.ts index 34fcec76c..f3714ef3b 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -111,6 +111,7 @@ const es: LocaleType = { }, IsChecking: "Comprobando...", Check: "Comprobar de nuevo", + NoAccess: "Introduzca la clave API para comprobar el saldo", }, AccessCode: { Title: "Código de acceso", diff --git a/app/locales/it.ts b/app/locales/it.ts index 8c4e01233..c4736c1e0 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -112,6 +112,7 @@ const it: LocaleType = { }, IsChecking: "Controllando...", Check: "Controlla ancora", + NoAccess: "Inserire la chiave API per controllare il saldo", }, AccessCode: { Title: "Codice d'accesso", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 3779a5672..fff2f15dc 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -109,6 +109,7 @@ const tw: LocaleType = { }, IsChecking: "正在檢查…", Check: "重新檢查", + NoAccess: "輸入API Key查看餘額", }, AccessCode: { Title: "訪問碼", diff --git a/app/styles/globals.scss b/app/styles/globals.scss index 73b9b36f3..48f56995b 100644 --- a/app/styles/globals.scss +++ b/app/styles/globals.scss @@ -184,7 +184,8 @@ input[type="range"]::-webkit-slider-thumb:hover { } input[type="number"], -input[type="text"] { +input[type="text"], +input[type="password"] { appearance: none; border-radius: 10px; border: var(--border-in-light); From 30ff915e9db9fa766e871118ff36f77818ec0d9c Mon Sep 17 00:00:00 2001 From: Yorun <547747006@qq.com> Date: Mon, 3 Apr 2023 16:24:59 +0000 Subject: [PATCH 2/6] fix: add media query to theme-color, fix auto theme not updating theme-color --- app/components/home.tsx | 22 +++++++++++++++++----- app/layout.tsx | 7 ++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index cbb5dbfc6..66f86348f 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -52,11 +52,23 @@ function useSwitchTheme() { document.body.classList.add("light"); } - const themeColor = getComputedStyle(document.body) - .getPropertyValue("--theme-color") - .trim(); - const metaDescription = document.querySelector('meta[name="theme-color"]'); - metaDescription?.setAttribute("content", themeColor); + const metaDescriptionDark = document.querySelector( + 'meta[name="theme-color"][media]', + ); + const metaDescriptionLight = document.querySelector( + 'meta[name="theme-color"]:not([media])', + ); + + if (config.theme === "auto") { + metaDescriptionDark?.setAttribute("content", "#151515"); + metaDescriptionLight?.setAttribute("content", "#fafafa"); + } else { + const themeColor = getComputedStyle(document.body) + .getPropertyValue("--theme-color") + .trim(); + metaDescriptionDark?.setAttribute("content", themeColor); + metaDescriptionLight?.setAttribute("content", themeColor); + } }, [config.theme]); } diff --git a/app/layout.tsx b/app/layout.tsx index 6b6999f83..49a6d644d 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -21,7 +21,7 @@ export const metadata = { description: "Your personal ChatGPT Chat Bot.", appleWebApp: { title: "ChatGPT Next Web", - statusBarStyle: "black-translucent", + statusBarStyle: "default", }, themeColor: "#fafafa", }; @@ -53,6 +53,11 @@ export default function RootLayout({ name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> + From 4e644cfca70914371586e8761fe63791c7a6b04e Mon Sep 17 00:00:00 2001 From: yidadaa Date: Tue, 4 Apr 2023 01:05:33 +0800 Subject: [PATCH 3/6] fix: #418 valid model config --- app/components/settings.module.scss | 9 +++++ app/components/settings.tsx | 41 +++++++++++-------- app/requests.ts | 7 +--- app/store/app.ts | 62 ++++++++++++++--------------- 4 files changed, 64 insertions(+), 55 deletions(-) diff --git a/app/components/settings.module.scss b/app/components/settings.module.scss index ad994f68d..7d40d83b8 100644 --- a/app/components/settings.module.scss +++ b/app/components/settings.module.scss @@ -18,3 +18,12 @@ .avatar { cursor: pointer; } + +.password-input { + display: flex; + justify-content: flex-end; + + .password-eye { + margin-right: 4px; + } +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 936588b79..4645f3191 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -21,6 +21,7 @@ import { ALL_MODELS, useUpdateStore, useAccessStore, + ModalConfigValidator, } from "../store"; import { Avatar } from "./chat"; @@ -30,6 +31,7 @@ import Link from "next/link"; import { UPDATE_URL } from "../constant"; import { SearchService, usePromptStore } from "../store/prompt"; import { requestUsage } from "../requests"; +import { ErrorBoundary } from "./error"; function SettingItem(props: { title: string; @@ -57,17 +59,14 @@ function PasswordInput(props: HTMLProps) { } return ( - - +
: } onClick={changeVisibility} + className={styles["password-eye"]} /> - + +
); } @@ -115,11 +114,13 @@ export function Settings(props: { closeSettings: () => void }) { useEffect(() => { checkUpdate(); checkUsage(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const accessStore = useAccessStore(); const enabledAccessControl = useMemo( () => accessStore.enabledAccessControl(), + // eslint-disable-next-line react-hooks/exhaustive-deps [], ); @@ -135,7 +136,7 @@ export function Settings(props: { closeSettings: () => void }) { }, [showUsage]); return ( - <> +
@@ -453,7 +454,9 @@ export function Settings(props: { closeSettings: () => void }) { onChange={(e) => { updateConfig( (config) => - (config.modelConfig.model = e.currentTarget.value), + (config.modelConfig.model = ModalConfigValidator.model( + e.currentTarget.value, + )), ); }} > @@ -470,7 +473,7 @@ export function Settings(props: { closeSettings: () => void }) { > void }) { updateConfig( (config) => (config.modelConfig.temperature = - e.currentTarget.valueAsNumber), + ModalConfigValidator.temperature( + e.currentTarget.valueAsNumber, + )), ); }} > @@ -490,13 +495,15 @@ export function Settings(props: { closeSettings: () => void }) { updateConfig( (config) => (config.modelConfig.max_tokens = - e.currentTarget.valueAsNumber), + ModalConfigValidator.max_tokens( + e.currentTarget.valueAsNumber, + )), ) } > @@ -507,7 +514,7 @@ export function Settings(props: { closeSettings: () => void }) { > void }) { updateConfig( (config) => (config.modelConfig.presence_penalty = - e.currentTarget.valueAsNumber), + ModalConfigValidator.presence_penalty( + e.currentTarget.valueAsNumber, + )), ); }} >
- + ); } diff --git a/app/requests.ts b/app/requests.ts index ee3103b77..da9b5c97f 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -1,5 +1,5 @@ import type { ChatRequest, ChatReponse } from "./api/openai/typing"; -import { filterConfig, Message, ModelConfig, useAccessStore } from "./store"; +import { Message, ModelConfig, useAccessStore } from "./store"; import Locale from "./locales"; import { showToast } from "./components/ui-lib"; @@ -123,11 +123,6 @@ export async function requestChatStream( filterBot: options?.filterBot, }); - // valid and assign model config - if (options?.modelConfig) { - Object.assign(req, filterConfig(options.modelConfig)); - } - console.log("[Request] ", req); const controller = new AbortController(); diff --git a/app/store/app.ts b/app/store/app.ts index b943c0d97..d01e3cdd5 100644 --- a/app/store/app.ts +++ b/app/store/app.ts @@ -85,43 +85,39 @@ export const ALL_MODELS = [ }, ]; -export function isValidModel(name: string) { - return ALL_MODELS.some((m) => m.name === name && m.available); +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 isValidNumber(x: number, min: number, max: number) { - return typeof x === "number" && x <= max && x >= min; +export function limitModel(name: string) { + return ALL_MODELS.some((m) => m.name === name && m.available) + ? name + : ALL_MODELS[4].name; } -export function filterConfig(oldConfig: ModelConfig): Partial { - const config = Object.assign({}, oldConfig); - - const validator: { - [k in keyof ModelConfig]: (x: ModelConfig[keyof ModelConfig]) => boolean; - } = { - model(x) { - return isValidModel(x as string); - }, - max_tokens(x) { - return isValidNumber(x as number, 100, 32000); - }, - presence_penalty(x) { - return isValidNumber(x as number, -2, 2); - }, - temperature(x) { - return isValidNumber(x as number, 0, 2); - }, - }; - - Object.keys(validator).forEach((k) => { - const key = k as keyof ModelConfig; - if (!validator[key](config[key])) { - delete config[key]; - } - }); - - return config; -} +export const ModalConfigValidator = { + model(x: string) { + return limitModel(x); + }, + 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, From 7d7f3716beb10d341fbb1bdc28371eb02a1b86ae Mon Sep 17 00:00:00 2001 From: Yifei Zhang Date: Tue, 4 Apr 2023 01:06:00 +0800 Subject: [PATCH 4/6] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca71c996b..40386a005 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ One-Click to deploy your own ChatGPT web UI. - 在 1 分钟内使用 Vercel **免费一键部署** - 精心设计的 UI,响应式设计,支持深色模式 -- 极快的首屏加载速度(~85kb) +- 极快的首屏加载速度(~100kb) - 海量的内置 prompt 列表,来自[中文](https://github.com/PlexPt/awesome-chatgpt-prompts-zh)和[英文](https://github.com/f/awesome-chatgpt-prompts) - 自动压缩上下文聊天记录,在节省 Token 的同时支持超长对话 - 一键导出聊天记录,完整的 Markdown 支持 @@ -31,7 +31,7 @@ One-Click to deploy your own ChatGPT web UI. - **Deploy for free with one-click** on Vercel in under 1 minute - Responsive design, and dark mode -- Fast first screen loading speed (~85kb) +- Fast first screen loading speed (~100kb) - Awesome prompts powered by [awesome-chatgpt-prompts-zh](https://github.com/PlexPt/awesome-chatgpt-prompts-zh) and [awesome-chatgpt-prompts](https://github.com/f/awesome-chatgpt-prompts) - Automatically compresses chat history to support long conversations while also saving your tokens - One-click export all chat history with full Markdown support From 47f055872eea825bd170f83f719952cc687e6338 Mon Sep 17 00:00:00 2001 From: kakawanyifan Date: Tue, 4 Apr 2023 13:27:50 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E8=AE=BF=E9=97=AE=E6=94=B9=E6=88=90?= =?UTF-8?q?=E6=8E=88=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/locales/cn.ts | 10 +++++----- app/locales/tw.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 49bcce235..b021c8497 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -3,7 +3,7 @@ import { SubmitKey } from "../store/app"; const cn = { WIP: "该功能仍在开发中……", Error: { - Unauthorized: "现在是未授权状态,请在设置页填写授权码。", + Unauthorized: "现在是未授权状态,请在设置页输入授权码。", }, ChatItem: { ChatItemCount: (count: number) => `${count} 条对话`, @@ -99,7 +99,7 @@ const cn = { }, Token: { Title: "API Key", - SubTitle: "使用自己的 Key 可绕过受控访问限制", + SubTitle: "使用自己的 Key 可绕过授权访问限制", Placeholder: "OpenAI API Key", }, Usage: { @@ -112,9 +112,9 @@ const cn = { NoAccess: "输入API Key查看余额", }, AccessCode: { - Title: "访问码", - SubTitle: "现在是受控访问状态", - Placeholder: "请输入访问码", + Title: "授权码", + SubTitle: "现在是未授权访问状态", + Placeholder: "请输入授权码", }, Model: "模型 (model)", Temperature: { diff --git a/app/locales/tw.ts b/app/locales/tw.ts index fff2f15dc..f27001971 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -4,7 +4,7 @@ import type { LocaleType } from "./index"; const tw: LocaleType = { WIP: "該功能仍在開發中……", Error: { - Unauthorized: "目前您的狀態是未授權,請前往設定頁面填寫授權碼。", + Unauthorized: "目前您的狀態是未授權,請前往設定頁面輸入授權碼。", }, ChatItem: { ChatItemCount: (count: number) => `${count} 條對話`, @@ -99,7 +99,7 @@ const tw: LocaleType = { }, Token: { Title: "API Key", - SubTitle: "使用自己的 Key 可規避受控訪問限制", + SubTitle: "使用自己的 Key 可規避授權訪問限制", Placeholder: "OpenAI API Key", }, Usage: { @@ -112,9 +112,9 @@ const tw: LocaleType = { NoAccess: "輸入API Key查看餘額", }, AccessCode: { - Title: "訪問碼", - SubTitle: "現在是受控訪問狀態", - Placeholder: "請輸入訪問碼", + Title: "授權碼", + SubTitle: "現在是未授權訪問狀態", + Placeholder: "請輸入授權碼", }, Model: "模型 (model)", Temperature: { From 0508b0080f845862140e395a479058d66e452fb0 Mon Sep 17 00:00:00 2001 From: kakawanyifan Date: Tue, 4 Apr 2023 16:33:36 +0800 Subject: [PATCH 6/6] =?UTF-8?q?linkTarget=3D{'=5Fblank'}=EF=BC=8C=E8=BF=99?= =?UTF-8?q?=E4=B8=AA=E5=AF=B9=E4=BA=8E=E5=AF=B9=E8=AF=9D=E6=A1=86=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E9=93=BE=E6=8E=A5=EF=BC=8C=E4=BC=9A=E5=9C=A8=E6=96=B0?= =?UTF-8?q?=E7=9A=84tab=E9=A1=B5=E6=89=93=E5=BC=80=EF=BC=8C=E4=BD=93?= =?UTF-8?q?=E9=AA=8C=E6=9B=B4=E5=A5=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/markdown.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/components/markdown.tsx b/app/components/markdown.tsx index 88e0f66f7..25991d742 100644 --- a/app/components/markdown.tsx +++ b/app/components/markdown.tsx @@ -67,6 +67,7 @@ export function Markdown(props: { content: string }) { components={{ pre: PreCode, }} + linkTarget={'_blank'} > {props.content}