| "use client"; |
| import { useState } from "react"; |
| import { ImageUpload } from "@/components/ImageUpload"; |
| import { ImagePromptInput } from "@/components/ImagePromptInput"; |
| import { ImageResultDisplay } from "@/components/ImageResultDisplay"; |
| import { ImageIcon, Wand2, ExternalLink } 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); |
|
|
| |
| const imageToEdit = generatedImage || image; |
|
|
| |
| 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) { |
| |
| setGeneratedImage(data.image); |
| setDescription(data.description || null); |
|
|
| |
| const userMessage: HistoryItem = { |
| role: "user", |
| parts: [ |
| { text: prompt }, |
| ...(imageToEdit ? [{ image: imageToEdit }] : []), |
| ], |
| }; |
|
|
| |
| const aiResponse: HistoryItem = { |
| role: "model", |
| parts: [ |
| ...(data.description ? [{ text: data.description }] : []), |
| ...(data.image ? [{ image: data.image }] : []), |
| ], |
| }; |
|
|
| |
| 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([]); |
| }; |
|
|
| |
| const currentImage = generatedImage || image; |
| const isEditing = !!currentImage; |
|
|
| |
| 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" /> |
| 4o image generation |
| </CardTitle> |
| </CardHeader> |
| <CardContent className="space-y-6 pt-6 w-full"> |
| <div className="p-4 mb-4 text-sm text-blue-700 bg-blue-100 rounded-lg flex items-center justify-between"> |
| <a |
| href="https://openai.com/index/introducing-4o-image-generation/" |
| target="_blank" |
| rel="noopener noreferrer" |
| className="flex items-center gap-1 font-medium hover:underline" |
| > |
| View Docs <ExternalLink className="h-3 w-3" /> |
| </a> |
| </div> |
| |
| {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> |
| ); |
| } |
|
|