import styles from './chatInputArea.module.css'; import React, { useEffect, useRef, useState } from 'react'; import { uploadDataForIndexing } from '../../common/chatFunctions'; import { Progress } from "@/components/ui/progress" import 'katex/dist/katex.min.css'; import { ArrowCircleUp, ArrowRight, Browser, ChatsTeardrop, FileArrowUp, GlobeSimple, Gps, Image, Microphone, Notebook, Question, Robot, Shapes, Stop, Waveform, WaveSine } from '@phosphor-icons/react'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@/components/ui/command" import { Textarea } from "@/components/ui/textarea" import { Button } from '@/components/ui/button'; import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog'; import { Popover, PopoverContent } from '@/components/ui/popover'; import { PopoverTrigger } from '@radix-ui/react-popover'; import Link from 'next/link'; import { AlertDialogCancel } from '@radix-ui/react-alert-dialog'; import LoginPrompt from '../loginPrompt/loginPrompt'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { InlineLoading } from '../loading/loading'; export interface ChatOptions { [key: string]: string } interface ChatInputProps { sendMessage: (message: string) => void; sendDisabled: boolean; setUploadedFiles?: (files: string[]) => void; conversationId?: string | null; chatOptionsData?: ChatOptions | null; isMobileWidth?: boolean; isLoggedIn: boolean; } async function createNewConvo() { try { const response = await fetch('/api/chat/sessions', { method: "POST" }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); const conversationID = data.conversation_id; if (!conversationID) { throw new Error("Conversation ID not found in response"); } const url = `/chat?conversationId=${conversationID}`; return url; } catch (error) { console.error("Error creating new conversation:", error); throw error; } } export default function ChatInputArea(props: ChatInputProps) { const [message, setMessage] = useState(''); const fileInputRef = useRef(null); const [warning, setWarning] = useState(null); const [error, setError] = useState(null); const [uploading, setUploading] = useState(false); const [loginRedirectMessage, setLoginRedirectMessage] = useState(null); const [showLoginPrompt, setShowLoginPrompt] = useState(false); const [recording, setRecording] = useState(false); const [mediaRecorder, setMediaRecorder] = useState(null); const [progressValue, setProgressValue] = useState(0); useEffect(() => { if (!uploading) { setProgressValue(0); } if (uploading) { const interval = setInterval(() => { setProgressValue((prev) => { const increment = Math.floor(Math.random() * 5) + 1; // Generates a random number between 1 and 5 const nextValue = prev + increment; return nextValue < 100 ? nextValue : 100; // Ensures progress does not exceed 100 }); }, 800); return () => clearInterval(interval); } }, [uploading]); function onSendMessage() { if (!message.trim()) return; if (!props.isLoggedIn) { setLoginRedirectMessage('Hey there, you need to be signed in to send messages to Khoj AI'); setShowLoginPrompt(true); return; } props.sendMessage(message.trim()); setMessage(''); } function handleSlashCommandClick(command: string) { setMessage(`/${command} `); } function handleFileButtonClick() { if (!fileInputRef.current) return; fileInputRef.current.click(); } function handleFileChange(event: React.ChangeEvent) { if (!event.target.files) return; if (!props.isLoggedIn) { setLoginRedirectMessage('Whoa! You need to login to upload files'); setShowLoginPrompt(true); return; } uploadDataForIndexing( event.target.files, setWarning, setUploading, setError, props.setUploadedFiles, props.conversationId); } function getIconForSlashCommand(command: string) { const className = 'h-4 w-4 mr-2'; if (command.includes('summarize')) { return } if (command.includes('help')) { return } if (command.includes('automation')) { return } if (command.includes('webpage')) { return } if (command.includes('notes')) { return } if (command.includes('image')) { return } if (command.includes('default')) { return } if (command.includes('general')) { return } if (command.includes('online')) { return } return } // Assuming this function is added within the same context as the provided excerpt async function startRecordingAndTranscribe() { try { const microphone = await navigator.mediaDevices.getUserMedia({ audio: true }); const mediaRecorder = new MediaRecorder(microphone, { mimeType: 'audio/webm' }); const audioChunks: Blob[] = []; mediaRecorder.ondataavailable = async (event) => { audioChunks.push(event.data); const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); const formData = new FormData(); formData.append('file', audioBlob); // Send the incremental audio blob to the server try { const response = await fetch('/api/transcribe', { method: 'POST', body: formData, }); if (!response.ok) { throw new Error('Network response was not ok'); } const transcription = await response.json(); setMessage(transcription.text.trim()); } catch (error) { console.error('Error sending audio to server:', error); } }; // Send an audio blob every 1.5 seconds mediaRecorder.start(1500); mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunks, { type: 'audio/webm' }); const formData = new FormData(); formData.append('file', audioBlob); // Send the audio blob to the server try { const response = await fetch('/api/transcribe', { method: 'POST', body: formData, }); if (!response.ok) { throw new Error('Network response was not ok'); } const transcription = await response.json(); mediaRecorder.stream.getTracks().forEach(track => track.stop()); setMediaRecorder(null); setMessage(transcription.text.trim()); } catch (error) { console.error('Error sending audio to server:', error); } }; setMediaRecorder(mediaRecorder); } catch (error) { console.error("Error getting microphone", error); } } useEffect(() => { if (!recording && mediaRecorder) { mediaRecorder.stop(); } if (recording && !mediaRecorder) { startRecordingAndTranscribe(); } }, [recording]); return ( <> { showLoginPrompt && loginRedirectMessage && ( ) } { uploading && ( Uploading data. Please wait. setUploading(false)}>Dismiss )} { warning && ( Data Upload Warning {warning} setWarning(null)}>Close ) } { error && ( Oh no! Something went wrong while uploading your data {error} setError(null)}>Close ) } { (message.startsWith('/') && message.split(' ').length === 1) &&
e.preventDefault()} className={`${props.isMobileWidth ? 'w-[100vw]' : 'w-full'} rounded-md`}> No matching commands. {props.chatOptionsData && Object.entries(props.chatOptionsData).map(([key, value]) => ( handleSlashCommandClick(key)}>
{getIconForSlashCommand(key)} /{key}
{value}
))}
}