Refactor WhisperButton and ChatBOX components to use Button component for consistency; improve layout and structure
This commit is contained in:
@@ -2,6 +2,7 @@ import { createRef } from "preact";
|
|||||||
|
|
||||||
import { ChatStore } from "@/types/chatstore";
|
import { ChatStore } from "@/types/chatstore";
|
||||||
import { StateUpdater, useEffect, useState, Dispatch } from "preact/hooks";
|
import { StateUpdater, useEffect, useState, Dispatch } from "preact/hooks";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
const WhisperButton = (props: {
|
const WhisperButton = (props: {
|
||||||
chatStore: ChatStore;
|
chatStore: ChatStore;
|
||||||
@@ -12,10 +13,9 @@ const WhisperButton = (props: {
|
|||||||
const mediaRef = createRef();
|
const mediaRef = createRef();
|
||||||
const [isRecording, setIsRecording] = useState("Mic");
|
const [isRecording, setIsRecording] = useState("Mic");
|
||||||
return (
|
return (
|
||||||
<button
|
<Button
|
||||||
className={`btn disabled:line-through disabled:btn-neutral disabled:text-white m-1 p-1 ${
|
variant={isRecording === "Recording" ? "destructive" : "default"}
|
||||||
isRecording === "Recording" ? "btn-error" : "btn-success"
|
className={`m-1 p-1 ${isRecording !== "Mic" ? "animate-pulse" : ""}`}
|
||||||
} ${isRecording !== "Mic" ? "animate-pulse" : ""}`}
|
|
||||||
disabled={isRecording === "Transcribing"}
|
disabled={isRecording === "Transcribing"}
|
||||||
ref={mediaRef}
|
ref={mediaRef}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -38,7 +38,7 @@ const WhisperButton = (props: {
|
|||||||
} else {
|
} else {
|
||||||
return content.map((c) => c?.text).join(" ");
|
return content.map((c) => c?.text).join(" ");
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
)
|
)
|
||||||
.concat([inputMsg])
|
.concat([inputMsg])
|
||||||
.join(" ");
|
.join(" ");
|
||||||
@@ -52,7 +52,7 @@ const WhisperButton = (props: {
|
|||||||
await navigator.mediaDevices.getUserMedia({
|
await navigator.mediaDevices.getUserMedia({
|
||||||
audio: true,
|
audio: true,
|
||||||
}),
|
}),
|
||||||
{ audioBitsPerSecond: 64 * 1000 },
|
{ audioBitsPerSecond: 64 * 1000 }
|
||||||
);
|
);
|
||||||
|
|
||||||
// mount mediaRecorder to ref
|
// mount mediaRecorder to ref
|
||||||
@@ -125,7 +125,7 @@ const WhisperButton = (props: {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isRecording}
|
{isRecording}
|
||||||
</button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -470,8 +470,7 @@ export default function ChatBOX(props: {
|
|||||||
setShow={setShowSearch}
|
setShow={setShowSearch}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<ChatMessageList>
|
||||||
<div className="grow overflow-scroll">
|
|
||||||
{!chatStore.apiKey && (
|
{!chatStore.apiKey && (
|
||||||
<p className="bg-base-200 p-6 rounded my-3 text-left">
|
<p className="bg-base-200 p-6 rounded my-3 text-left">
|
||||||
{Tr("Please click above to set")} (OpenAI) API KEY
|
{Tr("Please click above to set")} (OpenAI) API KEY
|
||||||
@@ -538,132 +537,131 @@ export default function ChatBOX(props: {
|
|||||||
setChatStore={setChatStore}
|
setChatStore={setChatStore}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ChatMessageList>
|
|
||||||
{chatStore.history.filter((msg) => !msg.example).length == 0 && (
|
|
||||||
<div className="bg-base-200 break-all p-3 my-3 text-left">
|
|
||||||
<h2>
|
|
||||||
<span>{Tr("Saved prompt templates")}</span>
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
className="mx-2"
|
|
||||||
onClick={() => {
|
|
||||||
chatStore.systemMessageContent = "";
|
|
||||||
chatStore.toolsString = "";
|
|
||||||
chatStore.history = [];
|
|
||||||
setChatStore({ ...chatStore });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Tr("Reset Current")}
|
|
||||||
</Button>
|
|
||||||
</h2>
|
|
||||||
<div className="divider"></div>
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<Templates
|
|
||||||
templates={templates}
|
|
||||||
setTemplates={setTemplates}
|
|
||||||
chatStore={chatStore}
|
|
||||||
setChatStore={setChatStore}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{chatStore.history.length === 0 && (
|
|
||||||
<p className="break-all opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
|
||||||
{Tr("No chat history here")}
|
|
||||||
<br />⚙{Tr("Model")}: {chatStore.model}
|
|
||||||
<br />⬆{Tr("Click above to change the settings of this chat")}
|
|
||||||
<br />↖{Tr("Click the conor to create a new chat")}
|
|
||||||
<br />⚠
|
|
||||||
{Tr(
|
|
||||||
"All chat history and settings are stored in the local browser"
|
|
||||||
)}
|
|
||||||
<br />
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{chatStore.systemMessageContent.trim() && (
|
|
||||||
<div className="chat chat-start">
|
|
||||||
<div className="chat-header">Prompt</div>
|
|
||||||
<div
|
|
||||||
className="chat-bubble chat-bubble-accent cursor-pointer message-content"
|
|
||||||
onClick={() => setShowSettings(true)}
|
|
||||||
>
|
|
||||||
{chatStore.systemMessageContent}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{chatStore.history.map((_, messageIndex) => (
|
{chatStore.history.filter((msg) => !msg.example).length == 0 && (
|
||||||
<Message
|
<div className="bg-base-200 break-all p-3 my-3 text-left">
|
||||||
chatStore={chatStore}
|
<h2>
|
||||||
setChatStore={setChatStore}
|
<span>{Tr("Saved prompt templates")}</span>
|
||||||
messageIndex={messageIndex}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{showGenerating && (
|
|
||||||
<p className="p-2 my-2 animate-pulse message-content">
|
|
||||||
{generatingMessage || Tr("Generating...")}
|
|
||||||
...
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<p className="text-center">
|
|
||||||
{chatStore.history.length > 0 && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="link"
|
||||||
size="sm"
|
className="mx-2"
|
||||||
className="m-2"
|
onClick={() => {
|
||||||
disabled={showGenerating}
|
chatStore.systemMessageContent = "";
|
||||||
onClick={async () => {
|
chatStore.toolsString = "";
|
||||||
const messageIndex = chatStore.history.length - 1;
|
chatStore.history = [];
|
||||||
if (chatStore.history[messageIndex].role === "assistant") {
|
|
||||||
chatStore.history[messageIndex].hide = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setChatStore({ ...chatStore });
|
setChatStore({ ...chatStore });
|
||||||
await complete();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Tr("Re-Generate")}
|
{Tr("Reset Current")}
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{chatStore.develop_mode && chatStore.history.length > 0 && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
disabled={showGenerating}
|
|
||||||
onClick={async () => {
|
|
||||||
await complete();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Tr("Completion")}
|
|
||||||
</Button>
|
</Button>
|
||||||
|
</h2>
|
||||||
|
<div className="divider"></div>
|
||||||
|
<div className="flex flex-wrap">
|
||||||
|
<Templates
|
||||||
|
templates={templates}
|
||||||
|
setTemplates={setTemplates}
|
||||||
|
chatStore={chatStore}
|
||||||
|
setChatStore={setChatStore}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{chatStore.history.length === 0 && (
|
||||||
|
<p className="break-all opacity-60 p-6 rounded bg-white my-3 text-left dark:text-black">
|
||||||
|
{Tr("No chat history here")}
|
||||||
|
<br />⚙{Tr("Model")}: {chatStore.model}
|
||||||
|
<br />⬆{Tr("Click above to change the settings of this chat")}
|
||||||
|
<br />↖{Tr("Click the conor to create a new chat")}
|
||||||
|
<br />⚠
|
||||||
|
{Tr(
|
||||||
|
"All chat history and settings are stored in the local browser"
|
||||||
)}
|
)}
|
||||||
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<p className="p-2 my-2 text-center opacity-50 dark:text-white">
|
)}
|
||||||
{chatStore.postBeginIndex !== 0 && (
|
{chatStore.systemMessageContent.trim() && (
|
||||||
<>
|
<div className="chat chat-start">
|
||||||
<br />
|
<div className="chat-header">Prompt</div>
|
||||||
{Tr("Info: chat history is too long, forget messages")}:{" "}
|
<div
|
||||||
{chatStore.postBeginIndex}
|
className="chat-bubble chat-bubble-accent cursor-pointer message-content"
|
||||||
</>
|
onClick={() => setShowSettings(true)}
|
||||||
)}
|
>
|
||||||
|
{chatStore.systemMessageContent}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{chatStore.history.map((_, messageIndex) => (
|
||||||
|
<Message
|
||||||
|
chatStore={chatStore}
|
||||||
|
setChatStore={setChatStore}
|
||||||
|
messageIndex={messageIndex}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{showGenerating && (
|
||||||
|
<p className="p-2 my-2 animate-pulse message-content">
|
||||||
|
{generatingMessage || Tr("Generating...")}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
<VersionHint chatStore={chatStore} />
|
)}
|
||||||
{showRetry && (
|
<p className="text-center">
|
||||||
<p className="text-right p-2 my-2 dark:text-white">
|
{chatStore.history.length > 0 && (
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="secondary"
|
||||||
onClick={async () => {
|
size="sm"
|
||||||
setShowRetry(false);
|
className="m-2"
|
||||||
await complete();
|
disabled={showGenerating}
|
||||||
}}
|
onClick={async () => {
|
||||||
>
|
const messageIndex = chatStore.history.length - 1;
|
||||||
{Tr("Retry")}
|
if (chatStore.history[messageIndex].role === "assistant") {
|
||||||
</Button>
|
chatStore.history[messageIndex].hide = true;
|
||||||
</p>
|
}
|
||||||
|
|
||||||
|
setChatStore({ ...chatStore });
|
||||||
|
await complete();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Tr("Re-Generate")}
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
<div ref={messagesEndRef}></div>
|
{chatStore.develop_mode && chatStore.history.length > 0 && (
|
||||||
</ChatMessageList>
|
<Button
|
||||||
</div>
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled={showGenerating}
|
||||||
|
onClick={async () => {
|
||||||
|
await complete();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Tr("Completion")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p className="p-2 my-2 text-center opacity-50 dark:text-white">
|
||||||
|
{chatStore.postBeginIndex !== 0 && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
{Tr("Info: chat history is too long, forget messages")}:{" "}
|
||||||
|
{chatStore.postBeginIndex}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<VersionHint chatStore={chatStore} />
|
||||||
|
{showRetry && (
|
||||||
|
<p className="text-right p-2 my-2 dark:text-white">
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={async () => {
|
||||||
|
setShowRetry(false);
|
||||||
|
await complete();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Tr("Retry")}
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div ref={messagesEndRef}></div>
|
||||||
|
</ChatMessageList>
|
||||||
{images.length > 0 && (
|
{images.length > 0 && (
|
||||||
<div className="flex flex-wrap">
|
<div className="flex flex-wrap">
|
||||||
{images.map((image, index) => (
|
{images.map((image, index) => (
|
||||||
@@ -727,14 +725,14 @@ export default function ChatBOX(props: {
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{chatStore.whisper_api && chatStore.whisper_key && (
|
{chatStore.whisper_api && chatStore.whisper_key && (
|
||||||
<Button variant="ghost" size="icon">
|
<>
|
||||||
<WhisperButton
|
<WhisperButton
|
||||||
chatStore={chatStore}
|
chatStore={chatStore}
|
||||||
inputMsg={inputMsg}
|
inputMsg={inputMsg}
|
||||||
setInputMsg={setInputMsg}
|
setInputMsg={setInputMsg}
|
||||||
/>
|
/>
|
||||||
<span className="sr-only">Use Microphone</span>
|
<span className="sr-only">Use Microphone</span>
|
||||||
</Button>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user