Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { useState } from 'react'; | |
| import Image from 'next/image'; | |
| import { useToast } from "@/hooks/use-toast"; | |
| import { generateChatbotResponse } from '@/ai/flows/generate-chatbot-response'; | |
| import type { Message } from '@/lib/types'; | |
| import MessageList from './message-list'; | |
| import MessageForm from './message-form'; | |
| import { ThemeToggle } from './theme-toggle'; | |
| import { Bot, X } from 'lucide-react'; | |
| import { Button } from './ui/button'; | |
| export function Chat() { | |
| const [messages, setMessages] = useState<Message[]>([ | |
| { | |
| id: '1', | |
| role: 'assistant', | |
| content: "जय जोहार! मैं चत्तीसवाणी हावंव। आप मन के का मदद कर सकत हंव?", | |
| }, | |
| ]); | |
| const [input, setInput] = useState(''); | |
| const [file, setFile] = useState<File | null>(null); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const { toast } = useToast(); | |
| const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
| if (e.target.files?.[0]) { | |
| setFile(e.target.files[0]); | |
| } | |
| }; | |
| const handleRemoveFile = () => { | |
| setFile(null); | |
| } | |
| const fileToDataUri = (file: File): Promise<string> => | |
| new Promise((resolve, reject) => { | |
| const reader = new FileReader(); | |
| reader.onload = () => resolve(reader.result as string); | |
| reader.onerror = reject; | |
| reader.readAsDataURL(file); | |
| }); | |
| const handleSendMessage = async (e: React.FormEvent<HTMLFormElement>) => { | |
| e.preventDefault(); | |
| if ((!input.trim() && !file) || isLoading) return; | |
| setIsLoading(true); | |
| let fileDataUri: string | undefined = undefined; | |
| let previewUrl: string | undefined = undefined; | |
| if (file) { | |
| previewUrl = URL.createObjectURL(file); | |
| fileDataUri = await fileToDataUri(file); | |
| } | |
| const userMessage: Message = { | |
| id: Date.now().toString(), | |
| role: 'user', | |
| content: input, | |
| imageUrl: previewUrl, | |
| }; | |
| setMessages((prev) => [...prev, userMessage]); | |
| const currentInput = input; | |
| setInput(''); | |
| setFile(null); | |
| try { | |
| const chatHistory = [...messages, userMessage].map(({ role, content }) => ({ role, content })); | |
| const response = await generateChatbotResponse({ | |
| message: currentInput, | |
| chatHistory, | |
| photoDataUri: fileDataUri | |
| }); | |
| const assistantMessage: Message = { | |
| id: (Date.now() + 1).toString(), | |
| role: 'assistant', | |
| content: response.response, | |
| }; | |
| setMessages((prev) => [...prev, assistantMessage]); | |
| } catch (error) { | |
| console.error("Error generating chatbot response:", error); | |
| toast({ | |
| variant: "destructive", | |
| title: "Oh no! Something went wrong.", | |
| description: "There was a problem with our AI. Please try again later.", | |
| }); | |
| const errorMessage: Message = { | |
| id: (Date.now() + 1).toString(), | |
| role: 'assistant', | |
| content: "Sorry, I'm having a little trouble right now. Please try again in a moment.", | |
| }; | |
| setMessages((prev) => [...prev, errorMessage]); | |
| } finally { | |
| setIsLoading(false); | |
| // Clean up the object URL | |
| if (previewUrl) { | |
| URL.revokeObjectURL(previewUrl); | |
| } | |
| } | |
| }; | |
| return ( | |
| <div className="flex h-screen w-full flex-col"> | |
| <header className="flex h-16 shrink-0 items-center justify-between border-b bg-background px-4"> | |
| <div className="flex items-center gap-2"> | |
| <Bot className="h-6 w-6 text-primary" /> | |
| <h1 className="text-lg font-semibold">ChhattisVani</h1> | |
| <span className="text-xs text-muted-foreground">29.05.02 beta</span> | |
| </div> | |
| <ThemeToggle /> | |
| </header> | |
| <div className="flex-1 overflow-hidden"> | |
| <MessageList messages={messages} isLoading={isLoading} /> | |
| </div> | |
| <div className="w-full max-w-3xl mx-auto p-4 shrink-0"> | |
| {file && ( | |
| <div className="mb-2 p-2 border rounded-lg bg-muted flex items-center justify-between animate-slide-in-fade-in"> | |
| <div className="flex items-center gap-3"> | |
| <Image src={URL.createObjectURL(file)} alt="File preview" width={40} height={40} className="rounded object-cover" /> | |
| <span className="text-sm font-medium truncate max-w-xs">{file.name}</span> | |
| </div> | |
| <Button variant="ghost" size="icon" onClick={handleRemoveFile} className="h-7 w-7 shrink-0"> | |
| <X className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| )} | |
| <MessageForm | |
| input={input} | |
| setInput={setInput} | |
| handleSendMessage={handleSendMessage} | |
| isLoading={isLoading} | |
| handleFileChange={handleFileChange} | |
| isFileSelected={!!file} | |
| /> | |
| <p className="text-xs text-center text-muted-foreground pt-2"> | |
| created with ❤️ by sameer banchhor | |
| </p> | |
| </div> | |
| </div> | |
| ); | |
| } | |