mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-14 02:20:12 +01:00
[FEAT] Implement model provider UI updates (#996)
* implement new LLM preference UI * implement new vector db preferences UI to match LLM preferences * implement new embedding preferences UI to match LLM preferences * normalize placeholder text for search input * implement new transcription preferences UI to match LLM preferences * remove uneeded css * implement new UI for llm preference onboarding * implement new UI for embedder preference onboarding * implement new UI for vector db preference onboarding * fix placeholder text * unset onboarding * move autocomplete field --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
b643639d0f
commit
1cd9e1336b
@ -9,8 +9,8 @@ export default function EmbedderItem({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => onClick(value)}
|
onClick={() => 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"
|
checked ? "bg-white/10" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@ -28,8 +28,8 @@ export default function EmbedderItem({
|
|||||||
className="w-10 h-10 rounded-md"
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-semibold">{name}</div>
|
<div className="text-sm font-semibold text-white">{name}</div>
|
||||||
<div className="mt-1 text-xs text-white/60">{description}</div>
|
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export default function NativeEmbeddingOptions() {
|
export default function NativeEmbeddingOptions() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-20 items-center justify-center flex">
|
<div className="w-full h-10 items-center flex">
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-sm font-base text-white text-opacity-60">
|
||||||
There is no set up required when using AnythingLLM's native embedding
|
There is no set up required when using AnythingLLM's native embedding
|
||||||
engine.
|
engine.
|
||||||
|
@ -9,8 +9,8 @@ export default function LLMItem({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => onClick(value)}
|
onClick={() => 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"
|
checked ? "bg-white/10" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@ -28,8 +28,8 @@ export default function LLMItem({
|
|||||||
className="w-10 h-10 rounded-md"
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-semibold">{name}</div>
|
<div className="text-sm font-semibold text-white">{name}</div>
|
||||||
<div className="mt-1 text-xs text-white/60">{description}</div>
|
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export default function LanceDBOptions() {
|
export default function LanceDBOptions() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-10 items-center justify-center flex">
|
<div className="w-full h-10 items-center flex">
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-sm font-base text-white text-opacity-60">
|
||||||
There is no configuration needed for LanceDB.
|
There is no configuration needed for LanceDB.
|
||||||
</p>
|
</p>
|
||||||
|
@ -9,7 +9,7 @@ export default function VectorDBItem({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => onClick(value)}
|
onClick={() => 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" : ""
|
checked ? "bg-white/10" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -28,8 +28,8 @@ export default function VectorDBItem({
|
|||||||
className="w-10 h-10 rounded-md"
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="text-sm font-semibold">{name}</div>
|
<div className="text-sm font-semibold text-white">{name}</div>
|
||||||
<div className="mt-1 text-xs text-white/60">{description}</div>
|
<div className="mt-1 text-xs text-[#D2D5DB]">{description}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -633,3 +633,32 @@ does not extend the close button beyond the viewport. */
|
|||||||
.upload-modal-arrow {
|
.upload-modal-arrow {
|
||||||
margin-top: 25%;
|
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;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import Sidebar from "@/components/SettingsSidebar";
|
import Sidebar from "@/components/SettingsSidebar";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
@ -16,7 +16,7 @@ import LocalAiOptions from "@/components/EmbeddingSelection/LocalAiOptions";
|
|||||||
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
|
import NativeEmbeddingOptions from "@/components/EmbeddingSelection/NativeEmbeddingOptions";
|
||||||
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
|
import OllamaEmbeddingOptions from "@/components/EmbeddingSelection/OllamaOptions";
|
||||||
import EmbedderItem from "@/components/EmbeddingSelection/EmbedderItem";
|
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 { useModal } from "@/hooks/useModal";
|
||||||
import ModalWrapper from "@/components/ModalWrapper";
|
import ModalWrapper from "@/components/ModalWrapper";
|
||||||
|
|
||||||
@ -29,6 +29,8 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [filteredEmbedders, setFilteredEmbedders] = useState([]);
|
const [filteredEmbedders, setFilteredEmbedders] = useState([]);
|
||||||
const [selectedEmbedder, setSelectedEmbedder] = useState(null);
|
const [selectedEmbedder, setSelectedEmbedder] = useState(null);
|
||||||
|
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
||||||
|
const searchInputRef = useRef(null);
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
@ -65,10 +67,21 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateChoice = (selection) => {
|
const updateChoice = (selection) => {
|
||||||
|
setSearchQuery("");
|
||||||
setSelectedEmbedder(selection);
|
setSelectedEmbedder(selection);
|
||||||
|
setSearchMenuOpen(false);
|
||||||
setHasChanges(true);
|
setHasChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleXButton = () => {
|
||||||
|
if (searchQuery.length > 0) {
|
||||||
|
setSearchQuery("");
|
||||||
|
if (searchInputRef.current) searchInputRef.current.value = "";
|
||||||
|
} else {
|
||||||
|
setSearchMenuOpen(!searchMenuOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchKeys() {
|
async function fetchKeys() {
|
||||||
const _settings = await System.keys();
|
const _settings = await System.keys();
|
||||||
@ -126,6 +139,10 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
setFilteredEmbedders(filtered);
|
setFilteredEmbedders(filtered);
|
||||||
}, [searchQuery, selectedEmbedder]);
|
}, [searchQuery, selectedEmbedder]);
|
||||||
|
|
||||||
|
const selectedEmbedderObject = EMBEDDERS.find(
|
||||||
|
(embedder) => embedder.value === selectedEmbedder
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -174,55 +191,96 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
format which AnythingLLM can use to process.
|
format which AnythingLLM can use to process.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium text-white mt-6 mb-4">
|
<div className="text-base font-bold text-white mt-6 mb-4">
|
||||||
Embedding Providers
|
Embedding Provider
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="relative">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
{searchMenuOpen && (
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div
|
||||||
<div className="w-full flex items-center sticky top-0">
|
className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-70 backdrop-blur-sm z-10"
|
||||||
<MagnifyingGlass
|
onClick={() => setSearchMenuOpen(false)}
|
||||||
size={16}
|
/>
|
||||||
weight="bold"
|
)}
|
||||||
className="absolute left-4 z-30 text-white"
|
{searchMenuOpen ? (
|
||||||
/>
|
<div className="absolute top-0 left-0 w-full max-w-[640px] max-h-[310px] overflow-auto white-scrollbar min-h-[64px] bg-[#18181B] rounded-lg flex flex-col justify-between cursor-pointer border-2 border-[#46C8FF] z-20">
|
||||||
<input
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
type="text"
|
<div className="flex items-center sticky top-0 border-b border-[#9CA3AF] mx-4 bg-[#18181B]">
|
||||||
placeholder="Search Embedding providers"
|
<MagnifyingGlass
|
||||||
className="bg-zinc-600 z-20 pl-10 h-[38px] rounded-full w-full px-4 py-1 text-sm border-2 border-slate-300/40 outline-none focus:border-white text-white"
|
size={20}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
weight="bold"
|
||||||
autoComplete="off"
|
className="absolute left-4 z-30 text-white -ml-4 my-2"
|
||||||
onKeyDown={(e) => {
|
/>
|
||||||
if (e.key === "Enter") e.preventDefault();
|
<input
|
||||||
}}
|
type="text"
|
||||||
/>
|
name="embedder-search"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Search all embedding providers"
|
||||||
|
className="-ml-4 my-2 bg-transparent z-20 pl-12 h-[38px] w-full px-4 py-1 text-sm outline-none focus:border-white text-white placeholder:text-white placeholder:font-medium"
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
ref={searchInputRef}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") e.preventDefault();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<X
|
||||||
|
size={20}
|
||||||
|
weight="bold"
|
||||||
|
className="cursor-pointer text-white hover:text-[#9CA3AF]"
|
||||||
|
onClick={handleXButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 pl-4 pr-2 flex flex-col gap-y-1 overflow-y-auto white-scrollbar pb-4">
|
||||||
|
{filteredEmbedders.map((embedder) => (
|
||||||
|
<EmbedderItem
|
||||||
|
key={embedder.name}
|
||||||
|
name={embedder.name}
|
||||||
|
value={embedder.value}
|
||||||
|
image={embedder.logo}
|
||||||
|
description={embedder.description}
|
||||||
|
checked={selectedEmbedder === embedder.value}
|
||||||
|
onClick={() => updateChoice(embedder.value)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 pt-[70px] flex flex-col gap-y-1 max-h-[390px] overflow-y-auto no-scroll pb-4">
|
) : (
|
||||||
{filteredEmbedders.map((embedder) => {
|
<button
|
||||||
return (
|
className="w-full max-w-[640px] h-[64px] bg-[#18181B] rounded-lg flex items-center p-[14px] justify-between cursor-pointer border-2 border-transparent hover:border-[#46C8FF] transition-all duration-300"
|
||||||
<EmbedderItem
|
type="button"
|
||||||
key={embedder.name}
|
onClick={() => setSearchMenuOpen(true)}
|
||||||
name={embedder.name}
|
>
|
||||||
value={embedder.value}
|
<div className="flex gap-x-4 items-center">
|
||||||
image={embedder.logo}
|
<img
|
||||||
description={embedder.description}
|
src={selectedEmbedderObject.logo}
|
||||||
checked={selectedEmbedder === embedder.value}
|
alt={`${selectedEmbedderObject.name} logo`}
|
||||||
onClick={() => updateChoice(embedder.value)}
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
);
|
<div className="flex flex-col text-left">
|
||||||
})}
|
<div className="text-sm font-semibold text-white">
|
||||||
</div>
|
{selectedEmbedderObject.name}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="mt-1 text-xs text-[#D2D5DB]">
|
||||||
onChange={() => setHasChanges(true)}
|
{selectedEmbedderObject.description}
|
||||||
className="mt-4 flex flex-col gap-y-1"
|
</div>
|
||||||
>
|
</div>
|
||||||
{selectedEmbedder &&
|
</div>
|
||||||
EMBEDDERS.find(
|
<CaretUpDown
|
||||||
(embedder) => embedder.value === selectedEmbedder
|
size={24}
|
||||||
)?.options}
|
weight="bold"
|
||||||
</div>
|
className="text-white"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onChange={() => setHasChanges(true)}
|
||||||
|
className="mt-4 flex flex-col gap-y-1"
|
||||||
|
>
|
||||||
|
{selectedEmbedder &&
|
||||||
|
EMBEDDERS.find(
|
||||||
|
(embedder) => embedder.value === selectedEmbedder
|
||||||
|
)?.options}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import Sidebar from "@/components/SettingsSidebar";
|
import Sidebar from "@/components/SettingsSidebar";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
@ -34,7 +34,7 @@ import OpenRouterOptions from "@/components/LLMSelection/OpenRouterOptions";
|
|||||||
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
import GroqAiOptions from "@/components/LLMSelection/GroqAiOptions";
|
||||||
|
|
||||||
import LLMItem from "@/components/LLMSelection/LLMItem";
|
import LLMItem from "@/components/LLMSelection/LLMItem";
|
||||||
import { MagnifyingGlass } from "@phosphor-icons/react";
|
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
||||||
|
|
||||||
export default function GeneralLLMPreference() {
|
export default function GeneralLLMPreference() {
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
@ -44,6 +44,8 @@ export default function GeneralLLMPreference() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [filteredLLMs, setFilteredLLMs] = useState([]);
|
const [filteredLLMs, setFilteredLLMs] = useState([]);
|
||||||
const [selectedLLM, setSelectedLLM] = useState(null);
|
const [selectedLLM, setSelectedLLM] = useState(null);
|
||||||
|
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
||||||
|
const searchInputRef = useRef(null);
|
||||||
const isHosted = window.location.hostname.includes("useanything.com");
|
const isHosted = window.location.hostname.includes("useanything.com");
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
@ -66,10 +68,21 @@ export default function GeneralLLMPreference() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateLLMChoice = (selection) => {
|
const updateLLMChoice = (selection) => {
|
||||||
|
setSearchQuery("");
|
||||||
setSelectedLLM(selection);
|
setSelectedLLM(selection);
|
||||||
|
setSearchMenuOpen(false);
|
||||||
setHasChanges(true);
|
setHasChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleXButton = () => {
|
||||||
|
if (searchQuery.length > 0) {
|
||||||
|
setSearchQuery("");
|
||||||
|
if (searchInputRef.current) searchInputRef.current.value = "";
|
||||||
|
} else {
|
||||||
|
setSearchMenuOpen(!searchMenuOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchKeys() {
|
async function fetchKeys() {
|
||||||
const _settings = await System.keys();
|
const _settings = await System.keys();
|
||||||
@ -193,6 +206,8 @@ export default function GeneralLLMPreference() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const selectedLLMObject = LLMS.find((llm) => llm.value === selectedLLM);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -234,54 +249,97 @@ export default function GeneralLLMPreference() {
|
|||||||
properly.
|
properly.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium text-white mt-6 mb-4">
|
<div className="text-base font-bold text-white mt-6 mb-4">
|
||||||
LLM Providers
|
LLM Provider
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="relative">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
{searchMenuOpen && (
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div
|
||||||
<div className="w-full flex items-center sticky top-0">
|
className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-70 backdrop-blur-sm z-10"
|
||||||
<MagnifyingGlass
|
onClick={() => setSearchMenuOpen(false)}
|
||||||
size={16}
|
/>
|
||||||
weight="bold"
|
)}
|
||||||
className="absolute left-4 z-30 text-white"
|
{searchMenuOpen ? (
|
||||||
/>
|
<div className="absolute top-0 left-0 w-full max-w-[640px] max-h-[310px] overflow-auto white-scrollbar min-h-[64px] bg-[#18181B] rounded-lg flex flex-col justify-between cursor-pointer border-2 border-[#46C8FF] z-20">
|
||||||
<input
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
type="text"
|
<div className="flex items-center sticky top-0 border-b border-[#9CA3AF] mx-4 bg-[#18181B]">
|
||||||
placeholder="Search LLM providers"
|
<MagnifyingGlass
|
||||||
className="bg-zinc-600 z-20 pl-10 h-[38px] rounded-full w-full px-4 py-1 text-sm border-2 border-slate-300/40 outline-none focus:border-white text-white"
|
size={20}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
weight="bold"
|
||||||
autoComplete="off"
|
className="absolute left-4 z-30 text-white -ml-4 my-2"
|
||||||
onKeyDown={(e) => {
|
/>
|
||||||
if (e.key === "Enter") e.preventDefault();
|
<input
|
||||||
}}
|
type="text"
|
||||||
/>
|
name="llm-search"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Search all LLM providers"
|
||||||
|
className="-ml-4 my-2 bg-transparent z-20 pl-12 h-[38px] w-full px-4 py-1 text-sm outline-none focus:border-white text-white placeholder:text-white placeholder:font-medium"
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
ref={searchInputRef}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") e.preventDefault();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<X
|
||||||
|
size={20}
|
||||||
|
weight="bold"
|
||||||
|
className="cursor-pointer text-white hover:text-[#9CA3AF]"
|
||||||
|
onClick={handleXButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 pl-4 pr-2 flex flex-col gap-y-1 overflow-y-auto white-scrollbar pb-4">
|
||||||
|
{filteredLLMs.map((llm) => {
|
||||||
|
if (llm.value === "native" && isHosted) return null;
|
||||||
|
return (
|
||||||
|
<LLMItem
|
||||||
|
key={llm.name}
|
||||||
|
name={llm.name}
|
||||||
|
value={llm.value}
|
||||||
|
image={llm.logo}
|
||||||
|
description={llm.description}
|
||||||
|
checked={selectedLLM === llm.value}
|
||||||
|
onClick={() => updateLLMChoice(llm.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 pt-[70px] flex flex-col gap-y-1 max-h-[390px] overflow-y-auto no-scroll pb-4">
|
) : (
|
||||||
{filteredLLMs.map((llm) => {
|
<button
|
||||||
if (llm.value === "native" && isHosted) return null;
|
className="w-full max-w-[640px] h-[64px] bg-[#18181B] rounded-lg flex items-center p-[14px] justify-between cursor-pointer border-2 border-transparent hover:border-[#46C8FF] transition-all duration-300"
|
||||||
return (
|
type="button"
|
||||||
<LLMItem
|
onClick={() => setSearchMenuOpen(true)}
|
||||||
key={llm.name}
|
>
|
||||||
name={llm.name}
|
<div className="flex gap-x-4 items-center">
|
||||||
value={llm.value}
|
<img
|
||||||
image={llm.logo}
|
src={selectedLLMObject.logo}
|
||||||
description={llm.description}
|
alt={`${selectedLLMObject.name} logo`}
|
||||||
checked={selectedLLM === llm.value}
|
className="w-10 h-10 rounded-md"
|
||||||
onClick={() => updateLLMChoice(llm.value)}
|
/>
|
||||||
/>
|
<div className="flex flex-col text-left">
|
||||||
);
|
<div className="text-sm font-semibold text-white">
|
||||||
})}
|
{selectedLLMObject.name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-1 text-xs text-[#D2D5DB]">
|
||||||
<div
|
{selectedLLMObject.description}
|
||||||
onChange={() => setHasChanges(true)}
|
</div>
|
||||||
className="mt-4 flex flex-col gap-y-1"
|
</div>
|
||||||
>
|
</div>
|
||||||
{selectedLLM &&
|
<CaretUpDown
|
||||||
LLMS.find((llm) => llm.value === selectedLLM)?.options}
|
size={24}
|
||||||
</div>
|
weight="bold"
|
||||||
|
className="text-white"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onChange={() => setHasChanges(true)}
|
||||||
|
className="mt-4 flex flex-col gap-y-1"
|
||||||
|
>
|
||||||
|
{selectedLLM &&
|
||||||
|
LLMS.find((llm) => llm.value === selectedLLM)?.options}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState, useRef } from "react";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import Sidebar from "@/components/SettingsSidebar";
|
import Sidebar from "@/components/SettingsSidebar";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import PreLoader from "@/components/Preloader";
|
import PreLoader from "@/components/Preloader";
|
||||||
|
|
||||||
import OpenAiLogo from "@/media/llmprovider/openai.png";
|
import OpenAiLogo from "@/media/llmprovider/openai.png";
|
||||||
import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
|
import AnythingLLMIcon from "@/media/logo/anything-llm-icon.png";
|
||||||
import OpenAiWhisperOptions from "@/components/TranscriptionSelection/OpenAiOptions";
|
import OpenAiWhisperOptions from "@/components/TranscriptionSelection/OpenAiOptions";
|
||||||
import NativeTranscriptionOptions from "@/components/TranscriptionSelection/NativeTranscriptionOptions";
|
import NativeTranscriptionOptions from "@/components/TranscriptionSelection/NativeTranscriptionOptions";
|
||||||
import LLMItem from "@/components/LLMSelection/LLMItem";
|
import LLMItem from "@/components/LLMSelection/LLMItem";
|
||||||
import { MagnifyingGlass } from "@phosphor-icons/react";
|
import { CaretUpDown, MagnifyingGlass, X } from "@phosphor-icons/react";
|
||||||
|
|
||||||
export default function TranscriptionModelPreference() {
|
export default function TranscriptionModelPreference() {
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
@ -20,6 +19,8 @@ export default function TranscriptionModelPreference() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [filteredProviders, setFilteredProviders] = useState([]);
|
const [filteredProviders, setFilteredProviders] = useState([]);
|
||||||
const [selectedProvider, setSelectedProvider] = useState(null);
|
const [selectedProvider, setSelectedProvider] = useState(null);
|
||||||
|
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
||||||
|
const searchInputRef = useRef(null);
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -41,10 +42,21 @@ export default function TranscriptionModelPreference() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updateProviderChoice = (selection) => {
|
const updateProviderChoice = (selection) => {
|
||||||
|
setSearchQuery("");
|
||||||
setSelectedProvider(selection);
|
setSelectedProvider(selection);
|
||||||
|
setSearchMenuOpen(false);
|
||||||
setHasChanges(true);
|
setHasChanges(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleXButton = () => {
|
||||||
|
if (searchQuery.length > 0) {
|
||||||
|
setSearchQuery("");
|
||||||
|
if (searchInputRef.current) searchInputRef.current.value = "";
|
||||||
|
} else {
|
||||||
|
setSearchMenuOpen(!searchMenuOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchKeys() {
|
async function fetchKeys() {
|
||||||
const _settings = await System.keys();
|
const _settings = await System.keys();
|
||||||
@ -55,13 +67,6 @@ export default function TranscriptionModelPreference() {
|
|||||||
fetchKeys();
|
fetchKeys();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const filtered = PROVIDERS.filter((provider) =>
|
|
||||||
provider.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
||||||
);
|
|
||||||
setFilteredProviders(filtered);
|
|
||||||
}, [searchQuery, selectedProvider]);
|
|
||||||
|
|
||||||
const PROVIDERS = [
|
const PROVIDERS = [
|
||||||
{
|
{
|
||||||
name: "OpenAI",
|
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 (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
@ -121,55 +137,96 @@ export default function TranscriptionModelPreference() {
|
|||||||
transcribe.
|
transcribe.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium text-white mt-6 mb-4">
|
<div className="text-base font-bold text-white mt-6 mb-4">
|
||||||
Transcription Providers
|
Transcription Provider
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="relative">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
{searchMenuOpen && (
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div
|
||||||
<div className="w-full flex items-center sticky top-0">
|
className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-70 backdrop-blur-sm z-10"
|
||||||
<MagnifyingGlass
|
onClick={() => setSearchMenuOpen(false)}
|
||||||
size={16}
|
/>
|
||||||
weight="bold"
|
)}
|
||||||
className="absolute left-4 z-30 text-white"
|
{searchMenuOpen ? (
|
||||||
/>
|
<div className="absolute top-0 left-0 w-full max-w-[640px] max-h-[310px] overflow-auto white-scrollbar min-h-[64px] bg-[#18181B] rounded-lg flex flex-col justify-between cursor-pointer border-2 border-[#46C8FF] z-20">
|
||||||
<input
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
type="text"
|
<div className="flex items-center sticky top-0 border-b border-[#9CA3AF] mx-4 bg-[#18181B]">
|
||||||
placeholder="Search audio transcription providers"
|
<MagnifyingGlass
|
||||||
className="bg-zinc-600 z-20 pl-10 h-[38px] rounded-full w-full px-4 py-1 text-sm border-2 border-slate-300/40 outline-none focus:border-white text-white"
|
size={20}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
weight="bold"
|
||||||
autoComplete="off"
|
className="absolute left-4 z-30 text-white -ml-4 my-2"
|
||||||
onKeyDown={(e) => {
|
/>
|
||||||
if (e.key === "Enter") e.preventDefault();
|
<input
|
||||||
}}
|
type="text"
|
||||||
/>
|
name="provider-search"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Search audio transcription providers"
|
||||||
|
className="-ml-4 my-2 bg-transparent z-20 pl-12 h-[38px] w-full px-4 py-1 text-sm outline-none focus:border-white text-white placeholder:text-white placeholder:font-medium"
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
ref={searchInputRef}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") e.preventDefault();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<X
|
||||||
|
size={20}
|
||||||
|
weight="bold"
|
||||||
|
className="cursor-pointer text-white hover:text-[#9CA3AF]"
|
||||||
|
onClick={handleXButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 pl-4 pr-2 flex flex-col gap-y-1 overflow-y-auto white-scrollbar pb-4">
|
||||||
|
{filteredProviders.map((provider) => (
|
||||||
|
<LLMItem
|
||||||
|
key={provider.name}
|
||||||
|
name={provider.name}
|
||||||
|
value={provider.value}
|
||||||
|
image={provider.logo}
|
||||||
|
description={provider.description}
|
||||||
|
checked={selectedProvider === provider.value}
|
||||||
|
onClick={() => updateProviderChoice(provider.value)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 pt-[70px] flex flex-col gap-y-1 max-h-[390px] overflow-y-auto no-scroll pb-4">
|
) : (
|
||||||
{filteredProviders.map((provider) => {
|
<button
|
||||||
return (
|
className="w-full max-w-[640px] h-[64px] bg-[#18181B] rounded-lg flex items-center p-[14px] justify-between cursor-pointer border-2 border-transparent hover:border-[#46C8FF] transition-all duration-300"
|
||||||
<LLMItem
|
type="button"
|
||||||
key={provider.name}
|
onClick={() => setSearchMenuOpen(true)}
|
||||||
name={provider.name}
|
>
|
||||||
value={provider.value}
|
<div className="flex gap-x-4 items-center">
|
||||||
image={provider.logo}
|
<img
|
||||||
description={provider.description}
|
src={selectedProviderObject.logo}
|
||||||
checked={selectedProvider === provider.value}
|
alt={`${selectedProviderObject.name} logo`}
|
||||||
onClick={() => updateProviderChoice(provider.value)}
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
);
|
<div className="flex flex-col text-left">
|
||||||
})}
|
<div className="text-sm font-semibold text-white">
|
||||||
</div>
|
{selectedProviderObject.name}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="mt-1 text-xs text-[#D2D5DB]">
|
||||||
onChange={() => setHasChanges(true)}
|
{selectedProviderObject.description}
|
||||||
className="mt-4 flex flex-col gap-y-1"
|
</div>
|
||||||
>
|
</div>
|
||||||
{selectedProvider &&
|
</div>
|
||||||
PROVIDERS.find(
|
<CaretUpDown
|
||||||
(provider) => provider.value === selectedProvider
|
size={24}
|
||||||
)?.options}
|
weight="bold"
|
||||||
</div>
|
className="text-white"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onChange={() => setHasChanges(true)}
|
||||||
|
className="mt-4 flex flex-col gap-y-1"
|
||||||
|
>
|
||||||
|
{selectedProvider &&
|
||||||
|
PROVIDERS.find(
|
||||||
|
(provider) => provider.value === selectedProvider
|
||||||
|
)?.options}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import Sidebar from "@/components/SettingsSidebar";
|
import Sidebar from "@/components/SettingsSidebar";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
@ -13,7 +13,7 @@ import ZillizLogo from "@/media/vectordbs/zilliz.png";
|
|||||||
import AstraDBLogo from "@/media/vectordbs/astraDB.png";
|
import AstraDBLogo from "@/media/vectordbs/astraDB.png";
|
||||||
import PreLoader from "@/components/Preloader";
|
import PreLoader from "@/components/Preloader";
|
||||||
import ChangeWarningModal from "@/components/ChangeWarning";
|
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 LanceDBOptions from "@/components/VectorDBSelection/LanceDBOptions";
|
||||||
import ChromaDBOptions from "@/components/VectorDBSelection/ChromaDBOptions";
|
import ChromaDBOptions from "@/components/VectorDBSelection/ChromaDBOptions";
|
||||||
import PineconeDBOptions from "@/components/VectorDBSelection/PineconeDBOptions";
|
import PineconeDBOptions from "@/components/VectorDBSelection/PineconeDBOptions";
|
||||||
@ -35,8 +35,55 @@ export default function GeneralVectorDatabase() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [filteredVDBs, setFilteredVDBs] = useState([]);
|
const [filteredVDBs, setFilteredVDBs] = useState([]);
|
||||||
const [selectedVDB, setSelectedVDB] = useState(null);
|
const [selectedVDB, setSelectedVDB] = useState(null);
|
||||||
|
const [searchMenuOpen, setSearchMenuOpen] = useState(false);
|
||||||
|
const searchInputRef = useRef(null);
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
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(() => {
|
useEffect(() => {
|
||||||
async function fetchKeys() {
|
async function fetchKeys() {
|
||||||
const _settings = await System.keys();
|
const _settings = await System.keys();
|
||||||
@ -48,6 +95,13 @@ export default function GeneralVectorDatabase() {
|
|||||||
fetchKeys();
|
fetchKeys();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const filtered = VECTOR_DBS.filter((vdb) =>
|
||||||
|
vdb.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
setFilteredVDBs(filtered);
|
||||||
|
}, [searchQuery, selectedVDB]);
|
||||||
|
|
||||||
const VECTOR_DBS = [
|
const VECTOR_DBS = [
|
||||||
{
|
{
|
||||||
name: "LanceDB",
|
name: "LanceDB",
|
||||||
@ -111,46 +165,7 @@ export default function GeneralVectorDatabase() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const updateVectorChoice = (selection) => {
|
const selectedVDBObject = VECTOR_DBS.find((vdb) => vdb.value === selectedVDB);
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
@ -176,7 +191,7 @@ export default function GeneralVectorDatabase() {
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="flex items-center gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-lg leading-6 font-bold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Vector Database
|
Vector Database
|
||||||
</p>
|
</p>
|
||||||
@ -196,55 +211,94 @@ export default function GeneralVectorDatabase() {
|
|||||||
are current and correct.
|
are current and correct.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium text-white mt-6 mb-4">
|
<div className="text-base font-bold text-white mt-6 mb-4">
|
||||||
Vector Database Providers
|
Vector Database Provider
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="relative">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
{searchMenuOpen && (
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div
|
||||||
<div className="w-full flex items-center sticky top-0">
|
className="fixed top-0 left-0 w-full h-full bg-black bg-opacity-70 backdrop-blur-sm z-10"
|
||||||
<MagnifyingGlass
|
onClick={() => setSearchMenuOpen(false)}
|
||||||
size={16}
|
/>
|
||||||
weight="bold"
|
)}
|
||||||
className="absolute left-4 z-30 text-white"
|
{searchMenuOpen ? (
|
||||||
/>
|
<div className="absolute top-0 left-0 w-full max-w-[640px] max-h-[310px] overflow-auto white-scrollbar min-h-[64px] bg-[#18181B] rounded-lg flex flex-col justify-between cursor-pointer border-2 border-[#46C8FF] z-20">
|
||||||
<input
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
type="text"
|
<div className="flex items-center sticky top-0 border-b border-[#9CA3AF] mx-4 bg-[#18181B]">
|
||||||
placeholder="Search vector databases"
|
<MagnifyingGlass
|
||||||
className="bg-zinc-600 z-20 pl-10 h-[38px] rounded-full w-full px-4 py-1 text-sm border-2 border-slate-300/40 outline-none focus:border-white text-white"
|
size={20}
|
||||||
onChange={(e) => {
|
weight="bold"
|
||||||
e.preventDefault();
|
className="absolute left-4 z-30 text-white -ml-4 my-2"
|
||||||
setSearchQuery(e.target.value);
|
/>
|
||||||
}}
|
<input
|
||||||
autoComplete="off"
|
type="text"
|
||||||
onKeyDown={(e) => {
|
name="vdb-search"
|
||||||
if (e.key === "Enter") e.preventDefault();
|
autoComplete="off"
|
||||||
}}
|
placeholder="Search all vector database providers"
|
||||||
/>
|
className="-ml-4 my-2 bg-transparent z-20 pl-12 h-[38px] w-full px-4 py-1 text-sm outline-none focus:border-white text-white placeholder:text-white placeholder:font-medium"
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
ref={searchInputRef}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") e.preventDefault();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<X
|
||||||
|
size={20}
|
||||||
|
weight="bold"
|
||||||
|
className="cursor-pointer text-white hover:text-[#9CA3AF]"
|
||||||
|
onClick={handleXButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 pl-4 pr-2 flex flex-col gap-y-1 overflow-y-auto white-scrollbar pb-4">
|
||||||
|
{filteredVDBs.map((vdb) => (
|
||||||
|
<VectorDBItem
|
||||||
|
key={vdb.name}
|
||||||
|
name={vdb.name}
|
||||||
|
value={vdb.value}
|
||||||
|
image={vdb.logo}
|
||||||
|
description={vdb.description}
|
||||||
|
checked={selectedVDB === vdb.value}
|
||||||
|
onClick={() => updateVectorChoice(vdb.value)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-4 pt-[70px] flex flex-col gap-y-1 max-h-[390px] overflow-y-auto no-scroll pb-4">
|
) : (
|
||||||
{filteredVDBs.map((vdb) => (
|
<button
|
||||||
<VectorDBItem
|
className="w-full max-w-[640px] h-[64px] bg-[#18181B] rounded-lg flex items-center p-[14px] justify-between cursor-pointer border-2 border-transparent hover:border-[#46C8FF] transition-all duration-300"
|
||||||
key={vdb.name}
|
type="button"
|
||||||
name={vdb.name}
|
onClick={() => setSearchMenuOpen(true)}
|
||||||
value={vdb.value}
|
>
|
||||||
image={vdb.logo}
|
<div className="flex gap-x-4 items-center">
|
||||||
description={vdb.description}
|
<img
|
||||||
checked={selectedVDB === vdb.value}
|
src={selectedVDBObject.logo}
|
||||||
onClick={() => updateVectorChoice(vdb.value)}
|
alt={`${selectedVDBObject.name} logo`}
|
||||||
|
className="w-10 h-10 rounded-md"
|
||||||
/>
|
/>
|
||||||
))}
|
<div className="flex flex-col text-left">
|
||||||
</div>
|
<div className="text-sm font-semibold text-white">
|
||||||
</div>
|
{selectedVDBObject.name}
|
||||||
<div
|
</div>
|
||||||
onChange={() => setHasChanges(true)}
|
<div className="mt-1 text-xs text-[#D2D5DB]">
|
||||||
className="mt-4 flex flex-col gap-y-1"
|
{selectedVDBObject.description}
|
||||||
>
|
</div>
|
||||||
{selectedVDB &&
|
</div>
|
||||||
VECTOR_DBS.find((vdb) => vdb.value === selectedVDB)
|
</div>
|
||||||
?.options}
|
<CaretUpDown
|
||||||
</div>
|
size={24}
|
||||||
|
weight="bold"
|
||||||
|
className="text-white"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onChange={() => setHasChanges(true)}
|
||||||
|
className="mt-4 flex flex-col gap-y-1"
|
||||||
|
>
|
||||||
|
{selectedVDB &&
|
||||||
|
VECTOR_DBS.find((vdb) => vdb.value === selectedVDB)?.options}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
Loading…
Reference in New Issue
Block a user