shashwatIDR commited on
Commit
3f49f6f
·
verified ·
1 Parent(s): dfc3a58

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +136 -609
public/index.html CHANGED
@@ -57,653 +57,180 @@
57
  </style>
58
  </head>
59
  <body class="bg-dark-bg text-white min-h-screen font-sans">
60
- <!-- Navigation -->
61
- <nav class="glass-effect fixed top-0 w-full z-50 border-b border-white/10">
62
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
63
- <div class="flex justify-between items-center h-16">
64
- <div class="flex items-center space-x-4">
65
- <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">
66
- <i class="fas fa-magic text-white text-lg"></i>
67
- </div>
68
- <h1 class="text-2xl font-bold gradient-text">AI Creator</h1>
69
- </div>
70
- <div class="hidden md:flex items-center space-x-6">
71
- <button onclick="showSection('create')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors">
72
- <i class="fas fa-plus mr-2"></i>Create
73
- </button>
74
- <button onclick="showSection('gallery')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors">
75
- <i class="fas fa-images mr-2"></i>Gallery
76
- </button>
77
- <button onclick="showSection('history')" class="nav-btn text-gray-300 hover:text-neon-blue transition-colors">
78
- <i class="fas fa-history mr-2"></i>History
79
- </button>
80
- <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">
81
- <i class="fas fa-sign-in-alt mr-2"></i>Login
82
- </button>
83
- </div>
84
- <div class="md:hidden">
85
- <button onclick="toggleMobileMenu()" class="text-gray-300 hover:text-neon-blue">
86
- <i class="fas fa-bars text-xl"></i>
87
- </button>
88
- </div>
89
- </div>
90
- </div>
91
- </nav>
92
-
93
- <!-- Mobile Menu -->
94
- <div id="mobileMenu" class="md:hidden fixed top-16 left-0 w-full glass-effect border-b border-white/10 hidden">
95
- <div class="px-4 py-4 space-y-4">
96
- <button onclick="showSection('create')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors">
97
- <i class="fas fa-plus mr-2"></i>Create
98
- </button>
99
- <button onclick="showSection('gallery')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors">
100
- <i class="fas fa-images mr-2"></i>Gallery
101
- </button>
102
- <button onclick="showSection('history')" class="nav-btn w-full text-left text-gray-300 hover:text-neon-blue transition-colors">
103
- <i class="fas fa-history mr-2"></i>History
104
- </button>
105
- <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">
106
- <i class="fas fa-sign-in-alt mr-2"></i>Login
107
- </button>
108
- </div>
109
- </div>
110
-
111
- <!-- Main Content -->
112
  <main class="pt-20">
113
- <!-- Create Section -->
114
- <section id="createSection" class="section active min-h-screen">
115
- <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
116
- <!-- Hero Section -->
117
- <div class="text-center mb-16">
118
- <h2 class="text-6xl font-bold gradient-text mb-6 animate-float">
119
- Create Magic
120
- </h2>
121
- <p class="text-xl text-gray-400 mb-8 max-w-2xl mx-auto">
122
- Transform your imagination into stunning visuals with our advanced AI image generation
123
- </p>
124
- <div class="flex justify-center space-x-4">
125
- <div class="w-4 h-4 bg-neon-blue rounded-full animate-pulse"></div>
126
- <div class="w-4 h-4 bg-neon-purple rounded-full animate-pulse" style="animation-delay: 0.2s;"></div>
127
- <div class="w-4 h-4 bg-neon-pink rounded-full animate-pulse" style="animation-delay: 0.4s;"></div>
128
- </div>
129
  </div>
130
-
131
- <!-- Creation Interface -->
132
  <div class="glass-effect rounded-3xl p-8 neon-border">
