chore: Update dependencies and fix import paths

This commit is contained in:
Jin Yucong 2024-07-05 15:49:43 +08:00
parent 3b737a078a
commit 81c5e30fda
46 changed files with 1626 additions and 371 deletions

View file

@ -20,13 +20,13 @@ const Chat = ({
rewrite: (messageId: string) => void;
}) => {
const [dividerWidth, setDividerWidth] = useState(0);
const dividerRef = useRef<HTMLDivElement | null>(null);
const dividerReference = useRef<HTMLDivElement | null>(null);
const messageEnd = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const updateDividerWidth = () => {
if (dividerRef.current) {
setDividerWidth(dividerRef.current.scrollWidth);
if (dividerReference.current) {
setDividerWidth(dividerReference.current.scrollWidth);
}
};
@ -43,29 +43,29 @@ const Chat = ({
messageEnd.current?.scrollIntoView({ behavior: "smooth" });
if (messages.length === 1) {
document.title = `${messages[0].content.substring(0, 30)} - Perplexica`;
document.title = `${messages[0].content.slice(0, 30)} - Perplexica`;
}
}, [messages]);
return (
<div className="flex flex-col space-y-6 pt-8 pb-44 lg:pb-32 sm:mx-4 md:mx-8">
{messages.map((msg, i) => {
const isLast = i === messages.length - 1;
{messages.map((message, index) => {
const isLast = index === messages.length - 1;
return (
<Fragment key={msg.messageId}>
<Fragment key={message.messageId}>
<MessageBox
key={i}
message={msg}
messageIndex={i}
key={index}
message={message}
messageIndex={index}
history={messages}
loading={loading}
dividerRef={isLast ? dividerRef : undefined}
dividerRef={isLast ? dividerReference : undefined}
isLast={isLast}
rewrite={rewrite}
sendMessage={sendMessage}
/>
{!isLast && msg.role === "assistant" && (
{!isLast && message.role === "assistant" && (
<div className="h-px w-full bg-light-secondary dark:bg-dark-secondary" />
)}
</Fragment>

View file

@ -6,7 +6,7 @@ import { Document } from "@langchain/core/documents";
import Navbar from "./Navbar";
import Chat from "./Chat";
import EmptyChat from "./EmptyChat";
import crypto from "crypto";
import crypto from "node:crypto";
import { toast } from "sonner";
import { useSearchParams } from "next/navigation";
import { getSuggestions } from "@/lib/actions";
@ -62,20 +62,20 @@ const useSocket = (url: string, setIsWSReady: (ready: boolean) => void, setError
}
const wsURL = new URL(url);
const searchParams = new URLSearchParams({});
const searchParameters = new URLSearchParams({});
searchParams.append("chatModel", chatModel!);
searchParams.append("chatModelProvider", chatModelProvider);
searchParameters.append("chatModel", chatModel!);
searchParameters.append("chatModelProvider", chatModelProvider);
if (chatModelProvider === "custom_openai") {
searchParams.append("openAIApiKey", localStorage.getItem("openAIApiKey")!);
searchParams.append("openAIBaseURL", localStorage.getItem("openAIBaseURL")!);
searchParameters.append("openAIApiKey", localStorage.getItem("openAIApiKey")!);
searchParameters.append("openAIBaseURL", localStorage.getItem("openAIBaseURL")!);
}
searchParams.append("embeddingModel", embeddingModel!);
searchParams.append("embeddingModelProvider", embeddingModelProvider);
searchParameters.append("embeddingModel", embeddingModel!);
searchParameters.append("embeddingModelProvider", embeddingModelProvider);
wsURL.search = searchParams.toString();
wsURL.search = searchParameters.toString();
const ws = new WebSocket(wsURL.toString());
@ -85,26 +85,27 @@ const useSocket = (url: string, setIsWSReady: (ready: boolean) => void, setError
setError(true);
toast.error("Failed to connect to the server. Please try again later.");
}
}, 10000);
}, 10_000);
ws.onopen = () => {
ws.addEventListener("open", () => {
console.log("[DEBUG] open");
clearTimeout(timeoutId);
setError(false);
setIsWSReady(true);
};
});
// eslint-disable-next-line unicorn/prefer-add-event-listener
ws.onerror = () => {
clearTimeout(timeoutId);
setError(true);
toast.error("WebSocket connection error.");
};
ws.onclose = () => {
ws.addEventListener("close", () => {
clearTimeout(timeoutId);
setError(true);
console.log("[DEBUG] closed");
};
});
setWs(ws);
};
@ -144,17 +145,17 @@ const loadMessages = async (
const data = await res.json();
const messages = data.messages.map((msg: any) => {
const messages = data.messages.map((message: any) => {
return {
...msg,
...JSON.parse(msg.metadata),
...message,
...JSON.parse(message.metadata),
};
}) as Message[];
setMessages(messages);
const history = messages.map(msg => {
return [msg.role, msg.content];
const history = messages.map(message => {
return [message.role, message.content];
}) as [string, string][];
console.log("[DEBUG] messages loaded");
@ -167,8 +168,8 @@ const loadMessages = async (
};
const ChatWindow = ({ id }: { id?: string }) => {
const searchParams = useSearchParams();
const initialMessage = searchParams.get("q");
const searchParameters = useSearchParams();
const initialMessage = searchParameters.get("q");
const [chatId, setChatId] = useState<string | undefined>(id);
const [newChatCreated, setNewChatCreated] = useState(false);
@ -202,10 +203,10 @@ const ChatWindow = ({ id }: { id?: string }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const messagesRef = useRef<Message[]>([]);
const messagesReference = useRef<Message[]>([]);
useEffect(() => {
messagesRef.current = messages;
messagesReference.current = messages;
}, [messages]);
useEffect(() => {
@ -219,7 +220,7 @@ const ChatWindow = ({ id }: { id?: string }) => {
setLoading(true);
setMessageAppeared(false);
let sources: Document[] | undefined = undefined;
let sources: Document[] | undefined;
let recievedMessage = "";
let added = false;
@ -237,8 +238,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
}),
);
setMessages(prevMessages => [
...prevMessages,
setMessages(previousMessages => [
...previousMessages,
{
content: message,
messageId: messageId,
@ -260,8 +261,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
if (data.type === "sources") {
sources = data.data;
if (!added) {
setMessages(prevMessages => [
...prevMessages,
setMessages(previousMessages => [
...previousMessages,
{
content: "",
messageId: data.messageId,
@ -278,8 +279,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
if (data.type === "message") {
if (!added) {
setMessages(prevMessages => [
...prevMessages,
setMessages(previousMessages => [
...previousMessages,
{
content: data.data,
messageId: data.messageId,
@ -292,8 +293,8 @@ const ChatWindow = ({ id }: { id?: string }) => {
added = true;
}
setMessages(prev =>
prev.map(message => {
setMessages(previous =>
previous.map(message => {
if (message.messageId === data.messageId) {
return { ...message, content: message.content + data.data };
}
@ -307,21 +308,27 @@ const ChatWindow = ({ id }: { id?: string }) => {
}
if (data.type === "messageEnd") {
setChatHistory(prevHistory => [...prevHistory, ["human", message], ["assistant", recievedMessage]]);
setChatHistory(previousHistory => [...previousHistory, ["human", message], ["assistant", recievedMessage]]);
ws?.removeEventListener("message", messageHandler);
setLoading(false);
const lastMsg = messagesRef.current[messagesRef.current.length - 1];
const lastMessage = messagesReference.current.at(-1);
if (lastMsg.role === "assistant" && lastMsg.sources && lastMsg.sources.length > 0 && !lastMsg.suggestions) {
const suggestions = await getSuggestions(messagesRef.current);
setMessages(prev =>
prev.map(msg => {
if (msg.messageId === lastMsg.messageId) {
return { ...msg, suggestions: suggestions };
if (
lastMessage &&
lastMessage.role === "assistant" &&
lastMessage.sources &&
lastMessage.sources.length > 0 &&
!lastMessage.suggestions
) {
const suggestions = await getSuggestions(messagesReference.current);
setMessages(previous =>
previous.map(message_ => {
if (message_.messageId === lastMessage.messageId) {
return { ...message_, suggestions: suggestions };
}
return msg;
return message_;
}),
);
}
@ -332,17 +339,19 @@ const ChatWindow = ({ id }: { id?: string }) => {
};
const rewrite = (messageId: string) => {
const index = messages.findIndex(msg => msg.messageId === messageId);
const index = messages.findIndex(message_ => message_.messageId === messageId);
if (index === -1) return;
const message = messages[index - 1];
setMessages(prev => {
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
setMessages(previous => {
// eslint-disable-next-line unicorn/no-useless-spread
return [...previous.slice(0, messages.length > 2 ? index - 1 : 0)];
});
setChatHistory(prev => {
return [...prev.slice(0, messages.length > 2 ? index - 1 : 0)];
setChatHistory(previous => {
// eslint-disable-next-line unicorn/no-useless-spread
return [...previous.slice(0, messages.length > 2 ? index - 1 : 0)];
});
sendMessage(message.content);

View file

@ -33,8 +33,9 @@ const DeleteChat = ({
const newChats = chats.filter(chat => chat.id !== chatId);
setChats(newChats);
} catch (err: any) {
toast.error(err.message);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
toast.error(error.message);
} finally {
setConfirmationDialogOpen(false);
setLoading(false);

View file

@ -16,12 +16,12 @@ const EmptyChatMessageInput = ({
const [copilotEnabled, setCopilotEnabled] = useState(false);
const [message, setMessage] = useState("");
const inputRef = useRef<HTMLTextAreaElement | null>(null);
const inputReference = useRef<HTMLTextAreaElement | null>(null);
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "/") {
e.preventDefault();
inputRef.current?.focus();
inputReference.current?.focus();
}
};
@ -51,7 +51,7 @@ const EmptyChatMessageInput = ({
>
<div className="flex flex-col bg-light-secondary dark:bg-dark-secondary px-5 pt-5 pb-2 rounded-lg w-full border border-light-200 dark:border-dark-200">
<TextareaAutosize
ref={inputRef}
ref={inputReference}
value={message}
onChange={e => setMessage(e.target.value)}
minRows={2}

View file

@ -8,7 +8,8 @@ const Copy = ({ message, initialMessage }: { message: Message; initialMessage: s
return (
<button
onClick={() => {
const contentToCopy = `${initialMessage}${message.sources && message.sources.length > 0 && `\n\nCitations:\n${message.sources?.map((source: any, i: any) => `[${i + 1}] ${source.metadata.url}`).join(`\n`)}`}`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const contentToCopy = `${initialMessage}${message.sources && message.sources.length > 0 && `\n\nCitations:\n${message.sources?.map((source: any, index: any) => `[${index + 1}] ${source.metadata.url}`).join(`\n`)}`}`;
navigator.clipboard.writeText(contentToCopy);
setCopied(true);
setTimeout(() => setCopied(false), 1000);

View file

@ -36,11 +36,11 @@ const MessageBox = ({
const [speechMessage, setSpeechMessage] = useState(message.content);
useEffect(() => {
const regex = /\[(\d+)\]/g;
const regex = /\[(\d+)]/g;
if (message.role === "assistant" && message?.sources && message.sources.length > 0) {
return setParsedMessage(
message.content.replace(
message.content.replaceAll(
regex,
(_, number) =>
`<a href="${message.sources?.[number - 1]?.metadata?.url}" target="_blank" className="bg-light-secondary dark:bg-dark-secondary px-1 rounded ml-1 no-underline text-xs text-black/70 dark:text-white/70 relative">${number}</a>`,
@ -48,7 +48,7 @@ const MessageBox = ({
);
}
setSpeechMessage(message.content.replace(regex, ""));
setSpeechMessage(message.content.replaceAll(regex, ""));
setParsedMessage(message.content);
}, [message.content, message.sources, message.role]);
@ -128,8 +128,8 @@ const MessageBox = ({
<h3 className="text-xl font-medium">Related</h3>
</div>
<div className="flex flex-col space-y-3">
{message.suggestions.map((suggestion, i) => (
<div className="flex flex-col space-y-3 text-sm" key={i}>
{message.suggestions.map((suggestion, index) => (
<div className="flex flex-col space-y-3 text-sm" key={index}>
<div className="h-px w-full bg-light-secondary dark:bg-dark-secondary" />
<div
onClick={() => {

View file

@ -19,12 +19,12 @@ const MessageInput = ({ sendMessage, loading }: { sendMessage: (message: string)
}
}, [textareaRows, mode, message]);
const inputRef = useRef<HTMLTextAreaElement | null>(null);
const inputReference = useRef<HTMLTextAreaElement | null>(null);
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "/") {
e.preventDefault();
inputRef.current?.focus();
inputReference.current?.focus();
}
};
@ -58,11 +58,11 @@ const MessageInput = ({ sendMessage, loading }: { sendMessage: (message: string)
>
{mode === "single" && <Attach />}
<TextareaAutosize
ref={inputRef}
ref={inputReference}
value={message}
onChange={e => setMessage(e.target.value)}
onHeightChange={(height, props) => {
setTextareaRows(Math.ceil(height / props.rowHeight));
onHeightChange={(height, properties) => {
setTextareaRows(Math.ceil(height / properties.rowHeight));
}}
className="transition bg-transparent dark:placeholder:text-white/50 placeholder:text-sm text-sm dark:text-white resize-none focus:outline-none w-full px-2 max-h-24 lg:max-h-36 xl:max-h-48 flex-grow flex-shrink"
placeholder="Ask a follow-up"

View file

@ -54,14 +54,14 @@ const Focus = ({ focusMode, setFocusMode }: { focusMode: string; setFocusMode: (
type="button"
className="p-2 text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary active:scale-95 transition duration-200 hover:text-black dark:hover:text-white"
>
{focusMode !== "webSearch" ? (
{focusMode === "webSearch" ? (
<ScanEye />
) : (
<div className="flex flex-row items-center space-x-1">
{focusModes.find(mode => mode.key === focusMode)?.icon}
<p className="text-xs font-medium">{focusModes.find(mode => mode.key === focusMode)?.title}</p>
<ChevronDown size={20} />
</div>
) : (
<ScanEye />
)}
</Popover.Button>
<Transition
@ -75,10 +75,10 @@ const Focus = ({ focusMode, setFocusMode }: { focusMode: string; setFocusMode: (
>
<Popover.Panel className="absolute z-10 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-1 bg-light-primary dark:bg-dark-primary border rounded-lg border-light-200 dark:border-dark-200 w-full p-2 max-h-[200px] md:max-h-none overflow-y-auto">
{focusModes.map((mode, i) => (
{focusModes.map((mode, index) => (
<Popover.Button
onClick={() => setFocusMode(mode.key)}
key={i}
key={index}
className={cn(
"p-2 rounded-lg flex flex-col items-start justify-start text-start space-y-2 duration-200 cursor-pointer transition",
focusMode === mode.key

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element */
import { Dialog, Transition } from "@headlessui/react";
import { Document } from "@langchain/core/documents";
import { Fragment, useState } from "react";
@ -18,12 +17,13 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
return (
<div className="grid grid-cols-2 lg:grid-cols-4 gap-2">
{sources.slice(0, 3).map((source, i) => (
{sources.slice(0, 3).map((source, index) => (
<a
className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium"
key={i}
key={index}
href={source.metadata.url}
target="_blank"
rel="noreferrer"
>
<p className="dark:text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.title}
@ -38,12 +38,12 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
className="rounded-lg h-4 w-4"
/>
<p className="text-xs text-black/50 dark:text-white/50 overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.url.replace(/.+\/\/|www.|\..+/g, "")}
{source.metadata.url.replaceAll(/.+\/\/|www.|\..+/g, "")}
</p>
</div>
<div className="flex flex-row items-center space-x-1 text-black/50 dark:text-white/50 text-xs">
<div className="bg-black/50 dark:bg-white/50 h-[4px] w-[4px] rounded-full" />
<span>{i + 1}</span>
<span>{index + 1}</span>
</div>
</div>
</a>
@ -54,14 +54,14 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium"
>
<div className="flex flex-row items-center space-x-1">
{sources.slice(3, 6).map((source, i) => (
{sources.slice(3, 6).map((source, index) => (
<img
src={`https://s2.googleusercontent.com/s2/favicons?domain_url=${source.metadata.url}`}
width={16}
height={16}
alt="favicon"
className="rounded-lg h-4 w-4"
key={i}
key={index}
/>
))}
</div>
@ -84,12 +84,13 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
<Dialog.Panel className="w-full max-w-md transform rounded-2xl bg-light-secondary dark:bg-dark-secondary border border-light-200 dark:border-dark-200 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title className="text-lg font-medium leading-6 dark:text-white">Sources</Dialog.Title>
<div className="grid grid-cols-2 gap-2 overflow-auto max-h-[300px] mt-2 pr-2">
{sources.map((source, i) => (
{sources.map((source, index) => (
<a
className="bg-light-secondary hover:bg-light-200 dark:bg-dark-secondary dark:hover:bg-dark-200 border border-light-200 dark:border-dark-200 transition duration-200 rounded-lg p-3 flex flex-col space-y-2 font-medium"
key={i}
key={index}
href={source.metadata.url}
target="_blank"
rel="noreferrer"
>
<p className="dark:text-white text-xs overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.title}
@ -104,12 +105,12 @@ const MessageSources = ({ sources }: { sources: Document[] }) => {
className="rounded-lg h-4 w-4"
/>
<p className="text-xs text-black/50 dark:text-white/50 overflow-hidden whitespace-nowrap text-ellipsis">
{source.metadata.url.replace(/.+\/\/|www.|\..+/g, "")}
{source.metadata.url.replaceAll(/.+\/\/|www.|\..+/g, "")}
</p>
</div>
<div className="flex flex-row items-center space-x-1 text-black/50 dark:text-white/50 text-xs">
<div className="bg-black/50 dark:bg-white/50 h-[4px] w-[4px] rounded-full" />
<span>{i + 1}</span>
<span>{index + 1}</span>
</div>
</div>
</a>

View file

@ -10,7 +10,7 @@ const Navbar = ({ messages }: { messages: Message[] }) => {
useEffect(() => {
if (messages.length > 0) {
const newTitle =
messages[0].content.length > 20 ? `${messages[0].content.substring(0, 20).trim()}...` : messages[0].content;
messages[0].content.length > 20 ? `${messages[0].content.slice(0, 20).trim()}...` : messages[0].content;
setTitle(newTitle);
const newTimeAgo = formatTimeDifference(new Date(), messages[0].createdAt);
setTimeAgo(newTimeAgo);

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element */
import { ImagesIcon, PlusIcon } from "lucide-react";
import { useState } from "react";
import Lightbox from "yet-another-react-lightbox";
@ -15,6 +14,7 @@ const SearchImages = ({ query, chat_history }: { query: string; chat_history: Me
const [images, setImages] = useState<Image[] | null>(null);
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [slides, setSlides] = useState<any[]>([]);
return (
@ -64,9 +64,9 @@ const SearchImages = ({ query, chat_history }: { query: string; chat_history: Me
)}
{loading && (
<div className="grid grid-cols-2 gap-2">
{[...Array(4)].map((_, i) => (
{Array.from({ length: 4 }).map((_, index) => (
<div
key={i}
key={index}
className="bg-light-secondary dark:bg-dark-secondary h-32 w-full rounded-lg animate-pulse aspect-video object-cover"
/>
))}
@ -76,25 +76,25 @@ const SearchImages = ({ query, chat_history }: { query: string; chat_history: Me
<>
<div className="grid grid-cols-2 gap-2">
{images.length > 4
? images.slice(0, 3).map((image, i) => (
? images.slice(0, 3).map((image, index) => (
<img
onClick={() => {
setOpen(true);
setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
key={i}
key={index}
src={image.img_src}
alt={image.title}
className="h-full w-full aspect-video object-cover rounded-lg transition duration-200 active:scale-95 hover:scale-[1.02] cursor-zoom-in"
/>
))
: images.map((image, i) => (
: images.map((image, index) => (
<img
onClick={() => {
setOpen(true);
setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
key={i}
key={index}
src={image.img_src}
alt={image.title}
className="h-full w-full aspect-video object-cover rounded-lg transition duration-200 active:scale-95 hover:scale-[1.02] cursor-zoom-in"
@ -106,9 +106,9 @@ const SearchImages = ({ query, chat_history }: { query: string; chat_history: Me
className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2"
>
<div className="flex flex-row items-center space-x-1">
{images.slice(3, 6).map((image, i) => (
{images.slice(3, 6).map((image, index) => (
<img
key={i}
key={index}
src={image.img_src}
alt={image.title}
className="h-6 w-12 rounded-md lg:h-3 lg:w-6 lg:rounded-sm aspect-video object-cover"

View file

@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-img-element */
import { PlayCircle, PlayIcon, PlusIcon, VideoIcon } from "lucide-react";
import { useState } from "react";
import Lightbox, { GenericSlide, VideoSlide } from "yet-another-react-lightbox";
@ -79,9 +78,9 @@ const Searchvideos = ({ query, chat_history }: { query: string; chat_history: Me
)}
{loading && (
<div className="grid grid-cols-2 gap-2">
{[...Array(4)].map((_, i) => (
{Array.from({ length: 4 }).map((_, index) => (
<div
key={i}
key={index}
className="bg-light-secondary dark:bg-dark-secondary h-32 w-full rounded-lg animate-pulse aspect-video object-cover"
/>
))}
@ -91,14 +90,14 @@ const Searchvideos = ({ query, chat_history }: { query: string; chat_history: Me
<>
<div className="grid grid-cols-2 gap-2">
{videos.length > 4
? videos.slice(0, 3).map((video, i) => (
? videos.slice(0, 3).map((video, index) => (
<div
onClick={() => {
setOpen(true);
setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer"
key={i}
key={index}
>
<img
src={video.img_src}
@ -111,14 +110,14 @@ const Searchvideos = ({ query, chat_history }: { query: string; chat_history: Me
</div>
</div>
))
: videos.map((video, i) => (
: videos.map((video, index) => (
<div
onClick={() => {
setOpen(true);
setSlides([slides[i], ...slides.slice(0, i), ...slides.slice(i + 1)]);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer"
key={i}
key={index}
>
<img
src={video.img_src}
@ -137,9 +136,9 @@ const Searchvideos = ({ query, chat_history }: { query: string; chat_history: Me
className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2"
>
<div className="flex flex-row items-center space-x-1">
{videos.slice(3, 6).map((video, i) => (
{videos.slice(3, 6).map((video, index) => (
<img
key={i}
key={index}
src={video.img_src}
alt={video.title}
className="h-6 w-12 rounded-md lg:h-3 lg:w-6 lg:rounded-sm aspect-video object-cover"

View file

@ -1,15 +1,16 @@
/* eslint-disable unicorn/no-nested-ternary */
import { cn } from "@/lib/utils";
import { Dialog, Transition } from "@headlessui/react";
import { CloudUpload, RefreshCcw, RefreshCw } from "lucide-react";
import React, { Fragment, useEffect, useState, type SelectHTMLAttributes } from "react";
import ThemeSwitcher from "./theme/Switcher";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
interface InputProperties extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = ({ className, ...restProps }: InputProps) => {
const Input = ({ className, ...restProperties }: InputProperties) => {
return (
<input
{...restProps}
{...restProperties}
className={cn(
"bg-light-secondary dark:bg-dark-secondary px-3 py-2 flex items-center overflow-hidden border border-light-200 dark:border-dark-200 dark:text-white rounded-lg text-sm",
className,
@ -18,14 +19,14 @@ const Input = ({ className, ...restProps }: InputProps) => {
);
};
interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
interface SelectProperties extends SelectHTMLAttributes<HTMLSelectElement> {
options: { value: string; label: string; disabled?: boolean }[];
}
export const Select = ({ className, options, ...restProps }: SelectProps) => {
export const Select = ({ className, options, ...restProperties }: SelectProperties) => {
return (
<select
{...restProps}
{...restProperties}
className={cn(
"bg-light-secondary dark:bg-dark-secondary px-3 py-2 flex items-center overflow-hidden border border-light-200 dark:border-dark-200 dark:text-white rounded-lg text-sm",
className,
@ -129,8 +130,8 @@ const SettingsDialog = ({ isOpen, setIsOpen }: { isOpen: boolean; setIsOpen: (is
localStorage.setItem("embeddingModel", selectedEmbeddingModel!);
localStorage.setItem("openAIApiKey", customOpenAIApiKey!);
localStorage.setItem("openAIBaseURL", customOpenAIBaseURL!);
} catch (err) {
console.log(err);
} catch (error) {
console.log(error);
} finally {
setIsUpdating(false);
setIsOpen(false);

View file

@ -46,9 +46,9 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
<SquarePen className="cursor-pointer" />
</a>
<VerticalIconContainer>
{navLinks.map((link, i) => (
{navLinks.map((link, index) => (
<Link
key={i}
key={index}
href={link.href}
className={cn(
"relative flex flex-row items-center justify-center cursor-pointer hover:bg-black/10 dark:hover:bg-white/10 duration-150 transition w-full py-2 rounded-lg",
@ -70,10 +70,10 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
</div>
<div className="fixed bottom-0 w-full z-50 flex flex-row items-center gap-x-6 bg-light-primary dark:bg-dark-primary px-4 py-4 shadow-sm lg:hidden">
{navLinks.map((link, i) => (
{navLinks.map((link, index) => (
<Link
href={link.href}
key={i}
key={index}
className={cn(
"relative flex flex-col items-center space-y-1 text-center w-full",
link.active ? "text-black dark:text-white" : "text-black dark:text-white/70",