Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>ImageX - Advanced Image Generator</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
primary: '#6366f1', | |
secondary: '#8b5cf6', | |
dark: '#1e293b', | |
light: '#f8fafc' | |
}, | |
boxShadow: { | |
'neumorphic': '8px 8px 16px #d1d9e6, -8px -8px 16px #ffffff', | |
'neumorphic-inset': 'inset 4px 4px 8px #d1d9e6, inset -4px -4px 8px #ffffff', | |
'neumorphic-sm': '4px 4px 8px #d1d9e6, -4px -4px 8px #ffffff' | |
} | |
} | |
} | |
} | |
</script> | |
<style> | |
body { | |
background-color: #f1f5f9; | |
font-family: 'Inter', sans-serif; | |
min-height: 100vh; | |
} | |
.neumorphic { | |
border-radius: 16px; | |
background: linear-gradient(145deg, #ffffff, #e6e6e6); | |
box-shadow: 8px 8px 16px #d1d9e6, -8px -8px 16px #ffffff; | |
} | |
.neumorphic-inset { | |
border-radius: 16px; | |
background: linear-gradient(145deg, #e6e6e6, #ffffff); | |
box-shadow: inset 4px 4px 8px #d1d9e6, inset -4px -4px 8px #ffffff; | |
} | |
.neumorphic-btn { | |
border-radius: 12px; | |
background: linear-gradient(145deg, #ffffff, #e6e6e6); | |
box-shadow: 4px 4px 8px #d1d9e6, -4px -4px 8px #ffffff; | |
transition: all 0.3s ease; | |
} | |
.neumorphic-btn:hover { | |
box-shadow: 2px 2px 4px #d1d9e6, -2px -2px 4px #ffffff; | |
} | |
.neumorphic-btn:active { | |
box-shadow: inset 2px 2px 4px #d1d9e6, inset -2px -2px 4px #ffffff; | |
} | |
.loading-dots { | |
display: inline-flex; | |
align-items: center; | |
} | |
.loading-dots span { | |
width: 8px; | |
height: 8px; | |
margin: 0 3px; | |
background-color: #6366f1; | |
border-radius: 50%; | |
display: inline-block; | |
animation: bounce 1.4s infinite ease-in-out both; | |
} | |
.loading-dots span:nth-child(1) { | |
animation-delay: -0.32s; | |
} | |
.loading-dots span:nth-child(2) { | |
animation-delay: -0.16s; | |
} | |
@keyframes bounce { | |
0%, 80%, 100% { | |
transform: scale(0); | |
} 40% { | |
transform: scale(1); | |
} | |
} | |
/* Custom scrollbar */ | |
::-webkit-scrollbar { | |
width: 8px; | |
} | |
::-webkit-scrollbar-track { | |
background: #f1f5f9; | |
border-radius: 10px; | |
} | |
::-webkit-scrollbar-thumb { | |
background: #c7d2fe; | |
border-radius: 10px; | |
} | |
::-webkit-scrollbar-thumb:hover { | |
background: #a5b4fc; | |
} | |
/* Fade-in animation */ | |
@keyframes fadeIn { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
.fade-in { | |
animation: fadeIn 0.5s ease-in-out; | |
} | |
/* Pulse animation for generate button */ | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
100% { transform: scale(1); } | |
} | |
.pulse { | |
animation: pulse 2s infinite; | |
} | |
</style> | |
<!-- Pixel Code for https://kriztech.in/analytics/ --> | |
<script defer src="https://kriztech.in/analytics/pixel/t5geuVzC1fN2oM1G"></script> | |
<!-- END Pixel Code --> | |
</head> | |
<body class="bg-gray-100 text-gray-800"> | |
<div class="container mx-auto px-4 py-8 max-w-6xl"> | |
<!-- Header --> | |
<header class="text-center mb-12"> | |
<h1 class="text-4xl md:text-5xl font-bold mb-2 text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary"> | |
ImageX | |
</h1> | |
<p class="text-lg text-gray-600">Advanced AI Image Generator with Neomorphic UI</p> | |
</header> | |
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
<!-- Left Panel - Controls --> | |
<div class="lg:col-span-1"> | |
<div class="neumorphic p-6 rounded-2xl mb-6"> | |
<h2 class="text-xl font-semibold mb-4 text-gray-700">Image Settings</h2> | |
<!-- Prompt Input --> | |
<div class="mb-6"> | |
<label for="prompt" class="block text-sm font-medium text-gray-600 mb-2">Describe your image</label> | |
<div class="neumorphic-inset rounded-xl p-1"> | |
<textarea | |
id="prompt" | |
rows="4" | |
class="w-full bg-transparent border-none focus:ring-0 focus:outline-none px-4 py-3 text-gray-700 placeholder-gray-400 resize-none" | |
placeholder="A futuristic cityscape at sunset with flying cars..." | |
></textarea> | |
</div> | |
<div class="flex justify-between mt-2 text-xs text-gray-500"> | |
<span id="charCount">0/500</span> | |
<button id="enhancePrompt" class="text-primary hover:text-secondary transition"> | |
<i class="fas fa-magic mr-1"></i> Enhance Prompt | |
</button> | |
</div> | |
</div> | |
<!-- Style Selection --> | |
<div class="mb-6"> | |
<label class="block text-sm font-medium text-gray-600 mb-2">Art Style</label> | |
<div class="grid grid-cols-3 gap-2"> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="realistic"> | |
<i class="fas fa-user text-primary mb-1"></i> | |
<span>Realistic</span> | |
</button> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="fantasy"> | |
<i class="fas fa-dragon text-purple-500 mb-1"></i> | |
<span>Fantasy</span> | |
</button> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="anime"> | |
<i class="fas fa-moon text-pink-500 mb-1"></i> | |
<span>Anime</span> | |
</button> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="cyberpunk"> | |
<i class="fas fa-robot text-blue-500 mb-1"></i> | |
<span>Cyberpunk</span> | |
</button> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="watercolor"> | |
<i class="fas fa-paint-brush text-green-500 mb-1"></i> | |
<span>Watercolor</span> | |
</button> | |
<button class="style-option neumorphic-btn py-2 px-3 rounded-lg text-xs flex flex-col items-center" data-style="pixel"> | |
<i class="fas fa-gamepad text-yellow-500 mb-1"></i> | |
<span>Pixel Art</span> | |
</button> | |
</div> | |
</div> | |
<!-- Advanced Options --> | |
<div class="mb-6"> | |
<div class="flex justify-between items-center mb-2"> | |
<label class="text-sm font-medium text-gray-600">Advanced Options</label> | |
<button id="toggleAdvanced" class="text-xs text-primary"> | |
<i class="fas fa-chevron-down mr-1"></i> Show | |
</button> | |
</div> | |
<div id="advancedOptions" class="hidden space-y-4 pt-2"> | |
<!-- Image Size --> | |
<div> | |
<label class="block text-xs font-medium text-gray-500 mb-1">Image Size</label> | |
<div class="grid grid-cols-3 gap-2"> | |
<button class="size-option neumorphic-btn py-1 px-2 rounded-lg text-xs" data-width="512" data-height="512"> | |
512x512 | |
</button> | |
<button class="size-option neumorphic-btn py-1 px-2 rounded-lg text-xs" data-width="768" data-height="512"> | |
768x512 | |
</button> | |
<button class="size-option neumorphic-btn py-1 px-2 rounded-lg text-xs" data-width="1024" data-height="1024"> | |
1024x1024 | |
</button> | |
</div> | |
</div> | |
<!-- Guidance Scale --> | |
<div> | |
<label class="block text-xs font-medium text-gray-500 mb-1">Creativity Level</label> | |
<input type="range" id="guidanceScale" min="1" max="20" value="7.5" step="0.5" class="w-full"> | |
<div class="flex justify-between text-xs text-gray-500 mt-1"> | |
<span>Precise</span> | |
<span id="guidanceValue">7.5</span> | |
<span>Creative</span> | |
</div> | |
</div> | |
<!-- Seed Input --> | |
<div> | |
<label for="seed" class="block text-xs font-medium text-gray-500 mb-1">Seed (for reproducibility)</label> | |
<div class="flex"> | |
<input | |
type="number" | |
id="seed" | |
class="neumorphic-inset w-full bg-transparent border-none focus:ring-0 focus:outline-none px-3 py-2 text-gray-700 rounded-l-lg" | |
placeholder="Random" | |
> | |
<button id="randomSeed" class="neumorphic-btn px-3 rounded-r-lg text-gray-600"> | |
<i class="fas fa-dice"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Generate Button --> | |
<button id="generateBtn" class="w-full neumorphic-btn py-3 px-6 rounded-xl bg-gradient-to-r from-primary to-secondary text-white font-medium text-lg shadow-lg hover:shadow-xl transition-all duration-300 pulse"> | |
<i class="fas fa-sparkles mr-2"></i> Generate Image | |
</button> | |
<!-- History Button --> | |
<div class="mt-4 flex justify-between"> | |
<button id="showHistory" class="neumorphic-btn py-2 px-4 rounded-lg text-sm text-gray-700"> | |
<i class="fas fa-history mr-2"></i> History | |
</button> | |
<button id="saveSettings" class="neumorphic-btn py-2 px-4 rounded-lg text-sm text-gray-700"> | |
<i class="fas fa-save mr-2"></i> Save Preset | |
</button> | |
</div> | |
</div> | |
<!-- Tips Section --> | |
<div class="neumorphic p-6 rounded-2xl"> | |
<h3 class="font-medium text-gray-700 mb-3 flex items-center"> | |
<i class="fas fa-lightbulb text-yellow-500 mr-2"></i> Prompt Tips | |
</h3> | |
<ul class="text-sm text-gray-600 space-y-2"> | |
<li class="flex items-start"> | |
<i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-xs"></i> | |
<span>Be specific about subjects, colors, and composition</span> | |
</li> | |
<li class="flex items-start"> | |
<i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-xs"></i> | |
<span>Include art style (e.g., "digital art", "oil painting")</span> | |
</li> | |
<li class="flex items-start"> | |
<i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-xs"></i> | |
<span>Add lighting details (e.g., "dramatic lighting", "soft glow")</span> | |
</li> | |
<li class="flex items-start"> | |
<i class="fas fa-check-circle text-green-500 mt-1 mr-2 text-xs"></i> | |
<span>Specify perspective (e.g., "close-up", "aerial view")</span> | |
</li> | |
</ul> | |
</div> | |
</div> | |
<!-- Right Panel - Results --> | |
<div class="lg:col-span-2"> | |
<!-- Generated Image Display --> | |
<div class="neumorphic rounded-2xl p-6 mb-6 min-h-[512px] flex flex-col"> | |
<div class="flex justify-between items-center mb-4"> | |
<h2 class="text-xl font-semibold text-gray-700">Generated Image</h2> | |
<div id="generationInfo" class="text-sm text-gray-500 hidden"> | |
<span id="modelInfo">SDXL</span> • <span id="timeInfo">5.2s</span> | |
</div> | |
</div> | |
<div id="imageContainer" class="flex-1 flex items-center justify-center bg-gray-50 rounded-xl overflow-hidden relative"> | |
<div id="placeholder" class="text-center p-8"> | |
<div class="mx-auto w-20 h-20 bg-gradient-to-r from-primary to-secondary rounded-full flex items-center justify-center text-white mb-4"> | |
<i class="fas fa-image text-3xl"></i> | |
</div> | |
<h3 class="text-lg font-medium text-gray-700 mb-2">Your AI-generated image will appear here</h3> | |
<p class="text-gray-500 text-sm">Enter a prompt and click "Generate Image" to create your masterpiece</p> | |
</div> | |
<div id="loadingIndicator" class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden"> | |
<div class="text-center p-6 neumorphic rounded-xl"> | |
<div class="loading-dots mb-4"> | |
<span></span> | |
<span></span> | |
<span></span> | |
</div> | |
<p class="text-white font-medium">Generating your image...</p> | |
<p class="text-gray-300 text-sm mt-1">This usually takes 10-30 seconds</p> | |
</div> | |
</div> | |
<img id="generatedImage" class="w-full h-auto max-h-[70vh] object-contain hidden rounded-lg" alt="Generated image"> | |
</div> | |
<!-- Image Actions --> | |
<div id="imageActions" class="mt-4 flex flex-wrap gap-2 hidden"> | |
<button id="downloadBtn" class="neumorphic-btn py-2 px-4 rounded-lg flex items-center text-sm"> | |
<i class="fas fa-download mr-2"></i> Download | |
</button> | |
<button id="upscaleBtn" class="neumorphic-btn py-2 px-4 rounded-lg flex items-center text-sm"> | |
<i class="fas fa-expand mr-2"></i> Upscale | |
</button> | |
<button id="variationsBtn" class="neumorphic-btn py-2 px-4 rounded-lg flex items-center text-sm"> | |
<i class="fas fa-copy mr-2"></i> Variations | |
</button> | |
<button id="shareBtn" class="neumorphic-btn py-2 px-4 rounded-lg flex items-center text-sm"> | |
<i class="fas fa-share-alt mr-2"></i> Share | |
</button> | |
<button id="remixBtn" class="neumorphic-btn py-2 px-4 rounded-lg flex items-center text-sm ml-auto"> | |
<i class="fas fa-random mr-2"></i> Remix | |
</button> | |
</div> | |
</div> | |
<!-- History Panel (Hidden by default) --> | |
<div id="historyPanel" class="neumorphic rounded-2xl p-6 hidden"> | |
<div class="flex justify-between items-center mb-4"> | |
<h2 class="text-xl font-semibold text-gray-700">Generation History</h2> | |
<button id="closeHistory" class="neumorphic-btn p-2 rounded-lg"> | |
<i class="fas fa-times"></i> | |
</button> | |
</div> | |
<div id="historyGrid" class="grid grid-cols-2 md:grid-cols-3 gap-3"> | |
<!-- History items will be added here dynamically --> | |
<div class="text-center py-8 text-gray-400"> | |
<i class="fas fa-box-open text-3xl mb-2"></i> | |
<p>Your generation history will appear here</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Toast Notification --> | |
<div id="toast" class="fixed bottom-4 right-4 neumorphic p-4 rounded-xl shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 hidden"> | |
<div class="flex items-center"> | |
<div id="toastIcon" class="mr-3"></div> | |
<div> | |
<p id="toastMessage" class="text-sm font-medium"></p> | |
</div> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', function() { | |
// DOM Elements | |
const promptInput = document.getElementById('prompt'); | |
const charCount = document.getElementById('charCount'); | |
const enhancePromptBtn = document.getElementById('enhancePrompt'); | |
const styleOptions = document.querySelectorAll('.style-option'); | |
const sizeOptions = document.querySelectorAll('.size-option'); | |
const guidanceScale = document.getElementById('guidanceScale'); | |
const guidanceValue = document.getElementById('guidanceValue'); | |
const seedInput = document.getElementById('seed'); | |
const randomSeedBtn = document.getElementById('randomSeed'); | |
const generateBtn = document.getElementById('generateBtn'); | |
const toggleAdvancedBtn = document.getElementById('toggleAdvanced'); | |
const advancedOptions = document.getElementById('advancedOptions'); | |
const showHistoryBtn = document.getElementById('showHistory'); | |
const closeHistoryBtn = document.getElementById('closeHistory'); | |
const historyPanel = document.getElementById('historyPanel'); | |
const imageContainer = document.getElementById('imageContainer'); | |
const placeholder = document.getElementById('placeholder'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const generatedImage = document.getElementById('generatedImage'); | |
const imageActions = document.getElementById('imageActions'); | |
const downloadBtn = document.getElementById('downloadBtn'); | |
const upscaleBtn = document.getElementById('upscaleBtn'); | |
const variationsBtn = document.getElementById('variationsBtn'); | |
const shareBtn = document.getElementById('shareBtn'); | |
const remixBtn = document.getElementById('remixBtn'); | |
const generationInfo = document.getElementById('generationInfo'); | |
const modelInfo = document.getElementById('modelInfo'); | |
const timeInfo = document.getElementById('timeInfo'); | |
const toast = document.getElementById('toast'); | |
const toastMessage = document.getElementById('toastMessage'); | |
const toastIcon = document.getElementById('toastIcon'); | |
const saveSettingsBtn = document.getElementById('saveSettings'); | |
// State variables | |
let currentSettings = { | |
style: 'realistic', | |
width: 512, | |
height: 512, | |
guidance: 7.5, | |
seed: null, | |
prompt: '' | |
}; | |
let generationHistory = JSON.parse(localStorage.getItem('generationHistory')) || []; | |
// Initialize | |
updateCharCount(); | |
setActiveStyle('realistic'); | |
setActiveSize(512, 512); | |
// Event Listeners | |
promptInput.addEventListener('input', updateCharCount); | |
enhancePromptBtn.addEventListener('click', enhancePrompt); | |
styleOptions.forEach(option => { | |
option.addEventListener('click', function() { | |
setActiveStyle(this.dataset.style); | |
}); | |
}); | |
sizeOptions.forEach(option => { | |
option.addEventListener('click', function() { | |
setActiveSize(parseInt(this.dataset.width), parseInt(this.dataset.height)); | |
}); | |
}); | |
guidanceScale.addEventListener('input', function() { | |
guidanceValue.textContent = this.value; | |
currentSettings.guidance = parseFloat(this.value); | |
}); | |
randomSeedBtn.addEventListener('click', function() { | |
const randomSeed = Math.floor(Math.random() * 1000000); | |
seedInput.value = randomSeed; | |
currentSettings.seed = randomSeed; | |
}); | |
toggleAdvancedBtn.addEventListener('click', function() { | |
advancedOptions.classList.toggle('hidden'); | |
const icon = this.querySelector('i'); | |
if (advancedOptions.classList.contains('hidden')) { | |
this.innerHTML = '<i class="fas fa-chevron-down mr-1"></i> Show'; | |
} else { | |
this.innerHTML = '<i class="fas fa-chevron-up mr-1"></i> Hide'; | |
} | |
}); | |
generateBtn.addEventListener('click', generateImage); | |
showHistoryBtn.addEventListener('click', function() { | |
historyPanel.classList.remove('hidden'); | |
loadHistory(); | |
}); | |
closeHistoryBtn.addEventListener('click', function() { | |
historyPanel.classList.add('hidden'); | |
}); | |
downloadBtn.addEventListener('click', downloadImage); | |
upscaleBtn.addEventListener('click', function() { | |
showToast('Upscaling feature coming soon!', 'info'); | |
}); | |
variationsBtn.addEventListener('click', function() { | |
showToast('Variations feature coming soon!', 'info'); | |
}); | |
shareBtn.addEventListener('click', function() { | |
if (navigator.share) { | |
navigator.share({ | |
title: 'Check out this AI-generated image!', | |
text: 'I created this using NeoGen AI Image Generator', | |
url: generatedImage.src | |
}).catch(err => { | |
showToast('Sharing failed: ' + err, 'error'); | |
}); | |
} else { | |
// Fallback for browsers that don't support Web Share API | |
copyToClipboard(generatedImage.src); | |
showToast('Image URL copied to clipboard!', 'success'); | |
} | |
}); | |
remixBtn.addEventListener('click', function() { | |
generateImage(); | |
}); | |
saveSettingsBtn.addEventListener('click', function() { | |
const presetName = prompt('Enter a name for this preset:'); | |
if (presetName) { | |
const presets = JSON.parse(localStorage.getItem('presets')) || []; | |
presets.push({ | |
name: presetName, | |
settings: currentSettings | |
}); | |
localStorage.setItem('presets', JSON.stringify(presets)); | |
showToast(`Preset "${presetName}" saved!`, 'success'); | |
} | |
}); | |
// Functions | |
function updateCharCount() { | |
const count = promptInput.value.length; | |
charCount.textContent = `${count}/500`; | |
currentSettings.prompt = promptInput.value; | |
} | |
function setActiveStyle(style) { | |
styleOptions.forEach(option => { | |
option.classList.remove('bg-primary', 'text-white'); | |
option.classList.add('neumorphic-btn'); | |
}); | |
const activeOption = document.querySelector(`.style-option[data-style="${style}"]`); | |
activeOption.classList.remove('neumorphic-btn'); | |
activeOption.classList.add('bg-primary', 'text-white'); | |
currentSettings.style = style; | |
} | |
function setActiveSize(width, height) { | |
sizeOptions.forEach(option => { | |
option.classList.remove('bg-primary', 'text-white'); | |
option.classList.add('neumorphic-btn'); | |
}); | |
const activeOption = document.querySelector(`.size-option[data-width="${width}"]`); | |
activeOption.classList.remove('neumorphic-btn'); | |
activeOption.classList.add('bg-primary', 'text-white'); | |
currentSettings.width = width; | |
currentSettings.height = height; | |
} | |
function enhancePrompt() { | |
if (!promptInput.value.trim()) { | |
showToast('Please enter a prompt first', 'error'); | |
return; | |
} | |
// Simulate AI enhancement (in a real app, this would call an API) | |
showToast('Enhancing your prompt...', 'info'); | |
setTimeout(() => { | |
const enhanced = enhancePromptAI(promptInput.value); | |
promptInput.value = enhanced; | |
updateCharCount(); | |
showToast('Prompt enhanced!', 'success'); | |
}, 1000); | |
} | |
function enhancePromptAI(prompt) { | |
// This is a mock enhancement - a real implementation would use an AI service | |
const enhancements = [ | |
"highly detailed, intricate, digital painting", | |
"trending on artstation, 4k resolution", | |
"cinematic lighting, ultra-detailed", | |
"concept art, smooth, sharp focus" | |
]; | |
const randomEnhancement = enhancements[Math.floor(Math.random() * enhancements.length)]; | |
return `${prompt}, ${randomEnhancement}`; | |
} | |
async function generateImage() { | |
if (!promptInput.value.trim()) { | |
showToast('Please enter a prompt', 'error'); | |
return; | |
} | |
// Update current settings | |
currentSettings.prompt = promptInput.value; | |
currentSettings.seed = seedInput.value || Math.floor(Math.random() * 1000000); | |
// Show loading state | |
placeholder.classList.add('hidden'); | |
loadingIndicator.classList.remove('hidden'); | |
generatedImage.classList.add('hidden'); | |
imageActions.classList.add('hidden'); | |
try { | |
const startTime = Date.now(); | |
// Construct the API URL for Pollinations AI | |
const apiUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent( | |
`${currentSettings.prompt}, ${currentSettings.style} style, high quality, high resolution` | |
)}?width=${currentSettings.width}&height=${currentSettings.height}&seed=${currentSettings.seed}&nologo=true`; | |
// Fetch the image | |
const response = await fetch(apiUrl); | |
if (!response.ok) { | |
throw new Error('Failed to generate image'); | |
} | |
const blob = await response.blob(); | |
const imageUrl = URL.createObjectURL(blob); | |
// Display the image | |
generatedImage.src = imageUrl; | |
generatedImage.onload = function() { | |
loadingIndicator.classList.add('hidden'); | |
generatedImage.classList.remove('hidden'); | |
imageActions.classList.remove('hidden'); | |
generationInfo.classList.remove('hidden'); | |
// Calculate generation time | |
const endTime = Date.now(); | |
const generationTime = ((endTime - startTime) / 1000).toFixed(1); | |
timeInfo.textContent = `${generationTime}s`; | |
// Add to history | |
addToHistory({ | |
prompt: currentSettings.prompt, | |
imageUrl: imageUrl, | |
settings: {...currentSettings}, | |
timestamp: new Date().toISOString() | |
}); | |
}; | |
} catch (error) { | |
console.error('Error generating image:', error); | |
loadingIndicator.classList.add('hidden'); | |
placeholder.classList.remove('hidden'); | |
showToast('Failed to generate image. Please try again.', 'error'); | |
} | |
} | |
function addToHistory(item) { | |
generationHistory.unshift(item); | |
if (generationHistory.length > 12) { | |
generationHistory = generationHistory.slice(0, 12); | |
} | |
localStorage.setItem('generationHistory', JSON.stringify(generationHistory)); | |
if (!historyPanel.classList.contains('hidden')) { | |
loadHistory(); | |
} | |
} | |
function loadHistory() { | |
const historyGrid = document.getElementById('historyGrid'); | |
historyGrid.innerHTML = ''; | |
if (generationHistory.length === 0) { | |
historyGrid.innerHTML = ` | |
<div class="col-span-3 text-center py-8 text-gray-400"> | |
<i class="fas fa-box-open text-3xl mb-2"></i> | |
<p>Your generation history is empty</p> | |
</div> | |
`; | |
return; | |
} | |
generationHistory.forEach((item, index) => { | |
const historyItem = document.createElement('div'); | |
historyItem.className = 'neumorphic-btn rounded-lg overflow-hidden cursor-pointer group'; | |
historyItem.innerHTML = ` | |
<div class="relative aspect-square"> | |
<img src="${item.imageUrl}" alt="Generated image" class="w-full h-full object-cover"> | |
<div class="absolute inset-0 bg-black bg-opacity-60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center p-2"> | |
<p class="text-white text-xs text-center">${item.prompt.substring(0, 60)}${item.prompt.length > 60 ? '...' : ''}</p> | |
</div> | |
</div> | |
<div class="p-2"> | |
<p class="text-xs text-gray-500 truncate">${new Date(item.timestamp).toLocaleString()}</p> | |
</div> | |
`; | |
historyItem.addEventListener('click', function() { | |
generatedImage.src = item.imageUrl; | |
generatedImage.classList.remove('hidden'); | |
placeholder.classList.add('hidden'); | |
imageActions.classList.remove('hidden'); | |
historyPanel.classList.add('hidden'); | |
// Update settings to match history item | |
promptInput.value = item.settings.prompt; | |
updateCharCount(); | |
setActiveStyle(item.settings.style || 'realistic'); | |
setActiveSize(item.settings.width || 512, item.settings.height || 512); | |
guidanceScale.value = item.settings.guidance || 7.5; | |
guidanceValue.textContent = item.settings.guidance || 7.5; | |
seedInput.value = item.settings.seed || ''; | |
showToast('History item loaded', 'success'); | |
}); | |
historyGrid.appendChild(historyItem); | |
}); | |
} | |
function downloadImage() { | |
if (!generatedImage.src) return; | |
const link = document.createElement('a'); | |
link.href = generatedImage.src; | |
link.download = `ai-image-${Date.now()}.jpg`; | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
showToast('Image downloaded!', 'success'); | |
} | |
function copyToClipboard(text) { | |
const textarea = document.createElement('textarea'); | |
textarea.value = text; | |
document.body.appendChild(textarea); | |
textarea.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(textarea); | |
} | |
function showToast(message, type) { | |
// Set message and icon based on type | |
toastMessage.textContent = message; | |
let iconClass, bgColor; | |
switch(type) { | |
case 'success': | |
iconClass = 'fas fa-check-circle text-green-500'; | |
bgColor = 'bg-green-100'; | |
break; | |
case 'error': | |
iconClass = 'fas fa-exclamation-circle text-red-500'; | |
bgColor = 'bg-red-100'; | |
break; | |
case 'info': | |
iconClass = 'fas fa-info-circle text-blue-500'; | |
bgColor = 'bg-blue-100'; | |
break; | |
default: | |
iconClass = 'fas fa-info-circle text-primary'; | |
bgColor = 'bg-primary'; | |
} | |
// Update toast | |
toastIcon.className = iconClass + ' text-xl'; | |
toast.className = `fixed bottom-4 right-4 neumorphic p-4 rounded-xl shadow-lg transform translate-y-10 opacity-0 transition-all duration-300 ${bgColor}`; | |
// Show toast | |
setTimeout(() => { | |
toast.classList.remove('hidden'); | |
setTimeout(() => { | |
toast.classList.remove('translate-y-10'); | |
toast.classList.remove('opacity-0'); | |
}, 10); | |
}, 10); | |
// Hide after 3 seconds | |
setTimeout(() => { | |
toast.classList.add('translate-y-10'); | |
toast.classList.add('opacity-0'); | |
setTimeout(() => { | |
toast.classList.add('hidden'); | |
}, 300); | |
}, 3000); | |
} | |
}); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=peterquill193/imagex" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |