Search This Blog

Friday, May 9, 2025

AI-Powered Art/Image Generator with User Prompts

 AI-Powered Art/Image Generator with User Prompts: Users provide text prompts, and an AI generates unique images, with interactive controls to refine or iterate on the visuals. (DALL-E is an example - Source 6.2)

HTML for Preview

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Image Generator Mock-up</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; }
        .control-panel-bg { background-color: #f9fafb; /* Tailwind gray-50 */ }
        .image-display-bg { background-color: #f3f4f6; /* Tailwind gray-100 */ }
        .btn-primary { background-color: #4f46e5; /* Tailwind indigo-600 */ color: white; }
        .btn-primary:hover { background-color: #4338ca; /* Tailwind indigo-700 */ }
        .btn-secondary { background-color: #6b7280; /* Tailwind gray-500 */ color: white; }
        .btn-secondary:hover { background-color: #4b5563; /* Tailwind gray-600 */ }
        
        /* Custom scrollbar for webkit browsers */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        /* Loading spinner */
        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border-left-color: #4f46e5; /* Tailwind indigo-600 */
            animation: spin 1s ease infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Ensure buttons in a group don't wrap weirdly on small screens */
        .button-group > button {
            min-width: max-content;
        }
    </style>
</head>
<body class="bg-gray-200 text-gray-800 antialiased">
    <div class="container mx-auto p-4 min-h-screen flex flex-col">

        <header class="mb-6 text-center">
            <h1 class="text-3xl sm:text-4xl font-bold text-indigo-700">AI-Powered Art Generator</h1>
            <p class="text-md sm:text-lg text-gray-600 mt-2">Craft unique visuals from your textual descriptions. (Conceptual Mock-up)</p>
        </header>

        <main class="flex-grow flex flex-col lg:flex-row gap-6">

            <div class="lg:w-2/5 xl:w-1/3 control-panel-bg p-6 rounded-xl shadow-xl flex flex-col gap-5">
                <div>
                    <label for="prompt" class="block text-sm font-medium text-gray-700 mb-1">Enter Your Prompt:</label>
                    <textarea id="prompt" name="prompt" rows="4" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., A majestic lion wearing a crown, digital painting"></textarea>
                </div>

                <div>
                    <label for="style" class="block text-sm font-medium text-gray-700 mb-1">Art Style:</label>
                    <select id="style" name="style" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 bg-white transition-shadow">
                        <option value="photorealistic">Photorealistic</option>
                        <option value="impressionistic">Impressionistic</option>
                        <option value="surreal">Surreal</option>
                        <option value="abstract">Abstract</option>
                        <option value="cartoon">Cartoon</option>
                        <option value="pixel_art">Pixel Art</option>
                        <option value="fantasy">Fantasy Art</option>
                        <option value="sci_fi">Sci-Fi Concept Art</option>
                        <option value="watercolor">Watercolor</option>
                        <option value="oil_painting">Oil Painting</option>
                        <option value="anime">Anime / Manga</option>
                        <option value="cyberpunk">Cyberpunk</option>
                        <option value="steampunk">Steampunk</option>
                    </select>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">Aspect Ratio:</label>
                    <div class="grid grid-cols-3 gap-2 button-group">
                        <button data-ratio="1:1" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">1:1</button>
                        <button data-ratio="16:9" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">16:9</button>
                        <button data-ratio="4:3" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">4:3</button>
                        <button data-ratio="3:4" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">3:4</button>
                        <button data-ratio="9:16" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">9:16</button>
                        <button data-ratio="custom" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">Custom</button>
                    </div>
                    <div id="custom-ratio-inputs" class="hidden mt-2 flex gap-2">
                        <input type="number" id="custom-width" placeholder="Width (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                        <input type="number" id="custom-height" placeholder="Height (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                    </div>
                </div>

                <div>
                    <label for="negative-prompt" class="block text-sm font-medium text-gray-700 mb-1">Negative Prompt (Optional):</label>
                    <textarea id="negative-prompt" name="negative-prompt" rows="2" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., blurry, watermark, text, ugly, deformed"></textarea>
                </div>

                <div class="mt-auto pt-5 border-t border-gray-300">
                     <p class="text-xs text-gray-500 mb-3">Note: Image generation is simulated. No actual AI is used. This is a UI concept.</p>
                    <button id="generateBtn" class="w-full btn-primary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed">
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wand-2"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M17 18h-4"/></svg>
                        Generate Image
                    </button>
                </div>
            </div>

            <div class="lg:w-3/5 xl:w-2/3 image-display-bg p-4 sm:p-6 rounded-xl shadow-xl flex flex-col items-center justify-center relative">
                <div id="image-container" class="w-full max-w-3xl bg-gray-700 rounded-lg overflow-hidden flex items-center justify-center relative shadow-inner">
                    <img id="generatedImage" src="https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here" alt="Generated AI Art" class="object-contain w-full h-full transition-opacity duration-500">
                    <div id="loading-overlay" class="absolute inset-0 bg-gray-800 bg-opacity-80 flex flex-col items-center justify-center hidden transition-opacity duration-300">
                        <div class="spinner"></div>
                        <p class="text-white mt-4 font-medium text-lg">Generating your masterpiece...</p>
                    </div>
                     <div id="placeholder-text-container" class="absolute inset-0 flex items-center justify-center pointer-events-none">
                        <p class="text-gray-400 text-xl text-center p-4">Your AI Art Will Appear Here</p>
                    </div>
                </div>
                <div class="mt-6 w-full max-w-3xl flex flex-col sm:flex-row gap-3 justify-center button-group">
                    <button id="regenerateBtn" class="flex-1 btn-secondary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M3 21c-1.66 0-3-1.34-3-3s1.34-3 3-3c1.06 0 1.98.54 2.5 1.35"/><path d="M21 15v4a2 2 0 0 1-2 2H5.5a2.5 2.5 0 0 1-2.5-2.5V15"/></svg>
                        Regenerate
                    </button>
                    <button id="downloadBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
                        Download
                    </button>
                </div>
                 <p class="text-xs text-gray-500 mt-4 text-center">Download functionality is for demonstration purposes only.</p>
            </div>
        </main>

        <footer class="mt-8 py-4 text-center text-sm text-gray-500 border-t border-gray-300">
            <p>&copy; <span id="currentYear"></span> AI Art Generator Mock-up. All rights reserved (conceptually).</p>
            <p>This interface is a simulation and does not connect to any actual AI image generation models like DALL-E.</p>
        </footer>
    </div>

    <script>
        // --- DOM Elements ---
        const promptTextarea = document.getElementById('prompt');
        const styleSelect = document.getElementById('style');
        const negativePromptTextarea = document.getElementById('negative-prompt');
        const generateBtn = document.getElementById('generateBtn');
        const regenerateBtn = document.getElementById('regenerateBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const generatedImage = document.getElementById('generatedImage');
        const imageContainer = document.getElementById('image-container');
        const loadingOverlay = document.getElementById('loading-overlay');
        const placeholderTextContainer = document.getElementById('placeholder-text-container');
        const aspectRatioBtns = document.querySelectorAll('.aspect-ratio-btn');
        const customRatioInputs = document.getElementById('custom-ratio-inputs');
        const customWidthInput = document.getElementById('custom-width');
        const customHeightInput = document.getElementById('custom-height');
        const initialPlaceholderSrc = 'https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here';

        // --- State Variables ---
        let currentImageSeed = null;
        let currentAspectRatio = { width: 512, height: 512, ratioString: '1/1' }; // Default 1:1

        // --- Helper Functions ---
        function setButtonsDisabledState(disabled) {
            generateBtn.disabled = disabled;
            // Regenerate and Download should only be enabled if an image has been "generated"
            if (currentImageSeed !== null) {
                regenerateBtn.disabled = disabled;
                downloadBtn.disabled = disabled;
            } else {
                regenerateBtn.disabled = true;
                downloadBtn.disabled = true;
            }
        }
        
        function showLoading(isLoading) {
            if (isLoading) {
                loadingOverlay.classList.remove('hidden');
                placeholderTextContainer.classList.add('hidden');
                generatedImage.classList.add('opacity-0');
            } else {
                loadingOverlay.classList.add('hidden');
                generatedImage.classList.remove('opacity-0');
                // Show placeholder text only if the image src is the initial one
                if (generatedImage.src === initialPlaceholderSrc) {
                    placeholderTextContainer.classList.remove('hidden');
                } else {
                     placeholderTextContainer.classList.add('hidden');
                }
            }
        }

        // --- Aspect Ratio Logic ---
        function updateImageContainerAspectRatio() {
            imageContainer.style.aspectRatio = currentAspectRatio.ratioString;
            // Update placeholder if it's currently showing the default text
            if (generatedImage.src === initialPlaceholderSrc || generatedImage.src.startsWith('https://placehold.co/')) {
                 if (placeholderTextContainer.classList.contains('hidden') && generatedImage.src !== initialPlaceholderSrc) {
                    // If an image was already generated, update its dimensions
                    const url = new URL(generatedImage.src);
                    const text = url.searchParams.get('text') || "Generated Art";
                    const colors = url.pathname.substring(1).split('/').slice(1,3).join('/'); // e.g. 374151/9ca3af
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${colors}?text=${encodeURIComponent(text)}`;
                 } else {
                    // Otherwise, just update the default placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here`;
                 }
            }
        }

        aspectRatioBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                aspectRatioBtns.forEach(b => b.classList.remove('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold'));
                btn.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');

                const ratio = btn.dataset.ratio;
                if (ratio === 'custom') {
                    customRatioInputs.classList.remove('hidden');
                    customWidthInput.value = customWidthInput.value || currentAspectRatio.width;
                    customHeightInput.value = customHeightInput.value || currentAspectRatio.height;
                    // Trigger update if custom is selected, using current input values or defaults
                    handleCustomRatioChange();
                } else {
                    customRatioInputs.classList.add('hidden');
                    const [w, h] = ratio.split(':').map(Number);
                    currentAspectRatio = { width: w * 128, height: h * 128, ratioString: `${w}/${h}` }; // Scale for placeholder
                    if (w === 1 && h === 1) currentAspectRatio = { width: 512, height: 512, ratioString: '1/1'};
                    if (w === 16 && h === 9) currentAspectRatio = { width: 1024, height: 576, ratioString: '16/9'};
                    if (w === 4 && h === 3) currentAspectRatio = { width: 640, height: 480, ratioString: '4/3'};
                    if (w === 3 && h === 4) currentAspectRatio = { width: 480, height: 640, ratioString: '3/4'};
                    if (w === 9 && h === 16) currentAspectRatio = { width: 576, height: 1024, ratioString: '9/16'};
                    updateImageContainerAspectRatio();
                }
            });
        });
        
        function handleCustomRatioChange() {
            let width = parseInt(customWidthInput.value);
            let height = parseInt(customHeightInput.value);

            // Basic validation and clamping
            width = Math.max(64, Math.min(2048, width || currentAspectRatio.width));
            height = Math.max(64, Math.min(2048, height || currentAspectRatio.height));
            
            customWidthInput.value = width; // Update input field with validated value
            customHeightInput.value = height; // Update input field with validated value

            if (width > 0 && height > 0) {
                currentAspectRatio = { width, height, ratioString: `${width}/${height}` };
                updateImageContainerAspectRatio();
            }
        }

        customWidthInput.addEventListener('change', handleCustomRatioChange); // Use 'change' to update when focus is lost or enter is pressed
        customHeightInput.addEventListener('change', handleCustomRatioChange);


        // --- Image Generation Logic ---
        function simulateImageGeneration(isRegeneration = false) {
            const prompt = promptTextarea.value.trim();
            if (!prompt && !isRegeneration) {
                // Using a more subtle way to indicate error than alert()
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }

            showLoading(true);
            setButtonsDisabledState(true);

            setTimeout(() => {
                const style = styleSelect.value;
                
                currentImageSeed = isRegeneration ? (currentImageSeed || Date.now()) + 1 : Date.now();

                let placeholderText = prompt.substring(0, 25) || "Generated Image";
                if (style) placeholderText += ` (${style.replace(/_/g, ' ')})`;
                placeholderText += ` #${currentImageSeed % 1000}`;

                const bgColor = Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // R
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // G
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0'); // B (avoiding too dark/light)
                const textColor = 'FFFFFF';

                generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${bgColor}/${textColor}?text=${encodeURIComponent(placeholderText)}`;
                
                generatedImage.onload = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                };
                generatedImage.onerror = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                    // Show error in placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/FF0000/FFFFFF?text=Error+Loading+Image`;
                    placeholderTextContainer.classList.add('hidden'); // Ensure main placeholder text is hidden
                };
            }, 1500 + Math.random() * 1000);
        }

        generateBtn.addEventListener('click', () => simulateImageGeneration(false));
        regenerateBtn.addEventListener('click', () => {
            if (!promptTextarea.value.trim() && !currentImageSeed) {
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }
            simulateImageGeneration(true);
        });

        // --- Download Logic (Conceptual) ---
        downloadBtn.addEventListener('click', () => {
            if (generatedImage.src && generatedImage.src !== initialPlaceholderSrc && !generatedImage.src.includes('Error+Loading')) {
                const link = document.createElement('a');
                link.href = generatedImage.src;
                
                const urlParams = new URLSearchParams(new URL(generatedImage.src).search);
                const textParam = urlParams.get('text') || 'ai_art_simulation';
                const filename = textParam.replace(/[^a-z0-9_]+/gi, '_').toLowerCase().substring(0,50) + '.png';
                link.download = filename;
                
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                
                const originalText = downloadBtn.innerHTML;
                const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><polyline points="20 6 9 17 4 12"></polyline></svg> Downloaded!`;
                downloadBtn.innerHTML = checkSVG;
                setTimeout(() => { downloadBtn.innerHTML = originalText; }, 2500);

            } else {
                // Could show a small message here instead of alert
                console.warn("No image generated to download or image is the initial placeholder/error.");
            }
        });

        // --- Initial Setup ---
        document.getElementById('currentYear').textContent = new Date().getFullYear();
        // Set 1:1 as default active aspect ratio button
        const defaultRatioButton = document.querySelector('.aspect-ratio-btn[data-ratio="1:1"]');
        if (defaultRatioButton) {
            defaultRatioButton.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');
        }
        updateImageContainerAspectRatio(); // Apply initial aspect ratio to container
        showLoading(false); // Ensure correct initial visibility of placeholder/image
        setButtonsDisabledState(true); // Regenerate/Download disabled initially

    </script>
</body>
</html>
 

----------------------------------------

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Image Generator Mock-up</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; }
        .control-panel-bg { background-color: #f9fafb; /* Tailwind gray-50 */ }
        .image-display-bg { background-color: #f3f4f6; /* Tailwind gray-100 */ }
        .btn-primary { background-color: #4f46e5; /* Tailwind indigo-600 */ color: white; }
        .btn-primary:hover { background-color: #4338ca; /* Tailwind indigo-700 */ }
        .btn-secondary { background-color: #6b7280; /* Tailwind gray-500 */ color: white; }
        .btn-secondary:hover { background-color: #4b5563; /* Tailwind gray-600 */ }
        
        /* Custom scrollbar for webkit browsers */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        /* Loading spinner */
        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border-left-color: #4f46e5; /* Tailwind indigo-600 */
            animation: spin 1s ease infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Ensure buttons in a group don't wrap weirdly on small screens */
        .button-group > button {
            min-width: max-content;
        }
    </style>
</head>
<body class="bg-gray-200 text-gray-800 antialiased">
    <div class="container mx-auto p-4 min-h-screen flex flex-col">

        <header class="mb-6 text-center">
            <h1 class="text-3xl sm:text-4xl font-bold text-indigo-700">AI-Powered Art Generator</h1>
            <p class="text-md sm:text-lg text-gray-600 mt-2">Craft unique visuals from your textual descriptions. (Conceptual Mock-up)</p>
        </header>

        <main class="flex-grow flex flex-col lg:flex-row gap-6">

            <div class="lg:w-2/5 xl:w-1/3 control-panel-bg p-6 rounded-xl shadow-xl flex flex-col gap-5">
                <div>
                    <label for="prompt" class="block text-sm font-medium text-gray-700 mb-1">Enter Your Prompt:</label>
                    <textarea id="prompt" name="prompt" rows="4" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., A majestic lion wearing a crown, digital painting"></textarea>
                </div>

                <div>
                    <label for="style" class="block text-sm font-medium text-gray-700 mb-1">Art Style:</label>
                    <select id="style" name="style" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 bg-white transition-shadow">
                        <option value="photorealistic">Photorealistic</option>
                        <option value="impressionistic">Impressionistic</option>
                        <option value="surreal">Surreal</option>
                        <option value="abstract">Abstract</option>
                        <option value="cartoon">Cartoon</option>
                        <option value="pixel_art">Pixel Art</option>
                        <option value="fantasy">Fantasy Art</option>
                        <option value="sci_fi">Sci-Fi Concept Art</option>
                        <option value="watercolor">Watercolor</option>
                        <option value="oil_painting">Oil Painting</option>
                        <option value="anime">Anime / Manga</option>
                        <option value="cyberpunk">Cyberpunk</option>
                        <option value="steampunk">Steampunk</option>
                    </select>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">Aspect Ratio:</label>
                    <div class="grid grid-cols-3 gap-2 button-group">
                        <button data-ratio="1:1" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">1:1</button>
                        <button data-ratio="16:9" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">16:9</button>
                        <button data-ratio="4:3" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">4:3</button>
                        <button data-ratio="3:4" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">3:4</button>
                        <button data-ratio="9:16" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">9:16</button>
                        <button data-ratio="custom" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">Custom</button>
                    </div>
                    <div id="custom-ratio-inputs" class="hidden mt-2 flex gap-2">
                        <input type="number" id="custom-width" placeholder="Width (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                        <input type="number" id="custom-height" placeholder="Height (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                    </div>
                </div>

                <div>
                    <label for="negative-prompt" class="block text-sm font-medium text-gray-700 mb-1">Negative Prompt (Optional):</label>
                    <textarea id="negative-prompt" name="negative-prompt" rows="2" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., blurry, watermark, text, ugly, deformed"></textarea>
                </div>

                <div class="mt-auto pt-5 border-t border-gray-300">
                     <p class="text-xs text-gray-500 mb-3">Note: Image generation is simulated. No actual AI is used. This is a UI concept.</p>
                    <button id="generateBtn" class="w-full btn-primary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed">
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wand-2"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M17 18h-4"/></svg>
                        Generate Image
                    </button>
                </div>
            </div>

            <div class="lg:w-3/5 xl:w-2/3 image-display-bg p-4 sm:p-6 rounded-xl shadow-xl flex flex-col items-center justify-center relative">
                <div id="image-container" class="w-full max-w-3xl bg-gray-700 rounded-lg overflow-hidden flex items-center justify-center relative shadow-inner">
                    <img id="generatedImage" src="https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here" alt="Generated AI Art" class="object-contain w-full h-full transition-opacity duration-500">
                    <div id="loading-overlay" class="absolute inset-0 bg-gray-800 bg-opacity-80 flex flex-col items-center justify-center hidden transition-opacity duration-300">
                        <div class="spinner"></div>
                        <p class="text-white mt-4 font-medium text-lg">Generating your masterpiece...</p>
                    </div>
                     <div id="placeholder-text-container" class="absolute inset-0 flex items-center justify-center pointer-events-none">
                        <p class="text-gray-400 text-xl text-center p-4">Your AI Art Will Appear Here</p>
                    </div>
                </div>
                <div class="mt-6 w-full max-w-3xl flex flex-col sm:flex-row gap-3 justify-center button-group">
                    <button id="regenerateBtn" class="flex-1 btn-secondary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M3 21c-1.66 0-3-1.34-3-3s1.34-3 3-3c1.06 0 1.98.54 2.5 1.35"/><path d="M21 15v4a2 2 0 0 1-2 2H5.5a2.5 2.5 0 0 1-2.5-2.5V15"/></svg>
                        Regenerate
                    </button>
                    <button id="downloadBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
                        Download
                    </button>
                </div>
                 <p class="text-xs text-gray-500 mt-4 text-center">Download functionality is for demonstration purposes only.</p>
            </div>
        </main>

        <footer class="mt-8 py-4 text-center text-sm text-gray-500 border-t border-gray-300">
            <p>&copy; <span id="currentYear"></span> AI Art Generator Mock-up. All rights reserved (conceptually).</p>
            <p>This interface is a simulation and does not connect to any actual AI image generation models like DALL-E.</p>
        </footer>
    </div>

    <script>
        // --- DOM Elements ---
        const promptTextarea = document.getElementById('prompt');
        const styleSelect = document.getElementById('style');
        const negativePromptTextarea = document.getElementById('negative-prompt');
        const generateBtn = document.getElementById('generateBtn');
        const regenerateBtn = document.getElementById('regenerateBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const generatedImage = document.getElementById('generatedImage');
        const imageContainer = document.getElementById('image-container');
        const loadingOverlay = document.getElementById('loading-overlay');
        const placeholderTextContainer = document.getElementById('placeholder-text-container');
        const aspectRatioBtns = document.querySelectorAll('.aspect-ratio-btn');
        const customRatioInputs = document.getElementById('custom-ratio-inputs');
        const customWidthInput = document.getElementById('custom-width');
        const customHeightInput = document.getElementById('custom-height');
        const initialPlaceholderSrc = 'https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here';

        // --- State Variables ---
        let currentImageSeed = null;
        let currentAspectRatio = { width: 512, height: 512, ratioString: '1/1' }; // Default 1:1

        // --- Helper Functions ---
        function setButtonsDisabledState(disabled) {
            generateBtn.disabled = disabled;
            // Regenerate and Download should only be enabled if an image has been "generated"
            if (currentImageSeed !== null) {
                regenerateBtn.disabled = disabled;
                downloadBtn.disabled = disabled;
            } else {
                regenerateBtn.disabled = true;
                downloadBtn.disabled = true;
            }
        }
        
        function showLoading(isLoading) {
            if (isLoading) {
                loadingOverlay.classList.remove('hidden');
                placeholderTextContainer.classList.add('hidden');
                generatedImage.classList.add('opacity-0');
            } else {
                loadingOverlay.classList.add('hidden');
                generatedImage.classList.remove('opacity-0');
                // Show placeholder text only if the image src is the initial one
                if (generatedImage.src === initialPlaceholderSrc) {
                    placeholderTextContainer.classList.remove('hidden');
                } else {
                     placeholderTextContainer.classList.add('hidden');
                }
            }
        }

        // --- Aspect Ratio Logic ---
        function updateImageContainerAspectRatio() {
            imageContainer.style.aspectRatio = currentAspectRatio.ratioString;
            // Update placeholder if it's currently showing the default text
            if (generatedImage.src === initialPlaceholderSrc || generatedImage.src.startsWith('https://placehold.co/')) {
                 if (placeholderTextContainer.classList.contains('hidden') && generatedImage.src !== initialPlaceholderSrc) {
                    // If an image was already generated, update its dimensions
                    const url = new URL(generatedImage.src);
                    const text = url.searchParams.get('text') || "Generated Art";
                    const colors = url.pathname.substring(1).split('/').slice(1,3).join('/'); // e.g. 374151/9ca3af
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${colors}?text=${encodeURIComponent(text)}`;
                 } else {
                    // Otherwise, just update the default placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here`;
                 }
            }
        }

        aspectRatioBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                aspectRatioBtns.forEach(b => b.classList.remove('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold'));
                btn.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');

                const ratio = btn.dataset.ratio;
                if (ratio === 'custom') {
                    customRatioInputs.classList.remove('hidden');
                    customWidthInput.value = customWidthInput.value || currentAspectRatio.width;
                    customHeightInput.value = customHeightInput.value || currentAspectRatio.height;
                    // Trigger update if custom is selected, using current input values or defaults
                    handleCustomRatioChange();
                } else {
                    customRatioInputs.classList.add('hidden');
                    const [w, h] = ratio.split(':').map(Number);
                    currentAspectRatio = { width: w * 128, height: h * 128, ratioString: `${w}/${h}` }; // Scale for placeholder
                    if (w === 1 && h === 1) currentAspectRatio = { width: 512, height: 512, ratioString: '1/1'};
                    if (w === 16 && h === 9) currentAspectRatio = { width: 1024, height: 576, ratioString: '16/9'};
                    if (w === 4 && h === 3) currentAspectRatio = { width: 640, height: 480, ratioString: '4/3'};
                    if (w === 3 && h === 4) currentAspectRatio = { width: 480, height: 640, ratioString: '3/4'};
                    if (w === 9 && h === 16) currentAspectRatio = { width: 576, height: 1024, ratioString: '9/16'};
                    updateImageContainerAspectRatio();
                }
            });
        });
        
        function handleCustomRatioChange() {
            let width = parseInt(customWidthInput.value);
            let height = parseInt(customHeightInput.value);

            // Basic validation and clamping
            width = Math.max(64, Math.min(2048, width || currentAspectRatio.width));
            height = Math.max(64, Math.min(2048, height || currentAspectRatio.height));
            
            customWidthInput.value = width; // Update input field with validated value
            customHeightInput.value = height; // Update input field with validated value

            if (width > 0 && height > 0) {
                currentAspectRatio = { width, height, ratioString: `${width}/${height}` };
                updateImageContainerAspectRatio();
            }
        }

        customWidthInput.addEventListener('change', handleCustomRatioChange); // Use 'change' to update when focus is lost or enter is pressed
        customHeightInput.addEventListener('change', handleCustomRatioChange);


        // --- Image Generation Logic ---
        function simulateImageGeneration(isRegeneration = false) {
            const prompt = promptTextarea.value.trim();
            if (!prompt && !isRegeneration) {
                // Using a more subtle way to indicate error than alert()
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }

            showLoading(true);
            setButtonsDisabledState(true);

            setTimeout(() => {
                const style = styleSelect.value;
                
                currentImageSeed = isRegeneration ? (currentImageSeed || Date.now()) + 1 : Date.now();

                let placeholderText = prompt.substring(0, 25) || "Generated Image";
                if (style) placeholderText += ` (${style.replace(/_/g, ' ')})`;
                placeholderText += ` #${currentImageSeed % 1000}`;

                const bgColor = Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // R
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // G
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0'); // B (avoiding too dark/light)
                const textColor = 'FFFFFF';

                generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${bgColor}/${textColor}?text=${encodeURIComponent(placeholderText)}`;
                
                generatedImage.onload = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                };
                generatedImage.onerror = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                    // Show error in placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/FF0000/FFFFFF?text=Error+Loading+Image`;
                    placeholderTextContainer.classList.add('hidden'); // Ensure main placeholder text is hidden
                };
            }, 1500 + Math.random() * 1000);
        }

        generateBtn.addEventListener('click', () => simulateImageGeneration(false));
        regenerateBtn.addEventListener('click', () => {
            if (!promptTextarea.value.trim() && !currentImageSeed) {
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }
            simulateImageGeneration(true);
        });

        // --- Download Logic (Conceptual) ---
        downloadBtn.addEventListener('click', () => {
            if (generatedImage.src && generatedImage.src !== initialPlaceholderSrc && !generatedImage.src.includes('Error+Loading')) {
                const link = document.createElement('a');
                link.href = generatedImage.src;
                
                const urlParams = new URLSearchParams(new URL(generatedImage.src).search);
                const textParam = urlParams.get('text') || 'ai_art_simulation';
                const filename = textParam.replace(/[^a-z0-9_]+/gi, '_').toLowerCase().substring(0,50) + '.png';
                link.download = filename;
                
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                
                const originalText = downloadBtn.innerHTML;
                const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><polyline points="20 6 9 17 4 12"></polyline></svg> Downloaded!`;
                downloadBtn.innerHTML = checkSVG;
                setTimeout(() => { downloadBtn.innerHTML = originalText; }, 2500);

            } else {
                // Could show a small message here instead of alert
                console.warn("No image generated to download or image is the initial placeholder/error.");
            }
        });

        // --- Initial Setup ---
        document.getElementById('currentYear').textContent = new Date().getFullYear();
        // Set 1:1 as default active aspect ratio button
        const defaultRatioButton = document.querySelector('.aspect-ratio-btn[data-ratio="1:1"]');
        if (defaultRatioButton) {
            defaultRatioButton.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');
        }
        updateImageContainerAspectRatio(); // Apply initial aspect ratio to container
        showLoading(false); // Ensure correct initial visibility of placeholder/image
        setButtonsDisabledState(true); // Regenerate/Download disabled initially

    </script>
</body>
</html>

-----------------------

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Image Generator Mock-up</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; }
        .control-panel-bg { background-color: #f9fafb; /* Tailwind gray-50 */ }
        .image-display-bg { background-color: #f3f4f6; /* Tailwind gray-100 */ }
        .btn-primary { background-color: #4f46e5; /* Tailwind indigo-600 */ color: white; }
        .btn-primary:hover { background-color: #4338ca; /* Tailwind indigo-700 */ }
        .btn-secondary { background-color: #6b7280; /* Tailwind gray-500 */ color: white; }
        .btn-secondary:hover { background-color: #4b5563; /* Tailwind gray-600 */ }
        
        /* Custom scrollbar for webkit browsers */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        /* Loading spinner */
        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border-left-color: #4f46e5; /* Tailwind indigo-600 */
            animation: spin 1s ease infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Ensure buttons in a group don't wrap weirdly on small screens */
        .button-group > button {
            min-width: max-content;
        }
    </style>
</head>
<body class="bg-gray-200 text-gray-800 antialiased">
    <div class="container mx-auto p-4 min-h-screen flex flex-col">

        <header class="mb-6 text-center">
            <h1 class="text-3xl sm:text-4xl font-bold text-indigo-700">AI-Powered Art Generator</h1>
            <p class="text-md sm:text-lg text-gray-600 mt-2">Craft unique visuals from your textual descriptions. (Conceptual Mock-up)</p>
        </header>

        <main class="flex-grow flex flex-col lg:flex-row gap-6">

            <div class="lg:w-2/5 xl:w-1/3 control-panel-bg p-6 rounded-xl shadow-xl flex flex-col gap-5">
                <div>
                    <label for="prompt" class="block text-sm font-medium text-gray-700 mb-1">Enter Your Prompt:</label>
                    <textarea id="prompt" name="prompt" rows="4" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., A majestic lion wearing a crown, digital painting"></textarea>
                </div>

                <div>
                    <label for="style" class="block text-sm font-medium text-gray-700 mb-1">Art Style:</label>
                    <select id="style" name="style" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 bg-white transition-shadow">
                        <option value="photorealistic">Photorealistic</option>
                        <option value="impressionistic">Impressionistic</option>
                        <option value="surreal">Surreal</option>
                        <option value="abstract">Abstract</option>
                        <option value="cartoon">Cartoon</option>
                        <option value="pixel_art">Pixel Art</option>
                        <option value="fantasy">Fantasy Art</option>
                        <option value="sci_fi">Sci-Fi Concept Art</option>
                        <option value="watercolor">Watercolor</option>
                        <option value="oil_painting">Oil Painting</option>
                        <option value="anime">Anime / Manga</option>
                        <option value="cyberpunk">Cyberpunk</option>
                        <option value="steampunk">Steampunk</option>
                    </select>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">Aspect Ratio:</label>
                    <div class="grid grid-cols-3 gap-2 button-group">
                        <button data-ratio="1:1" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">1:1</button>
                        <button data-ratio="16:9" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">16:9</button>
                        <button data-ratio="4:3" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">4:3</button>
                        <button data-ratio="3:4" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">3:4</button>
                        <button data-ratio="9:16" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">9:16</button>
                        <button data-ratio="custom" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">Custom</button>
                    </div>
                    <div id="custom-ratio-inputs" class="hidden mt-2 flex gap-2">
                        <input type="number" id="custom-width" placeholder="Width (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                        <input type="number" id="custom-height" placeholder="Height (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                    </div>
                </div>

                <div>
                    <label for="negative-prompt" class="block text-sm font-medium text-gray-700 mb-1">Negative Prompt (Optional):</label>
                    <textarea id="negative-prompt" name="negative-prompt" rows="2" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., blurry, watermark, text, ugly, deformed"></textarea>
                </div>

                <div class="mt-auto pt-5 border-t border-gray-300">
                     <p class="text-xs text-gray-500 mb-3">Note: Image generation is simulated. No actual AI is used. This is a UI concept.</p>
                    <button id="generateBtn" class="w-full btn-primary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed">
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wand-2"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M17 18h-4"/></svg>
                        Generate Image
                    </button>
                </div>
            </div>

            <div class="lg:w-3/5 xl:w-2/3 image-display-bg p-4 sm:p-6 rounded-xl shadow-xl flex flex-col items-center justify-center relative">
                <div id="image-container" class="w-full max-w-3xl bg-gray-700 rounded-lg overflow-hidden flex items-center justify-center relative shadow-inner">
                    <img id="generatedImage" src="https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here" alt="Generated AI Art" class="object-contain w-full h-full transition-opacity duration-500">
                    <div id="loading-overlay" class="absolute inset-0 bg-gray-800 bg-opacity-80 flex flex-col items-center justify-center hidden transition-opacity duration-300">
                        <div class="spinner"></div>
                        <p class="text-white mt-4 font-medium text-lg">Generating your masterpiece...</p>
                    </div>
                     <div id="placeholder-text-container" class="absolute inset-0 flex items-center justify-center pointer-events-none">
                        <p class="text-gray-400 text-xl text-center p-4">Your AI Art Will Appear Here</p>
                    </div>
                </div>
                <div class="mt-6 w-full max-w-3xl flex flex-col sm:flex-row gap-3 justify-center button-group">
                    <button id="regenerateBtn" class="flex-1 btn-secondary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M3 21c-1.66 0-3-1.34-3-3s1.34-3 3-3c1.06 0 1.98.54 2.5 1.35"/><path d="M21 15v4a2 2 0 0 1-2 2H5.5a2.5 2.5 0 0 1-2.5-2.5V15"/></svg>
                        Regenerate
                    </button>
                    <button id="downloadBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
                        Download
                    </button>
                </div>
                 <p class="text-xs text-gray-500 mt-4 text-center">Download functionality is for demonstration purposes only.</p>
            </div>
        </main>

        <footer class="mt-8 py-4 text-center text-sm text-gray-500 border-t border-gray-300">
            <p>&copy; <span id="currentYear"></span> AI Art Generator Mock-up. All rights reserved (conceptually).</p>
            <p>This interface is a simulation and does not connect to any actual AI image generation models like DALL-E.</p>
        </footer>
    </div>

    <script>
        // --- DOM Elements ---
        const promptTextarea = document.getElementById('prompt');
        const styleSelect = document.getElementById('style');
        const negativePromptTextarea = document.getElementById('negative-prompt');
        const generateBtn = document.getElementById('generateBtn');
        const regenerateBtn = document.getElementById('regenerateBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const generatedImage = document.getElementById('generatedImage');
        const imageContainer = document.getElementById('image-container');
        const loadingOverlay = document.getElementById('loading-overlay');
        const placeholderTextContainer = document.getElementById('placeholder-text-container');
        const aspectRatioBtns = document.querySelectorAll('.aspect-ratio-btn');
        const customRatioInputs = document.getElementById('custom-ratio-inputs');
        const customWidthInput = document.getElementById('custom-width');
        const customHeightInput = document.getElementById('custom-height');
        const initialPlaceholderSrc = 'https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here';

        // --- State Variables ---
        let currentImageSeed = null;
        let currentAspectRatio = { width: 512, height: 512, ratioString: '1/1' }; // Default 1:1

        // --- Helper Functions ---
        function setButtonsDisabledState(disabled) {
            generateBtn.disabled = disabled;
            // Regenerate and Download should only be enabled if an image has been "generated"
            if (currentImageSeed !== null) {
                regenerateBtn.disabled = disabled;
                downloadBtn.disabled = disabled;
            } else {
                regenerateBtn.disabled = true;
                downloadBtn.disabled = true;
            }
        }
        
        function showLoading(isLoading) {
            if (isLoading) {
                loadingOverlay.classList.remove('hidden');
                placeholderTextContainer.classList.add('hidden');
                generatedImage.classList.add('opacity-0');
            } else {
                loadingOverlay.classList.add('hidden');
                generatedImage.classList.remove('opacity-0');
                // Show placeholder text only if the image src is the initial one
                if (generatedImage.src === initialPlaceholderSrc) {
                    placeholderTextContainer.classList.remove('hidden');
                } else {
                     placeholderTextContainer.classList.add('hidden');
                }
            }
        }

        // --- Aspect Ratio Logic ---
        function updateImageContainerAspectRatio() {
            imageContainer.style.aspectRatio = currentAspectRatio.ratioString;
            // Update placeholder if it's currently showing the default text
            if (generatedImage.src === initialPlaceholderSrc || generatedImage.src.startsWith('https://placehold.co/')) {
                 if (placeholderTextContainer.classList.contains('hidden') && generatedImage.src !== initialPlaceholderSrc) {
                    // If an image was already generated, update its dimensions
                    const url = new URL(generatedImage.src);
                    const text = url.searchParams.get('text') || "Generated Art";
                    const colors = url.pathname.substring(1).split('/').slice(1,3).join('/'); // e.g. 374151/9ca3af
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${colors}?text=${encodeURIComponent(text)}`;
                 } else {
                    // Otherwise, just update the default placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here`;
                 }
            }
        }

        aspectRatioBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                aspectRatioBtns.forEach(b => b.classList.remove('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold'));
                btn.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');

                const ratio = btn.dataset.ratio;
                if (ratio === 'custom') {
                    customRatioInputs.classList.remove('hidden');
                    customWidthInput.value = customWidthInput.value || currentAspectRatio.width;
                    customHeightInput.value = customHeightInput.value || currentAspectRatio.height;
                    // Trigger update if custom is selected, using current input values or defaults
                    handleCustomRatioChange();
                } else {
                    customRatioInputs.classList.add('hidden');
                    const [w, h] = ratio.split(':').map(Number);
                    currentAspectRatio = { width: w * 128, height: h * 128, ratioString: `${w}/${h}` }; // Scale for placeholder
                    if (w === 1 && h === 1) currentAspectRatio = { width: 512, height: 512, ratioString: '1/1'};
                    if (w === 16 && h === 9) currentAspectRatio = { width: 1024, height: 576, ratioString: '16/9'};
                    if (w === 4 && h === 3) currentAspectRatio = { width: 640, height: 480, ratioString: '4/3'};
                    if (w === 3 && h === 4) currentAspectRatio = { width: 480, height: 640, ratioString: '3/4'};
                    if (w === 9 && h === 16) currentAspectRatio = { width: 576, height: 1024, ratioString: '9/16'};
                    updateImageContainerAspectRatio();
                }
            });
        });
        
        function handleCustomRatioChange() {
            let width = parseInt(customWidthInput.value);
            let height = parseInt(customHeightInput.value);

            // Basic validation and clamping
            width = Math.max(64, Math.min(2048, width || currentAspectRatio.width));
            height = Math.max(64, Math.min(2048, height || currentAspectRatio.height));
            
            customWidthInput.value = width; // Update input field with validated value
            customHeightInput.value = height; // Update input field with validated value

            if (width > 0 && height > 0) {
                currentAspectRatio = { width, height, ratioString: `${width}/${height}` };
                updateImageContainerAspectRatio();
            }
        }

        customWidthInput.addEventListener('change', handleCustomRatioChange); // Use 'change' to update when focus is lost or enter is pressed
        customHeightInput.addEventListener('change', handleCustomRatioChange);


        // --- Image Generation Logic ---
        function simulateImageGeneration(isRegeneration = false) {
            const prompt = promptTextarea.value.trim();
            if (!prompt && !isRegeneration) {
                // Using a more subtle way to indicate error than alert()
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }

            showLoading(true);
            setButtonsDisabledState(true);

            setTimeout(() => {
                const style = styleSelect.value;
                
                currentImageSeed = isRegeneration ? (currentImageSeed || Date.now()) + 1 : Date.now();

                let placeholderText = prompt.substring(0, 25) || "Generated Image";
                if (style) placeholderText += ` (${style.replace(/_/g, ' ')})`;
                placeholderText += ` #${currentImageSeed % 1000}`;

                const bgColor = Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // R
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // G
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0'); // B (avoiding too dark/light)
                const textColor = 'FFFFFF';

                generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${bgColor}/${textColor}?text=${encodeURIComponent(placeholderText)}`;
                
                generatedImage.onload = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                };
                generatedImage.onerror = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                    // Show error in placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/FF0000/FFFFFF?text=Error+Loading+Image`;
                    placeholderTextContainer.classList.add('hidden'); // Ensure main placeholder text is hidden
                };
            }, 1500 + Math.random() * 1000);
        }

        generateBtn.addEventListener('click', () => simulateImageGeneration(false));
        regenerateBtn.addEventListener('click', () => {
            if (!promptTextarea.value.trim() && !currentImageSeed) {
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }
            simulateImageGeneration(true);
        });

        // --- Download Logic (Conceptual) ---
        downloadBtn.addEventListener('click', () => {
            if (generatedImage.src && generatedImage.src !== initialPlaceholderSrc && !generatedImage.src.includes('Error+Loading')) {
                const link = document.createElement('a');
                link.href = generatedImage.src;
                
                const urlParams = new URLSearchParams(new URL(generatedImage.src).search);
                const textParam = urlParams.get('text') || 'ai_art_simulation';
                const filename = textParam.replace(/[^a-z0-9_]+/gi, '_').toLowerCase().substring(0,50) + '.png';
                link.download = filename;
                
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                
                const originalText = downloadBtn.innerHTML;
                const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><polyline points="20 6 9 17 4 12"></polyline></svg> Downloaded!`;
                downloadBtn.innerHTML = checkSVG;
                setTimeout(() => { downloadBtn.innerHTML = originalText; }, 2500);

            } else {
                // Could show a small message here instead of alert
                console.warn("No image generated to download or image is the initial placeholder/error.");
            }
        });

        // --- Initial Setup ---
        document.getElementById('currentYear').textContent = new Date().getFullYear();
        // Set 1:1 as default active aspect ratio button
        const defaultRatioButton = document.querySelector('.aspect-ratio-btn[data-ratio="1:1"]');
        if (defaultRatioButton) {
            defaultRatioButton.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');
        }
        updateImageContainerAspectRatio(); // Apply initial aspect ratio to container
        showLoading(false); // Ensure correct initial visibility of placeholder/image
        setButtonsDisabledState(true); // Regenerate/Download disabled initially

    </script>
</body>
</html>

Deploy

  <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Image Generator Mock-up</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        body { font-family: 'Inter', sans-serif; }
        .control-panel-bg { background-color: #f9fafb; /* Tailwind gray-50 */ }
        .image-display-bg { background-color: #f3f4f6; /* Tailwind gray-100 */ }
        .btn-primary { background-color: #4f46e5; /* Tailwind indigo-600 */ color: white; }
        .btn-primary:hover { background-color: #4338ca; /* Tailwind indigo-700 */ }
        .btn-secondary { background-color: #6b7280; /* Tailwind gray-500 */ color: white; }
        .btn-secondary:hover { background-color: #4b5563; /* Tailwind gray-600 */ }
        
        /* Custom scrollbar for webkit browsers */
        ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 10px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #555;
        }

        /* Loading spinner */
        .spinner {
            border: 4px solid rgba(0, 0, 0, 0.1);
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border-left-color: #4f46e5; /* Tailwind indigo-600 */
            animation: spin 1s ease infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        /* Ensure buttons in a group don't wrap weirdly on small screens */
        .button-group > button {
            min-width: max-content;
        }
    </style>
</head>
<body class="bg-gray-200 text-gray-800 antialiased">
    <div class="container mx-auto p-4 min-h-screen flex flex-col">

        <header class="mb-6 text-center">
            <h1 class="text-3xl sm:text-4xl font-bold text-indigo-700">AI-Powered Art Generator</h1>
            <p class="text-md sm:text-lg text-gray-600 mt-2">Craft unique visuals from your textual descriptions. (Conceptual Mock-up)</p>
        </header>

        <main class="flex-grow flex flex-col lg:flex-row gap-6">

            <div class="lg:w-2/5 xl:w-1/3 control-panel-bg p-6 rounded-xl shadow-xl flex flex-col gap-5">
                <div>
                    <label for="prompt" class="block text-sm font-medium text-gray-700 mb-1">Enter Your Prompt:</label>
                    <textarea id="prompt" name="prompt" rows="4" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., A majestic lion wearing a crown, digital painting"></textarea>
                </div>

                <div>
                    <label for="style" class="block text-sm font-medium text-gray-700 mb-1">Art Style:</label>
                    <select id="style" name="style" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 bg-white transition-shadow">
                        <option value="photorealistic">Photorealistic</option>
                        <option value="impressionistic">Impressionistic</option>
                        <option value="surreal">Surreal</option>
                        <option value="abstract">Abstract</option>
                        <option value="cartoon">Cartoon</option>
                        <option value="pixel_art">Pixel Art</option>
                        <option value="fantasy">Fantasy Art</option>
                        <option value="sci_fi">Sci-Fi Concept Art</option>
                        <option value="watercolor">Watercolor</option>
                        <option value="oil_painting">Oil Painting</option>
                        <option value="anime">Anime / Manga</option>
                        <option value="cyberpunk">Cyberpunk</option>
                        <option value="steampunk">Steampunk</option>
                    </select>
                </div>

                <div>
                    <label class="block text-sm font-medium text-gray-700 mb-1">Aspect Ratio:</label>
                    <div class="grid grid-cols-3 gap-2 button-group">
                        <button data-ratio="1:1" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">1:1</button>
                        <button data-ratio="16:9" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">16:9</button>
                        <button data-ratio="4:3" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">4:3</button>
                        <button data-ratio="3:4" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">3:4</button>
                        <button data-ratio="9:16" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">9:16</button>
                        <button data-ratio="custom" class="aspect-ratio-btn p-2 border border-gray-300 rounded-md hover:bg-indigo-100 focus:bg-indigo-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-colors">Custom</button>
                    </div>
                    <div id="custom-ratio-inputs" class="hidden mt-2 flex gap-2">
                        <input type="number" id="custom-width" placeholder="Width (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                        <input type="number" id="custom-height" placeholder="Height (px)" class="w-1/2 p-2 border border-gray-300 rounded-md focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" min="64" max="2048">
                    </div>
                </div>

                <div>
                    <label for="negative-prompt" class="block text-sm font-medium text-gray-700 mb-1">Negative Prompt (Optional):</label>
                    <textarea id="negative-prompt" name="negative-prompt" rows="2" class="w-full p-3 border border-gray-300 rounded-lg shadow-sm focus:ring-indigo-500 focus:border-indigo-500 transition-shadow" placeholder="e.g., blurry, watermark, text, ugly, deformed"></textarea>
                </div>

                <div class="mt-auto pt-5 border-t border-gray-300">
                     <p class="text-xs text-gray-500 mb-3">Note: Image generation is simulated. No actual AI is used. This is a UI concept.</p>
                    <button id="generateBtn" class="w-full btn-primary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed">
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-wand-2"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M17 18h-4"/></svg>
                        Generate Image
                    </button>
                </div>
            </div>

            <div class="lg:w-3/5 xl:w-2/3 image-display-bg p-4 sm:p-6 rounded-xl shadow-xl flex flex-col items-center justify-center relative">
                <div id="image-container" class="w-full max-w-3xl bg-gray-700 rounded-lg overflow-hidden flex items-center justify-center relative shadow-inner">
                    <img id="generatedImage" src="https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here" alt="Generated AI Art" class="object-contain w-full h-full transition-opacity duration-500">
                    <div id="loading-overlay" class="absolute inset-0 bg-gray-800 bg-opacity-80 flex flex-col items-center justify-center hidden transition-opacity duration-300">
                        <div class="spinner"></div>
                        <p class="text-white mt-4 font-medium text-lg">Generating your masterpiece...</p>
                    </div>
                     <div id="placeholder-text-container" class="absolute inset-0 flex items-center justify-center pointer-events-none">
                        <p class="text-gray-400 text-xl text-center p-4">Your AI Art Will Appear Here</p>
                    </div>
                </div>
                <div class="mt-6 w-full max-w-3xl flex flex-col sm:flex-row gap-3 justify-center button-group">
                    <button id="regenerateBtn" class="flex-1 btn-secondary font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M3 21c-1.66 0-3-1.34-3-3s1.34-3 3-3c1.06 0 1.98.54 2.5 1.35"/><path d="M21 15v4a2 2 0 0 1-2 2H5.5a2.5 2.5 0 0 1-2.5-2.5V15"/></svg>
                        Regenerate
                    </button>
                    <button id="downloadBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg shadow-md hover:shadow-lg transition duration-150 ease-in-out flex items-center justify-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed" disabled>
                        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
                        Download
                    </button>
                </div>
                 <p class="text-xs text-gray-500 mt-4 text-center">Download functionality is for demonstration purposes only.</p>
            </div>
        </main>

        <footer class="mt-8 py-4 text-center text-sm text-gray-500 border-t border-gray-300">
            <p>&copy; <span id="currentYear"></span> AI Art Generator Mock-up. All rights reserved (conceptually).</p>
            <p>This interface is a simulation and does not connect to any actual AI image generation models like DALL-E.</p>
        </footer>
    </div>

    <script>
        // --- DOM Elements ---
        const promptTextarea = document.getElementById('prompt');
        const styleSelect = document.getElementById('style');
        const negativePromptTextarea = document.getElementById('negative-prompt');
        const generateBtn = document.getElementById('generateBtn');
        const regenerateBtn = document.getElementById('regenerateBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const generatedImage = document.getElementById('generatedImage');
        const imageContainer = document.getElementById('image-container');
        const loadingOverlay = document.getElementById('loading-overlay');
        const placeholderTextContainer = document.getElementById('placeholder-text-container');
        const aspectRatioBtns = document.querySelectorAll('.aspect-ratio-btn');
        const customRatioInputs = document.getElementById('custom-ratio-inputs');
        const customWidthInput = document.getElementById('custom-width');
        const customHeightInput = document.getElementById('custom-height');
        const initialPlaceholderSrc = 'https://placehold.co/512x512/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here';

        // --- State Variables ---
        let currentImageSeed = null;
        let currentAspectRatio = { width: 512, height: 512, ratioString: '1/1' }; // Default 1:1

        // --- Helper Functions ---
        function setButtonsDisabledState(disabled) {
            generateBtn.disabled = disabled;
            // Regenerate and Download should only be enabled if an image has been "generated"
            if (currentImageSeed !== null) {
                regenerateBtn.disabled = disabled;
                downloadBtn.disabled = disabled;
            } else {
                regenerateBtn.disabled = true;
                downloadBtn.disabled = true;
            }
        }
        
        function showLoading(isLoading) {
            if (isLoading) {
                loadingOverlay.classList.remove('hidden');
                placeholderTextContainer.classList.add('hidden');
                generatedImage.classList.add('opacity-0');
            } else {
                loadingOverlay.classList.add('hidden');
                generatedImage.classList.remove('opacity-0');
                // Show placeholder text only if the image src is the initial one
                if (generatedImage.src === initialPlaceholderSrc) {
                    placeholderTextContainer.classList.remove('hidden');
                } else {
                     placeholderTextContainer.classList.add('hidden');
                }
            }
        }

        // --- Aspect Ratio Logic ---
        function updateImageContainerAspectRatio() {
            imageContainer.style.aspectRatio = currentAspectRatio.ratioString;
            // Update placeholder if it's currently showing the default text
            if (generatedImage.src === initialPlaceholderSrc || generatedImage.src.startsWith('https://placehold.co/')) {
                 if (placeholderTextContainer.classList.contains('hidden') && generatedImage.src !== initialPlaceholderSrc) {
                    // If an image was already generated, update its dimensions
                    const url = new URL(generatedImage.src);
                    const text = url.searchParams.get('text') || "Generated Art";
                    const colors = url.pathname.substring(1).split('/').slice(1,3).join('/'); // e.g. 374151/9ca3af
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${colors}?text=${encodeURIComponent(text)}`;
                 } else {
                    // Otherwise, just update the default placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/4B5563/9CA3AF?text=Your+AI+Art+Will+Appear+Here`;
                 }
            }
        }

        aspectRatioBtns.forEach(btn => {
            btn.addEventListener('click', () => {
                aspectRatioBtns.forEach(b => b.classList.remove('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold'));
                btn.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');

                const ratio = btn.dataset.ratio;
                if (ratio === 'custom') {
                    customRatioInputs.classList.remove('hidden');
                    customWidthInput.value = customWidthInput.value || currentAspectRatio.width;
                    customHeightInput.value = customHeightInput.value || currentAspectRatio.height;
                    // Trigger update if custom is selected, using current input values or defaults
                    handleCustomRatioChange();
                } else {
                    customRatioInputs.classList.add('hidden');
                    const [w, h] = ratio.split(':').map(Number);
                    currentAspectRatio = { width: w * 128, height: h * 128, ratioString: `${w}/${h}` }; // Scale for placeholder
                    if (w === 1 && h === 1) currentAspectRatio = { width: 512, height: 512, ratioString: '1/1'};
                    if (w === 16 && h === 9) currentAspectRatio = { width: 1024, height: 576, ratioString: '16/9'};
                    if (w === 4 && h === 3) currentAspectRatio = { width: 640, height: 480, ratioString: '4/3'};
                    if (w === 3 && h === 4) currentAspectRatio = { width: 480, height: 640, ratioString: '3/4'};
                    if (w === 9 && h === 16) currentAspectRatio = { width: 576, height: 1024, ratioString: '9/16'};
                    updateImageContainerAspectRatio();
                }
            });
        });
        
        function handleCustomRatioChange() {
            let width = parseInt(customWidthInput.value);
            let height = parseInt(customHeightInput.value);

            // Basic validation and clamping
            width = Math.max(64, Math.min(2048, width || currentAspectRatio.width));
            height = Math.max(64, Math.min(2048, height || currentAspectRatio.height));
            
            customWidthInput.value = width; // Update input field with validated value
            customHeightInput.value = height; // Update input field with validated value

            if (width > 0 && height > 0) {
                currentAspectRatio = { width, height, ratioString: `${width}/${height}` };
                updateImageContainerAspectRatio();
            }
        }

        customWidthInput.addEventListener('change', handleCustomRatioChange); // Use 'change' to update when focus is lost or enter is pressed
        customHeightInput.addEventListener('change', handleCustomRatioChange);


        // --- Image Generation Logic ---
        function simulateImageGeneration(isRegeneration = false) {
            const prompt = promptTextarea.value.trim();
            if (!prompt && !isRegeneration) {
                // Using a more subtle way to indicate error than alert()
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }

            showLoading(true);
            setButtonsDisabledState(true);

            setTimeout(() => {
                const style = styleSelect.value;
                
                currentImageSeed = isRegeneration ? (currentImageSeed || Date.now()) + 1 : Date.now();

                let placeholderText = prompt.substring(0, 25) || "Generated Image";
                if (style) placeholderText += ` (${style.replace(/_/g, ' ')})`;
                placeholderText += ` #${currentImageSeed % 1000}`;

                const bgColor = Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // R
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0') + // G
                                Math.floor(Math.random()*127 + 64).toString(16).padStart(2,'0'); // B (avoiding too dark/light)
                const textColor = 'FFFFFF';

                generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/${bgColor}/${textColor}?text=${encodeURIComponent(placeholderText)}`;
                
                generatedImage.onload = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                };
                generatedImage.onerror = () => {
                    showLoading(false);
                    setButtonsDisabledState(false);
                    // Show error in placeholder
                    generatedImage.src = `https://placehold.co/${currentAspectRatio.width}x${currentAspectRatio.height}/FF0000/FFFFFF?text=Error+Loading+Image`;
                    placeholderTextContainer.classList.add('hidden'); // Ensure main placeholder text is hidden
                };
            }, 1500 + Math.random() * 1000);
        }

        generateBtn.addEventListener('click', () => simulateImageGeneration(false));
        regenerateBtn.addEventListener('click', () => {
            if (!promptTextarea.value.trim() && !currentImageSeed) {
                promptTextarea.classList.add('border-red-500', 'ring-red-500');
                promptTextarea.focus();
                setTimeout(() => {
                    promptTextarea.classList.remove('border-red-500', 'ring-red-500');
                }, 2000);
                return;
            }
            simulateImageGeneration(true);
        });

        // --- Download Logic (Conceptual) ---
        downloadBtn.addEventListener('click', () => {
            if (generatedImage.src && generatedImage.src !== initialPlaceholderSrc && !generatedImage.src.includes('Error+Loading')) {
                const link = document.createElement('a');
                link.href = generatedImage.src;
                
                const urlParams = new URLSearchParams(new URL(generatedImage.src).search);
                const textParam = urlParams.get('text') || 'ai_art_simulation';
                const filename = textParam.replace(/[^a-z0-9_]+/gi, '_').toLowerCase().substring(0,50) + '.png';
                link.download = filename;
                
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                
                const originalText = downloadBtn.innerHTML;
                const checkSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><polyline points="20 6 9 17 4 12"></polyline></svg> Downloaded!`;
                downloadBtn.innerHTML = checkSVG;
                setTimeout(() => { downloadBtn.innerHTML = originalText; }, 2500);

            } else {
                // Could show a small message here instead of alert
                console.warn("No image generated to download or image is the initial placeholder/error.");
            }
        });

        // --- Initial Setup ---
        document.getElementById('currentYear').textContent = new Date().getFullYear();
        // Set 1:1 as default active aspect ratio button
        const defaultRatioButton = document.querySelector('.aspect-ratio-btn[data-ratio="1:1"]');
        if (defaultRatioButton) {
            defaultRatioButton.classList.add('bg-indigo-200', 'ring-2', 'ring-indigo-500', 'font-semibold');
        }
        updateImageContainerAspectRatio(); // Apply initial aspect ratio to container
        showLoading(false); // Ensure correct initial visibility of placeholder/image
        setButtonsDisabledState(true); // Regenerate/Download disabled initially

    </script>
</body>
</html>

 



 

No comments:

Post a Comment

Doubt Sermon

  John 20:24-29 ...