Spaces:
Sleeping
Sleeping
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); | |
} | |
}); |