350 S. Valley Dr. Las Cruces, NM 88005

import React, { useState, useEffect, useCallback } from 'react'; import { Camera, Upload, Wand2, ArrowLeft, LoaderCircle, ShieldAlert } from 'lucide-react'; // --- Main App Component --- export default function App() { const [view, setView] = useState('gallery'); // 'gallery' or 'editor' const [galleryImages, setGalleryImages] = useState([]); const [selectedImage, setSelectedImage] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // WordPress API Configuration const WP_API_URL = 'https://valleyhomeslc.com/wp-json/wp/v2/gallery?_embed&per_page=100'; const fetchGalleryImages = useCallback(async () => { setIsLoading(true); setError(null); try { // Direct API call without the proxy const response = await fetch(WP_API_URL); if (!response.ok) { // If this fails when embedded, it's likely a server or network issue, not CORS. throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); } const data = await response.json(); const formattedImages = data.map(item => { const featuredMedia = item._embedded?.['wp:featuredmedia']?.[0]; return { id: item.id, title: item.title.rendered, src: featuredMedia?.source_url || 'https://placehold.co/600x400/fff1de/0a3722?text=No+Image', alt: featuredMedia?.alt_text || item.title.rendered, }; }).filter(image => image.src && !image.src.includes('placehold.co')); setGalleryImages(formattedImages); } catch (e) { console.error('Failed to fetch gallery images:', e); // Simplified error message for a more general audience setError('Could not load images from the WordPress gallery. Please ensure you are connected to the internet. If the problem persists, the website server may be temporarily unavailable.'); } finally { setIsLoading(false); } }, [WP_API_URL]); useEffect(() => { fetchGalleryImages(); }, [fetchGalleryImages]); const handleImageSelect = (image) => { setSelectedImage(image); setView('editor'); }; const handleBackToGallery = () => { setView('gallery'); setSelectedImage(null); }; const renderContent = () => { // The CORS activation view is no longer needed. if (view === 'gallery') { return ( ); } if (view === 'editor' && selectedImage) { return ( ); } // Show a loading spinner or an error message if content isn't ready. if (isLoading) return ; if (error) return ; return null; } return (
{renderContent()}
); } // --- Header Component --- const Header = () => (

Valley Homes AI Staging Assistant

); // --- Gallery View Component --- const GalleryView = ({ images, onImageSelect, isLoading, error }) => (

Select an Image to Stage

{isLoading && } {error && } {!isLoading && !error && (
{images.map(img => (
onImageSelect(img)} > {img.alt}

{img.title}

))}
)}
); // --- 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