mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-08 19:16:37 +08:00
Update settings.tsx
This commit is contained in:
parent
dfbf78935a
commit
8202d9d613
@ -1,498 +1,99 @@
|
||||
import { useState, useEffect, useRef, useMemo } from "react";
|
||||
import { useState, useEffect, useMemo, HTMLProps } from "react";
|
||||
|
||||
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
|
||||
|
||||
import styles from "./settings.module.scss";
|
||||
|
||||
import ResetIcon from "../icons/reload.svg";
|
||||
@@ -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";
|
||||
|
||||
import { IconButton } from "./button";
|
||||
import {
|
||||
SubmitKey,
|
||||
useChatStore,
|
||||
Theme,
|
||||
ALL_MODELS,
|
||||
useUpdateStore,
|
||||
useAccessStore,
|
||||
} from "../store";
|
||||
import { Avatar, PromptHints } from "./home";
|
||||
@@ -47,6 +49,28 @@ function SettingItem(props: {
|
||||
);
|
||||
}
|
||||
|
||||
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 PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
function changeVisibility() {
|
||||
setVisible(!visible);
|
||||
}
|
||||
|
||||
function SettingItem(props: {
|
||||
title: string;
|
||||
subTitle?: string;
|
||||
children: JSX.Element;
|
||||
}) {
|
||||
return (
|
||||
<ListItem>
|
||||
<div className={styles["settings-title"]}>
|
||||
<div>{props.title}</div>
|
||||
{props.subTitle && (
|
||||
<div className={styles["settings-sub-title"]}>{props.subTitle}</div>
|
||||
)}
|
||||
</div>
|
||||
{props.children}
|
||||
</ListItem>
|
||||
<span style={{ display: "flex", justifyContent: "end" }}>
|
||||
<input
|
||||
{...props}
|
||||
style={{ minWidth: "150px" }}
|
||||
type={visible ? "text" : "password"}
|
||||
/>
|
||||
<IconButton
|
||||
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||
onClick={changeVisibility}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function Settings(props: { closeSettings: () => void }) {
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||
const [config, updateConfig, resetConfig, clearAllData, clearSessions] =
|
||||
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();
|
||||
@@ -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 (
|
||||
<>
|
||||
<div className={styles["window-header"]}>
|
||||
<div className={styles["window-header-title"]}>
|
||||
<div className={styles["window-header-main-title"]}>
|
||||
{Locale.Settings.Title}
|
||||
</div>
|
||||
<div className={styles["window-header-sub-title"]}>
|
||||
{Locale.Settings.SubTitle}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles["window-actions"]}>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<IconButton
|
||||
icon={<ClearIcon />}
|
||||
onClick={clearSessions}
|
||||
bordered
|
||||
title={Locale.Settings.Actions.ClearAll}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<IconButton
|
||||
icon={<ResetIcon />}
|
||||
onClick={resetConfig}
|
||||
bordered
|
||||
title={Locale.Settings.Actions.ResetAll}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<IconButton
|
||||
icon={<CloseIcon />}
|
||||
onClick={props.closeSettings}
|
||||
bordered
|
||||
title={Locale.Settings.Actions.Close}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles["settings"]}>
|
||||
<List>
|
||||
<SettingItem title={Locale.Settings.Avatar}>
|
||||
<Popover
|
||||
onClose={() => setShowEmojiPicker(false)}
|
||||
content={
|
||||
<EmojiPicker
|
||||
lazyLoadEmojis
|
||||
theme={EmojiTheme.AUTO}
|
||||
onEmojiClick={(e) => {
|
||||
updateConfig((config) => (config.avatar = e.unified));
|
||||
setShowEmojiPicker(false);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
open={showEmojiPicker}
|
||||
>
|
||||
<div
|
||||
className={styles.avatar}
|
||||
onClick={() => setShowEmojiPicker(true)}
|
||||
>
|
||||
<Avatar role="user" />
|
||||
</div>
|
||||
</Popover>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.Update.Version(currentId)}
|
||||
subTitle={
|
||||
checkingUpdate
|
||||
? Locale.Settings.Update.IsChecking
|
||||
: hasNewVersion
|
||||
? Locale.Settings.Update.FoundUpdate(remoteId ?? "ERROR")
|
||||
: Locale.Settings.Update.IsLatest
|
||||
}
|
||||
>
|
||||
{checkingUpdate ? (
|
||||
<div />
|
||||
) : hasNewVersion ? (
|
||||
<Link href="http://leichat.top/" target="_blank" className="link">
|
||||
{Locale.Settings.Update.GoToUpdate}
|
||||
</Link>
|
||||
) : (
|
||||
<IconButton
|
||||
icon={<ResetIcon></ResetIcon>}
|
||||
text={Locale.Settings.Update.CheckUpdate}
|
||||
onClick={() => checkUpdate(true)}
|
||||
/>
|
||||
)}
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.SendKey}>
|
||||
<select
|
||||
value={config.submitKey}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.submitKey = e.target.value as any as SubmitKey),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{Object.values(SubmitKey).map((v) => (
|
||||
<option value={v} key={v}>
|
||||
{v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingItem>
|
||||
|
||||
<ListItem>
|
||||
<div className={styles["settings-title"]}>
|
||||
{Locale.Settings.Theme}
|
||||
</div>
|
||||
<select
|
||||
value={config.theme}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) => (config.theme = e.target.value as any as Theme),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{Object.values(Theme).map((v) => (
|
||||
<option value={v} key={v}>
|
||||
{v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.Lang.Name}>
|
||||
<select
|
||||
value={getLang()}
|
||||
onChange={(e) => {
|
||||
changeLang(e.target.value as any);
|
||||
}}
|
||||
>
|
||||
{AllLangs.map((lang) => (
|
||||
<option value={lang} key={lang}>
|
||||
{Locale.Settings.Lang.Options[lang]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.FontSize.Title}
|
||||
subTitle={Locale.Settings.FontSize.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="range"
|
||||
title={`${config.fontSize ?? 14}px`}
|
||||
value={config.fontSize}
|
||||
min="12"
|
||||
max="18"
|
||||
step="1"
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.fontSize = Number.parseInt(e.currentTarget.value)),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.TightBorder}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.tightBorder}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) => (config.tightBorder = e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.SendPreviewBubble}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.sendPreviewBubble}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) => (config.sendPreviewBubble = e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</List>
|
||||
<List>
|
||||
<SettingItem
|
||||
title={Locale.Settings.Prompt.Disable.Title}
|
||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.disablePromptHint}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.disablePromptHint = e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.Prompt.List}
|
||||
subTitle={Locale.Settings.Prompt.ListCount(
|
||||
builtinCount,
|
||||
customCount,
|
||||
)}
|
||||
>
|
||||
<IconButton
|
||||
icon={<EditIcon />}
|
||||
text={Locale.Settings.Prompt.Edit}
|
||||
onClick={() => showToast(Locale.WIP)}
|
||||
/>
|
||||
</SettingItem>
|
||||
</List>
|
||||
<List>
|
||||
{enabledAccessControl ? (
|
||||
<SettingItem
|
||||
@@ -327,14 +358,14 @@ export function Settings(props: { closeSettings: () => void }) {
|
||||
title={Locale.Settings.AccessCode.Title}
|
||||
subTitle={Locale.Settings.AccessCode.SubTitle}
|
||||
>
|
||||
<input
|
||||
<PasswordInput
|
||||
value={accessStore.accessCode}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.AccessCode.Placeholder}
|
||||
onChange={(e) => {
|
||||
accessStore.updateCode(e.currentTarget.value);
|
||||
}}
|
||||
></input>
|
||||
/>
|
||||
</SettingItem>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<SettingItem
|
||||
@@ -344,25 +375,27 @@ export function Settings(props: { closeSettings: () => void }) {
|
||||
title={Locale.Settings.Token.Title}
|
||||
subTitle={Locale.Settings.Token.SubTitle}
|
||||
>
|
||||
<input
|
||||
<PasswordInput
|
||||
value={accessStore.token}
|
||||
type="text"
|
||||
placeholder={Locale.Settings.Token.Placeholder}
|
||||
onChange={(e) => {
|
||||
accessStore.updateToken(e.currentTarget.value);
|
||||
}}
|
||||
></input>
|
||||
/>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.Usage.Title}
|
||||
subTitle={
|
||||
loadingUsage
|
||||
? Locale.Settings.Usage.IsChecking
|
||||
: Locale.Settings.Usage.SubTitle(
|
||||
usage?.granted ?? "[?]",
|
||||
usage?.used ?? "[?]",
|
||||
)
|
||||
showUsage
|
||||
? loadingUsage
|
||||
? Locale.Settings.Usage.IsChecking
|
||||
: Locale.Settings.Usage.SubTitle(usage?.used ?? "[?]")
|
||||
: Locale.Settings.Usage.NoAccess
|
||||
}
|
||||
>
|
||||
{loadingUsage ? (
|
||||
{!showUsage || loadingUsage ? (
|
||||
<div />
|
||||
) : (
|
||||
<IconButton
|
||||
icon={<ResetIcon></ResetIcon>}
|
||||
text={Locale.Settings.Usage.Check}
|
||||
onClick={checkUsage}
|
||||
/>
|
||||
)}
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.HistoryCount.Title}
|
||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="range"
|
||||
title={config.historyMessageCount.toString()}
|
||||
value={config.historyMessageCount}
|
||||
min="0"
|
||||
max="25"
|
||||
step="2"
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.historyMessageCount = e.target.valueAsNumber),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
|
||||
<SettingItem
|
||||
title={Locale.Settings.CompressThreshold.Title}
|
||||
subTitle={Locale.Settings.CompressThreshold.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
min={500}
|
||||
max={4000}
|
||||
value={config.compressMessageLengthThreshold}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.compressMessageLengthThreshold =
|
||||
e.currentTarget.valueAsNumber),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
<SettingItem title={Locale.Settings.Model}>
|
||||
<select
|
||||
value={config.modelConfig.model}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.model = e.currentTarget.value),
|
||||
);
|
||||
}}
|
||||
>
|
||||
{ALL_MODELS.map((v) => (
|
||||
<option value={v.name} key={v.name} disabled={!v.available}>
|
||||
{v.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
title={Locale.Settings.Temperature.Title}
|
||||
subTitle={Locale.Settings.Temperature.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="range"
|
||||
value={config.modelConfig.temperature.toFixed(1)}
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.1"
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.temperature =
|
||||
e.currentTarget.valueAsNumber),
|
||||
);
|
||||
}}
|
||||
></input>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
title={Locale.Settings.MaxTokens.Title}
|
||||
subTitle={Locale.Settings.MaxTokens.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
min={100}
|
||||
max={4096}
|
||||
value={config.modelConfig.max_tokens}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.max_tokens =
|
||||
e.currentTarget.valueAsNumber),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
title={Locale.Settings.PresencePenlty.Title}
|
||||
subTitle={Locale.Settings.PresencePenlty.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="range"
|
||||
value={config.modelConfig.presence_penalty.toFixed(1)}
|
||||
min="-2"
|
||||
max="2"
|
||||
step="0.5"
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.presence_penalty =
|
||||
e.currentTarget.valueAsNumber),
|
||||
);
|
||||
}}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</List>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user