diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 3bf9434d5..5d3879b26 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -1,99 +1,498 @@ -import { useState, useEffect, useMemo, HTMLProps } from "react"; +import { useState, useEffect, useRef, useMemo } from "react"; import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react"; - @@ -8,6 +8,8 @@ import ResetIcon from "../icons/reload.svg"; +import styles from "./settings.module.scss"; + +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: { - ); -} +import { IconButton } from "./button"; +import { + SubmitKey, + useChatStore, + Theme, + ALL_MODELS, + useUpdateStore, + useAccessStore, +} from "../store"; +import { Avatar, PromptHints } from "./home"; -function PasswordInput(props: HTMLProps) { - const [visible, setVisible] = useState(false); - - function changeVisibility() { - setVisible(!visible); - } +import Locale, { AllLangs, changeLang, getLang } from "../locales"; +import { getCurrentVersion } from "../utils"; +import Link from "next/link"; +import { UPDATE_URL } from "../constant"; +import { SearchService, usePromptStore } from "../store/prompt"; +import { requestUsage } from "../requests"; +function SettingItem(props: { + title: string; + subTitle?: string; + children: JSX.Element; +}) { return ( - - - : } - onClick={changeVisibility} - /> - + +
+
{props.title}
+ {props.subTitle && ( +
{props.subTitle}
+ )} +
+ {props.children} +
); } 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 }) { + useChatStore((state) => [ + state.config, + state.updateConfig, + state.resetConfig, + state.clearAllData, + state.clearSessions, + ]); + + const updateStore = useUpdateStore(); + const [checkingUpdate, setCheckingUpdate] = useState(false); + const currentId = getCurrentVersion(); + const remoteId = updateStore.remoteId; + const hasNewVersion = currentId !== remoteId; + + function checkUpdate(force = false) { + setCheckingUpdate(true); + updateStore.getLatestCommitId(force).then(() => { + setCheckingUpdate(false); + }); + } + + const [usage, setUsage] = useState<{ + granted?: number; + used?: number; + }>(); + const [loadingUsage, setLoadingUsage] = useState(false); + function checkUsage() { + setLoadingUsage(true); + requestUsage() + .then((res) => + setUsage({ + granted: res?.total_granted, + used: res?.total_used, + }), + ) + .finally(() => { + setLoadingUsage(false); + }); + } + + useEffect(() => { + checkUpdate(); + checkUsage(); + }, []); + + const accessStore = useAccessStore(); + const enabledAccessControl = useMemo( + () => accessStore.enabledAccessControl(), + [], + ); + + const promptStore = usePromptStore(); 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 }) { +
+
+ {Locale.Settings.Title} +
+
+ {Locale.Settings.SubTitle} +
+
+
+
+ } + onClick={clearSessions} + bordered + title={Locale.Settings.Actions.ClearAll} + /> +
+
+ } + onClick={resetConfig} + bordered + title={Locale.Settings.Actions.ResetAll} + /> +
+
+ } + onClick={props.closeSettings} + bordered + title={Locale.Settings.Actions.Close} + /> +
+
+
+
+ + + setShowEmojiPicker(false)} + content={ + { + updateConfig((config) => (config.avatar = e.unified)); + setShowEmojiPicker(false); + }} + /> + } + open={showEmojiPicker} + > +
setShowEmojiPicker(true)} + > + +
+
+
+ + + {checkingUpdate ? ( +
+ ) : hasNewVersion ? ( + + {Locale.Settings.Update.GoToUpdate} + + ) : ( + } + text={Locale.Settings.Update.CheckUpdate} + onClick={() => checkUpdate(true)} + /> + )} + + + + + + + +
+ {Locale.Settings.Theme} +
+ +
+ + + + + + + + updateConfig( + (config) => + (config.fontSize = Number.parseInt(e.currentTarget.value)), + ) + } + > + + + + + updateConfig( + (config) => (config.tightBorder = e.currentTarget.checked), + ) + } + > + + + + + updateConfig( + (config) => (config.sendPreviewBubble = e.currentTarget.checked), + ) + } + > + + + + + + updateConfig( + (config) => + (config.disablePromptHint = e.currentTarget.checked), + ) + } + > + + + + } + text={Locale.Settings.Prompt.Edit} + onClick={() => showToast(Locale.WIP)} + /> + + + + {enabledAccessControl ? ( + - { accessStore.updateCode(e.currentTarget.value); }} - /> + > ) : ( <> - @@ -344,25 +375,27 @@ export function Settings(props: { closeSettings: () => void }) { + )} + + - { accessStore.updateToken(e.currentTarget.value); }} - /> + > - {!showUsage || loadingUsage ? ( + {loadingUsage ? (
) : ( } + text={Locale.Settings.Usage.Check} + onClick={checkUsage} + /> + )} + + + + + updateConfig( + (config) => + (config.historyMessageCount = e.target.valueAsNumber), + ) + } + > + + + + + updateConfig( + (config) => + (config.compressMessageLengthThreshold = + e.currentTarget.valueAsNumber), + ) + } + > + + + + + + + + + { + updateConfig( + (config) => + (config.modelConfig.temperature = + e.currentTarget.valueAsNumber), + ); + }} + > + + + + updateConfig( + (config) => + (config.modelConfig.max_tokens = + e.currentTarget.valueAsNumber), + ) + } + > + + + { + updateConfig( + (config) => + (config.modelConfig.presence_penalty = + e.currentTarget.valueAsNumber), + ); + }} + > + + +
+ + ); +}