133
- <div class="space-y-8">
134
- <!-- Prompt Input -->
135
- <div class="relative">
136
- <textarea id="promptInput"
137
- 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"
138
- placeholder="Describe your vision... What would you like to create?"></textarea>
139
- <div class="absolute bottom-4 right-4">
140
- <button id="generateBtn" onclick="generateImage()"
141
- 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">
142
- <i class="fas fa-magic mr-2"></i>Generate
143
- </button>
144
- </div>
145
- </div>
146
-
147
- <!-- Quick Prompts -->
148
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
149
- <button onclick="setPrompt('A futuristic city with flying cars and neon lights')"
150
- class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all">
151
- <i class="fas fa-city text-neon-blue mb-2"></i>
152
- <p class="text-sm">Futuristic City</p>
153
- </button>
154
- <button onclick="setPrompt('A magical forest with glowing mushrooms and fairy lights')"
155
- class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all">
156
- <i class="fas fa-tree text-neon-purple mb-2"></i>
157
- <p class="text-sm">Magical Forest</p>
158
- </button>
159
- <button onclick="setPrompt('A cyberpunk warrior with neon armor and glowing weapons')"
160
- class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all">
161
- <i class="fas fa-user-ninja text-neon-pink mb-2"></i>
162
- <p class="text-sm">Cyberpunk Warrior</p>
163
- </button>
164
- <button onclick="setPrompt('An underwater palace with coral and sea creatures')"
165
- class="quick-prompt glass-effect rounded-xl p-4 hover:neon-border transition-all">
166
- <i class="fas fa-water text-neon-blue mb-2"></i>
167
- <p class="text-sm">Underwater Palace</p>
168
- </button>
169
- </div>
170
-
171
- <!-- Loading State -->
172
- <div id="loadingState" class="hidden text-center py-12">
173
- <div class="inline-flex items-center space-x-4">
174
- <div class="w-8 h-8 border-4 border-neon-blue border-t-transparent rounded-full animate-spin"></div>
175
- <span class="text-neon-blue text-lg">Creating your masterpiece...</span>
176
- </div>
177
- <div id="queueInfo" class="mt-4 text-gray-400"></div>
178
- </div>
179
-
180
- <!-- Result -->
181
- <div id="resultSection" class="hidden">
182
- <div class="glass-effect rounded-2xl p-6 neon-border">
183
- <img id="generatedImage" class="w-full rounded-xl shadow-2xl" alt="Generated image">
184
- <div class="mt-6 flex justify-between items-center">
185
- <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">
186
- <i class="fas fa-plus mr-2"></i>Add to Gallery
187
- </button>
188
- <button onclick="downloadImage()" class="px-6 py-3 glass-effect rounded-full hover:neon-border transition-all">
189
- <i class="fas fa-download mr-2"></i>Download
190
- </button>
191
- </div>
192
- </div>
193
- </div>
194
  </div>
195
- </div>
196
- </div>
197
- </section>
198
-
199
- <!-- Gallery Section -->
200
- <section id="gallerySection" class="section hidden min-h-screen">
201
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
202
- <div class="text-center mb-12">
203
- <h2 class="text-5xl font-bold gradient-text mb-4">Community Gallery</h2>
204
- <p class="text-xl text-gray-400">Explore amazing creations from our community</p>
205
- </div>
206
-
207
- <div class="flex justify-center mb-8">
208
- <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">
209
- <option value="new-old">Newest First</option>
210
- <option value="old-new">Oldest First</option>
211
- </select>
212
- </div>
213
-
214
- <div id="galleryGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
215
- <!-- Gallery items will be loaded here -->
216
- </div>
217
- </div>
218
- </section>
219
-
220
- <!-- History Section -->
221
- <section id="historySection" class="section hidden min-h-screen">
222
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
223
- <div class="text-center mb-12">
224
- <h2 class="text-5xl font-bold gradient-text mb-4">Your Creations</h2>
225
- <p class="text-xl text-gray-400">Your personal collection of generated images</p>
226
- </div>
227
-
228
- <div id="historyGrid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
229
- <!-- History items will be loaded here -->
230
- </div>
231
- </div>
232
- </section>
233
- </main>
234
-
235
- <!-- Auth Modal -->
236
- <div id="authModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50">
237
- <div class="min-h-screen flex items-center justify-center p-4">
238
- <div class="glass-effect rounded-3xl border border-white/20 w-full max-w-md neon-border">
239
- <div class="p-8">
240
- <div class="text-center mb-8">
241
- <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">
242
- <i class="fas fa-user text-white text-2xl"></i>
243
- </div>
244
- <h3 class="text-2xl font-bold gradient-text" id="authTitle">Login</h3>
245
  </div>
