123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- import { useState, useEffect, useRef, useMemo } from "react";
- import EmojiPicker, { Theme as EmojiTheme } from "emoji-picker-react";
- 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 { 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";
- import Locale, { AllLangs, changeLang, getLang } from "../locales";
- import { getCurrentCommitId } from "../utils";
- import Link from "next/link";
- import { UPDATE_URL } from "../constant";
- import { SearchService, usePromptStore } from "../store/prompt";
- 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>
- );
- }
- export function Settings(props: { closeSettings: () => void }) {
- const [showEmojiPicker, setShowEmojiPicker] = useState(false);
- const [config, updateConfig, resetConfig, clearAllData] = useChatStore(
- (state) => [
- state.config,
- state.updateConfig,
- state.resetConfig,
- state.clearAllData,
- ]
- );
- const updateStore = useUpdateStore();
- const [checkingUpdate, setCheckingUpdate] = useState(false);
- const currentId = getCurrentCommitId();
- const remoteId = updateStore.remoteId;
- const hasNewVersion = currentId !== remoteId;
- function checkUpdate(force = false) {
- setCheckingUpdate(true);
- updateStore.getLatestCommitId(force).then(() => {
- setCheckingUpdate(false);
- });
- }
- useEffect(() => {
- checkUpdate();
- }, []);
- const accessStore = useAccessStore();
- const enabledAccessControl = useMemo(
- () => accessStore.enabledAccessControl(),
- []
- );
- const promptStore = usePromptStore();
- const builtinCount = SearchService.count.builtin;
- const customCount = promptStore.prompts.size ?? 0;
- 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={clearAllData}
- 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={UPDATE_URL} 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>
- <div className="no-mobile">
- <SettingItem title={Locale.Settings.TightBorder}>
- <input
- type="checkbox"
- checked={config.tightBorder}
- onChange={(e) =>
- updateConfig(
- (config) => (config.tightBorder = e.currentTarget.checked)
- )
- }
- ></input>
- </SettingItem>
- </div>
- </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
- title={Locale.Settings.AccessCode.Title}
- subTitle={Locale.Settings.AccessCode.SubTitle}
- >
- <input
- value={accessStore.accessCode}
- type="text"
- placeholder={Locale.Settings.AccessCode.Placeholder}
- onChange={(e) => {
- accessStore.updateCode(e.currentTarget.value);
- }}
- ></input>
- </SettingItem>
- ) : (
- <></>
- )}
- <SettingItem
- title={Locale.Settings.Token.Title}
- subTitle={Locale.Settings.Token.SubTitle}
- >
- <input
- value={accessStore.token}
- type="text"
- placeholder={Locale.Settings.Token.Placeholder}
- onChange={(e) => {
- accessStore.updateToken(e.currentTarget.value);
- }}
- ></input>
- </SettingItem>
- <SettingItem
- title={Locale.Settings.HistoryCount.Title}
- subTitle={Locale.Settings.HistoryCount.SubTitle}
- >
- <input
- type="range"
- title={config.historyMessageCount.toString()}
- value={config.historyMessageCount}
- min="2"
- 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="1"
- 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>
- </>
- );
- }
|