lokiai / image-playground.html
ParthSadaria's picture
Update image-playground.html
6fa9a95 verified
raw
history blame
17.8 kB
<!DOCTYPE html>
<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">&nbsp;</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>