mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-16 11:20:10 +01:00
- refactored chatContainer to handle both promptInput and dyanamicInput
- added toggle button between text promptInput and dynamicInput - added options sellect with 3 redering types, List, dropdown, buttons - styled OptionSelect component to match anythingllm buttons theme
This commit is contained in:
parent
5088d8471a
commit
6570ed0e57
@ -0,0 +1,79 @@
|
||||
const OptionSelect = ({ data, settings }) => {
|
||||
const handleSelection = (value) => {
|
||||
// Implement your logic to handle selection
|
||||
console.log(value);
|
||||
};
|
||||
|
||||
// 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="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="font-semibold">{option.label}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Normal List with Hyperlinks
|
||||
if (settings.displayType === "list") {
|
||||
return (
|
||||
<div className="mt-10 text-white/60 text-xs w-full">
|
||||
{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"
|
||||
>
|
||||
<p className="font-semibold">{option.label}</p>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 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>
|
||||
<select
|
||||
name="optionSelect"
|
||||
id="optionSelect"
|
||||
multiple={settings.allowMultiple}
|
||||
required={true}
|
||||
disabled={settings.disabled}
|
||||
className="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"
|
||||
>
|
||||
{settings.waitingForModels ? (
|
||||
<option disabled={true} selected={true}>
|
||||
-- waiting for models --
|
||||
</option>
|
||||
) : (
|
||||
data.options.map((option, index) => (
|
||||
<option key={index} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OptionSelect;
|
@ -0,0 +1,63 @@
|
||||
import React from "react";
|
||||
// import TextInput from './TextInput';
|
||||
import OptionSelect from "@/components/WorkspaceChat/ChatContainer/DynamicInput/OptionSelect";
|
||||
import { ArrowUUpLeft, Keyboard } from "@phosphor-icons/react";
|
||||
// import RangeSlider from './RangeSlider';
|
||||
// import DatePicker from './DatePicker';
|
||||
// import TimePicker from './TimePicker';
|
||||
// import DateTimePicker from './DateTimePicker';
|
||||
// import FileUpload from './FileUpload';
|
||||
// import Rating from './Rating';
|
||||
// import Checkbox from './Checkbox';
|
||||
|
||||
const inputComponents = {
|
||||
// text: TextInput,
|
||||
options: OptionSelect,
|
||||
// range: RangeSlider,
|
||||
// date: DatePicker,
|
||||
// time: TimePicker,
|
||||
// datetime: DateTimePicker,
|
||||
// file: FileUpload,
|
||||
// rating: Rating,
|
||||
// checkbox: Checkbox
|
||||
};
|
||||
|
||||
const DynamicInput = ({
|
||||
inputs,
|
||||
isDynamicInput,
|
||||
isForcedTextInput,
|
||||
setIsForcedTextInput,
|
||||
}) => {
|
||||
const InputComponent = inputComponents[inputs?.type] || null;
|
||||
if (!InputComponent) {
|
||||
return null; // or any fallback UI
|
||||
}
|
||||
return (
|
||||
<div className="w-full fixed md:absolute bottom-10 left-0 z-10 md:z-0 flex justify-center items-center">
|
||||
<div className="w-[600px]">
|
||||
<InputComponent {...inputs} />
|
||||
{isDynamicInput && inputs != undefined && (
|
||||
<div className="flex justify-end">
|
||||
<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"
|
||||
onClick={() => setIsForcedTextInput(!isForcedTextInput)}
|
||||
>
|
||||
{isForcedTextInput ? (
|
||||
<>
|
||||
<ArrowUUpLeft className="h-5 w-5" /> back to options
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Keyboard className="h-5 w-5" /> Type another answer
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DynamicInput;
|
@ -52,7 +52,7 @@ export default function PromptInput({
|
||||
|
||||
const watchForSlash = debounce(checkForSlash, 300);
|
||||
return (
|
||||
<div className="w-full fixed md:absolute bottom-0 left-0 z-10 md:z-0 flex justify-center items-center">
|
||||
<div className="w-full fixed md:absolute bottom-4 left-0 z-10 md:z-0 flex justify-center items-center">
|
||||
<SlashCommands
|
||||
showing={showSlashCommand}
|
||||
setShowing={setShowSlashCommand}
|
||||
|
@ -7,17 +7,24 @@ import { isMobile } from "react-device-detect";
|
||||
import { SidebarMobileHeader } from "../../Sidebar";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { extractMetaData } from "@/utils/chat/extractMetaData";
|
||||
import DynamicInput from "./DynamicInput";
|
||||
import { ArrowUUpLeft, Keyboard } from "@phosphor-icons/react";
|
||||
|
||||
|
||||
|
||||
export default function ChatContainer({ workspace, knownHistory = [], isDynamicInput}) {
|
||||
export default function ChatContainer({
|
||||
workspace,
|
||||
knownHistory = [],
|
||||
isDynamicInput,
|
||||
currentInputMeta,
|
||||
setCurrentInputMeta,
|
||||
}) {
|
||||
const { threadSlug = null } = useParams();
|
||||
const [message, setMessage] = useState("");
|
||||
const [loadingResponse, setLoadingResponse] = useState(false);
|
||||
const [chatHistory, setChatHistory] = useState(knownHistory);
|
||||
const [finalizedChatHistory, setFinalizedChatHistory] =
|
||||
useState(knownHistory);
|
||||
const [currentInputMeta, setCurrentInputMeta] = useState(null);
|
||||
const [isForcedTextInput, setIsForcedTextInput] = useState(false);
|
||||
|
||||
const handleMessageChange = (event) => {
|
||||
setMessage(event.target.value);
|
||||
};
|
||||
@ -123,6 +130,31 @@ export default function ChatContainer({ workspace, knownHistory = [], isDynamicI
|
||||
loadingResponse === true && fetchReply();
|
||||
}, [loadingResponse, chatHistory, workspace]);
|
||||
|
||||
const renderInputComponent = () => {
|
||||
if (
|
||||
!isDynamicInput ||
|
||||
currentInputMeta?.inputs?.type === "text" ||
|
||||
currentInputMeta?.inputs === undefined ||
|
||||
isForcedTextInput
|
||||
) {
|
||||
return (
|
||||
<PromptInput
|
||||
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)" }}
|
||||
@ -135,15 +167,26 @@ export default function ChatContainer({ workspace, knownHistory = [], isDynamicI
|
||||
workspace={workspace}
|
||||
sendCommand={sendCommand}
|
||||
/>
|
||||
<PromptInput
|
||||
workspace={workspace}
|
||||
message={message}
|
||||
submit={handleSubmit}
|
||||
onChange={handleMessageChange}
|
||||
inputDisabled={loadingResponse}
|
||||
buttonDisabled={loadingResponse}
|
||||
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 ? (
|
||||
<>
|
||||
<ArrowUUpLeft className="h-5 w-5" /> back to options
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Keyboard className="h-5 w-5" /> Type another answer
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -11,8 +11,9 @@ export default function WorkspaceChat({ loading, workspace }) {
|
||||
const { threadSlug = null } = useParams();
|
||||
const [history, setHistory] = useState([]);
|
||||
const [loadingHistory, setLoadingHistory] = useState(true);
|
||||
const [currentInputMeta, setCurrentInputMeta] = useState(null);
|
||||
|
||||
const isDynamicInput = false;
|
||||
const isDynamicInput = true;
|
||||
|
||||
useEffect(() => {
|
||||
async function getHistory() {
|
||||
@ -34,6 +35,7 @@ export default function WorkspaceChat({ loading, workspace }) {
|
||||
const { remainingText, metaData } = extractMetaData(
|
||||
message.content
|
||||
);
|
||||
setCurrentInputMeta(metaData);
|
||||
return { ...message, content: remainingText, metaData };
|
||||
}
|
||||
return message;
|
||||
@ -79,7 +81,15 @@ export default function WorkspaceChat({ loading, workspace }) {
|
||||
}
|
||||
|
||||
setEventDelegatorForCodeSnippets();
|
||||
return <ChatContainer workspace={workspace} knownHistory={history} isDynamicInput={isDynamicInput} />;
|
||||
return (
|
||||
<ChatContainer
|
||||
workspace={workspace}
|
||||
knownHistory={history}
|
||||
isDynamicInput={isDynamicInput}
|
||||
currentInputMeta={currentInputMeta}
|
||||
setCurrentInputMeta={setCurrentInputMeta}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Enables us to safely markdown and sanitize all responses without risk of injection
|
||||
|
Loading…
Reference in New Issue
Block a user