246
-
247
- <!-- Login Form -->
248
- <form id="loginForm" class="space-y-6">
249
- <div>
250
- <input type="email" id="loginEmail" placeholder="Email" required
251
- 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">
252
- </div>
253
- <div>
254
- <input type="password" id="loginPassword" placeholder="Password" required
255
- 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">
256
- </div>
257
- <button type="submit"
258
- 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">
259
- Login
260
  </button>
261
- </form>
262
-
263
- <!-- Register Form -->
264
- <form id="registerForm" class="space-y-6 hidden">
265
- <div>
266
- <input type="text" id="registerUsername" placeholder="Username" required
267
- 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">
268
- </div>
269
- <div>
270
- <input type="email" id="registerEmail" placeholder="Email" required
271
- 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">
272
- </div>
273
- <div>
274
- <input type="password" id="registerPassword" placeholder="Password" required
275
- 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">
276
  </div>
277
- <button type="submit"
278
- 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">
279
- Register
280
- </button>
281
- </form>
282
-
283
- <div class="text-center mt-6">
284
- <button onclick="toggleAuthForm()" class="text-neon-blue hover:text-neon-purple transition-colors" id="toggleAuthBtn">
285
- Don't have an account? Register
286
- </button>
287
  </div>
288
-
289
- <button onclick="closeAuthModal()" class="absolute top-4 right-4 text-gray-400 hover:text-white">
290
- <i class="fas fa-times text-xl"></i>
291
- </button>
292
- </div>
293
- </div>
294
- </div>
295
- </div>
296
-
297
- <!-- Image Preview Modal -->
298
- <div id="previewModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden z-50">
299
- <div class="min-h-screen flex items-center justify-center p-4">
300
- <div class="glass-effect rounded-3xl border border-white/20 w-full max-w-4xl neon-border">
301
- <div class="p-8">
302
- <div class="flex justify-between items-center mb-6">
303
- <h3 class="text-2xl font-bold gradient-text">Image Preview</h3>
304
- <button onclick="closePreviewModal()" class="text-gray-400 hover:text-white">
305
- <i class="fas fa-times text-xl"></i>
306
- </button>
307
  </div>
308
- <img id="previewImage" class="w-full rounded-2xl shadow-2xl" alt="Preview">
309
- <p id="previewPrompt" class="mt-4 text-center text-gray-300"></p>
310
  </div>
311
  </div>
312
- </div>
313
- </div>
314
-
315
  <script>
316
- let currentUser = null;
317
- let currentToken = null;
318
  let generatedImageUrl = null;
319
-
320
- // Navigation
321
- function showSection(sectionName) {
322
- document.querySelectorAll('.section').forEach(s => s.classList.add('hidden'));
323
- document.getElementById(sectionName + 'Section').classList.remove('hidden');
324
-
325
- if (sectionName === 'gallery') loadGallery();
326
- if (sectionName === 'history') loadHistory();
327
  }
328
-
329
- function toggleMobileMenu() {
330
- const menu = document.getElementById('mobileMenu');
331
- menu.classList.toggle('hidden');
332
  }
