"use client"; import "./globals.css"; import styles from "./page.module.css"; import "katex/dist/katex.min.css"; import React, { useEffect, useState } from "react"; import useSWR from "swr"; import Image from "next/image"; import { ArrowCounterClockwise } from "@phosphor-icons/react"; import { Card, CardTitle } from "@/components/ui/card"; import SuggestionCard from "@/app/components/suggestions/suggestionCard"; import SidePanel from "@/app/components/sidePanel/chatHistorySidePanel"; import Loading from "@/app/components/loading/loading"; import ChatInputArea, { ChatOptions } from "@/app/components/chatInputArea/chatInputArea"; import { Suggestion, suggestionsData } from "@/app/components/suggestions/suggestionsData"; import LoginPrompt from "@/app/components/loginPrompt/loginPrompt"; import { useAuthenticatedData, UserConfig, useUserConfig } from "@/app/common/auth"; import { convertColorToBorderClass } from "@/app/common/colorUtils"; import { getIconFromIconName } from "@/app/common/iconUtils"; import { AgentData } from "@/app/agents/page"; import { createNewConversation } from "./common/chatFunctions"; import { useIsMobileWidth } from "./common/utils"; import { useSearchParams } from "next/navigation"; interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; onConversationIdChange?: (conversationId: string) => void; setUploadedFiles: (files: string[]) => void; isMobileWidth?: boolean; isLoggedIn: boolean; userConfig: UserConfig | null; isLoadingUserConfig: boolean; } function FisherYatesShuffle(array: any[]) { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = useState(""); const [images, setImages] = useState([]); const [processingMessage, setProcessingMessage] = useState(false); const [greeting, setGreeting] = useState(""); const [shuffledOptions, setShuffledOptions] = useState([]); const [selectedAgent, setSelectedAgent] = useState("khoj"); const [agentIcons, setAgentIcons] = useState([]); const [agents, setAgents] = useState([]); const [showLoginPrompt, setShowLoginPrompt] = useState(false); const searchParams = useSearchParams(); const queryParam = searchParams.get("q"); useEffect(() => { if (queryParam) { setMessage(decodeURIComponent(queryParam)); } }, [queryParam]); const onConversationIdChange = props.onConversationIdChange; const agentsFetcher = () => window .fetch("/api/agents") .then((res) => res.json()) .catch((err) => console.log(err)); const { data: agentsData, error } = useSWR("agents", agentsFetcher, { revalidateOnFocus: false, }); function shuffleAndSetOptions() { const shuffled = FisherYatesShuffle(suggestionsData); setShuffledOptions(shuffled.slice(0, 3)); } useEffect(() => { if (props.isLoadingUserConfig) return; // Get today's day const today = new Date(); const day = today.getDay(); const timeOfDay = today.getHours() >= 17 || today.getHours() < 4 ? "evening" : today.getHours() >= 12 ? "afternoon" : "morning"; const nameSuffix = props.userConfig?.given_name ? `, ${props.userConfig?.given_name}` : ""; const greetings = [ `What would you like to get done${nameSuffix}?`, `Hey${nameSuffix}! How can I help?`, `Good ${timeOfDay}${nameSuffix}! What's on your mind?`, `Ready to breeze through your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]}?`, `Want help navigating your ${["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][day]} workload?`, ]; const greeting = greetings[Math.floor(Math.random() * greetings.length)]; setGreeting(greeting); }, [props.isLoadingUserConfig, props.userConfig]); useEffect(() => { if (props.chatOptionsData) { shuffleAndSetOptions(); } }, [props.chatOptionsData]); useEffect(() => { const nSlice = props.isMobileWidth ? 2 : 4; const shuffledAgents = agentsData ? [...agentsData].sort(() => 0.5 - Math.random()) : []; const agents = agentsData ? [agentsData[0]] : []; // Always add the first/default agent. shuffledAgents.slice(0, nSlice - 1).forEach((agent) => { if (!agents.find((a) => a.slug === agent.slug)) { agents.push(agent); } }); setAgents(agents); //generate colored icons for the selected agents const agentIcons = agents .filter((agent) => agent !== null && agent !== undefined) .map((agent) => getIconFromIconName(agent.icon, agent.color)!); setAgentIcons(agentIcons); }, [agentsData, props.isMobileWidth]); function shuffleSuggestionsCards() { shuffleAndSetOptions(); } useEffect(() => { const processMessage = async () => { if (message && !processingMessage) { setProcessingMessage(true); try { const newConversationId = await createNewConversation(selectedAgent || "khoj"); onConversationIdChange?.(newConversationId); localStorage.setItem("message", message); if (images.length > 0) { localStorage.setItem("images", JSON.stringify(images)); } window.location.href = `/chat?conversationId=${newConversationId}`; } catch (error) { console.error("Error creating new conversation:", error); setProcessingMessage(false); } setMessage(""); setImages([]); } }; processMessage(); if (message || images.length > 0) { setProcessingMessage(true); } }, [selectedAgent, message, processingMessage, onConversationIdChange]); function fillArea(link: string, type: string, prompt: string) { if (!link) { let message_str = ""; prompt = prompt.charAt(0).toLowerCase() + prompt.slice(1); if (type === "Online Search") { message_str = "/online " + prompt; } else if (type === "Paint") { message_str = "/image " + prompt; } else { message_str = prompt; } // Get the textarea element const message_area = document.getElementById("message") as HTMLTextAreaElement; if (message_area) { // Update the value directly message_area.value = message_str; setMessage(message_str); } } } return (
{showLoginPrompt && ( )}

{greeting}

{!props.isMobileWidth && (
{agentIcons.map((icon, index) => ( setSelectedAgent(agents[index].slug)} > {icon} {agents[index].name} ))} (window.location.href = "/agents")} > See All →
)}
{!props.isMobileWidth && (
setMessage(message)} sendImage={(image) => setImages((prevImages) => [...prevImages, image])} sendDisabled={processingMessage} chatOptionsData={props.chatOptionsData} conversationId={null} isMobileWidth={props.isMobileWidth} setUploadedFiles={props.setUploadedFiles} />
)}
{shuffledOptions.map((suggestion, index) => (
{ if (props.isLoggedIn) { fillArea( suggestion.link, suggestion.type, suggestion.description, ); } else { event.preventDefault(); event.stopPropagation(); setShowLoginPrompt(true); } }} >
))}
{props.isMobileWidth && ( <>
{agentIcons.map((icon, index) => ( setSelectedAgent(agents[index].slug)} > {icon} {agents[index].name} ))} (window.location.href = "/agents")} > See All →
setMessage(message)} sendImage={(image) => setImages((prevImages) => [...prevImages, image])} sendDisabled={processingMessage} chatOptionsData={props.chatOptionsData} conversationId={null} isMobileWidth={props.isMobileWidth} setUploadedFiles={props.setUploadedFiles} />
)}
); } export default function Home() { const [chatOptionsData, setChatOptionsData] = useState(null); const [isLoading, setLoading] = useState(true); const [conversationId, setConversationID] = useState(null); const [uploadedFiles, setUploadedFiles] = useState([]); const isMobileWidth = useIsMobileWidth(); const { userConfig: initialUserConfig, isLoadingUserConfig } = useUserConfig(true); const [userConfig, setUserConfig] = useState(null); const authenticatedData = useAuthenticatedData(); const handleConversationIdChange = (newConversationId: string) => { setConversationID(newConversationId); }; useEffect(() => { setUserConfig(initialUserConfig); }, [initialUserConfig]); useEffect(() => { fetch("/api/chat/options") .then((response) => response.json()) .then((data: ChatOptions) => { setLoading(false); if (data) { setChatOptionsData(data); } }) .catch((err) => { console.error(err); return; }); }, []); if (isLoading) { return ; } return (
Khoj AI - Your Second Brain
); }