refine setting layout

This commit is contained in:
ecwu
2024-07-17 11:25:09 +08:00
parent 415fb934ae
commit c2c17e5956
7 changed files with 545 additions and 563 deletions

View File

@@ -41,15 +41,15 @@ export function AddImage({
}} }}
> >
<div <div
className="bg-white rounded p-2 z-20" className="bg-base-200 p-2 z-20"
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
}} }}
> >
<div className="flex justify-between p-1"> <div className="flex justify-between items-center p-1">
<span>Add Images</span> <h3>Add Images</h3>
<button <button
className="m-1 p-1 border-2 bg-red-400" className="btn btn-sm btn-neutral"
onClick={() => { onClick={() => {
setShowAddImage(false); setShowAddImage(false);
}} }}
@@ -57,10 +57,9 @@ export function AddImage({
Done Done
</button> </button>
</div> </div>
<hr /> <span class="">
<span>
<button <button
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600" className="btn btn-secondary btn-sm disabled:btn-disabled"
onClick={() => { onClick={() => {
const image_url = prompt("Image URL"); const image_url = prompt("Image URL");
if (!image_url) { if (!image_url) {
@@ -81,7 +80,7 @@ export function AddImage({
Add from URL Add from URL
</button> </button>
<button <button
className="disabled:line-through disabled:bg-slate-500 rounded m-1 p-1 border-2 bg-cyan-400 hover:bg-cyan-600" className="btn btn-primary btn-sm disabled:btn-disabled"
onClick={() => { onClick={() => {
// select file and load it to base64 image URL format // select file and load it to base64 image URL format
const input = document.createElement("input"); const input = document.createElement("input");
@@ -122,23 +121,24 @@ export function AddImage({
<input type="checkbox" checked={enableHighResolution} /> <input type="checkbox" checked={enableHighResolution} />
</span> </span>
</span> </span>
<div class="divider"></div>
{chatStore.image_gen_api && chatStore.image_gen_key && ( {chatStore.image_gen_api && chatStore.image_gen_key && (
<div className="flex flex-col"> <div className="flex flex-col">
<hr className="my-2" />
<h3>Generate Image</h3> <h3>Generate Image</h3>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-col justify-between m-1 p-1">
<label>Prompt: </label> <label>Prompt: </label>
<textarea <textarea
className="border rounded border-gray-400" className="textarea textarea-sm textarea-bordered"
value={imageGenPrompt} value={imageGenPrompt}
onChange={(e: any) => { onChange={(e: any) => {
setImageGenPrompt(e.target.value); setImageGenPrompt(e.target.value);
}} }}
/> />
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>Model: </label> <label>Model: </label>
<select <select
class="select select-sm select-bordered"
value={imageGenModel} value={imageGenModel}
onChange={(e: any) => { onChange={(e: any) => {
setImageGenModel(e.target.value); setImageGenModel(e.target.value);
@@ -148,9 +148,10 @@ export function AddImage({
<option value="dall-e-2">DALL-E 2</option> <option value="dall-e-2">DALL-E 2</option>
</select> </select>
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>n: </label> <label>n: </label>
<input <input
class="input input-sm input-bordered"
value={imageGenN} value={imageGenN}
type="number" type="number"
min={1} min={1}
@@ -158,9 +159,10 @@ export function AddImage({
onChange={(e: any) => setImageGenN(parseInt(e.target.value))} onChange={(e: any) => setImageGenN(parseInt(e.target.value))}
/> />
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>Quality: </label> <label>Quality: </label>
<select <select
class="select select-sm select-bordered"
value={imageGenQuality} value={imageGenQuality}
onChange={(e: any) => setImageGEnQuality(e.target.value)} onChange={(e: any) => setImageGEnQuality(e.target.value)}
> >
@@ -168,9 +170,10 @@ export function AddImage({
<option value="standard">Standard</option> <option value="standard">Standard</option>
</select> </select>
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>Response Format: </label> <label>Response Format: </label>
<select <select
class="select select-sm select-bordered"
value={imageGenResponseFormat} value={imageGenResponseFormat}
onChange={(e: any) => setImageGenResponseFormat(e.target.value)} onChange={(e: any) => setImageGenResponseFormat(e.target.value)}
> >
@@ -178,9 +181,10 @@ export function AddImage({
<option value="url">url</option> <option value="url">url</option>
</select> </select>
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>Size: </label> <label>Size: </label>
<select <select
class="select select-sm select-bordered"
value={imageGenSize} value={imageGenSize}
onChange={(e: any) => setImageGenSize(e.target.value)} onChange={(e: any) => setImageGenSize(e.target.value)}
> >
@@ -191,9 +195,10 @@ export function AddImage({
<option value="1024x1792">1024x1792 (dall-e-3)</option> <option value="1024x1792">1024x1792 (dall-e-3)</option>
</select> </select>
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<label>Style (only dall-e-3): </label> <label>Style (only dall-e-3): </label>
<select <select
class="select select-sm select-bordered"
value={imageGenStyle} value={imageGenStyle}
onChange={(e: any) => setImageGenStyle(e.target.value)} onChange={(e: any) => setImageGenStyle(e.target.value)}
> >
@@ -201,9 +206,9 @@ export function AddImage({
<option value="natural">natural</option> <option value="natural">natural</option>
</select> </select>
</span> </span>
<span className="flex flex-row justify-between m-1 p-1"> <span className="flex flex-row justify-between items-center m-1 p-1">
<button <button
className="bg-sky-400 m-1 p-1 rounded disabled:bg-slate-500" className="btn btn-primary btn-sm"
disabled={imageGenGenerating} disabled={imageGenGenerating}
onClick={async () => { onClick={async () => {
try { try {

View File

@@ -170,9 +170,9 @@ export default function Message(props: Props) {
)} )}
</div> </div>
<div class="chat-footer opacity-50 flex gap-x-2"> <div class="chat-footer opacity-50 flex gap-x-2">
<TTSPlay chat={chat} />
<DeleteIcon /> <DeleteIcon />
<button onClick={() => setShowEdit(true)}>Edit</button> <button onClick={() => setShowEdit(true)}>Edit</button>
<CopyIcon textToCopy={getMessageText(chat)} />
{chatStore.tts_api && chatStore.tts_key && ( {chatStore.tts_api && chatStore.tts_key && (
<TTSButton <TTSButton
chatStore={chatStore} chatStore={chatStore}
@@ -180,7 +180,7 @@ export default function Message(props: Props) {
setChatStore={setChatStore} setChatStore={setChatStore}
/> />
)} )}
<CopyIcon textToCopy={getMessageText(chat)} /> <TTSPlay chat={chat} />
</div> </div>
</div> </div>
{showEdit && ( {showEdit && (

View File

@@ -22,7 +22,7 @@ export function MessageDetail({ chat, renderMarkdown }: Props) {
) )
) : ( ) : (
<img <img
className="cursor-pointer max-w-xs max-h-32 p-1" className="my-2 rounded-md max-w-64 max-h-64"
src={mdt.image_url?.url} src={mdt.image_url?.url}
onClick={() => { onClick={() => {
window.open(mdt.image_url?.url, "_blank"); window.open(mdt.image_url?.url, "_blank");

View File

@@ -17,7 +17,7 @@ export function SetAPIsTemplate({
}: Props) { }: Props) {
return ( return (
<button <button
className="p-1 m-1 rounded bg-blue-300" className="btn btn-primary btn-sm"
onClick={() => { onClick={() => {
const name = prompt(`Give this **${label}** template a name:`); const name = prompt(`Give this **${label}** template a name:`);
if (!name) { if (!name) {

View File

@@ -191,8 +191,7 @@ const Input = (props: {
</button> </button>
</span> </span>
</div> </div>
<div class="join"> <label class="input input-bordered flex items-center gap-2 grow">
<label class="input input-bordered flex items-center gap-2 join-item grow">
{hideInput ? ( {hideInput ? (
<EyeIcon <EyeIcon
class="w-4 h-4" class="w-4 h-4"
@@ -221,7 +220,6 @@ const Input = (props: {
}} }}
/> />
</label> </label>
</div>
</Help> </Help>
); );
}; };
@@ -254,7 +252,7 @@ const Slicer = (props: {
}; };
return ( return (
<Help help={props.help} field={props.field}> <Help help={props.help} field={props.field}>
<span> <span class="py-3">
<div class="form-control"> <div class="form-control">
<label class="flex gap-2"> <label class="flex gap-2">
<span class="label-text flex items-center gap-2"> <span class="label-text flex items-center gap-2">
@@ -342,7 +340,7 @@ const Number = (props: {
</button> </button>
{props.field === "maxGenTokens" && ( {props.field === "maxGenTokens" && (
<div class="form-control"> <div class="form-control">
<label class="label cursor-pointer"> <label class="label grow">
<input <input
type="checkbox" type="checkbox"
checked={props.chatStore.maxGenTokens_enabled} checked={props.chatStore.maxGenTokens_enabled}
@@ -366,7 +364,7 @@ const Number = (props: {
!props.chatStore.maxGenTokens_enabled !props.chatStore.maxGenTokens_enabled
} }
type="number" type="number"
className="input input-bordered input-sm w-full max-w-xs" className="input input-bordered input-sm w-full"
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);
@@ -477,14 +475,12 @@ export default (props: {
onClick={(event: any) => { onClick={(event: any) => {
event.stopPropagation(); event.stopPropagation();
}} }}
className="px-6 pt-2 pb-4 rounded-lg h-fit lg:w-2/3 z-20 shadow-2xl bg-base-200" className="modal-box"
> >
<div className="flex justify-between items-center"> <div className="flex justify-between items-center mb-2">
<h3 className="text-xl"> <h3 class="text-lg font-bold">{Tr("Settings")}</h3>
<span>{Tr("Settings")}</span>
</h3>
<button <button
className="btn" class="btn btn-secondary btn-sm"
onClick={() => { onClick={() => {
props.setShow(false); props.setShow(false);
}} }}
@@ -492,22 +488,19 @@ export default (props: {
{Tr("Close")} {Tr("Close")}
</button> </button>
</div> </div>
<div role="tablist" class="tabs tabs-lifted pt-2 w-full"> <div class="join join-vertical w-full">
<input <div class="join-item collapse collapse-plus bg-base-200">
type="radio" <input type="radio" name="setting-accordion" />
name="setting_tab" <div class="collapse-title text-xl font-medium">Session</div>
role="tab" <div class="collapse-content">
class="tab" <div class="stats shadow">
aria-label="Session" <div class="stat">
/> <div class="stat-title">Session cost</div>
<div <div class="stat-value">
role="tabpanel" {props.chatStore.cost.toFixed(4)} $
class="tab-content bg-base-100 border-base-300 rounded-box p-6" </div>
> </div>
<p> </div>
{Tr("Total cost in this session")} $
{props.chatStore.cost.toFixed(4)}
</p>
<LongInput <LongInput
label="System Prompt" label="System Prompt"
field="systemMessageContent" field="systemMessageContent"
@@ -556,35 +549,35 @@ export default (props: {
</div> </div>
</div> </div>
</div> </div>
<input </div>
type="radio" <div class="join-item collapse collapse-plus bg-base-200">
name="setting_tab" <input type="radio" name="setting-accordion" />
role="tab" <div class="collapse-title text-xl font-medium">System</div>
class="tab" <div class="collapse-content">
aria-label="System" <div class="stats shadow">
/> <div class="stat">
<div <div class="stat-title">Accumulated cost</div>
role="tabpanel" <div class="stat-value">{totalCost.toFixed(4)} $</div>
class="tab-content bg-base-100 border-base-300 rounded-box p-6" <div class="stat-desc">
> in all sessions -{" "}
<div className="flex justify-between"> <a
<div class="join join-vertical lg:join-horizontal"> class="btn btn-xs btn-primary"
<p class="btn no-animation join-item">
{Tr("Accumulated cost in all sessions")} $
{totalCost.toFixed(4)}
</p>
<button
class="btn join-item"
onClick={() => { onClick={() => {
clearTotalCost(); clearTotalCost();
setTotalCost(getTotalCost()); setTotalCost(getTotalCost());
}} }}
> >
Reset Reset
</button> </a>
</div> </div>
</div> </div>
<label class="form-control w-full max-w-xs"> </div>
<Choice
field="develop_mode"
help="开发者模式,开启后会显示更多选项及功能"
{...props}
/>
<label class="form-control w-full">
<div class="label"> <div class="label">
<span class="label-text">Theme Switch</span> <span class="label-text">Theme Switch</span>
</div> </div>
@@ -601,7 +594,7 @@ export default (props: {
<option value="black">Black</option> <option value="black">Black</option>
</select> </select>
</label> </label>
<label class="form-control w-full max-w-xs"> <label class="form-control w-full">
<div class="label"> <div class="label">
<span class="label-text">Language</span> <span class="label-text">Language</span>
</div> </div>
@@ -620,12 +613,13 @@ export default (props: {
))} ))}
</select> </select>
</label> </label>
<div class="join pt-2"> <label class="form-control w-full">
<button class="btn btn-info join-item no-animation"> <div class="label">
<CogIcon class="w-6 h-6" /> <span class="label-text">Quick Actions</span>
</button> </div>
<div class="flex flex-col gap-2">
<button <button
class="btn join-item" class="btn btn-sm btn-outline btn-neural"
onClick={() => { onClick={() => {
navigator.clipboard.writeText(link); navigator.clipboard.writeText(link);
alert(tr(`Copied link:`, langCode) + `${link}`); alert(tr(`Copied link:`, langCode) + `${link}`);
@@ -634,10 +628,12 @@ export default (props: {
{Tr("Copy Setting Link")} {Tr("Copy Setting Link")}
</button> </button>
<button <button
class="btn join-item" class="btn btn-sm btn-outline btn-neural"
onClick={() => { onClick={() => {
if ( if (
!confirm(tr("Are you sure to clear all history?", langCode)) !confirm(
tr("Are you sure to clear all history?", langCode)
)
) )
return; return;
props.chatStore.history = props.chatStore.history.filter( props.chatStore.history = props.chatStore.history.filter(
@@ -650,7 +646,7 @@ export default (props: {
{Tr("Clear History")} {Tr("Clear History")}
</button> </button>
<button <button
class="btn join-item" class="btn btn-sm btn-outline btn-neural"
onClick={() => { onClick={() => {
let dataStr = let dataStr =
"data:text/json;charset=utf-8," + "data:text/json;charset=utf-8," +
@@ -671,7 +667,7 @@ export default (props: {
{Tr("Export")} {Tr("Export")}
</button> </button>
<button <button
class="btn join-item" class="btn btn-sm btn-outline btn-neural"
onClick={() => { onClick={() => {
const name = prompt( const name = prompt(
tr("Give this template a name:", langCode) tr("Give this template a name:", langCode)
@@ -700,7 +696,7 @@ export default (props: {
{Tr("As template")} {Tr("As template")}
</button> </button>
<button <button
class="btn join-item" class="btn btn-sm btn-outline btn-neural"
onClick={() => { onClick={() => {
if ( if (
!confirm( !confirm(
@@ -717,6 +713,7 @@ export default (props: {
> >
Import Import
</button> </button>
</div>
<input <input
className="hidden" className="hidden"
ref={importFileRef} ref={importFileRef}
@@ -755,19 +752,13 @@ export default (props: {
reader.readAsText(file); reader.readAsText(file);
}} }}
/> />
</label>
</div> </div>
</div> </div>
<input <div class="join-item collapse collapse-plus bg-base-200">
type="radio" <input type="radio" name="setting-accordion" />
name="setting_tab" <div class="collapse-title text-xl font-medium">Chat</div>
role="tab" <div class="collapse-content">
class="tab"
aria-label="Chat"
/>
<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 bg-base-100 w-full shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title">Chat API</h2> <h2 class="card-title">Chat API</h2>
@@ -811,11 +802,6 @@ export default (props: {
{...props} {...props}
/> />
<Choice field="logprobs" help="返回每个Token的概率" {...props} /> <Choice field="logprobs" help="返回每个Token的概率" {...props} />
<Choice
field="develop_mode"
help="开发者模式,开启后会显示更多选项及功能"
{...props}
/>
<Number <Number
field="maxTokens" field="maxTokens"
help="最大上下文 token 数量。此值会根据选择的模型自动设置。" help="最大上下文 token 数量。此值会根据选择的模型自动设置。"
@@ -867,18 +853,13 @@ export default (props: {
{...props} {...props}
/> />
</div> </div>
</div>
<input <div class="join-item collapse collapse-plus bg-base-200">
type="radio" <input type="radio" name="setting-accordion" />
name="setting_tab" <div class="collapse-title text-xl font-medium">
role="tab" Speech Recognition
class="tab" </div>
aria-label="Speech Recognition" <div class="collapse-content">
/>
<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 bg-base-100 w-full shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title">Whisper API</h2> <h2 class="card-title">Whisper API</h2>
@@ -906,17 +887,11 @@ export default (props: {
</div> </div>
</div> </div>
</div> </div>
<input </div>
type="radio" <div class="join-item collapse collapse-plus bg-base-200">
name="setting_tab" <input type="radio" name="setting-accordion" />
role="tab" <div class="collapse-title text-xl font-medium">TTS</div>
class="tab" <div class="collapse-content">
aria-label="TTS"
/>
<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 bg-base-100 w-full shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title">TTS API</h2> <h2 class="card-title">TTS API</h2>
@@ -983,17 +958,13 @@ export default (props: {
</select> </select>
</Help> </Help>
</div> </div>
<input </div>
type="radio" <div class="join-item collapse collapse-plus bg-base-200">
name="setting_tab" <input type="radio" name="setting-accordion" />
role="tab" <div class="collapse-title text-xl font-medium">
class="tab" Image Generation
aria-label="Image Gen" </div>
/> <div class="collapse-content">
<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 bg-base-100 w-full shadow-xl">
<div class="card-body"> <div class="card-body">
<h2 class="card-title">Image Gen API</h2> <h2 class="card-title">Image Gen API</h2>
@@ -1022,6 +993,7 @@ export default (props: {
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="pt-4 pb-2"> <div className="pt-4 pb-2">
<p className="text-center"> <p className="text-center">
chatgpt-api-web ChatStore {Tr("Version")}{" "} chatgpt-api-web ChatStore {Tr("Version")}{" "}

View File

@@ -1,6 +1,7 @@
import { useMemo, useState } from "preact/hooks"; import { useMemo, useState } from "preact/hooks";
import { ChatStore, ChatStoreMessage, addTotalCost } from "./app"; import { ChatStore, ChatStoreMessage, addTotalCost } from "./app";
import { Message, getMessageText } from "./chatgpt"; import { Message, getMessageText } from "./chatgpt";
import { SpeakerWaveIcon } from "@heroicons/react/24/outline";
interface TTSProps { interface TTSProps {
chatStore: ChatStore; chatStore: ChatStore;
@@ -78,7 +79,11 @@ export default function TTSButton(props: TTSProps) {
}); });
}} }}
> >
{generating ? "🤔" : "🔈"} {generating ? (
<span class="loading loading-dots loading-xs"></span>
) : (
<SpeakerWaveIcon class="h-4 w-4" />
)}
</button> </button>
); );
} }