333
-
334
- // Auth Functions
335
- async function checkAuth() {
336
- const token = localStorage.getItem('token');
337
- if (!token) {
338
- updateAuthUI(false);
339
- return;
340
- }
341
-
342
- try {
343
- const response = await fetch('https://gen-image-main.vercel.app/api/auth/status', {
344
- headers: { 'Authorization': `Bearer ${token}` }
345
- });
346
-
347
- if (response.ok) {
348
- const data = await response.json();
349
- currentUser = data.user;
350
- currentToken = token;
351
- updateAuthUI(true);
352
- } else {
353
- updateAuthUI(false);
354
- }
355
- } catch (error) {
356
- updateAuthUI(false);
357
- }
358
- }
359
-
360
- function updateAuthUI(isAuthenticated) {
361
- const authBtn = document.getElementById('authBtn');
362
- if (isAuthenticated) {
363
- authBtn.innerHTML = `<i class="fas fa-user mr-2"></i>${currentUser.username}`;
364
- authBtn.onclick = logout;
365
- } else {
366
- authBtn.innerHTML = `<i class="fas fa-sign-in-alt mr-2"></i>Login`;
367
- authBtn.onclick = showAuthModal;
368
- }
369
- }
370
-
371
- function showAuthModal() {
372
- document.getElementById('authModal').classList.remove('hidden');
373
- }
374
-
375
- function closeAuthModal() {
376
- document.getElementById('authModal').classList.add('hidden');
377
- }
378
-
379
- function toggleAuthForm() {
380
- const loginForm = document.getElementById('loginForm');
381
- const registerForm = document.getElementById('registerForm');
382
- const authTitle = document.getElementById('authTitle');
383
- const toggleBtn = document.getElementById('toggleAuthBtn');
384
-
385
- if (loginForm.classList.contains('hidden')) {
386
- loginForm.classList.remove('hidden');
387
- registerForm.classList.add('hidden');
388
- authTitle.textContent = 'Login';
389
- toggleBtn.textContent = "Don't have an account? Register";
390
- } else {
391
- loginForm.classList.add('hidden');
392
- registerForm.classList.remove('hidden');
393
- authTitle.textContent = 'Register';
394
- toggleBtn.textContent = 'Already have an account? Login';
395
- }
396
- }
397
-
398
- async function login(event) {
399
- event.preventDefault();
400
- const email = document.getElementById('loginEmail').value;
401
- const password = document.getElementById('loginPassword').value;
402
-
403
- try {
404
- const response = await fetch('https://gen-image-main.vercel.app/api/login', {
405
- method: 'POST',
406
- headers: { 'Content-Type': 'application/json' },
407
- body: JSON.stringify({ email, password })
408
- });
409
-
410
- if (response.ok) {
411
- const data = await response.json();
412
- localStorage.setItem('token', data.token);
413
- currentUser = data.user;
414
- currentToken = data.token;
415
- updateAuthUI(true);
416
- closeAuthModal();
417
- } else {
418
- const error = await response.json();
419
- alert(error.error);
420
- }
421
- } catch (error) {
422
- alert('Login failed');
423
- }
424
  }
425
-
426
- async function register(event) {
427
- event.preventDefault();
428
- const username = document.getElementById('registerUsername').value;
429
- const email = document.getElementById('registerEmail').value;
430
- const password = document.getElementById('registerPassword').value;
431
-
432
- try {
433
- const response = await fetch('https://gen-image-main.vercel.app/api/register', {
434
- method: 'POST',
435
- headers: { 'Content-Type': 'application/json' },
436
- body: JSON.stringify({ username, email, password })
437
- });
438
-
439
- if (response.ok) {
440
- const data = await response.json();
441
- localStorage.setItem('token', data.token);
442
- currentUser = data.user;
443
- currentToken = data.token;
444
- updateAuthUI(true);
445
- closeAuthModal();
446
- } else {
447
- const error = await response.json();
448
- alert(error.error);
449
- }
450
- } catch (error) {
451
- alert('Registration failed');
452
  }
453
  }
