crystalai's picture
Upload 42 files
cf165dd verified
import React, { useState, useCallback } from 'react';
import { editImage } from '../services/geminiService';
import { fileToBase64 } from '../utils';
import { Spinner } from '../components/Spinner';
const ImageEditModule: React.FC = () => {
const [file, setFile] = useState<File | null>(null);
const [prompt, setPrompt] = useState<string>('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [originalImage, setOriginalImage] = useState<string | null>(null);
const [editedImage, setEditedImage] = useState<string | null>(null);
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const selectedFile = e.target.files[0];
setFile(selectedFile);
setOriginalImage(URL.createObjectURL(selectedFile));
setEditedImage(null);
}
};
const handleEdit = useCallback(async () => {
if (!file || !prompt) {
setError('Please upload an image and provide an edit prompt.');
return;
}
setIsLoading(true);
setError(null);
setEditedImage(null);
try {
const base64Image = await fileToBase64(file);
const response = await editImage(prompt, base64Image, file.type);
const newImagePart = response.candidates?.[0]?.content?.parts?.find(p => p.inlineData);
if (newImagePart && newImagePart.inlineData) {
const newImageData = newImagePart.inlineData;
setEditedImage(`data:${newImageData.mimeType};base64,${newImageData.data}`);
} else {
throw new Error("The model did not return an edited image.");
}
} catch (e: any) {
setError(e.message || 'An error occurred while editing the image.');
} finally {
setIsLoading(false);
}
}, [file, prompt]);
return (
<div className="flex flex-col h-full w-full max-w-6xl mx-auto">
<h2 className="text-2xl font-bold text-cyan-300 mb-4 text-center">AI Image Editor</h2>
<div className="flex flex-col md:flex-row gap-4 mb-4">
<div className="flex-1">
<label htmlFor="image-upload" className="block text-sm font-medium text-gray-300 mb-2">1. Upload Image</label>
<input
id="image-upload"
type="file"
accept="image/*"
onChange={handleFileChange}
className="block w-full text-sm text-gray-400 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-cyan-800 file:text-cyan-100 hover:file:bg-cyan-700"
/>
</div>
<div className="flex-1">
<label htmlFor="prompt" className="block text-sm font-medium text-gray-300 mb-2">2. Describe Your Edit</label>
<input
id="prompt"
type="text"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="w-full bg-gray-700 border border-gray-600 rounded-md p-2.5 focus:outline-none focus:ring-2 focus:ring-cyan-500 text-white"
placeholder="e.g., Add a retro filter, or remove the person in the background"
/>
</div>
<div className="self-end">
<button
onClick={handleEdit}
disabled={isLoading || !file || !prompt}
className="w-full bg-cyan-600 hover:bg-cyan-500 text-white font-bold py-3 px-6 rounded-md disabled:bg-gray-500 transition-colors"
>
{isLoading ? 'Editing...' : 'Apply Edit'}
</button>
</div>
</div>
{error && <p className="text-red-400 text-center mb-4">{error}</p>}
<div className="flex-grow grid grid-cols-1 md:grid-cols-2 gap-6 mt-4">
<div className="flex flex-col items-center">
<h3 className="text-lg font-semibold text-gray-400 mb-2">Original</h3>
<div className="w-full aspect-square bg-gray-800/50 rounded-lg border border-cyan-500/10 flex items-center justify-center overflow-hidden">
{originalImage ? <img src={originalImage} alt="Original" className="object-contain max-h-full max-w-full" /> : <p className="text-gray-500">Upload an image to start</p>}
</div>
</div>
<div className="flex flex-col items-center">
<h3 className="text-lg font-semibold text-gray-400 mb-2">Edited</h3>
<div className="w-full aspect-square bg-gray-800/50 rounded-lg border border-cyan-500/10 flex items-center justify-center overflow-hidden">
{isLoading && <Spinner text="Generating edit..."/>}
{!isLoading && editedImage && <img src={editedImage} alt="Edited" className="object-contain max-h-full max-w-full" />}
{!isLoading && !editedImage && <p className="text-gray-500">Your edited image will appear here</p>}
</div>
</div>
</div>
</div>
);
};
export default ImageEditModule;