File size: 6,049 Bytes
3bb377e faee5d2 6421be3 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 6421be3 3bb377e 6421be3 faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e faee5d2 3bb377e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
"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);
// 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 Generation & 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">
<div className="p-4 mb-4 text-sm text-blue-700 bg-blue-100 rounded-lg flex items-center justify-between">
<span>
Build your own image generation apps with the Gemini API!
</span>
<a
href="https://ai.google.dev/gemini-api/docs/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>
);
}
|