454
-
455
- function logout() {
456
- localStorage.removeItem('token');
457
- currentUser = null;
458
- currentToken = null;
459
- updateAuthUI(false);
460
- }
461
-
462
- // Image Generation
463
- function setPrompt(prompt) {
464
- document.getElementById('promptInput').value = prompt;
465
- }
466
-
467
  async function generateImage() {
468
  const prompt = document.getElementById('promptInput').value.trim();
 
 
469
  if (!prompt) {
470
  alert('Please enter a prompt');
471
  return;
472
  }
473
-
474
- if (!currentToken) {
475
- alert('Please login to generate images');
476
- return;
477
- }
478
-
479
- document.getElementById('loadingState').classList.remove('hidden');
480
- document.getElementById('resultSection').classList.add('hidden');
481
-
482
- try {
483
- const response = await fetch('https://gen-image-main.vercel.app/api/generate', {
484
- method: 'POST',
485
- headers: {
486
- 'Authorization': `Bearer ${currentToken}`,
487
- 'Content-Type': 'application/json'
488
- },
489
- body: JSON.stringify({ prompt })
490
- });
491
-
492
- const reader = response.body.getReader();
493
- const decoder = new TextDecoder();
494
-
495
- while (true) {
496
- const { done, value } = await reader.read();
497
- if (done) break;
498
-
499
- const chunk = decoder.decode(value);
500
- const lines = chunk.split('\n');
501
-
502
- for (const line of lines) {
503
- if (line.startsWith('data: ')) {
504
- try {
505
- const data = JSON.parse(line.substring(6));
506
-
507
- if (data.type === 'estimation') {
508
- document.getElementById('queueInfo').textContent =
509
- `Queue position: ${data.queueSize}, ETA: ${data.eta}s`;
510
- } else if (data.type === 'processing') {
511
- document.getElementById('queueInfo').textContent = 'Processing your image...';
512
- } else if (data.type === 'success') {
513
- generatedImageUrl = data.originalUrl;
514
- document.getElementById('generatedImage').src = data.originalUrl;
515
- document.getElementById('loadingState').classList.add('hidden');
516
- document.getElementById('resultSection').classList.remove('hidden');
517
- return;
518
- } else if (data.type === 'error') {
519
- throw new Error(data.message);
520
- }
521
- } catch (e) {
522
- console.error('Error parsing SSE data:', e);
523
  }
524
  }
525
  }
 
 
 
526
  }
527
- } catch (error) {
528
- document.getElementById('loadingState').classList.add('hidden');
529
- alert('Generation failed: ' + error.message);
530
- }
531
- }
532
-
533
- async function addToGallery() {
534
- if (!generatedImageUrl) return;
535
-
536
- try {
537
- const response = await fetch('https://gen-image-main.vercel.app/api/upload-image', {
538
- method: 'POST',
539
- headers: {
540
- 'Authorization': `Bearer ${currentToken}`,
541
- 'Content-Type': 'application/json'
542
- },
543
- body: JSON.stringify({
544
- imageUrl: generatedImageUrl,
545
- prompt: document.getElementById('promptInput').value
546
- })
547
- });
548
-
549
- if (response.ok) {
550
- alert('Image added to gallery successfully!');
551
- loadGallery();
552
- } else {
553
- const error = await response.json();
554
- alert(error.error);
555
  }
556
- } catch (error) {
557
- alert('Failed to add to gallery');
558
  }
559
  }
560
-
561
  function downloadImage() {
562
  if (!generatedImageUrl) return;
563
-
564
  const link = document.createElement('a');
565
  link.href = generatedImageUrl;
566
  link.download = 'generated-image.jpg';
567
  link.click();
568
  }
