Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>AI Image Creator - Next Gen</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
'neon-blue': '#00d4ff', | |
'neon-purple': '#8b5cf6', | |
'neon-pink': '#ec4899', | |
'dark-bg': '#0a0a0a', | |
'card-bg': '#1a1a1a', | |
'border-glow': '#00d4ff40' | |
}, | |
animation: { | |
'float': 'float 6s ease-in-out infinite', | |
'glow': 'glow 2s ease-in-out infinite alternate', | |
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', | |
} | |
} | |
} | |
} | |
</script> | |
<style> | |
@keyframes float { | |
0%, 100% { transform: translateY(0px); } | |
50% { transform: translateY(-20px); } | |
} | |
@keyframes glow { | |
from { box-shadow: 0 0 20px #00d4ff; } | |
to { box-shadow: 0 0 30px #00d4ff, 0 0 40px #00d4ff; } | |
} | |
.gradient-text { | |
background: linear-gradient(45deg, #00d4ff, #8b5cf6, #ec4899); | |
-webkit-background-clip: text; | |
-webkit-text-fill-color: transparent; | |
background-clip: text; | |
} | |
.glass-effect { | |
background: rgba(255, 255, 255, 0.05); | |
backdrop-filter: blur(10px); | |
border: 1px solid rgba(255, 255, 255, 0.1); | |
} | |
.neon-border { | |
border: 1px solid #00d4ff; | |
box-shadow: 0 0 10px #00d4ff40; | |
} | |
.neon-border:hover { | |
box-shadow: 0 0 20px #00d4ff, 0 0 30px #00d4ff40; | |
} | |
</style> | |
</head> | |
<body class="bg-dark-bg text-white min-h-screen font-sans"> | |
<!-- Navigation --> | |
<nav class="glass-effect fixed top-0 w-full z-50 border-b border-white/10"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
<div class="flex justify-between items-center h-16"> | |
<div class="flex items-center space-x-4"> | |
<div class="w-10 h-10 bg-gradient-to-r from-neon-blue to-neon-purple rounded-lg flex items-center justify-center animate-pulse-slow"> | |
<i class="fas fa-magic text-white text-lg"></i> | |
</div> | |
<h1 class="text-2xl font-bold gradient-text">AI Creator</h1> | |
</div> | |
<div class="hidden md:flex items-center space-x-6"> | |
<button onclick="showSection('create')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-plus mr-2"></i>Create | |
</button> | |
<button onclick="showSection('gallery')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-images mr-2"></i>Gallery | |
</button> | |
<button onclick="showSection('history')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-history mr-2"></i>History | |
</button> | |
<button id="authBtn" onclick="showAuthModal()" class="px-6 py-2 bg-gradient-to-r from-neon-blue to-neon-purple rounded-full hover:opacity-90 transition-opacity"> | |
<i class="fas fa-sign-in-alt mr-2"></i>Login | |
</button> | |
</div> | |
<div class="md:hidden"> | |
<button onclick="toggleMobileMenu()" class="text-gray-300 hover:text-neon-blue"> | |
<i class="fas fa-bars text-xl"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</nav> | |
<!-- Mobile Menu --> | |
<div id="mobileMenu" class="md:hidden fixed top-16 left-0 w-full glass-effect border-b border-white/10 hidden"> | |
<div class="px-4 py-4 space-y-4"> | |
<button onclick="showSection('create')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-plus mr-2"></i>Create | |
</button> | |
<button onclick="showSection('gallery')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-images mr-2"></i>Gallery | |
</button> | |
<button onclick="showSection('history')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors"> | |
<i class="fas fa-history mr-2"></i>History | |
</button> | |
<button onclick="showAuthModal()" class="w-full px-6 py-2 bg-gradient-to-r from-neon-blue to-neon-purple rounded-full hover:opacity-90 transition-opacity"> | |
<i class="fas fa-sign-in-alt mr-2"></i>Login | |
</button> | |
</div> | |
</div> | |
<!-- Main Content --> | |
<main class="pt-20"> | |
<!-- Create Section --> | |
<section id="createSection" class="section active min-h-screen"> | |
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12"> | |
<!-- Hero Section --> | |
<div class="text-center mb-16"> | |
<h2 class="text-6xl font-bold gradient-text mb-6 animate-float"> | |
Create Magic | |
</h2> | |
<p class="text-xl text-gray-400 mb-8 max-w-2xl mx-auto"> | |
Transform your imagination into stunning visuals with our advanced AI image generation | |
</p> | |
<div class="flex justify-center space-x-4"> | |
<div class="w-4 h-4 bg-neon-blue rounded-full animate-pulse"></div> | |
<div class="w-4 h-4 bg-neon-purple rounded-full animate-pulse" style="animation-delay: 0.2s;"></div> | |
<div class="w-4 h-4 bg-neon-pink rounded-full animate-pulse" style="animation-delay: 0.4s;"></div> | |
</div> | |
</div> | |
<!-- Creation Interface --> | |
<div class="glass-effect rounded-3xl p-8 neon-border"> | |
<div class="space-y-8"> | |
<!-- Prompt Input --> | |
<div class="relative"> | |
<textarea id="promptInput" | |
class="w-full h-32 bg-card-bg border border-white/20 rounded-2xl px-6 py-4 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue focus:ring-2 focus:ring-neon-blue/20 resize-none" | |
placeholder="Describe your vision... What would you like to create?"></textarea> | |
<div class="absolute bottom-4 right-4"> | |
<button id="generateBtn" onclick="generateImage()" | |
class="px-8 py-3 bg-gradient-to-r from-neon-blue to-neon-purple rounded-full font-semibold hover:opacity-90 transition-opacity animate-glow"> | |
<i class="fas fa-magic mr-2"></i>Generate | |
</button> | |
</div> | |
</div> | |
<!-- Quick Prompts --> | |
<div class="grid grid-cols-2 md:grid-cols-4 gap-4"> | |
<button onclick="setPrompt('A futuristic city with flying cars and neon lights')" | |
class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all"> | |
<i class="fas fa-city text-neon-blue mb-2"></i> | |
<p class="text-sm">Futuristic City</p> | |
</button> | |
<button onclick="setPrompt('A magical forest with glowing mushrooms and fairy lights')" | |
class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all"> | |
<i class="fas fa-tree text-neon-purple mb-2"></i> | |
<p class="text-sm">Magical Forest</p> | |
</button> | |
<button onclick="setPrompt('A cyberpunk warrior with neon armor and glowing weapons')" | |
class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all"> | |
<i class="fas fa-user-ninja text-neon-pink mb-2"></i> | |
<p class="text-sm">Cyberpunk Warrior</p> | |
</button> | |
<button onclick="setPrompt('An underwater palace with coral and sea creatures')" | |
class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all"> | |
<i class="fas fa-water text-neon-blue mb-2"></i> | |
<p class="text-sm">Underwater Palace</p> | |
</button> | |
</div> | |
<!-- Loading State --> | |
<div id="loadingState" class="hidden text-center py-12"> | |
<div class="inline-flex items-center space-x-4"> | |
<div class="w-8 h-8 border-4 border-neon-blue border-t-transparent rounded-full animate-spin"></div> | |
<span class="text-neon-blue text-lg">Creating your masterpiece...</span> | |
</div> | |
<div id="queueInfo" class="mt-4 text-gray-400"></div> | |
</div> | |
<!-- Result --> | |
<div id="resultSection" class="hidden"> | |
<div class="glass-effect rounded-2xl p-6 neon-border"> | |
<img id="generatedImage" class="w-full rounded-xl shadow-2xl" alt="Generated image"> | |
<div class="mt-6 flex justify-between items-center"> | |
<button onclick="addToGallery()" class="px-6 py-3 bg-gradient-to-r from-neon-purple to-neon-pink rounded-full hover:opacity-90 transition-opacity"> | |
<i class="fas fa-plus mr-2"></i>Add to Gallery | |
</button> | |
<button onclick="downloadImage()" class="px-6 py-3 glass-effect rounded-full hover:neon-border transition-all"> | |
<i class="fas fa-download mr-2"></i>Download | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</section> | |
<!-- Gallery Section --> | |
<section id="gallerySection" class="section hidden min-h-screen"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12"> | |
<div class="text-center mb-12"> | |
<h2 class="text-5xl font-bold gradient-text mb-4">Community Gallery</h2> | |
<p class="text-xl text-gray-400">Explore amazing creations from our community</p> | |
</div> | |
<div class="flex justify-center mb-8"> | |
<select id="sortOrder" onchange="loadGallery()" class="glass-effect rounded-full px-6 py-3 border border-white/20 focus:outline-none focus:border-neon-blue"> | |
<option value="new-old">Newest First</option> | |
<option value="old-new">Oldest First</option> | |
</select> | |
</div> | |
<div id="galleryGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> | |
<!-- Gallery items will be loaded here --> | |
</div> | |
</div> | |
</section> | |
<!-- History Section --> | |
<section id="historySection" class="section hidden min-h-screen"> | |
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12"> | |
<div class="text-center mb-12"> | |
<h2 class="text-5xl font-bold gradient-text mb-4">Your Creations</h2> | |
<p class="text-xl text-gray-400">Your personal collection of generated images</p> | |
</div> | |
<div id="historyGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> | |
<!-- History items will be loaded here --> | |
</div> | |
</div> | |
</section> | |
</main> | |
<!-- Auth Modal --> | |
<div id="authModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50"> | |
<div class="min-h-screen flex items-center justify-center p-4"> | |
<div class="glass-effect rounded-3xl border border-white/20 w-full max-w-md neon-border"> | |
<div class="p-8"> | |
<div class="text-center mb-8"> | |
<div class="w-16 h-16 bg-gradient-to-r from-neon-blue to-neon-purple rounded-full flex items-center justify-center mx-auto mb-4 animate-pulse-slow"> | |
<i class="fas fa-user text-white text-2xl"></i> | |
</div> | |
<h3 class="text-2xl font-bold gradient-text" id="authTitle">Login</h3> | |
</div> | |
<!-- Login Form --> | |
<form id="loginForm" class="space-y-6"> | |
<div> | |
<input type="email" id="loginEmail" placeholder="Email" required | |
class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue"> | |
</div> | |
<div> | |
<input type="password" id="loginPassword" placeholder="Password" required | |
class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue"> | |
</div> | |
<button type="submit" | |
class="w-full px-6 py-3 bg-gradient-to-r from-neon-blue to-neon-purple rounded-xl font-semibold hover:opacity-90 transition-opacity"> | |
Login | |
</button> | |
</form> | |
<!-- Register Form --> | |
<form id="registerForm" class="space-y-6 hidden"> | |
<div> | |
<input type="text" id="registerUsername" placeholder="Username" required | |
class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue"> | |
</div> | |
<div> | |
<input type="email" id="registerEmail" placeholder="Email" required | |
class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue"> | |
</div> | |
<div> | |
<input type="password" id="registerPassword" placeholder="Password" required | |
class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white placeholder-gray-500 focus:outline-none focus:border-neon-blue"> | |
</div> | |
<button type="submit" | |
class="w-full px-6 py-3 bg-gradient-to-r from-neon-purple to-neon-pink rounded-xl font-semibold hover:opacity-90 transition-opacity"> | |
Register | |
</button> | |
</form> | |
<div class="text-center mt-6"> | |
<button onclick="toggleAuthForm()" class="text-neon-blue hover:text-neon-purple transition-colors" id="toggleAuthBtn"> | |
Don't have an account? Register | |
</button> | |
</div> | |
<button onclick="closeAuthModal()" class="absolute top-4 right-4 text-gray-400 hover:text-white"> | |
<i class="fas fa-times text-xl"></i> | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Image Preview Modal --> | |
<div id="previewModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50"> | |
<div class="min-h-screen flex items-center justify-center p-4"> | |
<div class="glass-effect rounded-3xl border border-white/20 w-full max-w-4xl neon-border"> | |
<div class="p-8"> | |
<div class="flex justify-between items-center mb-6"> | |
<h3 class="text-2xl font-bold gradient-text">Image Preview</h3> | |
<button onclick="closePreviewModal()" class="text-gray-400 hover:text-white"> | |
<i class="fas fa-times text-xl"></i> | |
</button> | |
</div> | |
<img id="previewImage" class="w-full rounded-2xl shadow-2xl" alt="Preview"> | |
<p id="previewPrompt" class="mt-4 text-center text-gray-300"></p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
let currentUser = null; | |
let currentToken = null; | |
let generatedImageUrl = null; | |
// Navigation | |
function showSection(sectionName) { | |
document.querySelectorAll('.section').forEach(s => s.classList.add('hidden')); | |
document.getElementById(sectionName + 'Section').classList.remove('hidden'); | |
if (sectionName === 'gallery') loadGallery(); | |
if (sectionName === 'history') loadHistory(); | |
} | |
function toggleMobileMenu() { | |
const menu = document.getElementById('mobileMenu'); | |
menu.classList.toggle('hidden'); | |
} | |
// Auth Functions | |
async function checkAuth() { | |
const token = localStorage.getItem('token'); | |
if (!token) { | |
updateAuthUI(false); | |
return; | |
} | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/auth/status', { | |
headers: { 'Authorization': `Bearer ${token}` } | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
currentUser = data.user; | |
currentToken = token; | |
updateAuthUI(true); | |
} else { | |
updateAuthUI(false); | |
} | |
} catch (error) { | |
updateAuthUI(false); | |
} | |
} | |
function updateAuthUI(isAuthenticated) { | |
const authBtn = document.getElementById('authBtn'); | |
if (isAuthenticated) { | |
authBtn.innerHTML = `<i class="fas fa-user mr-2"></i>${currentUser.username}`; | |
authBtn.onclick = logout; | |
} else { | |
authBtn.innerHTML = `<i class="fas fa-sign-in-alt mr-2"></i>Login`; | |
authBtn.onclick = showAuthModal; | |
} | |
} | |
function showAuthModal() { | |
document.getElementById('authModal').classList.remove('hidden'); | |
} | |
function closeAuthModal() { | |
document.getElementById('authModal').classList.add('hidden'); | |
} | |
function toggleAuthForm() { | |
const loginForm = document.getElementById('loginForm'); | |
const registerForm = document.getElementById('registerForm'); | |
const authTitle = document.getElementById('authTitle'); | |
const toggleBtn = document.getElementById('toggleAuthBtn'); | |
if (loginForm.classList.contains('hidden')) { | |
loginForm.classList.remove('hidden'); | |
registerForm.classList.add('hidden'); | |
authTitle.textContent = 'Login'; | |
toggleBtn.textContent = "Don't have an account? Register"; | |
} else { | |
loginForm.classList.add('hidden'); | |
registerForm.classList.remove('hidden'); | |
authTitle.textContent = 'Register'; | |
toggleBtn.textContent = 'Already have an account? Login'; | |
} | |
} | |
async function login(event) { | |
event.preventDefault(); | |
const email = document.getElementById('loginEmail').value; | |
const password = document.getElementById('loginPassword').value; | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/login', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify({ email, password }) | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
localStorage.setItem('token', data.token); | |
currentUser = data.user; | |
currentToken = data.token; | |
updateAuthUI(true); | |
closeAuthModal(); | |
} else { | |
const error = await response.json(); | |
alert(error.error); | |
} | |
} catch (error) { | |
alert('Login failed'); | |
} | |
} | |
async function register(event) { | |
event.preventDefault(); | |
const username = document.getElementById('registerUsername').value; | |
const email = document.getElementById('registerEmail').value; | |
const password = document.getElementById('registerPassword').value; | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/register', { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify({ username, email, password }) | |
}); | |
if (response.ok) { | |
const data = await response.json(); | |
localStorage.setItem('token', data.token); | |
currentUser = data.user; | |
currentToken = data.token; | |
updateAuthUI(true); | |
closeAuthModal(); | |
} else { | |
const error = await response.json(); | |
alert(error.error); | |
} | |
} catch (error) { | |
alert('Registration failed'); | |
} | |
} | |
function logout() { | |
localStorage.removeItem('token'); | |
currentUser = null; | |
currentToken = null; | |
updateAuthUI(false); | |
} | |
// Image Generation | |
function setPrompt(prompt) { | |
document.getElementById('promptInput').value = prompt; | |
} | |
async function generateImage() { | |
const prompt = document.getElementById('promptInput').value.trim(); | |
if (!prompt) { | |
alert('Please enter a prompt'); | |
return; | |
} | |
if (!currentToken) { | |
alert('Please login to generate images'); | |
return; | |
} | |
document.getElementById('loadingState').classList.remove('hidden'); | |
document.getElementById('resultSection').classList.add('hidden'); | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/generate', { | |
method: 'POST', | |
headers: { | |
'Authorization': `Bearer ${currentToken}`, | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ prompt }) | |
}); | |
const reader = response.body.getReader(); | |
const decoder = new TextDecoder(); | |
while (true) { | |
const { done, value } = await reader.read(); | |
if (done) break; | |
const chunk = decoder.decode(value); | |
const lines = chunk.split('\n'); | |
for (const line of lines) { | |
if (line.startsWith('data: ')) { | |
try { | |
const data = JSON.parse(line.substring(6)); | |
if (data.type === 'estimation') { | |
document.getElementById('queueInfo').textContent = | |
`Queue position: ${data.queueSize}, ETA: ${data.eta}s`; | |
} else if (data.type === 'processing') { | |
document.getElementById('queueInfo').textContent = 'Processing your image...'; | |
} else if (data.type === 'success') { | |
generatedImageUrl = data.originalUrl; | |
document.getElementById('generatedImage').src = data.originalUrl; | |
document.getElementById('loadingState').classList.add('hidden'); | |
document.getElementById('resultSection').classList.remove('hidden'); | |
return; | |
} else if (data.type === 'error') { | |
throw new Error(data.message); | |
} | |
} catch (e) { | |
console.error('Error parsing SSE data:', e); | |
} | |
} | |
} | |
} | |
} catch (error) { | |
document.getElementById('loadingState').classList.add('hidden'); | |
alert('Generation failed: ' + error.message); | |
} | |
} | |
async function addToGallery() { | |
if (!generatedImageUrl) return; | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/upload-image', { | |
method: 'POST', | |
headers: { | |
'Authorization': `Bearer ${currentToken}`, | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
imageUrl: generatedImageUrl, | |
prompt: document.getElementById('promptInput').value | |
}) | |
}); | |
if (response.ok) { | |
alert('Image added to gallery successfully!'); | |
loadGallery(); | |
} else { | |
const error = await response.json(); | |
alert(error.error); | |
} | |
} catch (error) { | |
alert('Failed to add to gallery'); | |
} | |
} | |
function downloadImage() { | |
if (!generatedImageUrl) return; | |
const link = document.createElement('a'); | |
link.href = generatedImageUrl; | |
link.download = 'generated-image.jpg'; | |
link.click(); | |
} | |
// Gallery Functions | |
async function loadGallery() { | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/community-images'); | |
const images = await response.json(); | |
const grid = document.getElementById('galleryGrid'); | |
grid.innerHTML = ''; | |
const sortOrder = document.getElementById('sortOrder').value; | |
const sortedImages = [...images].sort((a, b) => { | |
const dateA = new Date(a.created_at); | |
const dateB = new Date(b.created_at); | |
return sortOrder === 'old-new' ? dateA - dateB : dateB - dateA; | |
}); | |
sortedImages.forEach(image => { | |
const card = createImageCard(image); | |
grid.appendChild(card); | |
}); | |
} catch (error) { | |
console.error('Failed to load gallery:', error); | |
} | |
} | |
async function loadHistory() { | |
if (!currentToken) return; | |
try { | |
const response = await fetch('https://gen-image-main.vercel.app/api/user-generations', { | |
headers: { 'Authorization': `Bearer ${currentToken}` } | |
}); | |
if (response.ok) { | |
const images = await response.json(); | |
const grid = document.getElementById('historyGrid'); | |
grid.innerHTML = ''; | |
if (images.length === 0) { | |
grid.innerHTML = ` | |
<div class="col-span-full text-center py-12"> | |
<div class="w-24 h-24 bg-gradient-to-r from-neon-blue to-neon-purple rounded-full flex items-center justify-center mx-auto mb-4 animate-pulse-slow"> | |
<i class="fas fa-magic text-white text-3xl"></i> | |
</div> | |
<p class="text-gray-400 text-lg">No images generated yet. Start creating!</p> | |
</div> | |
`; | |
return; | |
} | |
images.forEach(image => { | |
const card = createImageCard(image, true); | |
grid.appendChild(card); | |
}); | |
} | |
} catch (error) { | |
console.error('Failed to load history:', error); | |
} | |
} | |
function createImageCard(image, isHistory = false) { | |
const card = document.createElement('div'); | |
card.className = 'glass-effect rounded-2xl overflow-hidden neon-border hover:animate-glow transition-all cursor-pointer'; | |
card.innerHTML = ` | |
<div class="aspect-square overflow-hidden"> | |
<img src="${image.uploaded_url || image.image_url}" alt="${image.prompt}" | |
class="w-full h-full object-cover hover:scale-105 transition-transform duration-300"> | |
</div> | |
<div class="p-4 space-y-3"> | |
<p class="text-gray-300 line-clamp-2 text-sm">${image.prompt}</p> | |
<div class="flex items-center justify-between text-xs"> | |
<p class="text-neon-blue flex items-center"> | |
<i class="fas fa-user mr-1"></i>${image.username || 'You'} | |
</p> | |
<p class="text-gray-500 flex items-center"> | |
<i class="fas fa-clock mr-1"></i>${new Date(image.created_at).toLocaleDateString()} | |
</p> | |
</div> | |
${isHistory ? ` | |
<button onclick="deleteImage(${image.id})" | |
class="w-full mt-2 px-4 py-2 bg-red-500/20 text-red-400 rounded-xl hover:bg-red-500/30 transition-colors text-sm"> | |
<i class="fas fa-trash mr-2"></i>Delete | |
</button> | |
` : ''} | |
</div> | |
`; | |
card.querySelector('img').addEventListener('click', () => { | |
document.getElementById('previewImage').src = image.uploaded_url || image.image_url; | |
document.getElementById('previewPrompt').textContent = image.prompt; | |
document.getElementById('previewModal').classList.remove('hidden'); | |
}); | |
return card; | |
} | |
async function deleteImage(imageId) { | |
if (!confirm('Are you sure you want to delete this image?')) return; | |
try { | |
const response = await fetch(`https://gen-image-main.vercel.app/api/user-generations/${imageId}`, { | |
method: 'DELETE', | |
headers: { 'Authorization': `Bearer ${currentToken}` } | |
}); | |
if (response.ok) { | |
loadHistory(); | |
loadGallery(); | |
} else { | |
const error = await response.json(); | |
alert(error.error); | |
} | |
} catch (error) { | |
alert('Failed to delete image'); | |
} | |
} | |
function closePreviewModal() { | |
document.getElementById('previewModal').classList.add('hidden'); | |
} | |
// Event Listeners | |
document.addEventListener('DOMContentLoaded', () => { | |
checkAuth(); | |
document.getElementById('loginForm').addEventListener('submit', login); | |
document.getElementById('registerForm').addEventListener('submit', register); | |
}); | |
// Close modals on outside click | |
document.getElementById('authModal').addEventListener('click', (e) => { | |
if (e.target === e.currentTarget) closeAuthModal(); | |
}); | |
document.getElementById('previewModal').addEventListener('click', (e) => { | |
if (e.target === e.currentTarget) closePreviewModal(); | |
}); | |
</script> | |
</body> | |
</html> |