document.addEventListener('DOMContentLoaded', function() { // Initialize AOS (Animate On Scroll) AOS.init({ duration: 800, easing: 'ease-in-out', once: false }); // Elements const loadingScreen = document.getElementById('loading-screen'); const appContainer = document.getElementById('app-container'); const themeToggle = document.getElementById('theme-toggle'); const menuToggle = document.getElementById('menu-toggle'); const sidebarClose = document.getElementById('sidebar-close'); const sidebar = document.getElementById('sidebar'); const generationForm = document.getElementById('generation-form'); const promptInput = document.getElementById('prompt'); const negativePromptInput = document.getElementById('negative-prompt'); const widthSelect = document.getElementById('width'); const heightSelect = document.getElementById('height'); const seedInput = document.getElementById('seed'); const randomSeedBtn = document.getElementById('random-seed'); const tilingCheckbox = document.getElementById('tiling'); const advancedSettingsBtn = document.getElementById('advanced-settings-btn'); const upgradePromptBtn = document.getElementById('upgrade-prompt-btn'); const imageDisplay = document.getElementById('image-display'); const historyGallery = document.getElementById('history-gallery'); const latestCreation = document.getElementById('latest-creation'); // Image Controls const downloadCurrentBtn = document.getElementById('download-current'); const shareCurrentBtn = document.getElementById('share-current'); const favoriteCurrentBtn = document.getElementById('favorite-current'); // Modals const advancedSettingsModal = document.getElementById('advanced-settings-modal'); const promptModal = document.getElementById('prompt-modal'); const imageViewerModal = document.getElementById('image-viewer-modal'); const shareModal = document.getElementById('share-modal'); // Advanced Settings Elements const subseedInput = document.getElementById('subseed'); const attentionInput = document.getElementById('attention'); const attentionValue = document.getElementById('attention-value'); const referenceImageInput = document.getElementById('reference-image'); const referencePreview = document.getElementById('reference-preview'); const referencePreviewImg = document.getElementById('reference-preview-img'); const removeReferenceBtn = document.getElementById('remove-reference'); const referenceTypeSelect = document.getElementById('reference-type'); const referenceStrengthInput = document.getElementById('reference-strength'); const referenceStrengthValue = document.getElementById('reference-strength-value'); const resetAdvancedSettingsBtn = document.getElementById('reset-advanced-settings'); const saveAdvancedSettingsBtn = document.getElementById('save-advanced-settings'); // Prompt Modal Elements const originalPromptInput = document.getElementById('original-prompt'); const enhancedPromptInput = document.getElementById('enhanced-prompt'); const cancelPromptEnhancementBtn = document.getElementById('cancel-prompt-enhancement'); const useEnhancedPromptBtn = document.getElementById('use-enhanced-prompt'); // Image Viewer Modal Elements const viewerImage = document.getElementById('viewer-image'); const viewerPrompt = document.getElementById('viewer-prompt'); const viewerNegativePrompt = document.getElementById('viewer-negative-prompt'); const viewerSize = document.getElementById('viewer-size'); const viewerSeed = document.getElementById('viewer-seed'); const viewerTimestamp = document.getElementById('viewer-timestamp'); const downloadImageBtn = document.getElementById('download-image'); const shareImageBtn = document.getElementById('share-image'); const regenerateImageBtn = document.getElementById('regenerate-image'); const editPromptImageBtn = document.getElementById('edit-prompt-image'); // Share Modal const sharePreviewImg = document.getElementById('share-preview-img'); const shareLinkInput = document.getElementById('share-link-input'); const copyLinkBtn = document.getElementById('copy-link'); const shareBtns = document.querySelectorAll('.share-btn'); // Global variables let currentImage = null; let referenceImageData = null; let isGenerating = false; const defaultAdvancedSettings = { subseed: 0, attention: 0.5, referenceImage: null, referenceType: 'style', referenceStrength: 0.5 }; let advancedSettings = {...defaultAdvancedSettings}; let favoriteImages = []; // Initialize initializeApp(); function initializeApp() { // Slower loading screen for a better experience setTimeout(() => { loadingScreen.style.opacity = '0'; setTimeout(() => { loadingScreen.style.visibility = 'hidden'; appContainer.classList.remove('hidden'); // Trigger AOS animations on initial load AOS.refresh(); }, 800); }, 3000); // Set random seed initially seedInput.value = Math.floor(Math.random() * 999999); // Load theme preference loadThemePreference(); // Load image history loadImageHistory(); // Load favorites loadFavorites(); // Event listeners setupEventListeners(); } function setupEventListeners() { // Theme toggle themeToggle.addEventListener('click', toggleTheme); // Mobile menu menuToggle.addEventListener('click', () => sidebar.classList.add('active')); sidebarClose.addEventListener('click', () => sidebar.classList.remove('active')); // Generate random seed randomSeedBtn.addEventListener('click', generateRandomSeed); // Advanced settings modal advancedSettingsBtn.addEventListener('click', openAdvancedSettingsModal); // Handle sliders in advanced settings attentionInput.addEventListener('input', () => { attentionValue.textContent = attentionInput.value; advancedSettings.attention = parseFloat(attentionInput.value); }); referenceStrengthInput.addEventListener('input', () => { referenceStrengthValue.textContent = referenceStrengthInput.value; advancedSettings.referenceStrength = parseFloat(referenceStrengthInput.value); }); // Reference image handling referenceImageInput.addEventListener('change', handleReferenceImageUpload); removeReferenceBtn.addEventListener('click', removeReferenceImage); referenceTypeSelect.addEventListener('change', () => { advancedSettings.referenceType = referenceTypeSelect.value; }); // Advanced settings buttons resetAdvancedSettingsBtn.addEventListener('click', resetAdvancedSettings); saveAdvancedSettingsBtn.addEventListener('click', () => closeModal(advancedSettingsModal)); // Upgrade prompt button upgradePromptBtn.addEventListener('click', handleUpgradePrompt); // Prompt modal buttons cancelPromptEnhancementBtn.addEventListener('click', () => closeModal(promptModal)); useEnhancedPromptBtn.addEventListener('click', applyEnhancedPrompt); // Image controls downloadCurrentBtn.addEventListener('click', downloadCurrentImage); shareCurrentBtn.addEventListener('click', openShareModal); favoriteCurrentBtn.addEventListener('click', toggleFavoriteCurrentImage); // Image viewer modal buttons downloadImageBtn.addEventListener('click', downloadCurrentImage); shareImageBtn.addEventListener('click', openShareModal); regenerateImageBtn.addEventListener('click', regenerateSimilarImage); editPromptImageBtn.addEventListener('click', editAndCreateNew); // Share modal buttons copyLinkBtn.addEventListener('click', copyShareLink); shareBtns.forEach(btn => { btn.addEventListener('click', function() { shareToSocialMedia(this.className.split(' ')[1]); }); }); // Close modals document.querySelectorAll('.close-modal').forEach(btn => { btn.addEventListener('click', () => { closeAllModals(); }); }); // Form submission generationForm.addEventListener('submit', handleImageGeneration); // Escape key for modals document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeAllModals(); } }); // Disable context menu on images (for better UX) document.addEventListener('contextmenu', function(e) { if (e.target.tagName === 'IMG') { e.preventDefault(); return false; } }); } function closeAllModals() { closeModal(advancedSettingsModal); closeModal(promptModal); closeModal(imageViewerModal); closeModal(shareModal); } function loadThemePreference() { const isDarkMode = localStorage.getItem('darkMode') !== 'false'; // Default to dark setTheme(isDarkMode); } function toggleTheme() { const isDarkMode = !document.body.classList.contains('light-theme'); setTheme(!isDarkMode); localStorage.setItem('darkMode', !isDarkMode); } function setTheme(isDarkMode) { if (isDarkMode) { document.body.classList.remove('light-theme'); themeToggle.innerHTML = ''; } else { document.body.classList.add('light-theme'); themeToggle.innerHTML = ''; } } function generateRandomSeed() { const newSeed = Math.floor(Math.random() * 999999); seedInput.value = newSeed; // Add pulse animation to seed input seedInput.classList.add('pulse-effect'); setTimeout(() => { seedInput.classList.remove('pulse-effect'); }, 1000); showToast('Seed Updated', `Random seed generated: ${newSeed}`, 'info'); } function openAdvancedSettingsModal() { // Update modal with current settings subseedInput.value = advancedSettings.subseed; attentionInput.value = advancedSettings.attention; attentionValue.textContent = advancedSettings.attention; referenceTypeSelect.value = advancedSettings.referenceType; referenceStrengthInput.value = advancedSettings.referenceStrength; referenceStrengthValue.textContent = advancedSettings.referenceStrength; // Update reference image preview if exists if (referenceImageData) { referencePreviewImg.src = referenceImageData; referencePreview.classList.remove('hidden'); } else { referencePreview.classList.add('hidden'); } openModal(advancedSettingsModal); } function resetAdvancedSettings() { advancedSettings = {...defaultAdvancedSettings}; subseedInput.value = defaultAdvancedSettings.subseed; attentionInput.value = defaultAdvancedSettings.attention; attentionValue.textContent = defaultAdvancedSettings.attention; referenceTypeSelect.value = defaultAdvancedSettings.referenceType; referenceStrengthInput.value = defaultAdvancedSettings.referenceStrength; referenceStrengthValue.textContent = defaultAdvancedSettings.referenceStrength; removeReferenceImage(); showToast('Settings Reset', 'Advanced settings have been reset to defaults', 'info'); } function handleReferenceImageUpload(e) { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(event) { referenceImageData = event.target.result; referencePreviewImg.src = referenceImageData; referencePreview.classList.remove('hidden'); advancedSettings.referenceImage = referenceImageData; showToast('Reference Added', 'Reference image uploaded successfully', 'success'); }; reader.readAsDataURL(file); } } function removeReferenceImage() { referenceImageData = null; advancedSettings.referenceImage = null; referenceImageInput.value = ''; referencePreview.classList.add('hidden'); } async function handleUpgradePrompt() { const prompt = promptInput.value.trim(); if (!prompt) { showToast('Error', 'Please enter a prompt to enhance', 'error'); return; } showToast('Processing', 'Enhancing your prompt with AI...', 'info'); try { const response = await fetch('/api/upgrade-prompt', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }) }); const data = await response.json(); if (data.error) { throw new Error(data.error); } // Populate prompt modal originalPromptInput.value = prompt; enhancedPromptInput.value = data.upgradedPrompt || prompt; // Open modal openModal(promptModal); } catch (error) { console.error('Error upgrading prompt:', error); showToast('Error', error.message || 'Failed to enhance prompt. Please try again.', 'error'); } } function applyEnhancedPrompt() { const enhancedPrompt = enhancedPromptInput.value.trim(); if (enhancedPrompt) { promptInput.value = enhancedPrompt; showToast('Applied', 'Enhanced prompt applied successfully', 'success'); } closeModal(promptModal); } async function handleImageGeneration(e) { e.preventDefault(); if (isGenerating) { showToast('In Progress', 'An image is already being generated', 'warning'); return; } const prompt = promptInput.value.trim(); if (!prompt) { showToast('Error', 'Please enter a prompt', 'error'); return; } // Collect parameters const params = { prompt: prompt, seed: parseInt(seedInput.value) || Math.floor(Math.random() * 999999), subseed: parseInt(subseedInput.value) || 0, attention: parseFloat(attentionInput.value) || 0.5, width: parseInt(widthSelect.value) || 768, height: parseInt(heightSelect.value) || 768, tiling: tilingCheckbox.checked, negative_prompt: negativePromptInput.value.trim(), reference_image: referenceImageData, reference_image_type: referenceImageData ? referenceTypeSelect.value : null, reference_strength: referenceImageData ? parseFloat(referenceStrengthInput.value) : 0.5 }; isGenerating = true; showGeneratingUI(); try { // Generate the image const taskResponse = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); const taskData = await taskResponse.json(); if (taskData.error) { throw new Error(taskData.error); } const taskId = taskData.taskId; // Poll for completion await pollImageProgress(taskId, params); } catch (error) { console.error('Error generating image:', error); showToast('Error', error.message || 'Failed to generate image. Please try again.', 'error'); hideGeneratingUI(); isGenerating = false; } } async function pollImageProgress(taskId, originalParams) { // Start polling const maxAttempts = 30; // 60 seconds (2s per poll) let attempt = 0; const poll = async () => { try { const response = await fetch('/api/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ taskId }) }); const data = await response.json(); if (data.error) { throw new Error(data.error); } if (data.final_image_url) { // Image is ready const imageUrl = data.final_image_url; // Create image object currentImage = { imageUrl: imageUrl, prompt: originalParams.prompt, negativePrompt: originalParams.negative_prompt, seed: originalParams.seed, width: originalParams.width, height: originalParams.height, timestamp: new Date().toISOString() }; // Display the image displayGeneratedImage(currentImage); // Save to history saveImageToHistory(currentImage); return; } if (data.status === 'done' && !data.final_image_url) { throw new Error('Image generation completed but no image was returned.'); } // Continue polling if (attempt < maxAttempts) { attempt++; setTimeout(poll, 2000); } else { throw new Error('Image generation timed out. Please try again.'); } } catch (error) { console.error('Error polling progress:', error); showToast('Error', error.message || 'Failed to generate image. Please try again.', 'error'); hideGeneratingUI(); isGenerating = false; } }; await poll(); } function showGeneratingUI() { // Show loading state in image display imageDisplay.innerHTML = `
Creating your masterpiece...
Your masterpiece will appear here
Use the form to create something amazing