lokiai / image-playground.html
ParthSadaria's picture
Update image-playground.html
dd553d1 verified
raw
history blame
13.2 kB
<!DOCTYPE html>
<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>