);
// --- Editor View Component ---
const EditorView = ({ image, onBack }) => {
const [prompt, setPrompt] = useState('');
const [objectImage, setObjectImage] = useState(null); // base64
const [generatedImage, setGeneratedImage] = useState(null); // base64
const [isGenerating, setIsGenerating] = useState(false);
const [error, setError] = useState(null);
const handleObjectImageUpload = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onloadend = () => {
setObjectImage(reader.result.split(',')[1]);
};
reader.readAsDataURL(file);
}
};
const urlToBase64 = async (url) => {
// Since the app will be on the same domain, a direct fetch should work without CORS issues.
// We still need a proxy for the preview environment, so leaving it for now.
// In a production environment on the same domain, this proxy is not ideal.
try {
const proxyUrl = `https://cors-anywhere.herokuapp.com/${url}`;
const response = await fetch(proxyUrl);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch(e) {
console.error("Image processing failure:", e);
setError("Could not process the selected gallery image due to browser security restrictions. This may work when the app is embedded on the final website.");
throw e;
}
};
const generateImage = async () => {
if (!prompt || !objectImage) {
setError('Please provide a text prompt and upload an object image.');
return;
}
setIsGenerating(true);
setError(null);
setGeneratedImage(null);
try {
const baseImageBase64 = await urlToBase64(image.src);
const requestBody = {
contents: [{
parts: [
{ text: `Instructions: ${prompt}. Use the second image as the object to place into the first image.` },
{ inlineData: { mimeType: 'image/jpeg', data: baseImageBase64 } },
{ inlineData: { mimeType: 'image/png', data: objectImage } }
]
}],
generationConfig: {
responseModalities: ['TEXT', 'IMAGE']
},
};
const apiKey = ""; // Canvas will provide this
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent?key=${apiKey}`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorData = await response.json();
console.error('API Error:', errorData);
throw new Error(errorData.error?.message || 'The AI model failed to generate an image.');
}
const result = await response.json();
const base64Data = result?.candidates?.[0]?.content?.parts?.find(p => p.inlineData)?.inlineData?.data;
if (!base64Data) {
throw new Error('No image data found in the API response.');
}
setGeneratedImage(`data:image/png;base64,${base64Data}`);
} catch (e) {
console.error('Generation failed:', e);
setError(`An error occurred: ${e.message}`);
} finally {
setIsGenerating(false);
}
};
return (
{/* Left Side: Editor & Controls */}
Staging Controls
{error && }
{/* Right Side: Image Display */}
Original Image
{isGenerating ? 'Generating new image...' : 'Generated Image'}