Spaces:
Running
Running
<html lang="en" class="light"> | |
<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"> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
* { | |
font-family: 'DM Sans', sans-serif; | |
} | |
.loading { | |
width: 40px; | |
height: 40px; | |
border: 4px solid #f3f3f3; | |
border-bottom-color: #000; | |
border-radius: 50%; | |
animation: rotation 1.2s linear infinite; | |
} | |
.dark .loading { | |
border-color: #333; | |
border-bottom-color: #fff; | |
} | |
@keyframes rotation { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.image-container { | |
border-radius: 8px; | |
overflow: hidden; | |
} | |
.theme-toggle { | |
position: relative; | |
width: 48px; | |
height: 24px; | |
border-radius: 12px; | |
background-color: #e5e5e5; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
} | |
.dark .theme-toggle { | |
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); | |
} | |
.dark { | |
background-color: #121212; | |
color: #f5f5f5; | |
} | |
.dark .card { | |
background-color: #1e1e1e; | |
border-color: rgba(255, 255, 255, 0.1); | |
} | |
.dark .form-input { | |
background-color: #2d2d2d; | |
color: #f5f5f5; | |
border-color: #444; | |
} | |
.dark .btn-primary { | |
background-color: #f5f5f5; | |
color: #121212; | |
} | |
.dark .btn-secondary { | |
background-color: #2d2d2d; | |
color: #f5f5f5; | |
border: 1px solid #444; | |
} | |
</style> | |
</head> | |
<body class="p-4 md:p-6 bg-gray-50 dark:bg-gray-900 transition-colors duration-200"> | |
<div class="max-w-4xl mx-auto"> | |
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 transition-colors duration-200"> | |
<div class="flex flex-col md:flex-row items-center justify-between mb-6"> | |
<h1 class="text-3xl font-bold text-black dark:text-white mb-2 md:mb-0">LOKI.AI IMAGE PLAYGROUND</h1> | |
<div class="flex items-center space-x-4"> | |
<div class="text-sm text-gray-500 dark:text-gray-400">Theme</div> | |
<div id="theme-toggle" class="theme-toggle"> | |
<div class="toggle-thumb"></div> | |
</div> | |
</div> | |
</div> | |
<div class="space-y-4"> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<div> | |
<label for="model" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Select Model</label> | |
<div class="relative"> | |
<select id="model" class="form-input block w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white text-gray-900 dark:text-white appearance-none"> | |
<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 class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 dark:text-gray-300"> | |
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path> | |
</svg> | |
</div> | |
</div> | |
</div> | |
<div> | |
<label for="prompt" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Enter Prompt</label> | |
<input type="text" id="prompt" placeholder="Describe what you want to see..." class="form-input block w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white text-gray-900 dark:text-white" value="sky"> | |
</div> | |
</div> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-2"> | |
<div> | |
<label for="image-size" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Image Size</label> | |
<select id="image-size" class="form-input block w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white text-gray-900 dark:text-white appearance-none"> | |
<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> | |
<label for="image-count" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Number of Images</label> | |
<select id="image-count" class="form-input block w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-1 focus:ring-black dark:focus:ring-white text-gray-900 dark:text-white appearance-none"> | |
<option value="1" selected>1</option> | |
<option value="2">2</option> | |
<option value="4">4</option> | |
</select> | |
</div> | |
<div class="flex items-end"> | |
<button id="generate" class="btn-primary w-full bg-black dark:bg-white text-white dark:text-black px-6 py-2 rounded-lg font-medium hover:bg-gray-800 dark:hover:bg-gray-200 transition"> | |
Generate Image | |
</button> | |
</div> | |
</div> | |
<div id="result" class="mt-6 hidden"> | |
<div class="text-center mb-4"> | |
<h2 class="text-lg font-medium dark:text-white">Your Creation</h2> | |
<p class="text-sm text-gray-500 dark:text-gray-400">Created with <span id="model-used"></span></p> | |
</div> | |
<div id="images-container" class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<!-- Images will be inserted here dynamically --> | |
</div> | |
</div> | |
<div id="loading" class="flex flex-col items-center justify-center py-6 hidden"> | |
<div class="loading mb-3"></div> | |
<p class="text-gray-600 dark:text-gray-400 text-sm">Creating your image...</p> | |
</div> | |
<div id="error" class="bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-400 p-3 rounded-lg hidden"> | |
<p class="font-medium">Oops! Something went wrong.</p> | |
<p class="text-sm">Please try again or check your connection.</p> | |
</div> | |
</div> | |
</div> | |
<div class="text-center text-gray-400 dark:text-gray-600 text-xs mt-4"> | |
© 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.documentElement.classList.toggle('dark'); | |
localStorage.setItem('theme', document.documentElement.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.documentElement.classList.add('dark'); | |
} else { | |
document.documentElement.classList.remove('dark'); | |
} | |
// 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'; | |
setTimeout(() => { | |
promptInput.style.borderColor = ''; | |
}, 1500); | |
return; | |
} | |
// Show loading state | |
resultDiv.classList.add('hidden'); | |
errorDiv.classList.add('hidden'); | |
loadingDiv.classList.remove('hidden'); | |
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.classList.add('hidden'); | |
resultDiv.classList.remove('hidden'); | |
modelUsed.textContent = model; | |
// Clear previous images | |
imagesContainer.innerHTML = ''; | |
// Add generated images | |
if (data.images && data.images.length > 0) { | |
// Set grid columns based on number of images | |
if (number > 1) { | |
imagesContainer.className = `grid grid-cols-1 ${number > 1 ? 'md:grid-cols-2' : ''} gap-4`; | |
} else { | |
imagesContainer.className = 'mx-auto max-w-lg'; | |
} | |
data.images.forEach((imgSrc, index) => { | |
const imgContainer = document.createElement('div'); | |
imgContainer.className = 'image-container relative'; | |
const img = document.createElement('img'); | |
img.src = imgSrc; | |
img.className = 'w-full h-auto shadow-md'; | |
img.alt = `Generated image ${index + 1}`; | |
const downloadBtn = document.createElement('button'); | |
downloadBtn.className = 'btn-secondary absolute bottom-3 right-3 bg-white dark:bg-gray-800 text-black dark:text-white p-2 rounded-full shadow-lg'; | |
downloadBtn.innerHTML = ` | |
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path> | |
</svg> | |
`; | |
downloadBtn.addEventListener('click', () => { | |
const a = document.createElement('a'); | |
a.href = imgSrc; | |
a.download = `loki-ai-${model}-${index + 1}-${new Date().getTime()}.png`; | |
document.body.appendChild(a); | |
a.click(); | |
document.body.removeChild(a); | |
}); | |
imgContainer.appendChild(img); | |
imgContainer.appendChild(downloadBtn); | |
imagesContainer.appendChild(imgContainer); | |
}); | |
} | |
} catch (error) { | |
console.error('Error:', error); | |
loadingDiv.classList.add('hidden'); | |
errorDiv.classList.remove('hidden'); | |
} finally { | |
generateBtn.disabled = false; | |
} | |
}); | |
// Enter key to generate | |
promptInput.addEventListener('keypress', (e) => { | |
if (e.key === 'Enter') { | |
generateBtn.click(); | |
} | |
}); | |
}); | |
</script> | |
</body> | |
</html> |