mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-16 23:13:42 +08:00
merge上游更新,升级为2.0版本
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { useState, useEffect, useMemo, HTMLProps, useRef } from "react";
|
||||
|
||||
import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
|
||||
|
||||
import styles from "./settings.module.scss";
|
||||
|
||||
import ResetIcon from "../icons/reload.svg";
|
||||
@@ -9,33 +7,31 @@ 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 { Input, List, ListItem, Modal, PasswordInput, Popover } from "./ui-lib";
|
||||
import { ModelConfigList } from "./model-config";
|
||||
import EyeIcon from "../icons/eye.svg";
|
||||
import EyeOffIcon from "../icons/eye-off.svg";
|
||||
import ShareIcon from "../icons/share.svg";
|
||||
|
||||
import { Input, List, ListItem, Modal, Popover } from "./ui-lib";
|
||||
|
||||
import { IconButton } from "./button";
|
||||
import {
|
||||
SubmitKey,
|
||||
useChatStore,
|
||||
Theme,
|
||||
ALL_MODELS,
|
||||
useUpdateStore,
|
||||
useAccessStore,
|
||||
ModalConfigValidator,
|
||||
useAppConfig,
|
||||
} from "../store";
|
||||
import { Avatar } from "./chat";
|
||||
|
||||
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
||||
import { copyToClipboard, getEmojiUrl } from "../utils";
|
||||
import { copyToClipboard } from "../utils";
|
||||
import Link from "next/link";
|
||||
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";
|
||||
import { Avatar, AvatarPicker } from "./emoji";
|
||||
|
||||
function UserPromptModal(props: { onClose?: () => void }) {
|
||||
const promptStore = usePromptStore();
|
||||
@@ -138,57 +134,13 @@ function UserPromptModal(props: { onClose?: () => void }) {
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
function PasswordInput(props: HTMLProps<HTMLInputElement>) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
function changeVisibility() {
|
||||
setVisible(!visible);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles["password-input-container"]}>
|
||||
<IconButton
|
||||
icon={visible ? <EyeIcon /> : <EyeOffIcon />}
|
||||
onClick={changeVisibility}
|
||||
className={styles["password-eye"]}
|
||||
/>
|
||||
<input
|
||||
{...props}
|
||||
type={visible ? "text" : "password"}
|
||||
className={styles["password-input"]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Settings() {
|
||||
const navigate = useNavigate();
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||
const config = useAppConfig();
|
||||
const updateConfig = config.update;
|
||||
const resetConfig = config.reset;
|
||||
const [clearAllData, clearSessions] = useChatStore((state) => [
|
||||
state.clearAllData,
|
||||
state.clearSessions,
|
||||
]);
|
||||
const chatStore = useChatStore();
|
||||
|
||||
const updateStore = useUpdateStore();
|
||||
const [checkingUpdate, setCheckingUpdate] = useState(false);
|
||||
@@ -208,9 +160,9 @@ export function Settings() {
|
||||
subscription: updateStore.subscription,
|
||||
};
|
||||
const [loadingUsage, setLoadingUsage] = useState(false);
|
||||
function checkUsage() {
|
||||
function checkUsage(force = false) {
|
||||
setLoadingUsage(true);
|
||||
updateStore.updateUsage().finally(() => {
|
||||
updateStore.updateUsage(force).finally(() => {
|
||||
setLoadingUsage(false);
|
||||
});
|
||||
}
|
||||
@@ -254,39 +206,33 @@ export function Settings() {
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div className={styles["window-header"]}>
|
||||
<div className={styles["window-header-title"]}>
|
||||
<div className={styles["window-header-main-title"]}>
|
||||
<div className="window-header">
|
||||
<div className="window-header-title">
|
||||
<div className="window-header-main-title">
|
||||
{Locale.Settings.Title}
|
||||
</div>
|
||||
<div className={styles["window-header-sub-title"]}>
|
||||
<div className="window-header-sub-title">
|
||||
{Locale.Settings.SubTitle}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles["window-actions"]}>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<div className="window-actions">
|
||||
<div className="window-action-button">
|
||||
<IconButton
|
||||
icon={<ClearIcon />}
|
||||
onClick={() => {
|
||||
const confirmed = window.confirm(
|
||||
`${Locale.Settings.Actions.ConfirmClearAll.Confirm}`,
|
||||
);
|
||||
if (confirmed) {
|
||||
clearSessions();
|
||||
if (confirm(Locale.Settings.Actions.ConfirmClearAll)) {
|
||||
chatStore.clearAllData();
|
||||
}
|
||||
}}
|
||||
bordered
|
||||
title={Locale.Settings.Actions.ClearAll}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<div className="window-action-button">
|
||||
<IconButton
|
||||
icon={<ResetIcon />}
|
||||
onClick={() => {
|
||||
const confirmed = window.confirm(
|
||||
`${Locale.Settings.Actions.ConfirmResetAll.Confirm}`,
|
||||
);
|
||||
if (confirmed) {
|
||||
if (confirm(Locale.Settings.Actions.ConfirmResetAll)) {
|
||||
resetConfig();
|
||||
}
|
||||
}}
|
||||
@@ -294,7 +240,7 @@ export function Settings() {
|
||||
title={Locale.Settings.Actions.ResetAll}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles["window-action-button"]}>
|
||||
<div className="window-action-button">
|
||||
<IconButton
|
||||
icon={<CloseIcon />}
|
||||
onClick={() => navigate(Path.Home)}
|
||||
@@ -306,16 +252,13 @@ export function Settings() {
|
||||
</div>
|
||||
<div className={styles["settings"]}>
|
||||
<List>
|
||||
<SettingItem title={Locale.Settings.Avatar}>
|
||||
<ListItem title={Locale.Settings.Avatar}>
|
||||
<Popover
|
||||
onClose={() => setShowEmojiPicker(false)}
|
||||
content={
|
||||
<EmojiPicker
|
||||
lazyLoadEmojis
|
||||
theme={EmojiTheme.AUTO}
|
||||
getEmojiUrl={getEmojiUrl}
|
||||
onEmojiClick={(e) => {
|
||||
updateConfig((config) => (config.avatar = e.unified));
|
||||
<AvatarPicker
|
||||
onEmojiClick={(avatar: string) => {
|
||||
updateConfig((config) => (config.avatar = avatar));
|
||||
setShowEmojiPicker(false);
|
||||
}}
|
||||
/>
|
||||
@@ -326,12 +269,12 @@ export function Settings() {
|
||||
className={styles.avatar}
|
||||
onClick={() => setShowEmojiPicker(true)}
|
||||
>
|
||||
<Avatar role="user" />
|
||||
<Avatar avatar={config.avatar} />
|
||||
</div>
|
||||
</Popover>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.SendKey}>
|
||||
<ListItem title={Locale.Settings.SendKey}>
|
||||
<select
|
||||
value={config.submitKey}
|
||||
onChange={(e) => {
|
||||
@@ -347,12 +290,9 @@ export function Settings() {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<div className={styles["settings-title"]}>
|
||||
{Locale.Settings.Theme}
|
||||
</div>
|
||||
<ListItem title={Locale.Settings.Theme}>
|
||||
<select
|
||||
value={config.theme}
|
||||
onChange={(e) => {
|
||||
@@ -369,7 +309,7 @@ export function Settings() {
|
||||
</select>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem title={Locale.Settings.Lang.Name}>
|
||||
<ListItem title={Locale.Settings.Lang.Name}>
|
||||
<select
|
||||
value={getLang()}
|
||||
onChange={(e) => {
|
||||
@@ -382,9 +322,9 @@ export function Settings() {
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.FontSize.Title}
|
||||
subTitle={Locale.Settings.FontSize.SubTitle}
|
||||
>
|
||||
@@ -401,21 +341,12 @@ export function Settings() {
|
||||
)
|
||||
}
|
||||
></InputRange>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<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}>
|
||||
<ListItem
|
||||
title={Locale.Settings.SendPreviewBubble.Title}
|
||||
subTitle={Locale.Settings.SendPreviewBubble.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.sendPreviewBubble}
|
||||
@@ -426,7 +357,24 @@ export function Settings() {
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<ListItem
|
||||
title={Locale.Settings.Mask.Title}
|
||||
subTitle={Locale.Settings.Mask.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!config.dontShowMaskSplashScreen}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.dontShowMaskSplashScreen =
|
||||
!e.currentTarget.checked),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
@@ -443,12 +391,12 @@ export function Settings() {
|
||||
accessStore.updateCode(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
) : (
|
||||
<></>
|
||||
)} */}
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.Token.Title}
|
||||
subTitle={Locale.Settings.Token.SubTitle}
|
||||
>
|
||||
@@ -460,9 +408,9 @@ export function Settings() {
|
||||
accessStore.updateToken(e.currentTarget.value);
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.GetAccessToken.Title}
|
||||
subTitle={Locale.Settings.GetAccessToken.SubTitle}
|
||||
>
|
||||
@@ -471,9 +419,9 @@ export function Settings() {
|
||||
text={Locale.Settings.GetAccessToken.Text}
|
||||
onClick={getAccessToken}
|
||||
/>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.HistoryCount.Title}
|
||||
subTitle={Locale.Settings.HistoryCount.SubTitle}
|
||||
>
|
||||
@@ -490,9 +438,9 @@ export function Settings() {
|
||||
)
|
||||
}
|
||||
></InputRange>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.CompressThreshold.Title}
|
||||
subTitle={Locale.Settings.CompressThreshold.SubTitle}
|
||||
>
|
||||
@@ -509,11 +457,11 @@ export function Settings() {
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.Disable.Title}
|
||||
subTitle={Locale.Settings.Prompt.Disable.SubTitle}
|
||||
>
|
||||
@@ -527,9 +475,9 @@ export function Settings() {
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
|
||||
<SettingItem
|
||||
<ListItem
|
||||
title={Locale.Settings.Prompt.List}
|
||||
subTitle={Locale.Settings.Prompt.ListCount(
|
||||
builtinCount,
|
||||
@@ -541,89 +489,18 @@ export function Settings() {
|
||||
text={Locale.Settings.Prompt.Edit}
|
||||
onClick={() => setShowPromptModal(true)}
|
||||
/>
|
||||
</SettingItem>
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<List>
|
||||
<SettingItem title={Locale.Settings.Model}>
|
||||
<select
|
||||
value={config.modelConfig.model}
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.model = ModalConfigValidator.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}
|
||||
>
|
||||
<InputRange
|
||||
value={config.modelConfig.temperature?.toFixed(1)}
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.1"
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.temperature =
|
||||
ModalConfigValidator.temperature(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
title={Locale.Settings.MaxTokens.Title}
|
||||
subTitle={Locale.Settings.MaxTokens.SubTitle}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
min={100}
|
||||
max={32000}
|
||||
value={config.modelConfig.max_tokens}
|
||||
onChange={(e) =>
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.max_tokens =
|
||||
ModalConfigValidator.max_tokens(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
)
|
||||
}
|
||||
></input>
|
||||
</SettingItem>
|
||||
<SettingItem
|
||||
title={Locale.Settings.PresencePenlty.Title}
|
||||
subTitle={Locale.Settings.PresencePenlty.SubTitle}
|
||||
>
|
||||
<InputRange
|
||||
value={config.modelConfig.presence_penalty?.toFixed(1)}
|
||||
min="-2"
|
||||
max="2"
|
||||
step="0.1"
|
||||
onChange={(e) => {
|
||||
updateConfig(
|
||||
(config) =>
|
||||
(config.modelConfig.presence_penalty =
|
||||
ModalConfigValidator.presence_penalty(
|
||||
e.currentTarget.valueAsNumber,
|
||||
)),
|
||||
);
|
||||
}}
|
||||
></InputRange>
|
||||
</SettingItem>
|
||||
<ModelConfigList
|
||||
modelConfig={config.modelConfig}
|
||||
updateConfig={(upater) => {
|
||||
const modelConfig = { ...config.modelConfig };
|
||||
upater(modelConfig);
|
||||
config.update((config) => (config.modelConfig = modelConfig));
|
||||
}}
|
||||
/>
|
||||
</List>
|
||||
|
||||
{shouldShowPromptModal && (
|
||||
|
Reference in New Issue
Block a user