diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index 5dd8a92..9ca3510 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -1,3 +1,27 @@
+import { themeChange } from "theme-change";
+
+import { createRef } from "preact";
+import {
+ StateUpdater,
+ useContext,
+ useEffect,
+ useState,
+ Dispatch,
+} from "preact/hooks";
+import { clearTotalCost, getTotalCost } from "@/utils/totalCost";
+import {
+ ChatStore,
+ TemplateChatStore,
+ TemplateAPI,
+ TemplateTools,
+} from "@/types/chatstore";
+import { models } from "@/types/models";
+import { tr, Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
+import { isVailedJSON } from "@/message";
+import { SetAPIsTemplate } from "@/setAPIsTemplate";
+import { autoHeight } from "@/textarea";
+import { getDefaultParams } from "@/utils/getDefaultParam";
+
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
@@ -11,3 +35,1143 @@ import {
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/components/ui/accordion";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Checkbox } from "@/components/ui/checkbox";
+import { Textarea } from "@/components/ui/textarea";
+import {
+ BanIcon,
+ CheckIcon,
+ CircleEllipsisIcon,
+ CogIcon,
+ Ellipsis,
+ EyeIcon,
+ InfoIcon,
+ KeyIcon,
+ ListIcon,
+ MoveHorizontalIcon,
+} from "lucide-react";
+import { Separator } from "@/components/ui/separator";
+import { Slider } from "@/components/ui/slider";
+
+const TTS_VOICES: string[] = [
+ "alloy",
+ "echo",
+ "fable",
+ "onyx",
+ "nova",
+ "shimmer",
+];
+const TTS_FORMAT: string[] = ["mp3", "opus", "aac", "flac"];
+
+const Help = (props: { children: any; help: string; field: string }) => {
+ return (
+
+
+
+ );
+};
+
+const SelectModel = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ help: string;
+}) => {
+ let shouldIUseCustomModel: boolean = true;
+ for (const model in models) {
+ if (props.chatStore.model === model) {
+ shouldIUseCustomModel = false;
+ }
+ }
+ const [useCustomModel, setUseCustomModel] = useState(shouldIUseCustomModel);
+ return (
+
+
+
+
+
+
+ setUseCustomModel(!useCustomModel)}
+ />
+
+
+
+ {useCustomModel ? (
+
) => {
+ props.chatStore.model = e.target.value;
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+ ) : (
+
+ )}
+
+ );
+};
+
+const LongInput = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ field: "systemMessageContent" | "toolsString";
+ label: string;
+ help: string;
+}) => {
+ return (
+
+
+
+
+ );
+};
+
+const InputField = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ field:
+ | "apiKey"
+ | "apiEndpoint"
+ | "whisper_api"
+ | "whisper_key"
+ | "tts_api"
+ | "tts_key"
+ | "image_gen_api"
+ | "image_gen_key";
+ help: string;
+}) => {
+ const [hideInput, setHideInput] = useState(true);
+ return (
+ <>
+
+
+
+
+ ) => {
+ props.chatStore[props.field] = event.target.value;
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+
+
+
+ >
+ );
+};
+
+const Slicer = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ field: "temperature" | "top_p" | "tts_speed";
+ help: string;
+ min: number;
+ max: number;
+}) => {
+ const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
+ `${props.field}_enabled` as any;
+
+ const enabled = props.chatStore[enable_filed_name];
+
+ if (enabled === null || enabled === undefined) {
+ if (props.field === "temperature") {
+ props.chatStore[enable_filed_name] = true;
+ }
+ if (props.field === "top_p") {
+ props.chatStore[enable_filed_name] = false;
+ }
+ }
+
+ const setEnabled = (state: boolean) => {
+ props.chatStore[enable_filed_name] = state;
+ props.setChatStore({ ...props.chatStore });
+ };
+ return (
+
+
+
+ {enabled && (
+
+
+ {
+ props.chatStore[props.field] = value[0];
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+
+
) => {
+ const value = parseFloat(e.target.value);
+ props.chatStore[props.field] = value;
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+
+ )}
+
+ );
+};
+
+const Number = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ field:
+ | "totalTokens"
+ | "maxTokens"
+ | "maxGenTokens"
+ | "tokenMargin"
+ | "postBeginIndex"
+ | "presence_penalty"
+ | "frequency_penalty";
+ readOnly: boolean;
+ help: string;
+}) => {
+ return (
+
+
+
+ ) => {
+ let newNumber = parseFloat(event.target.value);
+ if (newNumber < 0) newNumber = 0;
+ props.chatStore[props.field] = newNumber;
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+
+ );
+};
+
+const Choice = (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
+ help: string;
+}) => {
+ return (
+
+
+ {
+ props.chatStore[props.field] = checked;
+ props.setChatStore({ ...props.chatStore });
+ }}
+ />
+
+
+
+ );
+};
+
+export default (props: {
+ chatStore: ChatStore;
+ setChatStore: (cs: ChatStore) => void;
+ setShow: Dispatch>;
+ selectedChatStoreIndex: number;
+ templates: TemplateChatStore[];
+ setTemplates: (templates: TemplateChatStore[]) => void;
+ templateAPIs: TemplateAPI[];
+ setTemplateAPIs: (templateAPIs: TemplateAPI[]) => void;
+ templateAPIsWhisper: TemplateAPI[];
+ setTemplateAPIsWhisper: (templateAPIs: TemplateAPI[]) => void;
+ templateAPIsTTS: TemplateAPI[];
+ setTemplateAPIsTTS: (templateAPIs: TemplateAPI[]) => void;
+ templateAPIsImageGen: TemplateAPI[];
+ setTemplateAPIsImageGen: (templateAPIs: TemplateAPI[]) => void;
+ templateTools: TemplateTools[];
+ setTemplateTools: (templateTools: TemplateTools[]) => void;
+}) => {
+ let link =
+ location.protocol +
+ "//" +
+ location.host +
+ location.pathname +
+ `?key=${encodeURIComponent(
+ props.chatStore.apiKey
+ )}&api=${encodeURIComponent(props.chatStore.apiEndpoint)}&mode=${
+ props.chatStore.streamMode ? "stream" : "fetch"
+ }&model=${props.chatStore.model}&sys=${encodeURIComponent(
+ props.chatStore.systemMessageContent
+ )}`;
+ if (props.chatStore.develop_mode) {
+ link = link + `&dev=true`;
+ }
+
+ const importFileRef = createRef();
+ const [totalCost, setTotalCost] = useState(getTotalCost());
+ // @ts-ignore
+ const { langCode, setLangCode } = useContext(langCodeContext);
+
+ useEffect(() => {
+ themeChange(false);
+ const handleKeyPress = (event: any) => {
+ if (event.keyCode === 27) {
+ // keyCode for ESC key is 27
+ props.setShow(false);
+ }
+ };
+
+ document.addEventListener("keydown", handleKeyPress);
+
+ return () => {
+ document.removeEventListener("keydown", handleKeyPress);
+ };
+ }, []); // The empty dependency array ensures that the effect runs only once
+ return (
+ // props.setShow(false)}
+ // className="left-0 top-0 overflow-scroll flex justify-center absolute w-full h-full z-10"
+ // >
+ //
{
+ // event.stopPropagation();
+ // }}
+ // className="modal-box"
+ // >
+ //
+ //
{Tr("Settings")}
+ //
+ //
+ //
+
+ //
+
+ //
+ //
+
+
+
+
+
+
+
+ {Tr("Settings")}
+
+ You can customize the settings here.
+
+
+
+
+ Session
+
+
+
+
+ Session Cost
+
+ Cost of the current session.
+
+
+
+
+
+ $ USD
+
+
+ {props.chatStore.cost.toFixed(4)}
+
+
+
+
+
+
+
+
+
+ JSON Check:{" "}
+ {isVailedJSON(props.chatStore.toolsString) ? (
+
+ ) : (
+
+ )}
+
+
+
+ {props.chatStore.toolsString.trim() && (
+
+ )}
+
+
+
+
+
+ System
+
+ <>
+
+
+
+ Accumulated Cost
+ in all sessions
+
+
+
+
+ $ USD
+
+
+ {totalCost.toFixed(4)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ const file = importFileRef.current.files[0];
+ if (!file || file.type !== "application/json") {
+ alert(tr("Please select a json file", langCode));
+ return;
+ }
+ const reader = new FileReader();
+ reader.onload = () => {
+ if (!reader) {
+ alert(tr("Empty file", langCode));
+ return;
+ }
+ try {
+ const newChatStore: ChatStore = JSON.parse(
+ reader.result as string
+ );
+ if (!newChatStore.chatgpt_api_web_version) {
+ throw tr(
+ "This is not an exported chatgpt-api-web chatstore file. The key 'chatgpt_api_web_version' is missing!",
+ langCode
+ );
+ }
+ props.setChatStore({ ...newChatStore });
+ } catch (e) {
+ alert(
+ tr(
+ `Import error on parsing json:`,
+ langCode
+ ) + `${e}`
+ );
+ }
+ };
+ reader.readAsText(file);
+ }}
+ />
+
+
+
+ >
+
+
+
+ Chat
+
+
+
+ Chat API
+
+ Configure the LLM API settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Speech Recognition
+
+
+
+
+ Whisper API
+
+ Configure speech recognition settings
+
+
+
+
+
+
+
+
+
+
+
+
+ TTS
+
+
+
+ TTS API
+
+ Configure text-to-speech settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Image Generation
+
+
+
+ Image Generation API
+
+ Configure image generation settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/Chatbox.tsx b/src/pages/Chatbox.tsx
index 1bb3390..9b01a42 100644
--- a/src/pages/Chatbox.tsx
+++ b/src/pages/Chatbox.tsx
@@ -29,7 +29,7 @@ import {
} from "../types/chatstore";
import Message from "@/message";
import { models } from "@/types/models";
-import Settings from "@/settings";
+import Settings from "@/components/Settings";
import { AddImage } from "@/addImage";
import { ListAPIs } from "@/listAPIs";
import { ListToolsTempaltes } from "@/listToolsTemplates";
diff --git a/src/settings.tsx b/src/settings.tsx
deleted file mode 100644
index 8b0a1d6..0000000
--- a/src/settings.tsx
+++ /dev/null
@@ -1,1177 +0,0 @@
-import { themeChange } from "theme-change";
-
-import { createRef } from "preact";
-import {
- StateUpdater,
- useContext,
- useEffect,
- useState,
- Dispatch,
-} from "preact/hooks";
-import { clearTotalCost, getTotalCost } from "@/utils/totalCost";
-import {
- ChatStore,
- TemplateChatStore,
- TemplateAPI,
- TemplateTools,
-} from "@/types/chatstore";
-import { models } from "@/types/models";
-import { tr, Tr, langCodeContext, LANG_OPTIONS } from "@/translate";
-import { isVailedJSON } from "@/message";
-import { SetAPIsTemplate } from "@/setAPIsTemplate";
-import { autoHeight } from "@/textarea";
-import { getDefaultParams } from "@/utils/getDefaultParam";
-
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Label } from "@/components/ui/label";
-import {
- Sheet,
- SheetClose,
- SheetContent,
- SheetDescription,
- SheetFooter,
- SheetHeader,
- SheetTitle,
- SheetTrigger,
-} from "@/components/ui/sheet";
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from "@/components/ui/accordion";
-import {
- Card,
- CardContent,
- CardDescription,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
-import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
-} from "@/components/ui/tooltip";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
- DialogTrigger,
-} from "@/components/ui/dialog";
-import { Checkbox } from "@/components/ui/checkbox";
-import { Textarea } from "@/components/ui/textarea";
-import {
- BanIcon,
- CheckIcon,
- CircleEllipsisIcon,
- CogIcon,
- Ellipsis,
- EyeIcon,
- InfoIcon,
- KeyIcon,
- ListIcon,
- MoveHorizontalIcon,
-} from "lucide-react";
-import { Separator } from "./components/ui/separator";
-import { Slider } from "./components/ui/slider";
-
-const TTS_VOICES: string[] = [
- "alloy",
- "echo",
- "fable",
- "onyx",
- "nova",
- "shimmer",
-];
-const TTS_FORMAT: string[] = ["mp3", "opus", "aac", "flac"];
-
-const Help = (props: { children: any; help: string; field: string }) => {
- return (
-
-
-
- );
-};
-
-const SelectModel = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- help: string;
-}) => {
- let shouldIUseCustomModel: boolean = true;
- for (const model in models) {
- if (props.chatStore.model === model) {
- shouldIUseCustomModel = false;
- }
- }
- const [useCustomModel, setUseCustomModel] = useState(shouldIUseCustomModel);
- return (
-
-
-
-
-
-
- setUseCustomModel(!useCustomModel)}
- />
-
-
-
- {useCustomModel ? (
-
) => {
- props.chatStore.model = e.target.value;
- props.setChatStore({ ...props.chatStore });
- }}
- />
- ) : (
-
- )}
-
- );
-};
-
-const LongInput = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- field: "systemMessageContent" | "toolsString";
- label: string;
- help: string;
-}) => {
- return (
-
-
-
-
- );
-};
-
-const InputField = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- field:
- | "apiKey"
- | "apiEndpoint"
- | "whisper_api"
- | "whisper_key"
- | "tts_api"
- | "tts_key"
- | "image_gen_api"
- | "image_gen_key";
- help: string;
-}) => {
- const [hideInput, setHideInput] = useState(true);
- return (
- <>
-
-
-
-
- ) => {
- props.chatStore[props.field] = event.target.value;
- props.setChatStore({ ...props.chatStore });
- }}
- />
-
-
-
- >
- );
-};
-
-const Slicer = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- field: "temperature" | "top_p" | "tts_speed";
- help: string;
- min: number;
- max: number;
-}) => {
- const enable_filed_name: "temperature_enabled" | "top_p_enabled" =
- `${props.field}_enabled` as any;
-
- const enabled = props.chatStore[enable_filed_name];
-
- if (enabled === null || enabled === undefined) {
- if (props.field === "temperature") {
- props.chatStore[enable_filed_name] = true;
- }
- if (props.field === "top_p") {
- props.chatStore[enable_filed_name] = false;
- }
- }
-
- const setEnabled = (state: boolean) => {
- props.chatStore[enable_filed_name] = state;
- props.setChatStore({ ...props.chatStore });
- };
- return (
-
-
-
- {enabled && (
-
-
- {
- props.chatStore[props.field] = value[0];
- props.setChatStore({ ...props.chatStore });
- }}
- />
-
-
) => {
- const value = parseFloat(e.target.value);
- props.chatStore[props.field] = value;
- props.setChatStore({ ...props.chatStore });
- }}
- />
-
- )}
-
- );
-};
-
-const Number = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- field:
- | "totalTokens"
- | "maxTokens"
- | "maxGenTokens"
- | "tokenMargin"
- | "postBeginIndex"
- | "presence_penalty"
- | "frequency_penalty";
- readOnly: boolean;
- help: string;
-}) => {
- return (
-
-
-
- ) => {
- let newNumber = parseFloat(event.target.value);
- if (newNumber < 0) newNumber = 0;
- props.chatStore[props.field] = newNumber;
- props.setChatStore({ ...props.chatStore });
- }}
- />
-
- );
-};
-
-const Choice = (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- field: "streamMode" | "develop_mode" | "json_mode" | "logprobs";
- help: string;
-}) => {
- return (
-
-
- {
- props.chatStore[props.field] = checked;
- props.setChatStore({ ...props.chatStore });
- }}
- />
-
-
-
- );
-};
-
-export default (props: {
- chatStore: ChatStore;
- setChatStore: (cs: ChatStore) => void;
- setShow: Dispatch>;
- selectedChatStoreIndex: number;
- templates: TemplateChatStore[];
- setTemplates: (templates: TemplateChatStore[]) => void;
- templateAPIs: TemplateAPI[];
- setTemplateAPIs: (templateAPIs: TemplateAPI[]) => void;
- templateAPIsWhisper: TemplateAPI[];
- setTemplateAPIsWhisper: (templateAPIs: TemplateAPI[]) => void;
- templateAPIsTTS: TemplateAPI[];
- setTemplateAPIsTTS: (templateAPIs: TemplateAPI[]) => void;
- templateAPIsImageGen: TemplateAPI[];
- setTemplateAPIsImageGen: (templateAPIs: TemplateAPI[]) => void;
- templateTools: TemplateTools[];
- setTemplateTools: (templateTools: TemplateTools[]) => void;
-}) => {
- let link =
- location.protocol +
- "//" +
- location.host +
- location.pathname +
- `?key=${encodeURIComponent(
- props.chatStore.apiKey
- )}&api=${encodeURIComponent(props.chatStore.apiEndpoint)}&mode=${
- props.chatStore.streamMode ? "stream" : "fetch"
- }&model=${props.chatStore.model}&sys=${encodeURIComponent(
- props.chatStore.systemMessageContent
- )}`;
- if (props.chatStore.develop_mode) {
- link = link + `&dev=true`;
- }
-
- const importFileRef = createRef();
- const [totalCost, setTotalCost] = useState(getTotalCost());
- // @ts-ignore
- const { langCode, setLangCode } = useContext(langCodeContext);
-
- useEffect(() => {
- themeChange(false);
- const handleKeyPress = (event: any) => {
- if (event.keyCode === 27) {
- // keyCode for ESC key is 27
- props.setShow(false);
- }
- };
-
- document.addEventListener("keydown", handleKeyPress);
-
- return () => {
- document.removeEventListener("keydown", handleKeyPress);
- };
- }, []); // The empty dependency array ensures that the effect runs only once
- return (
- // props.setShow(false)}
- // className="left-0 top-0 overflow-scroll flex justify-center absolute w-full h-full z-10"
- // >
- //
{
- // event.stopPropagation();
- // }}
- // className="modal-box"
- // >
- //
- //
{Tr("Settings")}
- //
- //
- //
-
- //
-
- //
- //
-
-
-
-
-
-
-
- {Tr("Settings")}
-
- You can customize the settings here.
-
-
-
-
- Session
-
-
-
-
- Session Cost
-
- Cost of the current session.
-
-
-
-
-
- $ USD
-
-
- {props.chatStore.cost.toFixed(4)}
-
-
-
-
-
-
-
-
-
- JSON Check:{" "}
- {isVailedJSON(props.chatStore.toolsString) ? (
-
- ) : (
-
- )}
-
-
-
- {props.chatStore.toolsString.trim() && (
-
- )}
-
-
-
-
-
- System
-
- <>
-
-
-
- Accumulated Cost
- in all sessions
-
-
-
-
- $ USD
-
-
- {totalCost.toFixed(4)}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- const file = importFileRef.current.files[0];
- if (!file || file.type !== "application/json") {
- alert(tr("Please select a json file", langCode));
- return;
- }
- const reader = new FileReader();
- reader.onload = () => {
- if (!reader) {
- alert(tr("Empty file", langCode));
- return;
- }
- try {
- const newChatStore: ChatStore = JSON.parse(
- reader.result as string
- );
- if (!newChatStore.chatgpt_api_web_version) {
- throw tr(
- "This is not an exported chatgpt-api-web chatstore file. The key 'chatgpt_api_web_version' is missing!",
- langCode
- );
- }
- props.setChatStore({ ...newChatStore });
- } catch (e) {
- alert(
- tr(
- `Import error on parsing json:`,
- langCode
- ) + `${e}`
- );
- }
- };
- reader.readAsText(file);
- }}
- />
-
-
-
- >
-
-
-
- Chat
-
-
-
- Chat API
-
- Configure the LLM API settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Speech Recognition
-
-
-
-
- Whisper API
-
- Configure speech recognition settings
-
-
-
-
-
-
-
-
-
-
-
-
- TTS
-
-
-
- TTS API
-
- Configure text-to-speech settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Image Generation
-
-
-
- Image Generation API
-
- Configure image generation settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};