AdarshJi's picture
Upload 5 files
2e342df verified
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 = '<i class="fas fa-sun"></i>';
} else {
document.body.classList.add('light-theme');
themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
}
}
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 = `
<div class="loading-shimmer"></div>
<div class="loading-indicator">
<div class="generation-status">
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
<p>Creating your masterpiece...</p>
</div>
</div>
`;
// Update generate button
const generateBtn = document.getElementById('generate-btn');
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 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 = '<i class="fas fa-bolt"></i> Create Image';
generateBtn.disabled = false;
advancedSettingsBtn.disabled = false;
upgradePromptBtn.disabled = false;
// If no image was generated, restore placeholder
if (!currentImage) {
imageDisplay.innerHTML = `
<div class="placeholder-wrapper">
<div class="placeholder-content">
<i class="fas fa-image placeholder-icon pulse-slow"></i>
<p>Your masterpiece will appear here</p>
<span class="helper-tip">Use the form to create something amazing</span>
</div>
</div>
`;
}
isGenerating = false;
}
function displayGeneratedImage(imageData) {
// Update image display
imageDisplay.innerHTML = `
<img src="${imageData.imageUrl}" alt="Generated Image" class="display-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 = `
<img src="${imageData.imageUrl}" alt="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 = '<i class="fas fa-heart"></i> Favorited';
favoriteCurrentBtn.classList.add('favorited');
} else {
favoriteCurrentBtn.innerHTML = '<i class="far fa-heart"></i> 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 += `
<div class="history-item" data-image-id="${image.id || ''}">
<img src="${image.imageUrl}" alt="${image.prompt}" class="history-img">
<div class="history-overlay">
<div class="history-prompt">${image.prompt}</div>
<div class="history-actions">
<button class="history-action-btn view-btn" title="View Details">
<i class="fas fa-eye"></i>
</button>
<button class="history-action-btn regenerate-btn" title="Regenerate Similar">
<i class="fas fa-sync-alt"></i>
</button>
<button class="history-action-btn download-btn" title="Download Image">
<i class="fas fa-download"></i>
</button>
</div>
</div>
</div>
`;
});
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 = `
<div class="empty-history-message">
<i class="fas fa-images"></i>
<p>Your creations will be displayed here</p>
<span class="helper-tip">Get started by generating your first image!</span>
</div>
`;
}
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 = `
<div class="toast-content">
<div class="toast-title">${title}</div>
<div class="toast-message">${message}</div>
</div>
<button class="toast-close"><i class="fas fa-times"></i></button>
<div class="toast-progress"></div>
`;
// 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);
}
});