mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-16 11:20:10 +01:00
- 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:
parent
fd1df23ff9
commit
40de9d7bd5
@ -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>
|
||||
</>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user