mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-10-02 08:50:11 +02: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);
|
const watchForSlash = debounce(checkForSlash, 300);
|
||||||
return (
|
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
|
<SlashCommands
|
||||||
showing={showSlashCommand}
|
showing={showSlashCommand}
|
||||||
setShowing={setShowSlashCommand}
|
setShowing={setShowSlashCommand}
|
||||||
|
@ -7,17 +7,24 @@ import { isMobile } from "react-device-detect";
|
|||||||
import { SidebarMobileHeader } from "../../Sidebar";
|
import { SidebarMobileHeader } from "../../Sidebar";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { extractMetaData } from "@/utils/chat/extractMetaData";
|
import { extractMetaData } from "@/utils/chat/extractMetaData";
|
||||||
|
import DynamicInput from "./DynamicInput";
|
||||||
|
import { ArrowUUpLeft, Keyboard } from "@phosphor-icons/react";
|
||||||
|
|
||||||
|
export default function ChatContainer({
|
||||||
|
workspace,
|
||||||
export default function ChatContainer({ workspace, knownHistory = [], isDynamicInput}) {
|
knownHistory = [],
|
||||||
|
isDynamicInput,
|
||||||
|
currentInputMeta,
|
||||||
|
setCurrentInputMeta,
|
||||||
|
}) {
|
||||||
const { threadSlug = null } = useParams();
|
const { threadSlug = null } = useParams();
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
const [loadingResponse, setLoadingResponse] = useState(false);
|
const [loadingResponse, setLoadingResponse] = useState(false);
|
||||||
const [chatHistory, setChatHistory] = useState(knownHistory);
|
const [chatHistory, setChatHistory] = useState(knownHistory);
|
||||||
const [finalizedChatHistory, setFinalizedChatHistory] =
|
const [finalizedChatHistory, setFinalizedChatHistory] =
|
||||||
useState(knownHistory);
|
useState(knownHistory);
|
||||||
const [currentInputMeta, setCurrentInputMeta] = useState(null);
|
const [isForcedTextInput, setIsForcedTextInput] = useState(false);
|
||||||
|
|
||||||
const handleMessageChange = (event) => {
|
const handleMessageChange = (event) => {
|
||||||
setMessage(event.target.value);
|
setMessage(event.target.value);
|
||||||
};
|
};
|
||||||
@ -123,6 +130,31 @@ export default function ChatContainer({ workspace, knownHistory = [], isDynamicI
|
|||||||
loadingResponse === true && fetchReply();
|
loadingResponse === true && fetchReply();
|
||||||
}, [loadingResponse, chatHistory, workspace]);
|
}, [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 (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
@ -135,15 +167,26 @@ export default function ChatContainer({ workspace, knownHistory = [], isDynamicI
|
|||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
sendCommand={sendCommand}
|
sendCommand={sendCommand}
|
||||||
/>
|
/>
|
||||||
<PromptInput
|
{renderInputComponent()}
|
||||||
workspace={workspace}
|
{isDynamicInput && currentInputMeta?.inputs != undefined && (
|
||||||
message={message}
|
<div className="w-full fixed md:absolute -bottom-1 left-0 z-10 md:z-0 flex justify-center items-center">
|
||||||
submit={handleSubmit}
|
<button
|
||||||
onChange={handleMessageChange}
|
type="button"
|
||||||
inputDisabled={loadingResponse}
|
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"
|
||||||
buttonDisabled={loadingResponse}
|
onClick={() => setIsForcedTextInput(!isForcedTextInput)}
|
||||||
sendCommand={sendCommand}
|
>
|
||||||
/>
|
{isForcedTextInput ? (
|
||||||
|
<>
|
||||||
|
<ArrowUUpLeft className="h-5 w-5" /> back to options
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Keyboard className="h-5 w-5" /> Type another answer
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -11,8 +11,9 @@ export default function WorkspaceChat({ loading, workspace }) {
|
|||||||
const { threadSlug = null } = useParams();
|
const { threadSlug = null } = useParams();
|
||||||
const [history, setHistory] = useState([]);
|
const [history, setHistory] = useState([]);
|
||||||
const [loadingHistory, setLoadingHistory] = useState(true);
|
const [loadingHistory, setLoadingHistory] = useState(true);
|
||||||
|
const [currentInputMeta, setCurrentInputMeta] = useState(null);
|
||||||
|
|
||||||
const isDynamicInput = false;
|
const isDynamicInput = true;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function getHistory() {
|
async function getHistory() {
|
||||||
@ -34,6 +35,7 @@ export default function WorkspaceChat({ loading, workspace }) {
|
|||||||
const { remainingText, metaData } = extractMetaData(
|
const { remainingText, metaData } = extractMetaData(
|
||||||
message.content
|
message.content
|
||||||
);
|
);
|
||||||
|
setCurrentInputMeta(metaData);
|
||||||
return { ...message, content: remainingText, metaData };
|
return { ...message, content: remainingText, metaData };
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
@ -79,7 +81,15 @@ export default function WorkspaceChat({ loading, workspace }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setEventDelegatorForCodeSnippets();
|
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
|
// Enables us to safely markdown and sanitize all responses without risk of injection
|
||||||
|
Loading…
Reference in New Issue
Block a user