569
-
570
- // Gallery Functions
571
- async function loadGallery() {
572
- try {
573
- const response = await fetch('https://gen-image-main.vercel.app/api/community-images');
574
- const images = await response.json();
575
-
576
- const grid = document.getElementById('galleryGrid');
577
- grid.innerHTML = '';
578
-
579
- const sortOrder = document.getElementById('sortOrder').value;
580
- const sortedImages = [...images].sort((a, b) => {
581
- const dateA = new Date(a.created_at);
582
- const dateB = new Date(b.created_at);
583
- return sortOrder === 'old-new' ? dateA - dateB : dateB - dateA;
584
- });
585
-
586
- sortedImages.forEach(image => {
587
- const card = createImageCard(image);
588
- grid.appendChild(card);
589
- });
590
- } catch (error) {
591
- console.error('Failed to load gallery:', error);
592
- }
593
- }
594
-
595
- async function loadHistory() {
596
- if (!currentToken) return;
597
-
598
- try {
599
- const response = await fetch('https://gen-image-main.vercel.app/api/user-generations', {
600
- headers: { 'Authorization': `Bearer ${currentToken}` }
601
- });
602
-
603
- if (response.ok) {
604
- const images = await response.json();
605
- const grid = document.getElementById('historyGrid');
606
- grid.innerHTML = '';
607
-
608
- if (images.length === 0) {
609
- grid.innerHTML = `
610
- <div class="col-span-full text-center py-12">
611
- <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">
612
- <i class="fas fa-magic text-white text-3xl"></i>
613
- </div>
614
- <p class="text-gray-400 text-lg">No images generated yet. Start creating!</p>
615
- </div>
616
- `;
617
- return;
618
- }
619
-
620
- images.forEach(image => {
621
- const card = createImageCard(image, true);
622
- grid.appendChild(card);
623
- });
624
- }
625
- } catch (error) {
626
- console.error('Failed to load history:', error);
627
- }
628
- }
629
-
630
- function createImageCard(image, isHistory = false) {
631
- const card = document.createElement('div');
632
- card.className = 'glass-effect rounded-2xl overflow-hidden neon-border hover:animate-glow transition-all cursor-pointer';
633
-
634
- card.innerHTML = `
635
- <div class="aspect-square overflow-hidden">
636
- <img src="${image.uploaded_url || image.image_url}" alt="${image.prompt}"
637
- class="w-full h-full object-cover hover:scale-105 transition-transform duration-300">
638
- </div>
639
- <div class="p-4 space-y-3">
640
- <p class="text-gray-300 line-clamp-2 text-sm">${image.prompt}</p>
641
- <div class="flex items-center justify-between text-xs">
642
- <p class="text-neon-blue flex items-center">
643
- <i class="fas fa-user mr-1"></i>${image.username || 'You'}
644
- </p>
645
- <p class="text-gray-500 flex items-center">
646
- <i class="fas fa-clock mr-1"></i>${new Date(image.created_at).toLocaleDateString()}
647
- </p>
648
- </div>
649
- ${isHistory ? `
650
- <button onclick="deleteImage(${image.id})"
651
- 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">
652
- <i class="fas fa-trash mr-2"></i>Delete
653
- </button>
654
- ` : ''}
655
- </div>
656
- `;
657
-
658
- card.querySelector('img').addEventListener('click', () => {
659
- document.getElementById('previewImage').src = image.uploaded_url || image.image_url;
660
- document.getElementById('previewPrompt').textContent = image.prompt;
661
- document.getElementById('previewModal').classList.remove('hidden');
662
- });
663
-
664
- return card;
665
- }
666
-
667
- async function deleteImage(imageId) {
668
- if (!confirm('Are you sure you want to delete this image?')) return;
669
-
670
- try {
671
- const response = await fetch(`https://gen-image-main.vercel.app/api/user-generations/${imageId}`, {
672
- method: 'DELETE',
673
- headers: { 'Authorization': `Bearer ${currentToken}` }
674
- });
675
-
676
- if (response.ok) {
677
- loadHistory();
678
- loadGallery();
679
- } else {
680
- const error = await response.json();
681
- alert(error.error);
682
- }
683
- } catch (error) {
684
- alert('Failed to delete image');
685
- }
686
- }
687
-
688
- function closePreviewModal() {
689
- document.getElementById('previewModal').classList.add('hidden');
690
- }
691
-
692
- // Event Listeners
693
- document.addEventListener('DOMContentLoaded', () => {
694
- checkAuth();
695
- document.getElementById('loginForm').addEventListener('submit', login);
696
- document.getElementById('registerForm').addEventListener('submit', register);
697
- });
698
-
699
- // Close modals on outside click
700
- document.getElementById('authModal').addEventListener('click', (e) => {
701
- if (e.target === e.currentTarget) closeAuthModal();
702
- });
703
-
704
- document.getElementById('previewModal').addEventListener('click', (e) => {
705
- if (e.target === e.currentTarget) closePreviewModal();
706
- });
707
  </script>
708
  </body>
709
  </html>
 
57
  </style>
58
  </head>
59
  <body class="bg-dark-bg text-white min-h-screen font-sans">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  <main class="pt-20">
