diff --git a/frontend/src/components/DefaultChat/index.jsx b/frontend/src/components/DefaultChat/index.jsx index b87ee1175..0952ebd73 100644 --- a/frontend/src/components/DefaultChat/index.jsx +++ b/frontend/src/components/DefaultChat/index.jsx @@ -4,6 +4,8 @@ import NewWorkspaceModal, { useNewWorkspaceModal, } from "../Modals/NewWorkspace"; import paths from "../../utils/paths"; +import { isMobile } from "react-device-detect"; +import { SidebarMobileHeader } from "../Sidebar"; export default function DefaultChatContainer() { const [mockMsgs, setMockMessages] = useState([]); @@ -21,8 +23,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> Welcome to AnythingLLM, AnythingLLM is an open-source AI tool by Mintplex Labs that turns <i>anything</i> into a trained chatbot you can query and chat with. AnythingLLM is a BYOK (bring-your-own-keys) @@ -39,8 +41,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> AnythingLLM is the easiest way to put powerful AI products like OpenAi, GPT-4, LangChain, PineconeDB, ChromaDB, and other services together in a neat package with no fuss to increase your @@ -56,8 +58,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> AnythingLLM can run totally locally on your machine with little overhead you wont even notice it's there! No GPU needed. Cloud and on-premises installation is available as well. @@ -71,7 +73,7 @@ export default function DefaultChatContainer() { className="mt-4 w-fit flex flex-grow gap-x-2 py-[5px] px-4 border border-slate-400 rounded-lg text-slate-800 dark:text-slate-200 justify-start items-center hover:bg-slate-100 dark:hover:bg-stone-900 dark:bg-stone-900" > <GitMerge className="h-4 w-4" /> - <p className="text-slate-800 dark:text-slate-200 text-lg leading-loose"> + <p className="text-slate-800 dark:text-slate-200 text-sm md:text-lg leading-loose"> Create an issue on Github </p> </a> @@ -85,8 +87,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> How do I get started?! </p> </div> @@ -99,8 +101,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> It's simple. All collections are organized into buckets we call{" "} <b>"Workspaces"</b>. Workspaces are buckets of files, documents, images, PDFs, and other files which will be transformed into @@ -114,7 +116,7 @@ export default function DefaultChatContainer() { className="mt-4 w-fit flex flex-grow gap-x-2 py-[5px] px-4 border border-slate-400 rounded-lg text-slate-800 dark:text-slate-200 justify-start items-center hover:bg-slate-100 dark:hover:bg-stone-900 dark:bg-stone-900" > <Plus className="h-4 w-4" /> - <p className="text-slate-800 dark:text-slate-200 text-lg leading-loose"> + <p className="text-slate-800 dark:text-slate-200 text-sm md:text-lg leading-loose"> Create your first workspace </p> </button> @@ -128,8 +130,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> Is this like an AI dropbox or something? What about chatting? It is a chatbot isn't it? </p> @@ -143,8 +145,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> AnythingLLM is more than a smarter Dropbox. <br /> <br /> @@ -174,8 +176,8 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> Wow, this sounds amazing, let me try it out already! </p> </div> @@ -188,18 +190,18 @@ export default function DefaultChatContainer() { popMsg ? "chat__message" : "" }`} > - <div className="p-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> - <p className="text-slate-800 dark:text-slate-200 font-semibold"> + <div className="p-4 max-w-full md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-b-2xl rounded-tr-2xl rounded-tl-sm"> + <p className="text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base"> Have Fun! </p> - <div className="flex items-center gap-x-4"> + <div className="flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-4"> <a href={paths.github()} target="_blank" className="mt-4 w-fit flex flex-grow gap-x-2 py-[5px] px-4 border border-slate-400 rounded-lg text-slate-800 dark:text-slate-200 justify-start items-center hover:bg-slate-100 dark:hover:bg-stone-900 dark:bg-stone-900" > <GitHub className="h-4 w-4" /> - <p className="text-slate-800 dark:text-slate-200 text-lg leading-loose"> + <p className="text-slate-800 dark:text-slate-200 text-sm md:text-lg leading-loose"> Star on GitHub </p> </a> @@ -208,7 +210,7 @@ export default function DefaultChatContainer() { className="mt-4 w-fit flex flex-grow gap-x-2 py-[5px] px-4 border border-slate-400 rounded-lg text-slate-800 dark:text-slate-200 justify-start items-center hover:bg-slate-100 dark:hover:bg-stone-900 dark:bg-stone-900" > <Mail className="h-4 w-4" /> - <p className="text-slate-800 dark:text-slate-200 text-lg leading-loose"> + <p className="text-slate-800 dark:text-slate-200 text-sm md:text-lg leading-loose"> Contact Mintplex Labs </p> </a> @@ -245,9 +247,10 @@ export default function DefaultChatContainer() { return ( <div - style={{ height: "calc(100% - 32px)" }} - className="transition-all duration-500 relative ml-[2px] mr-[8px] my-[16px] rounded-[26px] bg-white dark:bg-black-900 min-w-[82%] p-[18px] h-full overflow-y-scroll" + style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} + className="transition-all duration-500 relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-white dark:bg-black-900 md:min-w-[82%] p-[18px] h-full overflow-y-scroll" > + {isMobile && <SidebarMobileHeader />} {mockMsgs.map((content, i) => { return <React.Fragment key={i}>{content}</React.Fragment>; })} diff --git a/frontend/src/components/Modals/Keys.jsx b/frontend/src/components/Modals/Keys.jsx index c4c8a519b..7f472d86f 100644 --- a/frontend/src/components/Modals/Keys.jsx +++ b/frontend/src/components/Modals/Keys.jsx @@ -26,7 +26,7 @@ export default function KeysModal({ hideModal = noop }) { <div className="relative bg-white rounded-lg shadow dark:bg-stone-700"> <div className="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"> <h3 className="text-xl font-semibold text-gray-900 dark:text-white"> - Your System Settings + System Settings </h3> <button onClick={hideModal} @@ -48,7 +48,7 @@ export default function KeysModal({ hideModal = noop }) { <div className="w-full flex flex-col gap-y-4"> <div className="bg-orange-300 p-4 rounded-lg border border-orange-600 text-orange-700 w-full items-center flex gap-x-2"> <AlertCircle className="h-8 w-8" /> - <p> + <p className="text-sm md:text-base "> Ensure all fields are green before attempting to use AnythingLLM or it may not function as expected! </p> diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx index 7abc1fac0..fe5e38a3f 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Documents/Directory/index.jsx @@ -42,7 +42,7 @@ export default function Directory({ className="flex gap-x-2 items-center cursor-pointer w-full" onClick={() => toggleExpanded(!isExpanded)} > - <h2 className="text-2xl">{files.name}</h2> + <h2 className="text-base md:text-2xl">{files.name}</h2> {files.items.some((files) => files.type === "folder") ? ( <p className="text-xs italic">{files.items.length} folders</p> ) : ( diff --git a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx index cce5e2afa..559b60fd7 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Documents/index.jsx @@ -197,11 +197,11 @@ export default function DocumentSettings({ workspace }) { </div> </div> </div> - <div className="flex items-center justify-between p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600"> + <div className="flex items-center justify-between p-4 md:p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600"> <button onClick={deleteWorkspace} type="button" - className="border border-transparent text-gray-500 bg-white hover:bg-red-100 rounded-lg text-sm font-medium px-5 py-2.5 hover:text-red-900 focus:z-10 dark:bg-transparent dark:text-gray-300 dark:hover:text-white dark:hover:bg-red-600" + className="border border-transparent text-gray-500 bg-white hover:bg-red-100 rounded-lg whitespace-nowrap text-sm font-medium px-5 py-2.5 hover:text-red-900 focus:z-10 dark:bg-transparent dark:text-gray-300 dark:hover:text-white dark:hover:bg-red-600" > Delete Workspace </button> @@ -210,7 +210,7 @@ export default function DocumentSettings({ workspace }) { disabled={saving} onClick={confirmChanges} type="submit" - className="text-slate-200 bg-black-900 px-4 py-2 rounded-lg hover:bg-gray-900" + className="text-slate-200 bg-black-900 px-4 py-2 rounded-lg hover:bg-gray-900 whitespace-nowrap text-sm" > {saving ? "Saving..." : "Confirm Changes"} </button> diff --git a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx index 8e6529869..c35df7634 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Settings/index.jsx @@ -154,18 +154,18 @@ export default function WorkspaceSettings({ workspace }) { )} </div> </div> - <div className="flex items-center justify-between p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600"> + <div className="flex items-center justify-between p-2 md:p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600"> <button onClick={deleteWorkspace} type="button" - className="border border-transparent text-gray-500 bg-white hover:bg-red-100 rounded-lg text-sm font-medium px-5 py-2.5 hover:text-red-900 focus:z-10 dark:bg-transparent dark:text-gray-300 dark:hover:text-white dark:hover:bg-red-600" + className="border border-transparent text-gray-500 bg-white hover:bg-red-100 rounded-lg whitespace-nowrap text-sm font-medium px-5 py-2.5 hover:text-red-900 focus:z-10 dark:bg-transparent dark:text-gray-300 dark:hover:text-white dark:hover:bg-red-600" > Delete Workspace </button> {hasChanges && ( <button type="submit" - className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-black dark:text-slate-200 dark:border-transparent dark:hover:text-slate-200 dark:hover:bg-gray-900 dark:focus:ring-gray-800" + className="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 rounded-lg border border-gray-200 whitespace-nowrap text-sm font-medium px-2 md:px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-black dark:text-slate-200 dark:border-transparent dark:hover:text-slate-200 dark:hover:bg-gray-900 dark:focus:ring-gray-800" > {saving ? "Updating..." : "Update workspace"} </button> diff --git a/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx b/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx index 86c19564b..2f56dba7d 100644 --- a/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/Upload/index.jsx @@ -78,12 +78,12 @@ export default function UploadToWorkspace({ workspace, fileTypes }) { return ( <ModalWrapper deleteWorkspace={deleteWorkspace}> <div className="outline-none transition-all duration-300 bg-red-200 flex h-[20rem] overflow-y-scroll overflow-x-hidden rounded-lg"> - <div className="flex flex-col gap-y-1 w-full h-full items-center justify-center"> + <div className="flex flex-col gap-y-1 w-full h-full items-center justify-center md:px-0 px-2"> <Frown className="w-8 h-8 text-red-800" /> - <p className="text-red-800 text-xs"> + <p className="text-red-800 text-xs text-center"> Document processor is offline. </p> - <p className="text-red-800 text-xs"> + <p className="text-red-800 text-[10px] md:text-xs text-center"> you cannot upload documents from the UI right now </p> </div> diff --git a/frontend/src/components/Modals/MangeWorkspace/index.jsx b/frontend/src/components/Modals/MangeWorkspace/index.jsx index 9a1de8c2e..c35675ad1 100644 --- a/frontend/src/components/Modals/MangeWorkspace/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/index.jsx @@ -83,26 +83,26 @@ export default function ManageWorkspace({ function WorkspaceSettingTabs({ selectedTab, changeTab }) { return ( <div> - <ul className="flex flex-wrap -mb-px text-sm gap-x-2 font-medium text-center text-gray-500 dark:text-gray-400"> + <ul className="flex md:flex-wrap overflow-x-scroll no-scroll -mb-px text-sm gap-x-2 font-medium text-center text-gray-500 dark:text-gray-400"> <WorkspaceTab active={selectedTab === "documents"} displayName="Documents" tabName="documents" - icon={<Archive className="h-4 w-4" />} + icon={<Archive className="h-4 w-4 flex-shrink-0" />} onClick={changeTab} /> <WorkspaceTab active={selectedTab === "upload"} displayName="Upload Docs" tabName="upload" - icon={<UploadCloud className="h-4 w-4" />} + icon={<UploadCloud className="h-4 w-4 flex-shrink-0" />} onClick={changeTab} /> <WorkspaceTab active={selectedTab === "settings"} displayName="Settings" tabName="settings" - icon={<Sliders className="h-4 w-4" />} + icon={<Sliders className="h-4 w-4 flex-shrink-0" />} onClick={changeTab} /> </ul> @@ -126,7 +126,7 @@ function WorkspaceTab({ disabled={active} onClick={() => onClick(tabName)} className={ - "flex items-center gap-x-1 p-4 border-b-2 rounded-t-lg group " + + "flex items-center gap-x-1 p-4 border-b-2 rounded-t-lg group whitespace-nowrap " + classes } > diff --git a/frontend/src/components/Modals/NewWorkspace.jsx b/frontend/src/components/Modals/NewWorkspace.jsx index 81a87903b..07cb45860 100644 --- a/frontend/src/components/Modals/NewWorkspace.jsx +++ b/frontend/src/components/Modals/NewWorkspace.jsx @@ -63,7 +63,7 @@ export default function NewWorkspaceModal({ hideModal = noop }) { Error: {error} </p> )} - <p className="text-gray-800 dark:text-slate-200 text-sm"> + <p className="text-gray-800 dark:text-slate-200 text-xs md:text-sm"> After creating a workspace you will be able to add and remove documents from it. </p> diff --git a/frontend/src/components/Modals/Password.jsx b/frontend/src/components/Modals/Password.jsx index 280572feb..30c628fbb 100644 --- a/frontend/src/components/Modals/Password.jsx +++ b/frontend/src/components/Modals/Password.jsx @@ -31,7 +31,7 @@ export default function PasswordModal() { <form ref={formEl} onSubmit={handleLogin}> <div className="relative bg-white rounded-lg shadow dark:bg-stone-700"> <div className="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"> - <h3 className="text-xl font-semibold text-gray-900 dark:text-white"> + <h3 className="text-md md:text-xl font-semibold text-gray-900 dark:text-white"> This workspace is password protected. </h3> </div> @@ -58,7 +58,7 @@ export default function PasswordModal() { Error: {error} </p> )} - <p className="text-gray-800 dark:text-slate-200 text-sm"> + <p className="text-gray-800 dark:text-slate-200 md:text-sm text-xs"> You will only have to enter this password once. After successful login it will be stored in your browser. </p> diff --git a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx index 549d3f346..ce7adccfe 100644 --- a/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx +++ b/frontend/src/components/Sidebar/ActiveWorkspaces/index.jsx @@ -57,8 +57,8 @@ export default function ActiveWorkspaces() { : "hover:bg-slate-100 dark:hover:bg-stone-900 " }`} > - <Book className="h-4 w-4" /> - <p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold"> + <Book className="h-4 w-4 flex-shrink-0" /> + <p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold whitespace-nowrap overflow-hidden "> {workspace.name} </p> </a> diff --git a/frontend/src/components/Sidebar/index.jsx b/frontend/src/components/Sidebar/index.jsx index 2cd402662..fc50fe045 100644 --- a/frontend/src/components/Sidebar/index.jsx +++ b/frontend/src/components/Sidebar/index.jsx @@ -1,5 +1,13 @@ -import React, { useRef } from "react"; -import { BookOpen, Briefcase, Cpu, GitHub, Key, Plus } from "react-feather"; +import React, { useEffect, useRef, useState } from "react"; +import { + BookOpen, + Briefcase, + Cpu, + GitHub, + Key, + Menu, + Plus, +} from "react-feather"; import IndexCount from "./IndexCount"; import LLMStatus from "./LLMStatus"; import KeysModal, { useKeysModal } from "../Modals/Keys"; @@ -22,11 +30,6 @@ export default function Sidebar() { hideModal: hideNewWsModal, } = useNewWorkspaceModal(); - // const handleWidthToggle = () => { - // if (!sidebarRef.current) return false; - // sidebarRef.current.classList.add('translate-x-[-100%]') - // } - return ( <> <div @@ -34,11 +37,6 @@ export default function Sidebar() { style={{ height: "calc(100% - 32px)" }} className="transition-all duration-500 relative m-[16px] rounded-[26px] bg-white dark:bg-black-900 min-w-[15.5%] p-[18px] " > - {/* <button onClick={handleWidthToggle} className='absolute -right-[13px] top-[35%] bg-white w-auto h-auto bg-transparent flex items-center'> - <svg width="16" height="96" viewBox="0 0 16 96" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#141414"><path d="M2.5 0H3C3 20 15 12 15 32V64C15 84 3 76 3 96H2.5V0Z" fill="black" fill-opacity="0.12" stroke="transparent" stroke-width="0px"></path><path d="M0 0H2.5C2.5 20 14.5 12 14.5 32V64C14.5 84 2.5 76 2.5 96H0V0Z" fill="#141414"></path></svg> - <ChevronLeft className='absolute h-4 w-4 text-white mr-1' /> - </button> */} - <div className="w-full h-full flex flex-col overflow-x-hidden items-between"> {/* Header Information */} <div className="flex w-full items-center justify-between"> @@ -133,3 +131,161 @@ export default function Sidebar() { </> ); } + +export function SidebarMobileHeader() { + const [showSidebar, setShowSidebar] = useState(false); + const [showBgOverlay, setShowBgOverlay] = useState(false); + const sidebarRef = useRef(null); + const { + showing: showingKeyModal, + showModal: showKeyModal, + hideModal: hideKeyModal, + } = useKeysModal(); + const { + showing: showingNewWsModal, + showModal: showNewWsModal, + hideModal: hideNewWsModal, + } = useNewWorkspaceModal(); + + useEffect(() => { + function handleBg() { + if (showSidebar) { + setTimeout(() => { + setShowBgOverlay(true); + }, 300); + } else { + setShowBgOverlay(false); + } + } + handleBg(); + }, [showSidebar]); + + return ( + <> + <div className="flex justify-between relative top-0 left-0 w-full rounded-b-lg px-2 pb-4 bg-white dark:bg-black-900 text-slate-800 dark:text-slate-200"> + <button + onClick={() => setShowSidebar(true)} + className="rounded-md bg-stone-200 p-2 flex items-center justify-center text-slate-800 hover:bg-stone-300 group dark:bg-stone-800 dark:text-slate-200 dark:hover:bg-stone-900 dark:border dark:border-stone-800" + > + <Menu className="h-6 w-6" /> + </button> + <p className="text-xl font-base text-slate-600 dark:text-slate-200"> + AnythingLLM + </p> + </div> + <div + style={{ + transform: showSidebar ? `translateX(0vw)` : `translateX(-100vw)`, + }} + className={`z-99 fixed top-0 left-0 transition-all duration-500 w-[100vw] h-[100vh]`} + > + <div + className={`${ + showBgOverlay + ? "transition-all opacity-1" + : "transition-none opacity-0" + } duration-500 fixed top-0 left-0 bg-black-900 bg-opacity-75 w-screen h-screen`} + onClick={() => setShowSidebar(false)} + /> + <div + ref={sidebarRef} + className="h-[100vh] fixed top-0 left-0 rounded-r-[26px] bg-white dark:bg-black-900 w-[70%] p-[18px] " + > + <div className="w-full h-full flex flex-col overflow-x-hidden items-between"> + {/* Header Information */} + <div className="flex w-full items-center justify-between"> + <p className="text-xl font-base text-slate-600 dark:text-slate-200"> + AnythingLLM + </p> + <div className="flex gap-x-2 items-center text-slate-500"> + <button + onClick={showKeyModal} + className="transition-all duration-300 p-2 rounded-full bg-slate-200 text-slate-400 dark:bg-stone-800 hover:bg-slate-800 hover:text-slate-200 dark:hover:text-slate-200" + > + <Key className="h-4 w-4 " /> + </button> + </div> + </div> + + {/* Primary Body */} + <div className="h-full flex flex-col w-full justify-between pt-4 overflow-y-hidden "> + <div className="h-auto md:sidebar-items md:dark:sidebar-items"> + <div + style={{ height: "calc(100vw - -3rem)" }} + className=" flex flex-col gap-y-4 pb-8 overflow-y-scroll no-scroll" + > + <div className="flex gap-x-2 items-center justify-between"> + <button + onClick={showNewWsModal} + className="flex flex-grow w-[75%] h-[36px] gap-x-2 py-[5px] px-4 border border-slate-400 rounded-lg text-slate-800 dark:text-slate-200 justify-start items-center hover:bg-slate-100 dark:hover:bg-stone-900" + > + <Plus className="h-4 w-4" /> + <p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold"> + New workspace + </p> + </button> + </div> + <ActiveWorkspaces /> + </div> + </div> + <div> + <div className="flex flex-col gap-y-2"> + <div className="w-full flex items-center justify-between"> + <LLMStatus /> + <IndexCount /> + </div> + <a + href={paths.hosting()} + target="_blank" + className="flex flex-grow w-[100%] h-[36px] gap-x-2 py-[5px] px-4 border border-slate-400 dark:border-transparent rounded-lg text-slate-800 dark:text-slate-200 justify-center items-center hover:bg-slate-100 dark:bg-stone-800 dark:hover:bg-stone-900" + > + <Cpu className="h-4 w-4" /> + <p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold"> + Managed cloud hosting + </p> + </a> + <a + href={paths.hosting()} + target="_blank" + className="flex flex-grow w-[100%] h-[36px] gap-x-2 py-[5px] px-4 border border-slate-400 dark:border-transparent rounded-lg text-slate-800 dark:text-slate-200 justify-center items-center hover:bg-slate-100 dark:bg-stone-800 dark:hover:bg-stone-900" + > + <Briefcase className="h-4 w-4" /> + <p className="text-slate-800 dark:text-slate-200 text-xs leading-loose font-semibold"> + Enterprise Installation + </p> + </a> + </div> + + {/* Footer */} + <div className="flex items-end justify-between mt-2"> + <div className="flex gap-x-1 items-center"> + <a + href={paths.github()} + className="transition-all duration-300 p-2 rounded-full bg-slate-200 text-slate-400 dark:bg-slate-800 hover:bg-slate-800 hover:text-slate-200 dark:hover:text-slate-200" + > + <GitHub className="h-4 w-4 " /> + </a> + <a + href={paths.docs()} + className="transition-all duration-300 p-2 rounded-full bg-slate-200 text-slate-400 dark:bg-slate-800 hover:bg-slate-800 hover:text-slate-200 dark:hover:text-slate-200" + > + <BookOpen className="h-4 w-4 " /> + </a> + </div> + <a + href={paths.mailToMintplex()} + className="transition-all duration-300 text-xs text-slate-200 dark:text-slate-600 hover:text-blue-600 dark:hover:text-blue-400" + > + @MintplexLabs + </a> + </div> + </div> + </div> + </div> + </div> + {showingKeyModal && <KeysModal hideModal={hideKeyModal} />} + {showingNewWsModal && <NewWorkspaceModal hideModal={hideNewWsModal} />} + </div> + </> + ); +} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx index 0e2352406..d13f670f5 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/HistoricalMessage/index.jsx @@ -21,9 +21,9 @@ function HistoricalMessage({ if (role === "user") { return ( <div className="flex justify-end mb-4 items-start"> - <div className="mr-2 py-1 px-4 max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> + <div className="mr-2 py-1 px-4 w-fit md:max-w-[75%] bg-slate-200 dark:bg-amber-800 rounded-b-2xl rounded-tl-2xl rounded-tr-sm"> <span - className={`inline-block p-2 rounded-lg whitespace-pre-line text-slate-800 dark:text-slate-200 font-semibold`} + className={`inline-block p-2 rounded-lg whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base`} > {message} </span> @@ -52,9 +52,9 @@ function HistoricalMessage({ return ( <div ref={replyRef} className="flex justify-start items-end mb-4"> <Jazzicon size={30} user={{ uid: workspace.slug }} /> - <div className="ml-2 py-3 px-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> + <div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <span - className="whitespace-pre-line text-slate-800 dark:text-slate-200 font-semibold flex flex-col gap-y-1" + className="whitespace-pre-line text-slate-800 dark:text-slate-200 font-[500] md:font-semibold text-sm md:text-base flex flex-col gap-y-1" dangerouslySetInnerHTML={{ __html: renderMarkdown(message) }} /> <Citations sources={sources} /> diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/PromptReply/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/PromptReply/index.jsx index b3dd26e25..a0bf02bc3 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/PromptReply/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/PromptReply/index.jsx @@ -25,7 +25,7 @@ function PromptReply({ return ( <div className="chat__message flex justify-start mb-4 items-end"> <Jazzicon size={30} user={{ uid: workspace.slug }} /> - <div className="ml-2 pt-2 px-6 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> + <div className="ml-2 pt-2 px-6 w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <span className={`inline-block p-2`}> <div className="dot-falling"></div> </span> @@ -58,9 +58,9 @@ function PromptReply({ className="mb-4 flex justify-start items-end" > <Jazzicon size={30} user={{ uid: workspace.slug }} /> - <div className="ml-2 py-3 px-4 max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> + <div className="ml-2 py-3 px-4 overflow-x-scroll w-fit md:max-w-[75%] bg-orange-100 dark:bg-stone-700 rounded-t-2xl rounded-br-2xl rounded-bl-sm"> <span - className="whitespace-pre-line text-slate-800 dark:text-slate-200 font-semibold flex flex-col gap-y-1" + className="whitespace-pre-line text-slate-800 dark:text-slate-200 flex flex-col gap-y-1 font-[500] md:font-semibold text-sm md:text-base" dangerouslySetInnerHTML={{ __html: renderMarkdown(reply) }} /> <Citations sources={sources} /> diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx index 0d0222265..6e400c03f 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx @@ -1,7 +1,6 @@ import { Frown } from "react-feather"; import HistoricalMessage from "./HistoricalMessage"; import PromptReply from "./PromptReply"; -// import paths from '../../../../../utils/paths'; export default function ChatHistory({ history = [], workspace }) { if (history.length === 0) { @@ -20,7 +19,7 @@ export default function ChatHistory({ history = [], workspace }) { return ( <div - className="h-[89%] pb-[100px] pt-[50px] md:pt-0 md:pb-5 mx-2 md:mx-0 overflow-y-scroll flex flex-col justify-between md:justify-start" + className="h-[89%] pb-[100px] md:pt-[50px] md:pt-0 md:pb-5 mx-2 md:mx-0 overflow-y-scroll flex flex-col justify-start no-scroll" id="chat-history" > {history.map( diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx index a9e5d9c65..bd656a7f7 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx @@ -1,4 +1,5 @@ import React, { useState, useRef } from "react"; +import { isMobile } from "react-device-detect"; import { Loader, Menu, Send, X } from "react-feather"; export default function PromptInput({ @@ -24,6 +25,7 @@ export default function PromptInput({ } }; const adjustTextArea = (event) => { + if (isMobile) return false; const element = event.target; element.style.height = "1px"; element.style.height = @@ -37,10 +39,10 @@ export default function PromptInput({ }; return ( - <div className="w-full fixed md:absolute bottom-0 left-0"> + <div className="w-full fixed md:absolute bottom-0 left-0 z-10"> <form onSubmit={handleSubmit} - className="flex flex-col gap-y-1 bg-transparentrounded-t-lg w-3/4 mx-auto" + className="flex flex-col gap-y-1 bg-white dark:bg-black-900 md:bg-transparent rounded-t-lg md:w-3/4 w-full mx-auto" > <div className="flex items-center py-2 px-4 rounded-lg"> {/* Toggle selector? */} @@ -64,7 +66,11 @@ export default function PromptInput({ }} value={message} className="cursor-text max-h-[100px] md:min-h-[40px] block mx-2 md:mx-4 p-2.5 w-full text-[16px] md:text-sm rounded-lg border bg-gray-50 border-gray-300 placeholder-gray-400 text-gray-900 dark:text-white dark:bg-stone-600 dark:border-stone-700 dark:placeholder-stone-400" - placeholder="Shift + Enter for newline. Enter to submit." + placeholder={ + isMobile + ? "Enter your message here." + : "Shift + Enter for newline. Enter to submit." + } /> <button ref={formRef} diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx index 7f3eb9286..e26f0f09a 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/index.jsx @@ -3,6 +3,8 @@ import ChatHistory from "./ChatHistory"; import PromptInput from "./PromptInput"; import Workspace from "../../../models/workspace"; import handleChat from "../../../utils/chat"; +import { isMobile } from "react-device-detect"; +import { SidebarMobileHeader } from "../../Sidebar"; export default function ChatContainer({ workspace, knownHistory = [] }) { const [message, setMessage] = useState(""); @@ -68,9 +70,10 @@ export default function ChatContainer({ workspace, knownHistory = [] }) { return ( <div - style={{ height: "calc(100% - 32px)" }} - className="transition-all duration-500 relative ml-[2px] mr-[8px] my-[16px] rounded-[26px] bg-white dark:bg-black-900 min-w-[82%] p-[18px] h-full overflow-y-scroll" + style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} + className="transition-all duration-500 relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-white dark:bg-black-900 w-full md:min-w-[82%] p-[18px] h-full overflow-y-scroll" > + {isMobile && <SidebarMobileHeader />} <div className="flex flex-col h-full w-full flex"> <ChatHistory history={chatHistory} workspace={workspace} /> <PromptInput diff --git a/frontend/src/components/WorkspaceChat/LoadingChat/index.jsx b/frontend/src/components/WorkspaceChat/LoadingChat/index.jsx index c4180a692..f2844de7a 100644 --- a/frontend/src/components/WorkspaceChat/LoadingChat/index.jsx +++ b/frontend/src/components/WorkspaceChat/LoadingChat/index.jsx @@ -1,11 +1,12 @@ +import { isMobile } from "react-device-detect"; import * as Skeleton from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; export default function LoadingChat() { return ( <div - style={{ height: "calc(100% - 32px)" }} - className="transition-all duration-500 relative ml-[2px] mr-[8px] my-[16px] rounded-[26px] bg-white dark:bg-black-900 min-w-[82%] p-[18px] h-full overflow-y-scroll" + style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }} + className="transition-all duration-500 relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-white dark:bg-black-900 w-full md:min-w-[82%] p-[18px] h-full overflow-y-scroll" > <Skeleton.default height="100px" @@ -13,34 +14,34 @@ export default function LoadingChat() { baseColor={"#2a3a53"} highlightColor={"#395073"} count={1} - className="max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" + className="max-w-full md:max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" containerClassName="flex justify-start" /> <Skeleton.default height="100px" - width="45%" + width={isMobile ? "70%" : "45%"} baseColor={"#2a3a53"} highlightColor={"#395073"} count={1} - className="max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" + className="max-w-full md:max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" containerClassName="flex justify-end" /> <Skeleton.default height="100px" - width="30%" + width={isMobile ? "55%" : "30%"} baseColor={"#2a3a53"} highlightColor={"#395073"} count={1} - className="max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" + className="max-w-full md:max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" containerClassName="flex justify-start" /> <Skeleton.default height="100px" - width="25%" + width={isMobile ? "88%" : "25%"} baseColor={"#2a3a53"} highlightColor={"#395073"} count={1} - className="max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" + className="max-w-full md:max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" containerClassName="flex justify-end" /> <Skeleton.default @@ -49,7 +50,7 @@ export default function LoadingChat() { baseColor={"#2a3a53"} highlightColor={"#395073"} count={1} - className="max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" + className="max-w-full md:max-w-[75%] p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6" containerClassName="flex justify-start" /> </div> diff --git a/frontend/src/components/WorkspaceChat/index.jsx b/frontend/src/components/WorkspaceChat/index.jsx index 3d8ec1aaf..7663d46c2 100644 --- a/frontend/src/components/WorkspaceChat/index.jsx +++ b/frontend/src/components/WorkspaceChat/index.jsx @@ -24,7 +24,7 @@ export default function WorkspaceChat({ loading, workspace }) { }, [workspace, loading]); if (loadingHistory) return <LoadingChat />; - if (!loading && !loadingHistory && !workspace) + if (!loading && !loadingHistory && !workspace) { return ( <> {loading === false && !workspace && ( @@ -57,6 +57,7 @@ export default function WorkspaceChat({ loading, workspace }) { <LoadingChat /> </> ); + } return <ChatContainer workspace={workspace} knownHistory={history} />; } diff --git a/frontend/src/pages/Main/index.jsx b/frontend/src/pages/Main/index.jsx index 94ae31300..29b5ee3e7 100644 --- a/frontend/src/pages/Main/index.jsx +++ b/frontend/src/pages/Main/index.jsx @@ -6,6 +6,7 @@ import ChatPlaceholder from "../../components/WorkspaceChat/LoadingChat"; import PasswordModal, { usePasswordModal, } from "../../components/Modals/Password"; +import { isMobile } from "react-device-detect"; export default function Main() { const { requiresAuth } = usePasswordModal(); @@ -14,7 +15,7 @@ export default function Main() { <> {requiresAuth && <PasswordModal />} <div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex"> - <SidebarPlaceholder /> + {!isMobile && <SidebarPlaceholder />} <ChatPlaceholder /> </div> </> @@ -23,7 +24,7 @@ export default function Main() { return ( <div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex"> - <Sidebar /> + {!isMobile && <Sidebar />} <DefaultChatContainer /> </div> ); diff --git a/frontend/src/pages/WorkspaceChat/index.jsx b/frontend/src/pages/WorkspaceChat/index.jsx index 45fe5d31f..1eec90412 100644 --- a/frontend/src/pages/WorkspaceChat/index.jsx +++ b/frontend/src/pages/WorkspaceChat/index.jsx @@ -8,6 +8,7 @@ import ChatPlaceholder from "../../components/WorkspaceChat/LoadingChat"; import PasswordModal, { usePasswordModal, } from "../../components/Modals/Password"; +import { isMobile } from "react-device-detect"; export default function WorkspaceChat() { const { requiresAuth } = usePasswordModal(); @@ -16,7 +17,7 @@ export default function WorkspaceChat() { <> {requiresAuth && <PasswordModal />} <div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex"> - <SidebarPlaceholder /> + {!isMobile && <SidebarPlaceholder />} <ChatPlaceholder /> </div> </> @@ -43,7 +44,7 @@ function ShowWorkspaceChat() { return ( <div className="w-screen h-screen overflow-hidden bg-orange-100 dark:bg-stone-700 flex"> - <Sidebar /> + {!isMobile && <Sidebar />} <WorkspaceChatContainer loading={loading} workspace={workspace} /> </div> );