Sourav6861 commited on
Commit
dd1c12f
·
verified ·
1 Parent(s): 5014f21

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +15 -506
index.html CHANGED
@@ -1,511 +1,20 @@
 
 
 
1
 
2
- <!doctype html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="utf-8"/>
6
- <meta name="viewport" content="width=device-width,initial-scale=1"/>
7
- <title>imgGenPro — Free AI Image Generator</title>
8
-
9
- <!-- TailwindCSS -->
10
- <script src="https://cdn.tailwindcss.com"></script>
11
- <!-- Font Awesome -->
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
13
- </head>
14
-
15
- <style>
16
-
17
- .blur-nsfw {
18
- filter: blur(12px);
19
- position: relative;
20
- }
21
- .blur-nsfw::after {
22
- content: "⚠️ NSFW — Click to reveal";
23
- position: absolute;
24
- top:50%; left:50%;
25
- transform: translate(-50%, -50%);
26
- color:white;
27
- font-weight:bold;
28
- }
29
- </style>
30
-
31
- <body class="min-h-screen bg-black text-white selection:bg-fuchsia-400/30">
32
- <!-- Topbar -->
33
- <header class="sticky top-0 z-50 backdrop-blur bg-black/40 border-b border-white/10">
34
- <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
35
- <div class="flex items-center gap-3">
36
- <div class="w-9 h-9 rounded-xl bg-gradient-to-br from-fuchsia-500 via-purple-500 to-sky-400 glow"></div>
37
- <span class="font-semibold tracking-wide">imgGenPro</span>
38
- </div>
39
- <nav class="hidden md:flex items-center gap-2">
40
- <a class="px-3 py-2 rounded-lg hover:bg-white/5 text-sm" href="https://aniwall.free.nf" target="_blank" rel="noopener">
41
- <i class="fa-solid fa-image mr-2"></i>Wallpapers
42
- </a>
43
- <button id="openSettings" class="px-3 py-2 rounded-lg hover:bg-white/5 text-sm">
44
- <i class="fa-solid fa-gear mr-2"></i>Settings
45
- </button>
46
- <a class="px-3 py-2 rounded-lg hover:bg-white/5 text-sm" href="#" id="openHistory">
47
- <i class="fa-solid fa-clock-rotate-left mr-2"></i>History
48
- </a>
49
- <a class="px-3 py-2 rounded-lg hover:bg-white/5 text-sm" href="#" id="clearGallery">
50
- <i class="fa-solid fa-trash mr-2"></i>Clear
51
- </a>
52
- </nav>
53
- <button id="openSettingsSm" class="md:hidden px-3 py-2 rounded-lg hover:bg-white/5">
54
- <i class="fa-solid fa-sliders"></i>
55
- </button>
56
- </div>
57
- </header>
58
-
59
- <!-- Main -->
60
- <main class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-6">
61
- <!-- Prompt card -->
62
- <section class="rounded-2xl border border-white/10 bg-white/5 p-4 md:p-6">
63
- <div class="flex flex-col md:flex-row gap-3">
64
- <div class="flex-1">
65
- <label for="prompt" class="sr-only">Prompt</label>
66
- <div class="relative">
67
- <textarea id="prompt" rows="2" placeholder="Describe what you want to create (e.g., 'a cyberpunk city alley with neon reflections, rainy night')"
68
- class="w-full resize-y rounded-xl bg-black/50 border border-white/10 focus:border-fuchsia-400/60 outline-none p-4 pr-12 placeholder-white/40"></textarea>
69
- <button id="enhanceToggle" class="absolute right-2 top-2 text-xs px-2 py-1 rounded-lg border border-white/10 hover:bg-white/5">
70
- <i class="fa-solid fa-wand-magic-sparkles mr-1"></i>Enhance
71
- </button>
72
- </div>
73
-
74
- <!-- Quick styles -->
75
- <div class="mt-3 flex flex-wrap gap-2">
76
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="">No style</button>
77
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="cinematic shot, moody tone, film still">Cinema</button>
78
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="photo, candid, natural look, unposed">Realistic</button>
79
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="studio portrait, soft tone, subject centered">Portrait</button>
80
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="anime style, vibrant cel shading">Anime</button>
81
- <button class="style-btn px-3 py-1.5 rounded-lg bg-white/5 border border-white/10 text-xs hover:bg-white/10" data-style="fantasy concept art, painterly">Fantasy</button>
82
- </div>
83
- </div>
84
-
85
- <div class="w-full md:w-56 flex md:flex-col gap-2">
86
- <button id="generateBtn"
87
- class="flex-1 md:h-[66px] p-4 rounded-xl bg-white text-black font-semibold hover:opacity-90 active:opacity-100 transition mb-2">
88
- <span class="inline-flex items-center gap-2"><i class="fa-solid fa-bolt"></i> Generate</span>
89
- </button> <br>
90
- <button id="randomPrompt" class="flex-1 md:h-[56px] rounded-xl bg-white/5 border border-white/10 hover:bg-white/10">
91
- <i class="fa-solid fa-shuffle mr-2"></i>Random
92
- </button>
93
- <button id="downloadAll" class="hidden md:block h-[56px] rounded-xl bg-white/5 border border-white/10 hover:bg-white/10">
94
- <i class="fa-solid fa-download mr-2"></i>Download All
95
- </button>
96
- </div>
97
- </div>
98
-
99
- <p class="mt-3 text-xs text-white/60">
100
- Tip: NSFW may be filtered by models.
101
- </p>
102
- </section>
103
-
104
- <!-- Gallery -->
105
- <section class="mt-6">
106
- <div id="gallery" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4"></div>
107
- <div id="emptyState" class="text-center py-16 text-white/50">
108
- <i class="fa-regular fa-images text-4xl mb-3 block"></i>
109
- <p>No images yet — start by generating something ✨</p>
110
- </div>
111
- </section>
112
- </main>
113
-
114
- <!-- Settings Drawer -->
115
- <div id="settingsDrawer" class="fixed inset-0 hidden">
116
- <div class="absolute inset-0 backdrop"></div>
117
- <aside class="ml-auto h-full w-full sm:w-[420px] bg-black/90 border-l border-white/10 p-5 overflow-y-auto">
118
- <div class="flex items-center justify-between mb-4">
119
- <h2 class="text-lg font-semibold"><i class="fa-solid fa-sliders mr-2"></i>Settings</h2>
120
- <button id="closeSettings" class="p-2 rounded-lg hover:bg-white/5">
121
- <i class="fa-solid fa-xmark"></i>
122
- </button>
123
- </div>
124
-
125
- <div class="space-y-4">
126
- <label class="block text-sm">
127
- <span class="text-white/70">Model</span>
128
- <select id="model" class="mt-1 w-full rounded-lg bg-black/50 border border-white/10 p-2">
129
- <!-- Popular, fast models -->
130
- <option value="black-forest-labs/FLUX.1-schnell">FLUX.1-schnell (fast)</option>
131
- <option value="stabilityai/stable-diffusion-xl-base-1.0">SDXL Base 1.0</option>
132
- <option value="runwayml/stable-diffusion-v1-5">SD 1.5</option>
133
- </select>
134
- </label>
135
-
136
- <div class="grid grid-cols-2 gap-3">
137
- <label class="block text-sm">
138
- <span class="text-white/70">Width</span>
139
- <input id="width" type="number" min="64" max="2048" value="1024"
140
- class="mt-1 w-full rounded-lg bg-black/50 border border-white/10 p-2">
141
- </label>
142
- <label class="block text-sm">
143
- <span class="text-white/70">Height</span>
144
- <input id="height" type="number" min="64" max="2048" value="1024"
145
- class="mt-1 w-full rounded-lg bg-black/50 border border-white/10 p-2">
146
- </label>
147
- </div>
148
-
149
- <div class="grid grid-cols-2 gap-3">
150
- <label class="block text-sm">
151
- <span class="text-white/70">Seed</span>
152
- <input id="seed" type="number" value="42"
153
- class="mt-1 w-full rounded-lg bg-black/50 border border-white/10 p-2">
154
- </label>
155
- <label class="flex items-center gap-2 text-sm mt-7">
156
- <input id="randomSeed" type="checkbox" class="size-4 accent-fuchsia-500" checked>
157
- Random seed
158
- </label>
159
- </div>
160
-
161
- <label class="block text-sm">
162
- <span class="text-white/70">Negative Prompt (optional)</span>
163
- <input id="negPrompt" type="text" placeholder="blurry, low quality, watermark"
164
- class="mt-1 w-full rounded-lg bg-black/50 border border-white/10 p-2">
165
- </label>
166
-
167
- <div class="flex items-center justify-between">
168
- <label class="flex items-center gap-2 text-sm">
169
- <input id="enhance" type="checkbox" class="size-4 accent-fuchsia-500">
170
- Enhance prompt (client-side)
171
- </label>
172
- <button id="saveSettings" class="px-3 py-2 rounded-lg border border-white/10 hover:bg-white/5">
173
- <i class="fa-regular fa-floppy-disk mr-2"></i>Save
174
- </button>
175
- </div>
176
-
177
-
178
- </div>
179
- </aside>
180
- </div>
181
-
182
- <!-- Image Modal -->
183
- <div id="imageModal" class="fixed inset-0 hidden">
184
- <div class="absolute inset-0 backdrop"></div>
185
- <div class="relative z-10 h-full w-full flex items-center justify-center p-4">
186
- <div class="max-w-5xl w-full rounded-2xl bg-black/90 border border-white/10 p-4">
187
- <div class="flex justify-between items-center mb-3">
188
- <h3 class="font-semibold"><i class="fa-regular fa-image mr-2"></i>Preview</h3>
189
- <button id="closeModal" class="p-2 rounded-lg hover:bg-white/5">
190
- <i class="fa-solid fa-xmark"></i>
191
- </button>
192
- </div>
193
- <img id="modalImg" src="" alt="Preview" class="w-full rounded-xl border border-white/10">
194
- <div class="mt-3 flex gap-2">
195
- <button id="downloadBtn" class="px-3 py-2 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10">
196
- <i class="fa-solid fa-download mr-2"></i>Download
197
- </button>
198
- <button id="copyPromptBtn" class="px-3 py-2 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10">
199
- <i class="fa-solid fa-quote-left mr-2"></i>Copy Prompt
200
- </button>
201
- </div>
202
- </div>
203
- </div>
204
- </div>
205
-
206
- <!-- Toast -->
207
- <div id="toast" class="fixed bottom-4 left-1/2 -translate-x-1/2 hidden">
208
- <div class="rounded-xl bg-white/10 border border-white/10 px-4 py-2 text-sm">
209
- <span id="toastMsg">Saved</span>
210
- </div>
211
- </div>
212
-
213
-
214
-
215
-
216
-
217
- <footer class="text-center text-white-600 pt-8 border-t border-gray-200"><p>ImgGenPro &copy; 2025 - All rights reserved</p><p class="mt-2">Created by <a href="https://souravs.great-site.net"> SouRav </a></p></footer></div>
218
-
219
- <!-- JS -->
220
- <script>
221
-
222
- // ------------ State ------------
223
- const el = {
224
- prompt: document.getElementById('prompt'),
225
- model: document.getElementById('model'),
226
- width: document.getElementById('width'),
227
- height: document.getElementById('height'),
228
- seed: document.getElementById('seed'),
229
- randomSeed: document.getElementById('randomSeed'),
230
- negPrompt: document.getElementById('negPrompt'),
231
- enhance: document.getElementById('enhance'),
232
-
233
- enhanceToggle: document.getElementById('enhanceToggle'),
234
- generateBtn: document.getElementById('generateBtn'),
235
- randomPrompt: document.getElementById('randomPrompt'),
236
- downloadAll: document.getElementById('downloadAll'),
237
-
238
- gallery: document.getElementById('gallery'),
239
- emptyState: document.getElementById('emptyState'),
240
-
241
- openSettings: document.getElementById('openSettings'),
242
- openSettingsSm: document.getElementById('openSettingsSm'),
243
- settingsDrawer: document.getElementById('settingsDrawer'),
244
- closeSettings: document.getElementById('closeSettings'),
245
- saveSettings: document.getElementById('saveSettings'),
246
-
247
- openHistory: document.getElementById('openHistory'),
248
- clearGallery: document.getElementById('clearGallery'),
249
-
250
- imageModal: document.getElementById('imageModal'),
251
- modalImg: document.getElementById('modalImg'),
252
- closeModal: document.getElementById('closeModal'),
253
- downloadBtn: document.getElementById('downloadBtn'),
254
- copyPromptBtn: document.getElementById('copyPromptBtn'),
255
-
256
- toast: document.getElementById('toast'),
257
- toastMsg: document.getElementById('toastMsg'),
258
- };
259
-
260
- let currentStyle = "";
261
- let history = JSON.parse(localStorage.getItem('img_history') || '[]');
262
-
263
- // ------------ Helpers ------------
264
- function showToast(msg){
265
- el.toastMsg.textContent = msg;
266
- el.toast.classList.remove('hidden');
267
- setTimeout(()=>el.toast.classList.add('hidden'), 1600);
268
- }
269
-
270
- function randInt(min,max){ return Math.floor(Math.random()*(max-min+1))+min; }
271
-
272
- function saveSettings(){
273
- const s = {
274
- model: el.model.value,
275
- width: +el.width.value || 1024,
276
- height: +el.height.value || 1024,
277
- seed: +el.seed.value || 42,
278
- randomSeed: el.randomSeed.checked,
279
- negPrompt: el.negPrompt.value || '',
280
- enhance: el.enhance.checked
281
- };
282
- localStorage.setItem('img_settings', JSON.stringify(s));
283
- showToast('Settings saved');
284
- }
285
-
286
- function loadSettings(){
287
- try{
288
- const s = JSON.parse(localStorage.getItem('img_settings')||'{}');
289
- if(s.model) el.model.value = s.model;
290
- if(s.width) el.width.value = s.width;
291
- if(s.height) el.height.value = s.height;
292
- if(s.seed !== undefined) el.seed.value = s.seed;
293
- if(s.randomSeed !== undefined) el.randomSeed.checked = s.randomSeed;
294
- if(s.negPrompt !== undefined) el.negPrompt.value = s.negPrompt;
295
- if(s.enhance !== undefined) el.enhance.checked = s.enhance;
296
- }catch(e){}
297
- }
298
- loadSettings();
299
-
300
- // Random prompt pool (30 mixed)
301
- const promptPool = [
302
- "a cozy reading nook by a window with rain outside",
303
- "a neon-lit ramen shop alley in Tokyo at night, reflections on wet ground",
304
- "a majestic dragon soaring above snow-capped mountains, fantasy concept",
305
- "a retro-futuristic living room, 80s aesthetic meets sci-fi",
306
- "a cute corgi astronaut floating in space",
307
- "a cyberpunk cityscape with neon signs and flying cars",
308
- "a serene waterfall in a mystical forest, foggy morning",
309
- "an ancient temple on a cliff during sunset, epic lighting",
310
- "a photorealistic portrait of an old wise wizard",
311
- "a futuristic robot chef cooking in a sleek kitchen",
312
- "a fantasy castle floating above clouds, glowing lights",
313
- "a retro diner interior, 50s style with neon lights",
314
- "a giant mech battling dragons in a ruined city",
315
- "a cozy cabin in snowy mountains with smoke from chimney",
316
- "a glowing jellyfish underwater, ethereal lighting",
317
- "a mystical portal opening in a dark forest",
318
- "a cute kitten astronaut exploring Mars",
319
- "an epic spaceship battle in outer space",
320
- "a painterly landscape of a lavender field at sunrise",
321
- "a realistic lion sitting on a cliff, dramatic sky",
322
- "a neon anime girl with a futuristic city background",
323
- "a steampunk airship flying above mountains",
324
- "a magical library with floating books and candles",
325
- "a cinematic scene of a samurai duel at sunset",
326
- "a cozy coffee shop interior with warm lighting",
327
- "a fantasy elf archer in a misty forest",
328
- "a cyberpunk hacker in a dark room with neon monitors",
329
- "a whimsical fairy village in the trees",
330
- "a majestic phoenix rising from flames",
331
- "a realistic portrait of a smiling elderly woman"
332
- ];
333
-
334
- function promptVariants(){ return promptPool[randInt(0, promptPool.length-1)]; }
335
-
336
- function buildPrompt(){
337
- let p = el.prompt.value.trim();
338
- if(currentStyle) p += `, ${currentStyle}`;
339
- const neg = el.negPrompt.value.trim();
340
- return { prompt: p, negative_prompt: neg };
341
- }
342
-
343
- function addCardSkeleton(){
344
- el.emptyState.classList.add('hidden');
345
- const card = document.createElement('div');
346
- card.className = "group relative rounded-2xl border border-white/10 bg-white/5 overflow-hidden";
347
- card.innerHTML = `
348
- <div class="aspect-[4/3] skeleton"></div>
349
- <div class="absolute inset-0 flex items-center justify-center">
350
- <div class="w-14 h-14 rounded-full skeleton"></div>
351
- </div>
352
- <div class="p-3 text-xs text-white/50">Generating...</div>
353
- `;
354
- el.gallery.prepend(card);
355
- return card;
356
- }
357
-
358
- function addCard(imgUrl, meta){
359
- el.emptyState.classList.add('hidden');
360
- const card = document.createElement('div');
361
- card.className = "group relative rounded-2xl border border-white/10 bg-white/5 overflow-hidden hover:border-fuchsia-400/40 transition";
362
- card.innerHTML = `
363
- <img src="${imgUrl}" alt="AI image" class="w-full h-auto block">
364
- <div class="absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition">
365
- <button class="px-2 py-1 rounded-lg bg-black/60 hover:bg-black/80 border border-white/10 text-xs" data-act="download"><i class="fa-solid fa-download"></i></button>
366
- <button class="px-2 py-1 rounded-lg bg-black/60 hover:bg-black/80 border border-white/10 text-xs" data-act="open"><i class="fa-regular fa-image"></i></button>
367
- <button class="px-2 py-1 rounded-lg bg-black/60 hover:bg-black/80 border border-white/10 text-xs" data-act="copy"><i class="fa-solid fa-quote-left"></i></button>
368
- </div>
369
- <div class="p-3 text-[11px] text-white/60 line-clamp-2">${meta.prompt}</div>
370
- `;
371
-
372
- const imgEl = card.querySelector("img");
373
- if(isNSFW(meta.prompt)) imgEl.classList.add("blur-nsfw");
374
- imgEl.addEventListener("click", () => imgEl.classList.remove("blur-nsfw"));
375
-
376
- card.addEventListener('click', e=>{
377
- const btn = e.target.closest('button');
378
- if(!btn) return;
379
- const act = btn.dataset.act;
380
- if(act==='download'){
381
- const a = document.createElement('a');
382
- a.href = imgUrl; a.download = 'imggenpro.png'; a.click();
383
- } else if(act==='open'){
384
- openModal(imgUrl, meta.prompt);
385
- } else if(act==='copy'){
386
- navigator.clipboard.writeText(meta.prompt);
387
- showToast('Prompt copied');
388
- }
389
- });
390
-
391
- el.gallery.prepend(card);
392
- return card;
393
- }
394
-
395
- function openModal(src, prompt){
396
- el.modalImg.src = src;
397
- el.copyPromptBtn.dataset.prompt = prompt;
398
- el.imageModal.classList.remove('hidden');
399
- }
400
- function closeModal(){ el.imageModal.classList.add('hidden'); }
401
-
402
- // ------------ Events ------------
403
- document.querySelectorAll('.style-btn').forEach(b=>{
404
- b.addEventListener('click', ()=>{
405
- document.querySelectorAll('.style-btn').forEach(x=>x.classList.remove('ring-2','ring-fuchsia-400/60'));
406
- b.classList.add('ring-2','ring-fuchsia-400/60');
407
- currentStyle = b.dataset.style || "";
408
- });
409
- });
410
-
411
- el.enhanceToggle.addEventListener('click', ()=>{
412
- el.enhance.checked = !el.enhance.checked;
413
- el.enhanceToggle.classList.toggle('bg-white/10', el.enhance.checked);
414
- });
415
-
416
- el.openSettings?.addEventListener('click', ()=> el.settingsDrawer.classList.remove('hidden'));
417
- el.openSettingsSm?.addEventListener('click', ()=> el.settingsDrawer.classList.remove('hidden'));
418
- el.closeSettings.addEventListener('click', ()=> el.settingsDrawer.classList.add('hidden'));
419
- el.saveSettings.addEventListener('click', ()=>{ saveSettings(); el.settingsDrawer.classList.add('hidden'); });
420
-
421
- el.openHistory?.addEventListener('click', ()=>{
422
- if(!history.length){ showToast('No history yet'); return; }
423
- el.gallery.innerHTML = '';
424
- el.emptyState.classList.add('hidden');
425
- history.forEach(h=> addCard(h.url, {prompt: h.prompt}));
426
- });
427
-
428
- el.clearGallery?.addEventListener('click', ()=>{
429
- el.gallery.innerHTML = '';
430
- el.emptyState.classList.remove('hidden');
431
- });
432
-
433
- el.randomPrompt.addEventListener('click', ()=>{ el.prompt.value = promptVariants(); });
434
-
435
- el.downloadAll.addEventListener('click', ()=>{
436
- const imgs = el.gallery.querySelectorAll('img');
437
- if(!imgs.length){ showToast('Nothing to download'); return; }
438
- imgs.forEach((img,i)=>{
439
- const a = document.createElement('a');
440
- a.href = img.src; a.download = `imggenpro_${i+1}.png`; a.click();
441
- });
442
- });
443
-
444
- document.addEventListener('keydown', (e)=>{
445
- if(e.key==='Escape'){ closeModal(); el.settingsDrawer.classList.add('hidden'); }
446
- });
447
-
448
- el.closeModal.addEventListener('click', closeModal);
449
- el.downloadBtn.addEventListener('click', ()=>{
450
- const a = document.createElement('a'); a.href = el.modalImg.src; a.download='imggenpro.png'; a.click();
451
- });
452
- el.copyPromptBtn.addEventListener('click', ()=>{
453
- const p = el.copyPromptBtn.dataset.prompt || '';
454
- navigator.clipboard.writeText(p);
455
- showToast('Prompt copied');
456
- });
457
-
458
- function isNSFW(prompt){
459
- const nsfwWords = ["bikini","nude","nsfw","sex","erotic","porn"];
460
- return nsfwWords.some(w=>prompt.toLowerCase().includes(w));
461
- }
462
-
463
- async function doGenerate(){
464
- const card = addCardSkeleton();
465
- try{
466
- el.generateBtn.disabled = true;
467
- el.generateBtn.innerHTML = '<span class="inline-flex items-center gap-2"><i class="fa-solid fa-spinner animate-spin"></i> Generating…</span>';
468
-
469
- let { prompt } = buildPrompt();
470
- if(!prompt){ throw new Error('Please enter a prompt'); }
471
-
472
- const model = el.model.value;
473
-
474
- const res = await fetch('/generate', {
475
- method: 'POST',
476
- headers: { 'Content-Type': 'application/json' },
477
  body: JSON.stringify({ prompt, model })
478
- });
479
-
480
- if(!res.ok){ throw new Error('Generation failed'); }
481
-
482
- const blob = await res.blob();
483
- const url = URL.createObjectURL(blob);
484
-
485
- card.remove();
486
- addCard(url, { prompt });
487
- history.unshift({ url, prompt });
488
- history = history.slice(0, 30);
489
- localStorage.setItem('img_history', JSON.stringify(history));
490
 
491
- } catch(err){
492
- card.remove();
493
- showToast(err.message);
494
- console.error(err);
495
- } finally{
496
- el.generateBtn.disabled = false;
497
- el.generateBtn.innerHTML = '<span class="inline-flex items-center gap-2"><i class="fa-solid fa-bolt"></i> Generate</span>';
498
  }
499
- }
500
-
501
- el.generateBtn.addEventListener('click', doGenerate);
502
- el.prompt.addEventListener('keydown', e=>{
503
- if((e.ctrlKey||e.metaKey) && e.key==='Enter'){ doGenerate(); }
504
- });
505
- </script>
506
 
507
-
508
-
509
-
510
- </body>
511
- </html>
 
1
+ async function generateImage() {
2
+ const prompt = document.getElementById("prompt").value;
3
+ const model = document.getElementById("model").value;
4
 
5
+ const res = await fetch("/generate", {
6
+ method: "POST",
7
+ headers: { "Content-Type": "application/json" },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  body: JSON.stringify({ prompt, model })
9
+ });
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ if (!res.ok) {
12
+ const error = await res.json();
13
+ alert("Error: " + error.error);
14
+ return;
 
 
 
15
  }
 
 
 
 
 
 
 
16
 
17
+ const blob = await res.blob();
18
+ const url = URL.createObjectURL(blob);
19
+ document.getElementById("gallery").innerHTML = `<img src="${url}" class="rounded-xl border border-white/10">`;
20
+ }