- toned down chat copy,thumbs up and down icons

- refactored Dynamic Input Component to encapsulate all dynamic input logic, gate and controllers
This commit is contained in:
sherifButt 2024-03-11 06:40:13 +00:00
parent fd1df23ff9
commit 40de9d7bd5
7 changed files with 159 additions and 104 deletions

View File

@ -57,11 +57,11 @@ function FeedbackButton({
onClick={handleFeedback}
data-tooltip-id={tooltipId}
data-tooltip-content={tooltipContent}
className="text-zinc-300"
className="text-white/40"
>
<IconComponent
size={18}
className="mb-1"
className={`mb-1 hover:text-white/80 ${isSelected ? "text-white" : "text-white/40"}`}
weight={isSelected ? "fill" : "regular"}
/>
</button>
@ -85,10 +85,10 @@ function CopyMessage({ message }) {
onClick={() => copyText(message)}
data-tooltip-id="copy-assistant-text"
data-tooltip-content="Copy"
className="text-zinc-300"
className="text-white/40 hover:text-white/80"
>
{copied ? (
<Check size={18} className="mb-1" />
<Check size={18} className="mb-1 text-white/80" />
) : (
<ClipboardText size={18} className="mb-1" />
)}
@ -97,7 +97,7 @@ function CopyMessage({ message }) {
id="copy-assistant-text"
place="bottom"
delayShow={300}
className="tooltip !text-xs"
className="tooltip !text-xs "
/>
</div>
</>

View File

@ -1,22 +1,42 @@
const OptionSelect = ({ data, settings }) => {
import { useEffect, useState } from "react";
const OptionSelect = ({ data, settings, submit, message, setMessage }) => {
const [submitMessage, setSubmitMessage] = useState(false);
useEffect(() => {
if (submitMessage) {
submit();
setSubmitMessage(false);
}
}, [message]);
const handleSelection = (value) => {
// Implement your logic to handle selection
console.log(value);
setMessage(value);
setSubmitMessage(true);
};
// Grid of Buttons
if (settings.displayType === "buttons") {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-white/60 text-xs mt-10 w-full justify-center">
{data.options.map((option, index) => (
<button
key={index}
className="shadow-lg hover:shadow-sm transition-all duration-200 hover:animate-shine ease-in-out text-left p-2.5 border rounded-xl border-white/20 bg-sidebar hover:bg-workspace-item-selected-gradient"
onClick={() => handleSelection(option.value)}
>
<p className="">{option.label}</p>
</button>
))}
<div className=" mb-2 w-full p-4 backdrop-blur-sm rounded-t-xl overflow-hidden py-4 px-6 border-l border-t border-r border-[#2f3238]">
<Label label={data?.label} />
<div className=" pb-0 mt-2 grid grid-cols-1 md:grid-cols-2 gap-4 text-white/80 text-xs ">
{data.options.map((option, index) => (
<button
key={index}
className="group relative shadow-lg hover:shadow-sm transition-all duration-200 ease-in-out text-left p-2.5 border rounded-xl border-white/20 bg-sidebar hover:bg-sidebar/50 overflow-hidden "
onClick={() => handleSelection(option.value)}
>
<p className="truncate max-w-xl group-hover:max-w-xl group-hover:truncate-0">
<span style={{ color: "grey" }}>{index + 1}.</span>{" "}
{option.label}
</p>
<span className="absolute invisible group-hover:visible bg-black text-white text-xs rounded-lg p-2 left-0 bottom-full mb-2">
<span style={{ color: "grey" }}>{index + 1}.</span>{" "}
{option.label}
</span>
</button>
))}
</div>
</div>
);
}
@ -24,14 +44,18 @@ const OptionSelect = ({ data, settings }) => {
// Normal List with Hyperlinks
if (settings.displayType === "list") {
return (
<div className="mt-10 text-white/60 text-xs w-full">
<div className=" text-white/70 text-xs w-full backdrop-blur-sm rounded-t-xl overflow-hidden py-4 px-6 border-l border-t border-r border-[#2f3238]">
<Label {...data} />
{data.options.map((option, index) => (
<a
key={index}
href={option.href} // assuming `href` is available in your option object
className="block p-2.5 border-b border-white/20 last:border-0 hover:bg-workspace-item-selected-gradient"
className="block p-2.5 border-b border-white/10 last:border-0 hover:bg-sidebar/50 cursor-pointer"
onClick={() => handleSelection(option.value)}
>
<p className="font-semibold">{option.label}</p>
<p className="">
<span style={{ color: "grey" }}>{index + 1}.</span> {option.label}
</p>
</a>
))}
</div>
@ -40,18 +64,8 @@ const OptionSelect = ({ data, settings }) => {
// Dropdown Menu
return (
<div className="mt-5 w-full">
<div className="flex flex-col">
<label
htmlFor="chatModel"
className="block input-label text-white text-opacity-60 text-xs font-medium py-1.5"
>
{data?.label}
</label>
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
{data?.description}
</p>
</div>
<div className="mt-5 mb-5 w-full backdrop-blur-sm rounded-t-xl overflow-hidden py-4 px-6 border-l border-t border-r border-[#2f3238]">
<Label {...data} />
<select
name="optionSelect"
id="optionSelect"
@ -59,7 +73,11 @@ const OptionSelect = ({ data, settings }) => {
required={true}
disabled={settings.disabled}
className="shadow-xl bg-sidebar text-white text-xs rounded-xl p-2.5 w-full border border-white/20 focus:ring-blue-500 focus:border-blue-500"
onChange={(e) => handleSelection(e.target.value)}
>
<option value="placeholder" disabled selected>
Select an option
</option>
{settings.waitingForModels ? (
<option disabled={true} selected={true}>
-- waiting for models --
@ -67,7 +85,7 @@ const OptionSelect = ({ data, settings }) => {
) : (
data.options.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
<span style={{ color: "grey" }}>{index + 1}.</span> {option.label}
</option>
))
)}
@ -76,4 +94,24 @@ const OptionSelect = ({ data, settings }) => {
);
};
const Label = ({ label, description }) => {
return (
<div className="hidden md:flex flex-col">
{label && (
<label
htmlFor="chatModel"
className="block input-label text-white text-opacity-60 text-xs font-medium py-1.5"
>
{label}
</label>
)}
{description && (
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
{description}
</p>
)}
</div>
);
};
export default OptionSelect;

View File

@ -1,7 +1,8 @@
import React from "react";
import React, { useEffect, useState } from "react";
// import TextInput from './TextInput';
import OptionSelect from "@/components/WorkspaceChat/ChatContainer/DynamicInput/OptionSelect";
import { ArrowUUpLeft, Keyboard } from "@phosphor-icons/react";
import { Cursor, Keyboard } from "@phosphor-icons/react";
import PromptInput from "../PromptInput";
// import RangeSlider from './RangeSlider';
// import DatePicker from './DatePicker';
// import TimePicker from './TimePicker';
@ -25,31 +26,75 @@ const inputComponents = {
const DynamicInput = ({
inputs,
isDynamicInput,
isForcedTextInput,
setIsForcedTextInput,
submit,
setMessage,
workspace,
message,
onChange,
inputDisabled,
buttonDisabled,
sendCommand,
}) => {
const [isForcedTextInput, setIsForcedTextInput] = useState(false);
useEffect(() => {
if (inputs?.type !== "text") {
setIsForcedTextInput(false);
}
}, [inputs]);
const InputComponent = inputComponents[inputs?.type] || null;
if (!InputComponent) {
return null; // or any fallback UI
}
const renderPromptInput = () => {
if (inputs?.type === "text" || inputs === undefined || isForcedTextInput) {
return (
<PromptInput
className={
inputs !== undefined ? "bottom-8 md:-bottom-5" : "-bottom-2"
}
workspace={workspace}
message={message}
submit={submit}
onChange={onChange}
inputDisabled={inputDisabled}
buttonDisabled={buttonDisabled}
sendCommand={sendCommand}
/>
);
}
};
return (
<div className="w-full px-4 fixed md:absolute bottom-10 left-0 z-10 md:z-0 flex justify-center items-center">
<div className="w-full md:px-4 fixed md:absolute bottom-10 left-0 z-10 md:z-0 flex justify-center items-center">
<div className="w-[600px]">
<InputComponent {...inputs} />
{inputs?.type !== "text" &&
isDynamicInput &&
inputs !== undefined &&
!isForcedTextInput ? (
<InputComponent
submit={submit}
setMessage={setMessage}
message={message}
{...inputs}
/>
) : (
renderPromptInput()
)}
{isDynamicInput && inputs != undefined && (
<div className="flex justify-end">
<div className="w-full fixed absolute -bottom-8 left-0 z-10 md:z-0 flex justify-center items-center">
<button
type="button"
className="pb-5 transition-all w-fit duration-300 px-5 py-2.5 rounded-lg text-white/50 text-xs items-center flex gap-x-2 hover:text-white focus:ring-gray-800"
className="transition-all w-fit duration-300 px-5 py-2.5 rounded-lg text-white/40 text-xs items-center flex gap-x-2 shadow-sm hover:text-white/60 focus:ring-gray-800"
onClick={() => setIsForcedTextInput(!isForcedTextInput)}
>
{isForcedTextInput ? (
<>
<ArrowUUpLeft className="h-5 w-5" /> back to options
<Cursor className="h-5 w-5" /> Select an option
</>
) : (
<>
<Keyboard className="h-5 w-5" /> Type another answer
<Keyboard className="h-5 w-5" /> Type a response
</>
)}
</button>

View File

@ -8,7 +8,7 @@ import { isMobile } from "react-device-detect";
import debounce from "lodash.debounce";
export default function PromptInput({
className,
className = "bottom-0",
workspace,
message,
submit,

View File

@ -1,7 +1,6 @@
import Workspace from "@/models/workspace";
import handleChat from "@/utils/chat";
import { extractMetaData } from "@/utils/chat/extractMetaData";
import { CursorClick, Keyboard } from "@phosphor-icons/react";
import { useEffect, useState } from "react";
import { isMobile } from "react-device-detect";
import { useParams } from "react-router-dom";
@ -21,16 +20,14 @@ export default function ChatContainer({
const [message, setMessage] = useState("");
const [loadingResponse, setLoadingResponse] = useState(false);
const [chatHistory, setChatHistory] = useState(knownHistory);
const [finalizedChatHistory, setFinalizedChatHistory] =
useState(knownHistory);
const [isForcedTextInput, setIsForcedTextInput] = useState(false);
const [finalizedChatHistory, setFinalizedChatHistory] = useState(knownHistory);
const handleMessageChange = (event) => {
setMessage(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
event?.preventDefault();
if (!message || message === "") return false;
const prevChatHistory = [
@ -122,7 +119,6 @@ export default function ChatContainer({
_chatHistory[_chatHistory.length - 1].content = remainingText;
setFinalizedChatHistory(_chatHistory);
setCurrentInputMeta(metaData);
console.log("metaData", metaData);
}
return;
@ -130,32 +126,6 @@ export default function ChatContainer({
loadingResponse === true && fetchReply();
}, [loadingResponse, chatHistory, workspace]);
const renderInputComponent = () => {
if (
!isDynamicInput ||
currentInputMeta?.inputs?.type === "text" ||
currentInputMeta?.inputs === undefined ||
isForcedTextInput
) {
return (
<PromptInput
className={isDynamicInput && currentInputMeta?.inputs !== undefined ? "bottom-4" : "-bottom-2"}
workspace={workspace}
message={message}
submit={handleSubmit}
onChange={handleMessageChange}
inputDisabled={loadingResponse}
buttonDisabled={loadingResponse}
sendCommand={sendCommand}
/>
);
}
if (currentInputMeta?.inputs?.type !== "text") {
return <DynamicInput {...currentInputMeta} />;
}
};
return (
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
@ -168,25 +138,29 @@ export default function ChatContainer({
workspace={workspace}
sendCommand={sendCommand}
/>
{renderInputComponent()}
{isDynamicInput && currentInputMeta?.inputs != undefined && (
<div className="w-full fixed md:absolute -bottom-1 left-0 z-10 md:z-0 flex justify-center items-center">
<button
type="button"
className="transition-all w-fit duration-300 px-5 py-2.5 rounded-lg text-white/50 text-xs items-center flex gap-x-2 hover:text-white focus:ring-gray-800"
onClick={() => setIsForcedTextInput(!isForcedTextInput)}
>
{isForcedTextInput ? (
<>
<CursorClick className="h-5 w-5" /> use dynamic input
</>
) : (
<>
<Keyboard className="h-5 w-5" /> use keyboard input
</>
)}
</button>
</div>
{isDynamicInput && currentInputMeta?.inputs?.type !== undefined ? (
<DynamicInput
inputs={currentInputMeta?.inputs}
isDynamicInput={isDynamicInput}
submit={handleSubmit}
setMessage={setMessage}
workspace={workspace}
message={message}
onChange={handleMessageChange}
inputDisabled={loadingResponse}
buttonDisabled={loadingResponse}
sendCommand={sendCommand}
/>
) : (
<PromptInput
workspace={workspace}
message={message}
submit={handleSubmit}
onChange={handleMessageChange}
inputDisabled={loadingResponse}
buttonDisabled={loadingResponse}
sendCommand={sendCommand}
/>
)}
</div>
</div>

View File

@ -1,11 +1,11 @@
import React, { useEffect, useState } from "react";
import Workspace from "@/models/workspace";
import LoadingChat from "./LoadingChat";
import ChatContainer from "./ChatContainer";
import paths from "@/utils/paths";
import ModalWrapper from "../ModalWrapper";
import { useParams } from "react-router-dom";
import { extractMetaData } from "@/utils/chat/extractMetaData";
import paths from "@/utils/paths";
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import ModalWrapper from "../ModalWrapper";
import ChatContainer from "./ChatContainer";
import LoadingChat from "./LoadingChat";
export default function WorkspaceChat({ loading, workspace }) {
const { threadSlug = null } = useParams();

View File

@ -97,7 +97,6 @@ export default function handleChat(
});
}
setChatHistory([..._chatHistory]);
console.log("chunk: ", [..._chatHistory]);
} else if (type === "finalizeResponseStream") {
const chatIdx = _chatHistory.findIndex((chat) => chat.uuid === uuid);
if (chatIdx !== -1) {
@ -109,7 +108,6 @@ export default function handleChat(
_chatHistory[chatIdx] = updatedHistory;
}
setChatHistory([..._chatHistory]);
console.log("final: ", [..._chatHistory]);
}
}