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...

`; // Update generate button const generateBtn = document.getElementById('generate-btn'); generateBtn.innerHTML = ' Creating...'; generateBtn.disabled = true; // Disable sidebar controls advancedSettingsBtn.disabled = true; upgradePromptBtn.disabled = true; } function hideGeneratingUI() { // Enable form controls const generateBtn = document.getElementById('generate-btn'); generateBtn.innerHTML = ' Create Image'; generateBtn.disabled = false; advancedSettingsBtn.disabled = false; upgradePromptBtn.disabled = false; // If no image was generated, restore placeholder if (!currentImage) { imageDisplay.innerHTML = `

Your masterpiece will appear here

Use the form to create something amazing
`; } isGenerating = false; } function displayGeneratedImage(imageData) { // Update image display imageDisplay.innerHTML = ` Generated Image `; // Update latest creation updateLatestCreation(imageData); // Enable image controls downloadCurrentBtn.disabled = false; shareCurrentBtn.disabled = false; favoriteCurrentBtn.disabled = false; // Update favorite button state updateFavoriteButtonState(); hideGeneratingUI(); // Show success message showToast('Success', 'Your creation is ready!', 'success'); } function updateLatestCreation(imageData) { latestCreation.innerHTML = ` Latest Creation `; latestCreation.addEventListener('click', () => { openImageViewer(imageData); }); } function openImageViewer(imageData) { // Set current image currentImage = imageData; // Populate viewer details viewerImage.src = imageData.imageUrl; viewerPrompt.textContent = imageData.prompt; viewerNegativePrompt.textContent = imageData.negativePrompt || 'None'; viewerSize.textContent = `${imageData.width} × ${imageData.height}`; viewerSeed.textContent = imageData.seed; // Format date const date = new Date(imageData.timestamp); const formattedDate = date.toLocaleString(); viewerTimestamp.textContent = formattedDate; // Open modal openModal(imageViewerModal); } function regenerateSimilarImage(imageData) { // Close modal closeModal(imageViewerModal); // Fill form with same parameters promptInput.value = currentImage.prompt; negativePromptInput.value = currentImage.negativePrompt || ''; // Select closest dimensions in dropdowns selectClosestOption(widthSelect, currentImage.width); selectClosestOption(heightSelect, currentImage.height); // Use same seed seedInput.value = currentImage.seed; // Scroll to form sidebar.scrollIntoView({ behavior: 'smooth' }); // Show toast showToast('Ready to Regenerate', 'Parameters loaded from selected image', 'info'); } function editAndCreateNew() { // Close modal closeModal(imageViewerModal); // Fill form with same parameters but focus on prompt promptInput.value = currentImage.prompt; negativePromptInput.value = currentImage.negativePrompt || ''; // Select closest dimensions in dropdowns selectClosestOption(widthSelect, currentImage.width); selectClosestOption(heightSelect, currentImage.height); // Generate new seed for variation generateRandomSeed(); // Scroll to form and focus on prompt sidebar.scrollIntoView({ behavior: 'smooth' }); promptInput.focus(); // Show toast showToast('Ready to Edit', 'Edit the prompt to create a new variation', 'info'); } function selectClosestOption(selectElement, value) { let closestOption = null; let closestDiff = Infinity; // Find the closest option for (let i = 0; i < selectElement.options.length; i++) { const optionValue = parseInt(selectElement.options[i].value); const diff = Math.abs(optionValue - value); if (diff < closestDiff) { closestDiff = diff; closestOption = i; } } if (closestOption !== null) { selectElement.selectedIndex = closestOption; } } function downloadCurrentImage() { if (currentImage) { downloadImage(currentImage.imageUrl); } } function downloadImage(url) { // Create an anchor element const a = document.createElement('a'); a.href = url; // Set the download attribute with a filename const filename = 'adarshkumar-creation-' + Date.now() + '.png'; a.download = filename; // Append to the body, click, and remove document.body.appendChild(a); a.click(); document.body.removeChild(a); showToast('Downloaded', 'Image saved to your device', 'success'); } function openShareModal() { if (!currentImage) return; // Set share preview image sharePreviewImg.src = currentImage.imageUrl; // Set share link shareLinkInput.value = currentImage.imageUrl; openModal(shareModal); } function copyShareLink() { shareLinkInput.select(); document.execCommand('copy'); showToast('Copied', 'Link copied to clipboard', 'success'); } function shareToSocialMedia(platform) { if (!currentImage) return; const url = encodeURIComponent(currentImage.imageUrl); const text = encodeURIComponent(`Check out this AI image I created: "${currentImage.prompt.substring(0, 50)}..."`); let shareUrl = ''; switch (platform) { case 'facebook': shareUrl = `https://www.facebook.com/sharer/sharer.php?u=${url}`; break; case 'twitter': shareUrl = `https://twitter.com/intent/tweet?text=${text}&url=${url}`; break; case 'pinterest': shareUrl = `https://pinterest.com/pin/create/button/?url=${url}&media=${url}&description=${text}`; break; case 'whatsapp': shareUrl = `https://api.whatsapp.com/send?text=${text} ${url}`; break; } if (shareUrl) { window.open(shareUrl, '_blank'); } } function toggleFavoriteCurrentImage() { if (!currentImage) return; const index = favoriteImages.findIndex(img => img.imageUrl === currentImage.imageUrl); if (index === -1) { // Add to favorites favoriteImages.push(currentImage); showToast('Added to Favorites', 'Image saved to your favorites', 'success'); } else { // Remove from favorites favoriteImages.splice(index, 1); showToast('Removed from Favorites', 'Image removed from your favorites', 'info'); } // Save favorites to localStorage localStorage.setItem('favoriteImages', JSON.stringify(favoriteImages)); // Update button state updateFavoriteButtonState(); } function updateFavoriteButtonState() { if (!currentImage) return; const isFavorite = favoriteImages.some(img => img.imageUrl === currentImage.imageUrl); if (isFavorite) { favoriteCurrentBtn.innerHTML = ' Favorited'; favoriteCurrentBtn.classList.add('favorited'); } else { favoriteCurrentBtn.innerHTML = ' Favorite'; favoriteCurrentBtn.classList.remove('favorited'); } } function loadFavorites() { const storedFavorites = localStorage.getItem('favoriteImages'); if (storedFavorites) { try { favoriteImages = JSON.parse(storedFavorites); } catch (e) { favoriteImages = []; } } } async function saveImageToHistory(imageData) { try { // Save locally first for immediate feedback let history = getLocalHistory(); // Set an ID if none exists if (!imageData.id) { imageData.id = Date.now().toString(); } // Add to the beginning of the array history.unshift(imageData); // Keep only the last 20 images history = history.slice(0, 20); // Save to localStorage localStorage.setItem('imageHistory', JSON.stringify(history)); // Save to server await fetch('/api/save-history', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(imageData) }); // Render gallery renderHistoryGallery(history); } catch (error) { console.error('Error saving to history:', error); } } async function loadImageHistory() { try { // Try to get from server first const response = await fetch('/api/get-history'); const data = await response.json(); if (data.images && Array.isArray(data.images) && data.images.length > 0) { // Save to localStorage for offline access localStorage.setItem('imageHistory', JSON.stringify(data.images)); renderHistoryGallery(data.images); } else { // Fall back to local storage const history = getLocalHistory(); renderHistoryGallery(history); } } catch (error) { console.error('Error loading history:', error); // Fall back to local storage const history = getLocalHistory(); renderHistoryGallery(history); } } function getLocalHistory() { const storageData = localStorage.getItem('imageHistory'); if (storageData) { try { return JSON.parse(storageData); } catch (e) { return []; } } return []; } function renderHistoryGallery(images) { if (!images || !Array.isArray(images) || images.length === 0) { showEmptyHistoryMessage(); return; } let galleryHTML = ''; images.forEach(image => { galleryHTML += `
${image.prompt}
${image.prompt}
`; }); historyGallery.innerHTML = galleryHTML; // Add click events to history items document.querySelectorAll('.history-item').forEach(item => { const imageId = item.getAttribute('data-image-id'); const image = images.find(img => img.id === imageId); if (!image) return; // View button click const viewBtn = item.querySelector('.view-btn'); viewBtn.addEventListener('click', (e) => { e.stopPropagation(); // Prevent triggering the parent click openImageViewer(image); }); // Regenerate button click const regenerateBtn = item.querySelector('.regenerate-btn'); regenerateBtn.addEventListener('click', (e) => { e.stopPropagation(); // Prevent triggering the parent click regenerateSimilarImage(image); }); // Download button click const downloadBtn = item.querySelector('.download-btn'); downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); // Prevent triggering the parent click downloadImage(image.imageUrl); }); // Main image click (for viewing) item.addEventListener('click', () => { openImageViewer(image); }); }); } function showEmptyHistoryMessage() { historyGallery.innerHTML = `

Your creations will be displayed here

Get started by generating your first image!
`; } function openModal(modal) { modal.classList.add('active'); // Add animation to modal content for better UX const modalContent = modal.querySelector('.modal-content'); if (modalContent) { modalContent.style.animation = 'none'; setTimeout(() => { modalContent.style.animation = ''; }, 10); } } function closeModal(modal) { modal.classList.remove('active'); } function showToast(title, message, type = 'info') { const toastContainer = document.getElementById('toast-container'); // Create toast element const toast = document.createElement('div'); toast.className = `toast ${type}`; // Add toast content toast.innerHTML = `
${title}
${message}
`; // Add to container toastContainer.appendChild(toast); // Add close functionality toast.querySelector('.toast-close').addEventListener('click', () => { toast.classList.add('exit'); setTimeout(() => { toast.remove(); }, 300); }); // Auto remove after 5 seconds setTimeout(() => { if (toast.parentNode) { toast.classList.add('exit'); setTimeout(() => { toast.remove(); }, 300); } }, 5000); } });