clean up setting panel

This commit is contained in:
ecwu
2024-07-17 02:06:13 +08:00
parent 148d912be5
commit 415fb934ae
3 changed files with 366 additions and 236 deletions

View File

@@ -798,7 +798,7 @@ export default function ChatBOX(props: {
<p className="text-center"> <p className="text-center">
{chatStore.history.length > 0 && ( {chatStore.history.length > 0 && (
<button <button
className="btn btn-outline btn-sm btn-warning disabled:line-through disabled:btn-neutral disabled:text-white m-2 p-2" className="btn btn-sm btn-warning disabled:line-through disabled:btn-neutral disabled:text-white m-2 p-2"
disabled={showGenerating} disabled={showGenerating}
onClick={async () => { onClick={async () => {
const messageIndex = chatStore.history.length - 1; const messageIndex = chatStore.history.length - 1;
@@ -818,7 +818,7 @@ export default function ChatBOX(props: {
)} )}
{chatStore.develop_mode && chatStore.history.length > 0 && ( {chatStore.develop_mode && chatStore.history.length > 0 && (
<button <button
className="btn btn-warning disabled:line-through disabled:bg-neural m-2 p-2" className="btn btn-outline btn-sm btn-warning disabled:line-through disabled:bg-neural"
disabled={showGenerating} disabled={showGenerating}
onClick={async () => { onClick={async () => {
await complete(); await complete();

View File

@@ -10,6 +10,7 @@ import { MessageToolCall } from "./messageToolCall";
import { MessageToolResp } from "./messageToolResp"; import { MessageToolResp } from "./messageToolResp";
import { EditMessage } from "./editMessage"; import { EditMessage } from "./editMessage";
import logprobToColor from "./logprob"; import logprobToColor from "./logprob";
import { XMarkIcon } from "@heroicons/react/24/outline";
export const isVailedJSON = (str: string): boolean => { export const isVailedJSON = (str: string): boolean => {
try { try {
@@ -101,8 +102,8 @@ export default function Message(props: Props) {
chatStore.history.slice(0, messageIndex).filter(({ hide }) => !hide) chatStore.history.slice(0, messageIndex).filter(({ hide }) => !hide)
.length && ( .length && (
<div className="flex items-center relative justify-center"> <div className="flex items-center relative justify-center">
<hr className="w-full h-px my-4 border-0 bg-slate-800 dark:bg-white" /> <hr className="w-full h-px my-4 border-0" />
<span className="absolute px-3 bg-slate-800 text-white rounded p-1 dark:bg-white dark:text-black"> <span className="absolute px-3 rounded p-1">
Above messages are "forgotten" Above messages are "forgotten"
</span> </span>
</div> </div>
@@ -192,11 +193,11 @@ export default function Message(props: Props) {
)} )}
{showCopiedHint && <CopiedHint />} {showCopiedHint && <CopiedHint />}
{chatStore.develop_mode && ( {chatStore.develop_mode && (
<div> <div class="flex items-center gap-1">
<span className="dark:text-white">token</span> <span className="">token</span>
<input <input
value={chat.token} value={chat.token}
className="w-20" className="input input-bordered input-xs w-16"
onChange={(event: any) => { onChange={(event: any) => {
chat.token = parseInt(event.target.value); chat.token = parseInt(event.target.value);
props.update_total_tokens(); props.update_total_tokens();
@@ -221,7 +222,7 @@ export default function Message(props: Props) {
setChatStore({ ...chatStore }); setChatStore({ ...chatStore });
}} }}
> >
<XMarkIcon class="w-4 h-4" />
</button> </button>
<span <span
onClick={(event: any) => { onClick={(event: any) => {
@@ -229,17 +230,17 @@ export default function Message(props: Props) {
setChatStore({ ...chatStore }); setChatStore({ ...chatStore });
}} }}
> >
<label className="dark:text-white">{Tr("example")}</label> <label className="">{Tr("example")}</label>
<input type="checkbox" checked={chat.example} /> <input type="checkbox" checked={chat.example} />
</span> </span>
<span <span
onClick={(event: any) => setRenderWorkdown(!renderMarkdown)} onClick={(event: any) => setRenderWorkdown(!renderMarkdown)}
> >
<label className="dark:text-white">{Tr("render")}</label> <label className="">{Tr("render")}</label>
<input type="checkbox" checked={renderMarkdown} /> <input type="checkbox" checked={renderMarkdown} />
</span> </span>
<span onClick={(event: any) => setRenderColor(!renderColor)}> <span onClick={(event: any) => setRenderColor(!renderColor)}>
<label className="dark:text-white">{Tr("color")}</label> <label className="">{Tr("color")}</label>
<input type="checkbox" checked={renderColor} /> <input type="checkbox" checked={renderColor} />
</span> </span>
</div> </div>

View File

@@ -20,6 +20,14 @@ import {
InformationCircleIcon, InformationCircleIcon,
CheckIcon, CheckIcon,
NoSymbolIcon, NoSymbolIcon,
CogIcon,
KeyIcon,
EyeIcon,
EllipsisHorizontalCircleIcon,
HandRaisedIcon,
AdjustmentsHorizontalIcon,
Cog6ToothIcon,
ListBulletIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { themeChange } from "theme-change"; import { themeChange } from "theme-change";
@@ -34,10 +42,10 @@ const TTS_VOICES: string[] = [
]; ];
const TTS_FORMAT: string[] = ["mp3", "opus", "aac", "flac"]; const TTS_FORMAT: string[] = ["mp3", "opus", "aac", "flac"];
const Help = (props: { children: any; help: string }) => { const Help = (props: { children: any; help: string; field: string }) => {
return ( return (
<div> <div class="b-2">
<p className="flex justify-between">{props.children}</p> <label class="form-control w-full">{props.children}</label>
</div> </div>
); );
}; };
@@ -55,46 +63,64 @@ const SelectModel = (props: {
} }
const [useCustomModel, setUseCustomModel] = useState(shouldIUseCustomModel); const [useCustomModel, setUseCustomModel] = useState(shouldIUseCustomModel);
return ( return (
<Help help={props.help}> <Help help={props.help} field="">
<label className="m-2 p-2">Model</label> <div class="label">
<span <span class="flex gap-2 items-center">
onClick={() => { <ListBulletIcon class="w-4 h-4" />
setUseCustomModel(!useCustomModel); Model
}} </span>{" "}
className="m-2 p-2" <div class="flex gap-3">
> <span class="label-text">
<label>{Tr("Custom")}</label> <span class="label-text flex gap-2 items-center">
<input className="" type="checkbox" checked={useCustomModel} /> <Cog6ToothIcon class="w-4 h-4" />
</span> {Tr("Custom")}
{useCustomModel ? ( </span>
<input </span>
className="m-2 p-2 border rounded focus w-32 md:w-fit" <span class="label-text-alt">
value={props.chatStore.model} <input
onChange={(event: any) => { type="checkbox"
const model = event.target.value as string; checked={useCustomModel}
props.chatStore.model = model; class="checkbox"
props.setChatStore({ ...props.chatStore }); onClick={() => {
}} setUseCustomModel(!useCustomModel);
/> }}
) : ( />
<select </span>
className="m-2 p-2" </div>
value={props.chatStore.model} {/* <span class="label-text-alt">Top Right label</span> */}
onChange={(event: any) => { </div>
const model = event.target.value as string; <label class="form-control w-full">
props.chatStore.model = model; {useCustomModel ? (
props.chatStore.maxTokens = getDefaultParams( <input
"max", className="input input-bordered"
models[model].maxToken value={props.chatStore.model}
); onChange={(event: any) => {
props.setChatStore({ ...props.chatStore }); const model = event.target.value as string;
}} props.chatStore.model = model;
> props.setChatStore({ ...props.chatStore });
{Object.keys(models).map((opt) => ( }}
<option value={opt}>{opt}</option> />
))} ) : (
</select> <select
)} className="select select-bordered"
value={props.chatStore.model}
onChange={(event: any) => {
const model = event.target.value as string;
props.chatStore.model = model;
props.chatStore.maxTokens = getDefaultParams(
"max",
models[model].maxToken
);
props.setChatStore({ ...props.chatStore });
}}
>
{Object.keys(models).map((opt) => (
<option value={opt}>{opt}</option>
))}
</select>
)}
</label>
<div class="pb-5"></div>
</Help> </Help>
); );
}; };
@@ -152,25 +178,50 @@ const Input = (props: {
}) => { }) => {
const [hideInput, setHideInput] = useState(true); const [hideInput, setHideInput] = useState(true);
return ( return (
<Help help={props.help}> <Help field={props.field} help={props.help}>
<label className="m-2 p-2">{props.field}</label> <div class="label">
<button <span class="label-text">{props.field}</span>
onClick={() => { <span class="label-text-alt">
setHideInput(!hideInput); <button
console.log("clicked", hideInput); onClick={() => {
}} alert(props.help);
> }}
{hideInput ? "👀" : "🙈"} >
</button> <InformationCircleIcon className="w-4 h-4" />
<input </button>
type={hideInput ? "password" : "text"} </span>
className="m-2 p-2 border rounded focus w-32 md:w-fit" </div>
value={props.chatStore[props.field]} <div class="join">
onChange={(event: any) => { <label class="input input-bordered flex items-center gap-2 join-item grow">
props.chatStore[props.field] = event.target.value; {hideInput ? (
props.setChatStore({ ...props.chatStore }); <EyeIcon
}} class="w-4 h-4"
></input> onClick={() => {
setHideInput(!hideInput);
console.log("clicked", hideInput);
}}
/>
) : (
<KeyIcon
class="w-4 h-4"
onClick={() => {
setHideInput(!hideInput);
console.log("clicked", hideInput);
}}
/>
)}
<input
type={hideInput ? "password" : "text"}
class="grow"
value={props.chatStore[props.field]}
onChange={(event: any) => {
props.chatStore[props.field] = event.target.value;
props.setChatStore({ ...props.chatStore });
}}
/>
</label>
</div>
</Help> </Help>
); );
}; };
@@ -202,42 +253,63 @@ const Slicer = (props: {
props.setChatStore({ ...props.chatStore }); props.setChatStore({ ...props.chatStore });
}; };
return ( return (
<Help help={props.help}> <Help help={props.help} field={props.field}>
<span> <span>
<label className="m-2 p-2">{props.field}</label> <div class="form-control">
<input <label class="flex gap-2">
type="checkbox" <span class="label-text flex items-center gap-2">
checked={props.chatStore[enable_filed_name]} <AdjustmentsHorizontalIcon className="w-4 h-4" />
onClick={() => { {props.field}{" "}
setEnabled(!enabled); <button
}} onClick={() => {
/> alert(props.help);
}}
>
<InformationCircleIcon className="w-4 h-4" />
</button>
</span>
<input
type="checkbox"
checked={props.chatStore[enable_filed_name]}
class="checkbox"
onClick={() => {
setEnabled(!enabled);
}}
/>
</label>
</div>
</span> </span>
<input {enabled ? (
disabled={!enabled} <div class="flex items-center">
className="m-2 p-2 border rounded focus w-16" <input
type="range" disabled={!enabled}
min={props.min} className="range"
max={props.max} type="range"
step="0.01" min={props.min}
value={props.chatStore[props.field]} max={props.max}
onChange={(event: any) => { step="0.01"
const value = parseFloat(event.target.value); value={props.chatStore[props.field]}
props.chatStore[props.field] = value; onChange={(event: any) => {
props.setChatStore({ ...props.chatStore }); const value = parseFloat(event.target.value);
}} props.chatStore[props.field] = value;
/> props.setChatStore({ ...props.chatStore });
<input }}
disabled={!enabled} />
className="m-2 p-2 border rounded focus w-28" <input
type="number" disabled={!enabled}
value={props.chatStore[props.field]} className="m-2 p-2 border rounded focus w-28"
onChange={(event: any) => { type="number"
const value = parseFloat(event.target.value); value={props.chatStore[props.field]}
props.chatStore[props.field] = value; onChange={(event: any) => {
props.setChatStore({ ...props.chatStore }); const value = parseFloat(event.target.value);
}} props.chatStore[props.field] = value;
/> props.setChatStore({ ...props.chatStore });
}}
/>
</div>
) : (
""
)}
</Help> </Help>
); );
}; };
@@ -257,21 +329,35 @@ const Number = (props: {
help: string; help: string;
}) => { }) => {
return ( return (
<Help help={props.help}> <Help help={props.help} field="">
<span> <span>
<label className="m-2 p-2">{props.field}</label> <label className="py-2 flex items-center gap-1">
{props.field === "maxGenTokens" && ( <EllipsisHorizontalCircleIcon class="h-4 w-4" /> {props.field}{" "}
<input <button
type="checkbox" onClick={() => {
checked={props.chatStore.maxGenTokens_enabled} alert(props.help);
onChange={() => {
const newChatStore = { ...props.chatStore };
newChatStore.maxGenTokens_enabled =
!newChatStore.maxGenTokens_enabled;
props.setChatStore({ ...newChatStore });
}} }}
/> >
)} <InformationCircleIcon className="w-4 h-4" />
</button>
{props.field === "maxGenTokens" && (
<div class="form-control">
<label class="label cursor-pointer">
<input
type="checkbox"
checked={props.chatStore.maxGenTokens_enabled}
onChange={() => {
const newChatStore = { ...props.chatStore };
newChatStore.maxGenTokens_enabled =
!newChatStore.maxGenTokens_enabled;
props.setChatStore({ ...newChatStore });
}}
class="checkbox"
/>
</label>
</div>
)}
</label>
</span> </span>
<input <input
readOnly={props.readOnly} readOnly={props.readOnly}
@@ -280,7 +366,7 @@ const Number = (props: {
!props.chatStore.maxGenTokens_enabled !props.chatStore.maxGenTokens_enabled
} }
type="number" type="number"
className="m-2 p-2 border rounded focus w-28" className="input input-bordered input-sm w-full max-w-xs"
value={props.chatStore[props.field]} value={props.chatStore[props.field]}
onChange={(event: any) => { onChange={(event: any) => {
console.log("type", typeof event.target.value); console.log("type", typeof event.target.value);
@@ -300,17 +386,29 @@ const Choice = (props: {
help: string; help: string;
}) => { }) => {
return ( return (
<Help help={props.help}> <Help help={props.help} field={props.field}>
<label className="m-2 p-2">{props.field}</label> <div class="form-control">
<input <label class="py-2 flex items-center gap-1">
type="checkbox" <CheckIcon class="h-4 w-4" />
className="m-2 p-2 border rounded focus" <span class="label-text">{props.field}</span>
checked={props.chatStore[props.field]} <button
onChange={(event: any) => { onClick={() => {
props.chatStore[props.field] = event.target.checked; alert(props.help);
props.setChatStore({ ...props.chatStore }); }}
}} >
></input> <InformationCircleIcon className="w-4 h-4" />
</button>
<input
type="checkbox"
checked={props.chatStore[props.field]}
class="checkbox"
onChange={(event: any) => {
props.chatStore[props.field] = event.target.checked;
props.setChatStore({ ...props.chatStore });
}}
/>
</label>
</div>
</Help> </Help>
); );
}; };
@@ -394,9 +492,7 @@ export default (props: {
{Tr("Close")} {Tr("Close")}
</button> </button>
</div> </div>
<div role="tablist" class="tabs tabs-lifted pt-2 w-full">
<hr className="pt-2" />
<div role="tablist" class="tabs tabs-bordered pt-2 w-full">
<input <input
type="radio" type="radio"
name="setting_tab" name="setting_tab"
@@ -472,17 +568,21 @@ export default (props: {
class="tab-content bg-base-100 border-base-300 rounded-box p-6" class="tab-content bg-base-100 border-base-300 rounded-box p-6"
> >
<div className="flex justify-between"> <div className="flex justify-between">
<p> <div class="join join-vertical lg:join-horizontal">
{Tr("Accumulated cost in all sessions")} ${totalCost.toFixed(4)} <p class="btn no-animation join-item">
</p> {Tr("Accumulated cost in all sessions")} $
<button {totalCost.toFixed(4)}
onClick={() => { </p>
clearTotalCost(); <button
setTotalCost(getTotalCost()); class="btn join-item"
}} onClick={() => {
> clearTotalCost();
{Tr("Reset")} setTotalCost(getTotalCost());
</button> }}
>
Reset
</button>
</div>
</div> </div>
<label class="form-control w-full max-w-xs"> <label class="form-control w-full max-w-xs">
<div class="label"> <div class="label">
@@ -521,6 +621,9 @@ export default (props: {
</select> </select>
</label> </label>
<div class="join pt-2"> <div class="join pt-2">
<button class="btn btn-info join-item no-animation">
<CogIcon class="w-6 h-6" />
</button>
<button <button
class="btn join-item" class="btn join-item"
onClick={() => { onClick={() => {
@@ -654,7 +757,6 @@ export default (props: {
/> />
</div> </div>
</div> </div>
<input <input
type="radio" type="radio"
name="setting_tab" name="setting_tab"
@@ -666,28 +768,31 @@ export default (props: {
role="tabpanel" role="tabpanel"
class="tab-content bg-base-100 border-base-300 rounded-box p-6" class="tab-content bg-base-100 border-base-300 rounded-box p-6"
> >
<div className="relative border-slate-300 border rounded"> <div class="card bg-base-100 w-full shadow-xl">
<div className="flex justify-between"> <div class="card-body">
<strong className="p-1 m-1">Chat API</strong> <h2 class="card-title">Chat API</h2>
<SetAPIsTemplate <p>
label="Chat API" <Input
endpoint={props.chatStore.apiEndpoint} field="apiKey"
APIkey={props.chatStore.apiKey} help="OPEN AI API 密钥,请勿泄漏此密钥"
tmps={props.templateAPIs} {...props}
setTmps={props.setTemplateAPIs} />
/> <Input
field="apiEndpoint"
help="API 端点,方便在不支持的地区使用反向代理服务,默认为 https://api.openai.com/v1/chat/completions"
{...props}
/>
</p>
<div class="card-actions justify-end">
<SetAPIsTemplate
label="Chat API"
endpoint={props.chatStore.apiEndpoint}
APIkey={props.chatStore.apiKey}
tmps={props.templateAPIs}
setTmps={props.setTemplateAPIs}
/>
</div>
</div> </div>
<hr />
<Input
field="apiKey"
help="OPEN AI API 密钥,请勿泄漏此密钥"
{...props}
/>
<Input
field="apiEndpoint"
help="API 端点,方便在不支持的地区使用反向代理服务,默认为 https://api.openai.com/v1/chat/completions"
{...props}
/>
</div> </div>
<SelectModel <SelectModel
help="模型,默认 3.5。不同模型性能和定价也不同,请参考 API 文档。" help="模型,默认 3.5。不同模型性能和定价也不同,请参考 API 文档。"
@@ -763,6 +868,44 @@ export default (props: {
/> />
</div> </div>
<input
type="radio"
name="setting_tab"
role="tab"
class="tab"
aria-label="Speech Recognition"
/>
<div
role="tabpanel"
class="tab-content bg-base-100 border-base-300 rounded-box p-6"
>
<div class="card bg-base-100 w-full shadow-xl">
<div class="card-body">
<h2 class="card-title">Whisper API</h2>
<p>
<Input
field="whisper_key"
help="用于 Whisper 服务的 key默认为 上方使用的OPENAI key可在此单独配置专用key"
{...props}
/>
<Input
field="whisper_api"
help="Whisper 语言转文字服务填入此api才会开启默认为 https://api.openai.com/v1/audio/transriptions"
{...props}
/>
</p>
<div class="card-actions justify-end">
<SetAPIsTemplate
label="Whisper API"
endpoint={props.chatStore.whisper_api}
APIkey={props.chatStore.whisper_key}
tmps={props.templateAPIsWhisper}
setTmps={props.setTemplateAPIsWhisper}
/>
</div>
</div>
</div>
</div>
<input <input
type="radio" type="radio"
name="setting_tab" name="setting_tab"
@@ -774,53 +917,36 @@ export default (props: {
role="tabpanel" role="tabpanel"
class="tab-content bg-base-100 border-base-300 rounded-box p-6" class="tab-content bg-base-100 border-base-300 rounded-box p-6"
> >
<div className="relative border-slate-300 border rounded"> <div class="card bg-base-100 w-full shadow-xl">
<div className="flex justify-between"> <div class="card-body">
<strong className="p-1 m-1">Whisper API</strong> <h2 class="card-title">TTS API</h2>
<SetAPIsTemplate <p>
label="Whisper API" <Input
endpoint={props.chatStore.whisper_api} field="tts_key"
APIkey={props.chatStore.whisper_key} help="tts service api key"
tmps={props.templateAPIsWhisper} {...props}
setTmps={props.setTemplateAPIsWhisper} />
/> <Input
field="tts_api"
help="tts api, eg. https://api.openai.com/v1/audio/speech"
{...props}
/>
</p>
<div class="card-actions justify-end">
<SetAPIsTemplate
label="TTS API"
endpoint={props.chatStore.tts_api}
APIkey={props.chatStore.tts_key}
tmps={props.templateAPIsTTS}
setTmps={props.setTemplateAPIsTTS}
/>
</div>
</div> </div>
<hr />
<Input
field="whisper_key"
help="用于 Whisper 服务的 key默认为 上方使用的OPENAI key可在此单独配置专用key"
{...props}
/>
<Input
field="whisper_api"
help="Whisper 语言转文字服务填入此api才会开启默认为 https://api.openai.com/v1/audio/transriptions"
{...props}
/>
</div> </div>
<Help help="tts voice style" field="AAAAA">
<div className="relative border-slate-300 border rounded mt-1"> <label className="">TTS Voice</label>
<div className="flex justify-between">
<strong className="p-1 m-1">TTS API</strong>
<SetAPIsTemplate
label="TTS API"
endpoint={props.chatStore.tts_api}
APIkey={props.chatStore.tts_key}
tmps={props.templateAPIsTTS}
setTmps={props.setTemplateAPIsTTS}
/>
</div>
<hr />
<Input field="tts_key" help="tts service api key" {...props} />
<Input
field="tts_api"
help="tts api, eg. https://api.openai.com/v1/audio/speech"
{...props}
/>
</div>
<Help help="tts voice style">
<label className="m-2 p-2">TTS Voice</label>
<select <select
className="m-2 p-2" className="select select-bordered"
value={props.chatStore.tts_voice} value={props.chatStore.tts_voice}
onChange={(event: any) => { onChange={(event: any) => {
const voice = event.target.value as string; const voice = event.target.value as string;
@@ -840,10 +966,10 @@ export default (props: {
help={"TTS Speed"} help={"TTS Speed"}
{...props} {...props}
/> />
<Help help="tts response format"> <Help help="tts response format" field="AAAAA">
<label className="m-2 p-2">TTS Format</label> <label className="">TTS Format</label>
<select <select
className="m-2 p-2" className="select select-bordered"
value={props.chatStore.tts_format} value={props.chatStore.tts_format}
onChange={(event: any) => { onChange={(event: any) => {
const format = event.target.value as string; const format = event.target.value as string;
@@ -868,28 +994,31 @@ export default (props: {
role="tabpanel" role="tabpanel"
class="tab-content bg-base-100 border-base-300 rounded-box p-6" class="tab-content bg-base-100 border-base-300 rounded-box p-6"
> >
<div className="relative border-slate-300 border rounded"> <div class="card bg-base-100 w-full shadow-xl">
<div className="flex justify-between"> <div class="card-body">
<strong className="p-1 m-1">Image Gen API</strong> <h2 class="card-title">Image Gen API</h2>
<SetAPIsTemplate <p>
label="Image Gen API" <Input
endpoint={props.chatStore.image_gen_api} field="image_gen_key"
APIkey={props.chatStore.image_gen_key} help="image generation service api key"
tmps={props.templateAPIsImageGen} {...props}
setTmps={props.setTemplateAPIsImageGen} />
/> <Input
field="image_gen_api"
help="DALL image gen key, eg. https://api.openai.com/v1/images/generations"
{...props}
/>
</p>
<div class="card-actions justify-end">
<SetAPIsTemplate
label="Image Gen API"
endpoint={props.chatStore.image_gen_api}
APIkey={props.chatStore.image_gen_key}
tmps={props.templateAPIsImageGen}
setTmps={props.setTemplateAPIsImageGen}
/>
</div>
</div> </div>
<hr />
<Input
field="image_gen_key"
help="image generation service api key"
{...props}
/>
<Input
field="image_gen_api"
help="DALL image gen key, eg. https://api.openai.com/v1/images/generations"
{...props}
/>
</div> </div>
</div> </div>
</div> </div>