'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, ClockCounterClockwise } 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'; interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; onConversationIdChange?: (conversationId: string) => void; setUploadedFiles: (files: string[]) => void; isMobileWidth?: boolean; isLoggedIn: boolean; userConfig: UserConfig | null; isLoadingUserConfig: boolean; } function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = 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 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 = [...suggestionsData].sort(() => 0.5 - Math.random()); 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.map( agent => getIconFromIconName(agent.icon, agent.color) || {agent.name} ); 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); window.location.href = `/chat?conversationId=${newConversationId}`; localStorage.setItem('message', message); } catch (error) { console.error("Error creating new conversation:", error); setProcessingMessage(false); } setMessage(''); } }; processMessage(); if (message) { 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)} 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)} 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, setIsMobileWidth] = useState(false); 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; }); setIsMobileWidth(window.innerWidth < 786); window.addEventListener('resize', () => { setIsMobileWidth(window.innerWidth < 786); }); }, []); if (isLoading) { return ; } return (
Khoj AI - Your Second Brain
); }