61
+ <section class="min-h-screen flex flex-col items-center justify-center">
62
+ <div class="max-w-xl w-full mx-auto px-4 py-12">
63
+ <div class="text-center mb-10">
64
+ <h2 class="text-5xl font-bold gradient-text mb-4 animate-float">AI Image Generator</h2>
65
+ <p class="text-lg text-gray-400 mb-6">Choose a model, enter your prompt, and create stunning images instantly!</p>
 
 
 
 
 
 
 
 
 
 
 
66
  </div>
 
 
67
  <div class="glass-effect rounded-3xl p-8 neon-border">
68
+ <div class="mb-6">
69
+ <label class="block mb-2 text-neon-blue font-semibold">Select Model</label>
70
+ <select id="modelSelect" class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white focus:outline-none focus:border-neon-blue" onchange="toggleFluxOptions()">
71
+ <option value="heartsync">Heartsync (SSE, best for anime)</option>
72
+ <option value="flux">FLUX.1-dev (fast, general purpose)</option>
73
+ </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  </div>
75
+ <div id="fluxOptions" class="mb-6 hidden">
76
+ <label class="block mb-2 text-neon-blue font-semibold">Aspect Ratio (FLUX only)</label>
77
+ <select id="fluxAspect" class="w-full bg-card-bg border border-white/20 rounded-xl px-4 py-3 text-white focus:outline-none focus:border-neon-blue" onchange="updateFluxResolution()">
78
+ <option value="1:1" selected>1:1 (Square)</option>
79
+ <option value="16:9">16:9 (Landscape)</option>
80
+ <option value="9:16">9:16 (Portrait)</option>
81
+ <option value="4:3">4:3</option>
82
+ <option value="3:2">3:2</option>
83
+ <option value="2:3">2:3</option>
84
+ </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  </div>
86
+ <div class="mb-6">
87
+ <label class="block mb-2 text-neon-blue font-semibold">Prompt</label>
88
+ <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... (Resolution options below are for FLUX only)"></textarea>
89
+ </div>
90
+ <div class="mb-6">
91
+ <button id="generateBtn" onclick="generateImage()" class="w-full 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">
92
+ <i class="fas fa-magic mr-2"></i>Generate
 
 
 
 
 
 
 
93
  </button>
94
+ </div>
95
+ <div id="loadingState" class="hidden text-center py-6">
96
+ <div class="inline-flex items-center space-x-4">
97
+ <div class="w-8 h-8 border-4 border-neon-blue border-t-transparent rounded-full animate-spin"></div>
98
+ <span class="text-neon-blue text-lg">Generating image...</span>
 
 
 
 
 
 
 
 
 
 
99
  </div>
100
+ <div id="queueInfo" class="mt-4 text-gray-400"></div>
 
 
 
 
 
 
 
 
 
101
  </div>
102
+ <div id="resultSection" class="hidden mt-8">
103
+ <div class="glass-effect rounded-2xl p-6 neon-border">
104
+ <img id="generatedImage" class="w-full rounded-xl shadow-2xl" alt="Generated image">
105
+ <div class="mt-6 flex justify-center">
106
+ <button onclick="downloadImage()" class="px-6 py-3 glass-effect rounded-full hover:neon-border transition-all">
107
+ <i class="fas fa-download mr-2"></i>Download
108
+ </button>
109
+ </div>
110
+ </div>
 
 
 
 
 
 
 
 
 
 
111
  </div>
 
 
112
  </div>
113
  </div>
114
+ </section>
115
+ </main>
 
116
  <script>
 
 
117
  let generatedImageUrl = null;
118
+ function showLoading(show, queueText = '') {
119
+ document.getElementById('loadingState').classList.toggle('hidden', !show);
120
+ document.getElementById('queueInfo').textContent = queueText;
 
 
 
 
 
121
  }
122
+ function showResult(url) {
123
+ generatedImageUrl = url;
124
+ document.getElementById('generatedImage').src = url;
125
+ document.getElementById('resultSection').classList.remove('hidden');
126
  }
