Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>LOKI.AI IMAGE PLAYGROUND</title> | |
| <link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,700;1,400&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"> | |
| <style> | |
| :root { | |
| --primary-bg: #ffffff; | |
| --secondary-bg: #f8f8f8; | |
| --text-color: #000000; | |
| --border-color: #e0e0e0; | |
| --accent-color: #000000; | |
| --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .dark { | |
| --primary-bg: #121212; | |
| --secondary-bg: #1e1e1e; | |
| --text-color: #ffffff; | |
| --border-color: #333333; | |
| --accent-color: #ffffff; | |
| --card-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'DM Sans', sans-serif; | |
| } | |
| body { | |
| background-color: var(--secondary-bg); | |
| color: var(--text-color); | |
| transition: all 0.3s ease; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1000px; | |
| margin: 0 auto; | |
| } | |
| .card { | |
| background-color: var(--primary-bg); | |
| border-radius: 12px; | |
| padding: 24px; | |
| box-shadow: var(--card-shadow); | |
| transition: all 0.3s ease; | |
| } | |
| .header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 24px; | |
| flex-wrap: wrap; | |
| gap: 12px; | |
| } | |
| .title { | |
| font-size: 28px; | |
| font-weight: 700; | |
| color: var(--text-color); | |
| } | |
| .theme-toggle { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .toggle-label { | |
| font-size: 14px; | |
| color: var(--text-color); | |
| opacity: 0.7; | |
| } | |
| .toggle-switch { | |
| position: relative; | |
| width: 48px; | |
| height: 24px; | |
| background-color: #e5e5e5; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: background-color 0.3s; | |
| } | |
| .dark .toggle-switch { | |
| background-color: #555; | |
| } | |
| .toggle-thumb { | |
| position: absolute; | |
| top: 2px; | |
| left: 2px; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background-color: white; | |
| box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); | |
| transition: transform 0.3s; | |
| } | |
| .dark .toggle-thumb { | |
| transform: translateX(24px); | |
| } | |
| .form-row { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 16px; | |
| margin-bottom: 16px; | |
| } | |
| @media (min-width: 768px) { | |
| .form-row { | |
| grid-template-columns: 1fr 1fr; | |
| } | |
| .form-row.three-cols { | |
| grid-template-columns: 1fr 1fr 1fr; | |
| } | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| } | |
| .form-label { | |
| font-size: 14px; | |
| font-weight: 500; | |
| color: var(--text-color); | |
| opacity: 0.8; | |
| } | |
| .form-control { | |
| padding: 10px 12px; | |
| border-radius: 8px; | |
| border: 1px solid var(--border-color); | |
| background-color: var(--secondary-bg); | |
| color: var(--text-color); | |
| font-size: 14px; | |
| transition: all 0.2s; | |
| appearance: none; | |
| } | |
| .form-control:focus { | |
| outline: none; | |
| border-color: var(--accent-color); | |
| box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); | |
| } | |
| .dark .form-control:focus { | |
| box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); | |
| } | |
| .select-wrapper { | |
| position: relative; | |
| } | |
| .select-wrapper:after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| right: 12px; | |
| transform: translateY(-50%); | |
| width: 0; | |
| height: 0; | |
| border-left: 5px solid transparent; | |
| border-right: 5px solid transparent; | |
| border-top: 5px solid var(--text-color); | |
| pointer-events: none; | |
| } | |
| .btn { | |
| padding: 10px 18px; | |
| border-radius: 8px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| border: none; | |
| text-align: center; | |
| } | |
| .btn-primary { | |
| background-color: var(--accent-color); | |
| color: var(--primary-bg); | |
| } | |
| .btn-primary:hover { | |
| opacity: 0.9; | |
| transform: translateY(-2px); | |
| } | |
| .btn-primary:active { | |
| transform: translateY(0); | |
| } | |
| .btn-download { | |
| background-color: rgba(255, 255, 255, 0.2); | |
| backdrop-filter: blur(4px); | |
| padding: 8px; | |
| border-radius: 50%; | |
| position: absolute; | |
| bottom: 10px; | |
| right: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); | |
| } | |
| .dark .btn-download { | |
| background-color: rgba(0, 0, 0, 0.3); | |
| } | |
| .btn-download svg { | |
| width: 20px; | |
| height: 20px; | |
| stroke: var(--accent-color); | |
| } | |
| .btn-full { | |
| width: 100%; | |
| } | |
| .text-center { | |
| text-align: center; | |
| } | |
| .result { | |
| margin-top: 24px; | |
| display: none; | |
| } | |
| .result-title { | |
| font-size: 18px; | |
| font-weight: 500; | |
| margin-bottom: 4px; | |
| } | |
| .result-subtitle { | |
| font-size: 14px; | |
| opacity: 0.7; | |
| margin-bottom: 16px; | |
| } | |
| .images-grid { | |
| display: grid; | |
| grid-template-columns: 1fr; | |
| gap: 16px; | |
| } | |
| .images-grid.multi-column { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| .image-wrapper { | |
| position: relative; | |
| border-radius: 8px; | |
| overflow: hidden; | |
| box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1); | |
| transform-origin: center; | |
| } | |
| .dark .image-wrapper { | |
| box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3); | |
| } | |
| .image-wrapper img { | |
| width: 100%; | |
| height: auto; | |
| display: block; | |
| transition: transform 0.3s ease; | |
| } | |
| .image-wrapper:hover img { | |
| transform: scale(1.03); | |
| } | |
| .loading { | |
| display: none; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 32px 0; | |
| } | |
| .loading-spinner { | |
| width: 40px; | |
| height: 40px; | |
| border: 4px solid rgba(0, 0, 0, 0.1); | |
| border-left-color: var(--accent-color); | |
| border-radius: 50%; | |
| animation: spinner 1s linear infinite; | |
| margin-bottom: 16px; | |
| } | |
| .dark .loading-spinner { | |
| border-color: rgba(255, 255, 255, 0.1); | |
| border-left-color: var(--accent-color); | |
| } | |
| @keyframes spinner { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .loading-text { | |
| font-size: 14px; | |
| opacity: 0.7; | |
| } | |
| .error { | |
| background-color: rgba(255, 0, 0, 0.1); | |
| color: #e53e3e; | |
| padding: 16px; | |
| border-radius: 8px; | |
| margin-top: 24px; | |
| display: none; | |
| } | |
| .dark .error { | |
| background-color: rgba(255, 0, 0, 0.05); | |
| } | |
| .error-title { | |
| font-size: 16px; | |
| font-weight: 500; | |
| margin-bottom: 4px; | |
| } | |
| .error-message { | |
| font-size: 14px; | |
| opacity: 0.8; | |
| } | |
| .footer { | |
| margin-top: 16px; | |
| text-align: center; | |
| font-size: 12px; | |
| opacity: 0.5; | |
| } | |
| .confetti { | |
| position: fixed; | |
| width: 10px; | |
| height: 10px; | |
| background-color: #f00; | |
| opacity: 0; | |
| top: 0; | |
| left: 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="card animate__animated animate__fadeIn"> | |
| <div class="header"> | |
| <h1 class="title animate__animated animate__slideInLeft">LOKI.AI IMAGE PLAYGROUND</h1> | |
| <div class="theme-toggle animate__animated animate__slideInRight"> | |
| <span class="toggle-label">Theme</span> | |
| <div id="theme-toggle" class="toggle-switch"> | |
| <div class="toggle-thumb"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="form-row animate__animated animate__fadeInUp" style="animation-delay: 0.1s;"> | |
| <div class="form-group"> | |
| <label for="model" class="form-label">Select Model</label> | |
| <div class="select-wrapper"> | |
| <select id="model" class="form-control"> | |
| <option value="Flux Realism">Flux Realism</option> | |
| <option value="Flux Pro Ultra">Flux Pro Ultra</option> | |
| <option value="grok-2-aurora">grok-2-aurora</option> | |
| <option value="Flux Pro">Flux Pro</option> | |
| <option value="Flux Pro Ultra Raw">Flux Pro Ultra Raw</option> | |
| <option value="Flux Dev">Flux Dev</option> | |
| <option value="Flux Schnell">Flux Schnell</option> | |
| <option value="stable-diffusion-3-large-turbo">stable-diffusion-3-large-turbo</option> | |
| <option value="sdxl-lightning-4step">sdxl-lightning-4step</option> | |
| <option value="dall-e-3">dall-e-3</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="prompt" class="form-label">Enter Prompt</label> | |
| <input type="text" id="prompt" class="form-control" placeholder="Describe what you want to see..." value="sky"> | |
| </div> | |
| </div> | |
| <div class="form-row three-cols animate__animated animate__fadeInUp" style="animation-delay: 0.2s;"> | |
| <div class="form-group"> | |
| <label for="image-size" class="form-label">Image Size</label> | |
| <div class="select-wrapper"> | |
| <select id="image-size" class="form-control"> | |
| <option value="512">512 x 512</option> | |
| <option value="768">768 x 768</option> | |
| <option value="1024" selected>1024 x 1024</option> | |
| <option value="1536">1536 x 1536</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="image-count" class="form-label">Number of Images</label> | |
| <div class="select-wrapper"> | |
| <select id="image-count" class="form-control"> | |
| <option value="1" selected>1</option> | |
| <option value="2">2</option> | |
| <option value="4">4</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label class="form-label"> </label> | |
| <button id="generate" class="btn btn-primary btn-full animate__animated animate__pulse animate__infinite"> | |
| Generate Image | |
| </button> | |
| </div> | |
| </div> | |
| <div id="result" class="result"> | |
| <div class="text-center animate__animated animate__fadeIn"> | |
| <h2 class="result-title">Your Creation</h2> | |
| <p class="result-subtitle">Created with <span id="model-used"></span></p> | |
| </div> | |
| <div id="images-container" class="images-grid"></div> | |
| </div> | |
| <div id="loading" class="loading"> | |
| <div class="loading-spinner"></div> | |
| <p class="loading-text">Creating your masterpiece...</p> | |
| </div> | |
| <div id="error" class="error"> | |
| <h3 class="error-title">Oops! Something went wrong.</h3> | |
| <p class="error-message">Please try again or check your connection.</p> | |
| </div> | |
| </div> | |
| <div class="footer"> | |
| © 2025 LOKI.AI IMAGE PLAYGROUND | All rights reserved | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Elements | |
| const themeToggle = document.getElementById('theme-toggle'); | |
| const generateBtn = document.getElementById('generate'); | |
| const promptInput = document.getElementById('prompt'); | |
| const modelSelect = document.getElementById('model'); | |
| const imageSizeSelect = document.getElementById('image-size'); | |
| const imageCountSelect = document.getElementById('image-count'); | |
| const resultDiv = document.getElementById('result'); | |
| const loadingDiv = document.getElementById('loading'); | |
| const errorDiv = document.getElementById('error'); | |
| const imagesContainer = document.getElementById('images-container'); | |
| const modelUsed = document.getElementById('model-used'); | |
| // Theme Toggle | |
| themeToggle.addEventListener('click', () => { | |
| document.body.classList.toggle('dark'); | |
| localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light'); | |
| }); | |
| // Check for saved theme preference | |
| if (localStorage.getItem('theme') === 'dark' || | |
| (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { | |
| document.body.classList.add('dark'); | |
| } | |
| // Create confetti | |
| function createConfetti() { | |
| const colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']; | |
| const confettiCount = 100; | |
| for (let i = 0; i < confettiCount; i++) { | |
| const confetti = document.createElement('div'); | |
| confetti.className = 'confetti'; | |
| confetti.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)]; | |
| confetti.style.left = Math.random() * 100 + 'vw'; | |
| confetti.style.opacity = Math.random() + 0.5; | |
| confetti.style.width = Math.random() * 10 + 5 + 'px'; | |
| confetti.style.height = Math.random() * 10 + 5 + 'px'; | |
| document.body.appendChild(confetti); | |
| const animation = confetti.animate([ | |
| { transform: 'translateY(0) rotate(0)', opacity: 1 }, | |
| { transform: `translateY(${window.innerHeight}px) rotate(${Math.random() * 360}deg)`, opacity: 0 } | |
| ], { | |
| duration: Math.random() * 2000 + 2000, | |
| easing: 'cubic-bezier(0, 0.55, 0.45, 1)' | |
| }); | |
| animation.onfinish = () => confetti.remove(); | |
| } | |
| } | |
| // Generate image | |
| generateBtn.addEventListener('click', async () => { | |
| const prompt = promptInput.value.trim(); | |
| const model = modelSelect.value; | |
| const size = parseInt(imageSizeSelect.value); | |
| const number = parseInt(imageCountSelect.value); | |
| if (!prompt) { | |
| promptInput.style.borderColor = 'red'; | |
| promptInput.classList.add('animate__animated', 'animate__shakeX'); | |
| setTimeout(() => { | |
| promptInput.style.borderColor = ''; | |
| promptInput.classList.remove('animate__animated', 'animate__shakeX'); | |
| }, 1000); | |
| return; | |
| } | |
| // Show loading state | |
| resultDiv.style.display = 'none'; | |
| errorDiv.style.display = 'none'; | |
| loadingDiv.style.display = 'flex'; | |
| generateBtn.disabled = true; | |
| try { | |
| const response = await fetch('https://parthsadaria-lokiai.hf.space/images/generations', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| model: model, | |
| prompt: prompt, | |
| size: size, | |
| number: number | |
| }) | |
| }); | |
| if (!response.ok) { | |
| throw new Error('API request failed'); | |
| } | |
| const data = await response.json(); | |
| // Display result | |
| loadingDiv.style.display = 'none'; | |
| resultDiv.style.display = 'block'; | |
| modelUsed.textContent = model; | |
| // Clear previous images | |
| imagesContainer.innerHTML = ''; | |
| // Trigger confetti animation | |
| createConfetti(); | |
| // Set grid columns based on number of images | |
| if (number > 1) { | |
| imagesContainer.className = 'images-grid multi-column'; | |
| } else { | |
| imagesContainer.className = 'images-grid'; | |
| } | |
| // Add generated images | |
| if (data.data && data.data.length > 0) { | |
| data.data.forEach((item, index) => { | |
| const imgWrapper = document.createElement('div'); | |
| imgWrapper.className = 'image-wrapper animate__animated animate__zoomIn'; | |
| imgWrapper.style.animationDelay = `${index * 0.2}s`; | |
| const img = document.createElement('img'); | |
| img.src = item.url; | |
| img.alt = `Generated image ${index + 1}`; | |
| const downloadBtn = document.createElement('button'); | |
| downloadBtn.className = 'btn-download'; | |
| downloadBtn.innerHTML = ` | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path> | |
| <polyline points="7 10 12 15 17 10"></polyline> | |
| <line x1="12" y1="15" x2="12" y2="3"></line> | |
| </svg> | |
| `; | |
| downloadBtn.addEventListener('click', () => { | |
| const a = document.createElement('a'); | |
| a.href = item.url; | |
| a.download = `loki-ai-${model}-${index + 1}.jpg`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| }); | |
| imgWrapper.appendChild(img); | |
| imgWrapper.appendChild(downloadBtn); | |
| imagesContainer.appendChild(imgWrapper); | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| loadingDiv.style.display = 'none'; | |
| errorDiv.style.display = 'block'; | |
| errorDiv.classList.add('animate__animated', 'animate__fadeIn'); | |
| } finally { | |
| generateBtn.disabled = false; | |
| } | |
| }); | |
| // Enter key to generate | |
| promptInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') { | |
| generateBtn.click(); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |