vision / public /index.html
shashwatIDR's picture
Upload index.html
ea94f5e verified
<!DOCTYPE html>
<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>