diff --git a/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx b/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx index e1f164a6..72b3d73e 100644 --- a/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx +++ b/frontend/src/components/EmbeddingSelection/EmbedderItem/index.jsx @@ -9,8 +9,8 @@ export default function EmbedderItem({ return (
onClick(value)} - className={`w-full hover:bg-white/10 p-2 rounded-md hover:cursor-pointer ${ - checked && "bg-white/10" + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-white/10 ${ + checked ? "bg-white/10" : "" }`} >
-
{name}
-
{description}
+
{name}
+
{description}
diff --git a/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx b/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx index 0b384c1f..1cbfc689 100644 --- a/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx +++ b/frontend/src/components/EmbeddingSelection/NativeEmbeddingOptions/index.jsx @@ -1,6 +1,6 @@ export default function NativeEmbeddingOptions() { return ( -
+

There is no set up required when using AnythingLLM's native embedding engine. diff --git a/frontend/src/components/LLMSelection/LLMItem/index.jsx b/frontend/src/components/LLMSelection/LLMItem/index.jsx index 5e37738c..e6b643a4 100644 --- a/frontend/src/components/LLMSelection/LLMItem/index.jsx +++ b/frontend/src/components/LLMSelection/LLMItem/index.jsx @@ -9,8 +9,8 @@ export default function LLMItem({ return (

onClick(value)} - className={`w-full hover:bg-white/10 p-2 rounded-md hover:cursor-pointer ${ - checked && "bg-white/10" + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-white/10 ${ + checked ? "bg-white/10" : "" }`} >
-
{name}
-
{description}
+
{name}
+
{description}
diff --git a/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx b/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx index 942a3666..e78f571a 100644 --- a/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx +++ b/frontend/src/components/VectorDBSelection/LanceDBOptions/index.jsx @@ -1,6 +1,6 @@ export default function LanceDBOptions() { return ( -
+

There is no configuration needed for LanceDB.

diff --git a/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx b/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx index ec35537b..04a61b40 100644 --- a/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx +++ b/frontend/src/components/VectorDBSelection/VectorDBItem/index.jsx @@ -9,7 +9,7 @@ export default function VectorDBItem({ return (
onClick(value)} - className={`w-full hover:bg-white/10 p-2 rounded-md hover:cursor-pointer ${ + className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-white/10 ${ checked ? "bg-white/10" : "" }`} > @@ -28,8 +28,8 @@ export default function VectorDBItem({ className="w-10 h-10 rounded-md" />
-
{name}
-
{description}
+
{name}
+
{description}
diff --git a/frontend/src/index.css b/frontend/src/index.css index c94ca23b..9b69ac9c 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -633,3 +633,32 @@ does not extend the close button beyond the viewport. */ .upload-modal-arrow { margin-top: 25%; } + +/* Scrollbar container */ +.white-scrollbar { + overflow-y: scroll; + scrollbar-width: thin; + scrollbar-color: #ffffff #18181b; + margin-right: 8px; +} + +/* Webkit browsers (Chrome, Safari) */ +.white-scrollbar::-webkit-scrollbar { + width: 3px; + background-color: #18181b; +} + +.white-scrollbar::-webkit-scrollbar-track { + background-color: #18181b; + margin-right: 8px; +} + +.white-scrollbar::-webkit-scrollbar-thumb { + background-color: #ffffff; + border-radius: 4px; + border: 2px solid #18181b; +} + +.white-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: #cccccc; +} diff --git a/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx b/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx index 77204c3c..d2b26556 100644 --- a/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/EmbeddingPreference/index.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import Sidebar from "@/components/SettingsSidebar"; import { isMobile } from "react-device-detect"; import System from "@/models/system"; @@ -16,7 +16,7 @@ import LocalAiOptions from "@/components/EmbeddingSelection/LocalAiOptions"; import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions"; import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions"; import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem"; -import { MagnifyingGlass } from "@phosphor-icons/react"; +import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; import { useModal } from "@/hooks/useModal"; import ModalWrapper from "@/components/ModalWrapper"; @@ -29,6 +29,8 @@ export default function GeneralEmbeddingPreference() { const [searchQuery, setSearchQuery] = useState(""); const [filteredEmbedders, setFilteredEmbedders] = useState([]); const [selectedEmbedder, setSelectedEmbedder] = useState(null); + const [searchMenuOpen, setSearchMenuOpen] = useState(false); + const searchInputRef = useRef(null); const { isOpen, openModal, closeModal } = useModal(); const handleSubmit = async (e) => { @@ -65,10 +67,21 @@ export default function GeneralEmbeddingPreference() { }; const updateChoice = (selection) => { + setSearchQuery(""); setSelectedEmbedder(selection); + setSearchMenuOpen(false); setHasChanges(true); }; + const handleXButton = () => { + if (searchQuery.length > 0) { + setSearchQuery(""); + if (searchInputRef.current) searchInputRef.current.value = ""; + } else { + setSearchMenuOpen(!searchMenuOpen); + } + }; + useEffect(() => { async function fetchKeys() { const _settings = await System.keys(); @@ -126,6 +139,10 @@ export default function GeneralEmbeddingPreference() { setFilteredEmbedders(filtered); }, [searchQuery, selectedEmbedder]); + const selectedEmbedderObject = EMBEDDERS.find( + (embedder) => embedder.value === selectedEmbedder + ); + return (
@@ -174,55 +191,96 @@ export default function GeneralEmbeddingPreference() { format which AnythingLLM can use to process.

-
- Embedding Providers +
+ Embedding Provider
-
-
-
-
- - setSearchQuery(e.target.value)} - autoComplete="off" - onKeyDown={(e) => { - if (e.key === "Enter") e.preventDefault(); - }} - /> +
+ {searchMenuOpen && ( +
setSearchMenuOpen(false)} + /> + )} + {searchMenuOpen ? ( +
+
+
+ + setSearchQuery(e.target.value)} + ref={searchInputRef} + onKeyDown={(e) => { + if (e.key === "Enter") e.preventDefault(); + }} + /> + +
+
+ {filteredEmbedders.map((embedder) => ( + updateChoice(embedder.value)} + /> + ))} +
-
- {filteredEmbedders.map((embedder) => { - return ( - updateChoice(embedder.value)} - /> - ); - })} -
-
-
setHasChanges(true)} - className="mt-4 flex flex-col gap-y-1" - > - {selectedEmbedder && - EMBEDDERS.find( - (embedder) => embedder.value === selectedEmbedder - )?.options} -
+ ) : ( + + )} +
+
setHasChanges(true)} + className="mt-4 flex flex-col gap-y-1" + > + {selectedEmbedder && + EMBEDDERS.find( + (embedder) => embedder.value === selectedEmbedder + )?.options}
diff --git a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx index c0a2a600..b9525c92 100644 --- a/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/LLMPreference/index.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import Sidebar from "@/components/SettingsSidebar"; import { isMobile } from "react-device-detect"; import System from "@/models/system"; @@ -34,7 +34,7 @@ import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions"; import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; -import { MagnifyingGlass } from "@phosphor-icons/react"; +import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; export default function GeneralLLMPreference() { const [saving, setSaving] = useState(false); @@ -44,6 +44,8 @@ export default function GeneralLLMPreference() { const [searchQuery, setSearchQuery] = useState(""); const [filteredLLMs, setFilteredLLMs] = useState([]); const [selectedLLM, setSelectedLLM] = useState(null); + const [searchMenuOpen, setSearchMenuOpen] = useState(false); + const searchInputRef = useRef(null); const isHosted = window.location.hostname.includes("useanything.com"); const handleSubmit = async (e) => { @@ -66,10 +68,21 @@ export default function GeneralLLMPreference() { }; const updateLLMChoice = (selection) => { + setSearchQuery(""); setSelectedLLM(selection); + setSearchMenuOpen(false); setHasChanges(true); }; + const handleXButton = () => { + if (searchQuery.length > 0) { + setSearchQuery(""); + if (searchInputRef.current) searchInputRef.current.value = ""; + } else { + setSearchMenuOpen(!searchMenuOpen); + } + }; + useEffect(() => { async function fetchKeys() { const _settings = await System.keys(); @@ -193,6 +206,8 @@ export default function GeneralLLMPreference() { }, ]; + const selectedLLMObject = LLMS.find((llm) => llm.value === selectedLLM); + return (
@@ -234,54 +249,97 @@ export default function GeneralLLMPreference() { properly.

-
- LLM Providers +
+ LLM Provider
-
-
-
-
- - setSearchQuery(e.target.value)} - autoComplete="off" - onKeyDown={(e) => { - if (e.key === "Enter") e.preventDefault(); - }} - /> +
+ {searchMenuOpen && ( +
setSearchMenuOpen(false)} + /> + )} + {searchMenuOpen ? ( +
+
+
+ + setSearchQuery(e.target.value)} + ref={searchInputRef} + onKeyDown={(e) => { + if (e.key === "Enter") e.preventDefault(); + }} + /> + +
+
+ {filteredLLMs.map((llm) => { + if (llm.value === "native" && isHosted) return null; + return ( + updateLLMChoice(llm.value)} + /> + ); + })} +
-
- {filteredLLMs.map((llm) => { - if (llm.value === "native" && isHosted) return null; - return ( - updateLLMChoice(llm.value)} - /> - ); - })} -
-
-
setHasChanges(true)} - className="mt-4 flex flex-col gap-y-1" - > - {selectedLLM && - LLMS.find((llm) => llm.value === selectedLLM)?.options} -
+ ) : ( + + )} +
+
setHasChanges(true)} + className="mt-4 flex flex-col gap-y-1" + > + {selectedLLM && + LLMS.find((llm) => llm.value === selectedLLM)?.options}
diff --git a/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx b/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx index a56dc26e..c4d20ef4 100644 --- a/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx +++ b/frontend/src/pages/GeneralSettings/TranscriptionPreference/index.jsx @@ -1,16 +1,15 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import { isMobile } from "react-device-detect"; import Sidebar from "@/components/SettingsSidebar"; import System from "@/models/system"; import showToast from "@/utils/toast"; import PreLoader from "@/components/Preloader"; - import OpenAiLogo from "@/media/llmprovider/openai.png"; import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png"; import OpenAiWhisperOptions from "@/components/TranscriptionSelection/OpenAiOptions"; import NativeTranscriptionOptions from "@/components/TranscriptionSelection/NativeTranscriptionOptions"; import LLMItem from "@/components/LLMSelection/LLMItem"; -import { MagnifyingGlass } from "@phosphor-icons/react"; +import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; export default function TranscriptionModelPreference() { const [saving, setSaving] = useState(false); @@ -20,6 +19,8 @@ export default function TranscriptionModelPreference() { const [searchQuery, setSearchQuery] = useState(""); const [filteredProviders, setFilteredProviders] = useState([]); const [selectedProvider, setSelectedProvider] = useState(null); + const [searchMenuOpen, setSearchMenuOpen] = useState(false); + const searchInputRef = useRef(null); const handleSubmit = async (e) => { e.preventDefault(); @@ -41,10 +42,21 @@ export default function TranscriptionModelPreference() { }; const updateProviderChoice = (selection) => { + setSearchQuery(""); setSelectedProvider(selection); + setSearchMenuOpen(false); setHasChanges(true); }; + const handleXButton = () => { + if (searchQuery.length > 0) { + setSearchQuery(""); + if (searchInputRef.current) searchInputRef.current.value = ""; + } else { + setSearchMenuOpen(!searchMenuOpen); + } + }; + useEffect(() => { async function fetchKeys() { const _settings = await System.keys(); @@ -55,13 +67,6 @@ export default function TranscriptionModelPreference() { fetchKeys(); }, []); - useEffect(() => { - const filtered = PROVIDERS.filter((provider) => - provider.name.toLowerCase().includes(searchQuery.toLowerCase()) - ); - setFilteredProviders(filtered); - }, [searchQuery, selectedProvider]); - const PROVIDERS = [ { name: "OpenAI", @@ -80,6 +85,17 @@ export default function TranscriptionModelPreference() { }, ]; + useEffect(() => { + const filtered = PROVIDERS.filter((provider) => + provider.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + setFilteredProviders(filtered); + }, [searchQuery, selectedProvider]); + + const selectedProviderObject = PROVIDERS.find( + (provider) => provider.value === selectedProvider + ); + return (
@@ -121,55 +137,96 @@ export default function TranscriptionModelPreference() { transcribe.

-
- Transcription Providers +
+ Transcription Provider
-
-
-
-
- - setSearchQuery(e.target.value)} - autoComplete="off" - onKeyDown={(e) => { - if (e.key === "Enter") e.preventDefault(); - }} - /> +
+ {searchMenuOpen && ( +
setSearchMenuOpen(false)} + /> + )} + {searchMenuOpen ? ( +
+
+
+ + setSearchQuery(e.target.value)} + ref={searchInputRef} + onKeyDown={(e) => { + if (e.key === "Enter") e.preventDefault(); + }} + /> + +
+
+ {filteredProviders.map((provider) => ( + updateProviderChoice(provider.value)} + /> + ))} +
-
- {filteredProviders.map((provider) => { - return ( - updateProviderChoice(provider.value)} - /> - ); - })} -
-
-
setHasChanges(true)} - className="mt-4 flex flex-col gap-y-1" - > - {selectedProvider && - PROVIDERS.find( - (provider) => provider.value === selectedProvider - )?.options} -
+ ) : ( + + )} +
+
setHasChanges(true)} + className="mt-4 flex flex-col gap-y-1" + > + {selectedProvider && + PROVIDERS.find( + (provider) => provider.value === selectedProvider + )?.options}
diff --git a/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx b/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx index 0b2225cb..df0307d1 100644 --- a/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx +++ b/frontend/src/pages/GeneralSettings/VectorDatabase/index.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef } from "react"; import Sidebar from "@/components/SettingsSidebar"; import { isMobile } from "react-device-detect"; import System from "@/models/system"; @@ -13,7 +13,7 @@ import ZillizLogo from "@/media/vectordbs/zilliz.png"; import AstraDBLogo from "@/media/vectordbs/astraDB.png"; import PreLoader from "@/components/Preloader"; import ChangeWarningModal from "@/components/ChangeWarning"; -import { MagnifyingGlass } from "@phosphor-icons/react"; +import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react"; import LanceDBOptions from "@/components/VectorDBSelection/LanceDBOptions"; import ChromaDBOptions from "@/components/VectorDBSelection/ChromaDBOptions"; import PineconeDBOptions from "@/components/VectorDBSelection/PineconeDBOptions"; @@ -35,8 +35,55 @@ export default function GeneralVectorDatabase() { const [searchQuery, setSearchQuery] = useState(""); const [filteredVDBs, setFilteredVDBs] = useState([]); const [selectedVDB, setSelectedVDB] = useState(null); + const [searchMenuOpen, setSearchMenuOpen] = useState(false); + const searchInputRef = useRef(null); const { isOpen, openModal, closeModal } = useModal(); + const handleSubmit = async (e) => { + e.preventDefault(); + if (selectedVDB !== settings?.VectorDB && hasChanges && hasEmbeddings) { + openModal(); + } else { + await handleSaveSettings(); + } + }; + + const handleSaveSettings = async () => { + setSaving(true); + const form = document.getElementById("vectordb-form"); + const settingsData = {}; + const formData = new FormData(form); + settingsData.VectorDB = selectedVDB; + for (var [key, value] of formData.entries()) settingsData[key] = value; + + const { error } = await System.updateSystem(settingsData); + if (error) { + showToast(`Failed to save vector database settings: ${error}`, "error"); + setHasChanges(true); + } else { + showToast("Vector database preferences saved successfully.", "success"); + setHasChanges(false); + } + setSaving(false); + closeModal(); + }; + + const updateVectorChoice = (selection) => { + setSearchQuery(""); + setSelectedVDB(selection); + setSearchMenuOpen(false); + setHasChanges(true); + }; + + const handleXButton = () => { + if (searchQuery.length > 0) { + setSearchQuery(""); + if (searchInputRef.current) searchInputRef.current.value = ""; + } else { + setSearchMenuOpen(!searchMenuOpen); + } + }; + useEffect(() => { async function fetchKeys() { const _settings = await System.keys(); @@ -48,6 +95,13 @@ export default function GeneralVectorDatabase() { fetchKeys(); }, []); + useEffect(() => { + const filtered = VECTOR_DBS.filter((vdb) => + vdb.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + setFilteredVDBs(filtered); + }, [searchQuery, selectedVDB]); + const VECTOR_DBS = [ { name: "LanceDB", @@ -111,46 +165,7 @@ export default function GeneralVectorDatabase() { }, ]; - const updateVectorChoice = (selection) => { - setHasChanges(true); - setSelectedVDB(selection); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - if (selectedVDB !== settings?.VectorDB && hasChanges && hasEmbeddings) { - openModal(); - } else { - await handleSaveSettings(); - } - }; - - const handleSaveSettings = async () => { - setSaving(true); - const form = document.getElementById("vectordb-form"); - const settingsData = {}; - const formData = new FormData(form); - settingsData.VectorDB = selectedVDB; - for (var [key, value] of formData.entries()) settingsData[key] = value; - - const { error } = await System.updateSystem(settingsData); - if (error) { - showToast(`Failed to save vector database settings: ${error}`, "error"); - setHasChanges(true); - } else { - showToast("Vector database preferences saved successfully.", "success"); - setHasChanges(false); - } - setSaving(false); - closeModal(); - }; - - useEffect(() => { - const filtered = VECTOR_DBS.filter((vdb) => - vdb.name.toLowerCase().includes(searchQuery.toLowerCase()) - ); - setFilteredVDBs(filtered); - }, [searchQuery, selectedVDB]); + const selectedVDBObject = VECTOR_DBS.find((vdb) => vdb.value === selectedVDB); return (
@@ -176,7 +191,7 @@ export default function GeneralVectorDatabase() { >
-
+

Vector Database

@@ -196,55 +211,94 @@ export default function GeneralVectorDatabase() { are current and correct.

-
- Vector Database Providers +
+ Vector Database Provider
-
-
-
-
- - { - e.preventDefault(); - setSearchQuery(e.target.value); - }} - autoComplete="off" - onKeyDown={(e) => { - if (e.key === "Enter") e.preventDefault(); - }} - /> +
+ {searchMenuOpen && ( +
setSearchMenuOpen(false)} + /> + )} + {searchMenuOpen ? ( +
+
+
+ + setSearchQuery(e.target.value)} + ref={searchInputRef} + onKeyDown={(e) => { + if (e.key === "Enter") e.preventDefault(); + }} + /> + +
+
+ {filteredVDBs.map((vdb) => ( + updateVectorChoice(vdb.value)} + /> + ))} +
-
- {filteredVDBs.map((vdb) => ( - updateVectorChoice(vdb.value)} + ) : ( +
-
setHasChanges(true)} - className="mt-4 flex flex-col gap-y-1" - > - {selectedVDB && - VECTOR_DBS.find((vdb) => vdb.value === selectedVDB) - ?.options} -
+
+
+ {selectedVDBObject.name} +
+
+ {selectedVDBObject.description} +
+
+
+ + + )} +
+
setHasChanges(true)} + className="mt-4 flex flex-col gap-y-1" + > + {selectedVDB && + VECTOR_DBS.find((vdb) => vdb.value === selectedVDB)?.options}