127
+ function hideResult() {
128
+ document.getElementById('resultSection').classList.add('hidden');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
+ function toggleFluxOptions() {
131
+ const model = document.getElementById('modelSelect').value;
132
+ document.getElementById('fluxOptions').classList.toggle('hidden', model !== 'flux');
133
+ }
134
+ function updateFluxResolution() {
135
+ // This function is for future extensibility if you want to display the actual width/height to the user
136
+ }
137
+ function getFluxResolution() {
138
+ const aspect = document.getElementById('fluxAspect').value;
139
+ switch (aspect) {
140
+ case '1:1': return { width: 1024, height: 1024 };
141
+ case '16:9': return { width: 1280, height: 720 };
142
+ case '9:16': return { width: 720, height: 1280 };
143
+ case '4:3': return { width: 1024, height: 768 };
144
+ case '3:2': return { width: 1200, height: 800 };
145
+ case '2:3': return { width: 800, height: 1200 };
146
+ default: return { width: 1024, height: 1024 };
 
 
 
 
 
 
 
 
 
 
147
  }
148
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  async function generateImage() {
150
  const prompt = document.getElementById('promptInput').value.trim();
151
+ const model = document.getElementById('modelSelect').value;
152
+ let width, height;
153
  if (!prompt) {
154
  alert('Please enter a prompt');
155
  return;
156
  }
157
+ hideResult();
158
+ showLoading(true);
159
+ if (model === 'heartsync') {
160
+ // SSE logic
161
+ try {
162
+ const response = await fetch('/api/generate', {
163
+ method: 'POST',
164
+ headers: { 'Content-Type': 'application/json' },
165
+ body: JSON.stringify({ prompt })
166
+ });
167
+ const reader = response.body.getReader();
168
+ const decoder = new TextDecoder();
169
+ while (true) {
170
+ const { done, value } = await reader.read();
171
+ if (done) break;
172
+ const chunk = decoder.decode(value);
173
+ const lines = chunk.split('\n');
174
+ for (const line of lines) {
175
+ if (line.startsWith('data: ')) {
176
+ try {
177
+ const data = JSON.parse(line.substring(6));
178
+ if (data.type === 'estimation') {
179
+ showLoading(true, `Queue position: ${data.queueSize}, ETA: ${data.eta}s`);
180
+ } else if (data.type === 'processing') {
181
+ showLoading(true, 'Processing your image...');
182
+ } else if (data.type === 'success') {
183
+ showLoading(false);
184
+ showResult(data.originalUrl);
185
+ return;
186
+ } else if (data.type === 'error') {
187
+ throw new Error(data.message);
188
+ }
189
+ } catch (e) { console.error('Error parsing SSE data:', e); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  }
191
  }
192
  }
193
+ } catch (error) {
194
+ showLoading(false);
195
+ alert('Generation failed: ' + error.message);
196
  }
197
+ } else if (model === 'flux') {
198
+ // FLUX.1-dev JSON logic
199
+ const res = getFluxResolution();
200
+ width = res.width;
201
+ height = res.height;
202
+ try {
203
+ const response = await fetch('/api/generate/flux', {
204
+ method: 'POST',
205
+ headers: { 'Content-Type': 'application/json' },
206
+ body: JSON.stringify({ prompt, width, height })
207
+ });
208
+ const data = await response.json();
209
+ if (data.success && data.image && data.image.url) {
210
+ showLoading(false);
211
+ showResult(data.image.url);
212
+ } else if (data.success && typeof data.image === 'string') {
213
+ // fallback for string URL
214
+ showLoading(false);
215
+ showResult(data.image);
216
+ } else {
217
+ showLoading(false);
218
+ alert('Generation failed: ' + (data.error || 'Unknown error'));
219
+ }
220
+ } catch (error) {
221
+ showLoading(false);
222
+ alert('Generation failed: ' + error.message);
 
 
223
  }
 
 
224
  }
225
  }
 
226
  function downloadImage() {
227
  if (!generatedImageUrl) return;
 
228
  const link = document.createElement('a');
229
  link.href = generatedImageUrl;
230
  link.download = 'generated-image.jpg';
231
  link.click();
232
  }
233
+ window.onload = function() { toggleFluxOptions(); updateFluxResolution(); };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  </script>
235
  </body>
236
  </html>