app/page.tsx (137 lines of code) (raw):

"use client"; import { useState } from "react"; import { ImageUpload } from "@/components/ImageUpload"; import { ImagePromptInput } from "@/components/ImagePromptInput"; import { ImageResultDisplay } from "@/components/ImageResultDisplay"; import { ImageIcon, Wand2 } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { HistoryItem } from "@/lib/types"; export default function Home() { const [image, setImage] = useState<string | null>(null); const [generatedImage, setGeneratedImage] = useState<string | null>(null); const [description, setDescription] = useState<string | null>(null); const [loading, setLoading] = useState(false); const [error, setError] = useState<string | null>(null); const [history, setHistory] = useState<HistoryItem[]>([]); const handleImageSelect = (imageData: string) => { setImage(imageData || null); }; const handlePromptSubmit = async (prompt: string) => { try { setLoading(true); setError(null); // If we have a generated image, use that for editing, otherwise use the uploaded image const imageToEdit = generatedImage || image; // Prepare the request data as JSON const requestData = { prompt, image: imageToEdit, history: history.length > 0 ? history : undefined, }; const response = await fetch("/api/image", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(requestData), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || "Failed to generate image"); } const data = await response.json(); if (data.image) { // Update the generated image and description setGeneratedImage(data.image); setDescription(data.description || null); // Update history locally - add user message const userMessage: HistoryItem = { role: "user", parts: [ { text: prompt }, ...(imageToEdit ? [{ image: imageToEdit }] : []), ], }; // Add AI response const aiResponse: HistoryItem = { role: "model", parts: [ ...(data.description ? [{ text: data.description }] : []), ...(data.image ? [{ image: data.image }] : []), ], }; // Update history with both messages setHistory((prevHistory) => [...prevHistory, userMessage, aiResponse]); } else { setError("No image returned from API"); } } catch (error) { setError(error instanceof Error ? error.message : "An error occurred"); console.error("Error processing request:", error); } finally { setLoading(false); } }; const handleReset = () => { setImage(null); setGeneratedImage(null); setDescription(null); setLoading(false); setError(null); setHistory([]); }; // If we have a generated image, we want to edit it next time const currentImage = generatedImage || image; const isEditing = !!currentImage; // Get the latest image to display (always the generated image) const displayImage = generatedImage; return ( <main className="min-h-screen flex items-center justify-center bg-background p-8"> <Card className="w-full max-w-4xl border-0 bg-card shadow-none"> <CardHeader className="flex flex-col items-center justify-center space-y-2"> <CardTitle className="flex items-center gap-2 text-foreground"> <Wand2 className="w-8 h-8 text-primary" /> Image Creation & Editing </CardTitle> <span className="text-sm font-mono text-muted-foreground"> powered by Google DeepMind Gemini 2.0 Flash </span> </CardHeader> <CardContent className="space-y-6 pt-6 w-full"> {error && ( <div className="p-4 mb-4 text-sm text-red-700 bg-red-100 rounded-lg"> {error} </div> )} {!displayImage && !loading ? ( <> <ImageUpload onImageSelect={handleImageSelect} currentImage={currentImage} /> <ImagePromptInput onSubmit={handlePromptSubmit} isEditing={isEditing} isLoading={loading} /> </> ) : loading ? ( <div role="status" className="flex items-center mx-auto justify-center h-56 max-w-sm bg-gray-300 rounded-lg animate-pulse dark:bg-secondary" > <ImageIcon className="w-10 h-10 text-gray-200 dark:text-muted-foreground" /> <span className="pl-4 font-mono font-xs text-muted-foreground"> Processing... </span> </div> ) : ( <> <ImageResultDisplay imageUrl={displayImage || ""} description={description} onReset={handleReset} conversationHistory={history} /> <ImagePromptInput onSubmit={handlePromptSubmit} isEditing={true} isLoading={loading} /> </> )} </CardContent> </Card> </main> ); }