anycoder-e0aedbe6 / index.html
inoculatemedia's picture
Upload folder using huggingface_hub
d3a63d2 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Sequence Generator</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--accent-color: #fd79a8;
--dark-color: #2d3436;
--light-color: #f5f6fa;
--success-color: #00b894;
--warning-color: #fdcb6e;
--danger-color: #d63031;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
color: var(--dark-color);
line-height: 1.6;
}
.header {
width: 100%;
background: white;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 100;
}
.logo {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: var(--dark-color);
}
.logo-icon {
font-size: 1.5rem;
color: var(--primary-color);
}
.logo-text {
font-weight: 700;
font-size: 1.2rem;
}
.built-with {
font-size: 0.8rem;
color: var(--dark-color);
}
.built-with a {
color: var(--primary-color);
text-decoration: none;
font-weight: 600;
}
.main-container {
width: 90%;
max-width: 1200px;
margin: 2rem auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.controls {
background: white;
border-radius: 15px;
padding: 2rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.preview {
background: white;
border-radius: 15px;
padding: 2rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 1rem;
}
.section-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1.5rem;
color: var(--primary-color);
display: flex;
align-items: center;
gap: 10px;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: var(--dark-color);
}
.form-control {
width: 100%;
padding: 0.8rem;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 1rem;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(108, 92, 231, 0.2);
}
.btn {
display: inline-block;
padding: 0.8rem 1.5rem;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.btn:hover {
background: #5649c0;
transform: translateY(-2px);
}
.btn:disabled {
background: #a29bfe;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: var(--secondary-color);
}
.btn-secondary:hover {
background: #8a82e5;
}
.btn-danger {
background: var(--danger-color);
}
.btn-danger:hover {
background: #b72424;
}
.image-buffer {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem;
margin-top: 1rem;
max-height: 400px;
overflow-y: auto;
padding: 0.5rem;
border: 2px dashed #e0e0e0;
border-radius: 8px;
}
.buffer-item {
position: relative;
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.buffer-item:hover {
transform: scale(1.05);
z-index: 10;
}
.buffer-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.buffer-item .remove-btn {
position: absolute;
top: 5px;
right: 5px;
background: rgba(214, 48, 49, 0.8);
color: white;
border: none;
border-radius: 50%;
width: 25px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
transition: opacity 0.3s ease;
}
.buffer-item:hover .remove-btn {
opacity: 1;
}
.status {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
}
.status-item {
text-align: center;
}
.status-label {
font-size: 0.9rem;
color: #666;
margin-bottom: 0.3rem;
}
.status-value {
font-weight: 700;
color: var(--primary-color);
}
.player-controls {
display: flex;
gap: 1rem;
margin-top: 1rem;
}
.player-display {
width: 100%;
aspect-ratio: 16/9;
background: #f0f0f0;
border-radius: 8px;
overflow: hidden;
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin-top: 1rem;
}
.player-display img {
width: 100%;
height: 100%;
object-fit: contain;
display: none;
}
.player-display img.active {
display: block;
}
.empty-state {
text-align: center;
padding: 2rem;
color: #999;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 1rem;
color: #ddd;
}
.progress-container {
margin-top: 1rem;
}
.progress-bar {
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.progress {
height: 100%;
background: var(--primary-color);
width: 0%;
transition: width 0.3s ease;
}
.settings-panel {
background: white;
border-radius: 15px;
padding: 1.5rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
margin-top: 2rem;
}
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
@media (max-width: 768px) {
.main-container {
grid-template-columns: 1fr;
}
.settings-grid {
grid-template-columns: 1fr;
}
.header {
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
}
@media (max-width: 480px) {
.image-buffer {
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
}
.form-control {
padding: 0.6rem;
}
.btn {
padding: 0.6rem 1rem;
}
}
</style>
</head>
<body>
<header class="header">
<a href="#" class="logo">
<i class="fas fa-images logo-icon"></i>
<span class="logo-text">Image Sequence Generator</span>
</a>
<div class="built-with">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
</header>
<div class="main-container">
<div class="controls">
<h2 class="section-title">
<i class="fas fa-cog"></i>
Generation Controls
</h2>
<div class="form-group">
<label for="prompt">Prompt</label>
<input type="text" id="prompt" class="form-control" placeholder="Describe the image you want to generate">
</div>
<div class="form-group">
<label for="negative-prompt">Negative Prompt</label>
<input type="text" id="negative-prompt" class="form-control" placeholder="What you don't want to see">
</div>
<div class="settings-panel">
<h3 class="section-title" style="font-size: 1.2rem; margin-bottom: 1rem;">
<i class="fas fa-sliders-h"></i>
Advanced Settings
</h3>
<div class="settings-grid">
<div class="form-group">
<label for="steps">Steps</label>
<input type="number" id="steps" class="form-control" value="4" min="1" max="20">
</div>
<div class="form-group">
<label for="guidance-scale">Guidance Scale</label>
<input type="number" id="guidance-scale" class="form-control" value="0" min="0" max="20" step="0.1">
</div>
<div class="form-group">
<label for="seed">Seed</label>
<input type="number" id="seed" class="form-control" value="-1" min="-1">
</div>
<div class="form-group">
<label for="batch-size">Batch Size</label>
<input type="number" id="batch-size" class="form-control" value="1" min="1" max="4">
</div>
</div>
</div>
<button id="generate-btn" class="btn">
<i class="fas fa-magic"></i> Generate Image
</button>
<div class="progress-container">
<div class="progress-bar">
<div class="progress" id="progress-bar"></div>
</div>
</div>
</div>
<div class="preview">
<h2 class="section-title">
<i class="fas fa-film"></i>
Image Buffer & Player
</h2>
<div class="status">
<div class="status-item">
<div class="status-label">Images in Buffer</div>
<div class="status-value" id="buffer-count">0</div>
</div>
<div class="status-item">
<div class="status-label">Required for Playback</div>
<div class="status-value" id="required-count">10</div>
</div>
<div class="status-item">
<div class="status-label">Playback Speed</div>
<div class="status-value">30 FPS</div>
</div>
</div>
<div class="player-controls">
<button id="clear-buffer" class="btn btn-secondary">
<i class="fas fa-trash"></i> Clear Buffer
</button>
<button id="play-sequence" class="btn" disabled>
<i class="fas fa-play"></i> Play Sequence
</button>
<button id="stop-sequence" class="btn btn-danger" disabled>
<i class="fas fa-stop"></i> Stop
</button>
</div>
<div class="player-display" id="player-display">
<div class="empty-state">
<i class="fas fa-image"></i>
<p>No images in buffer yet</p>
</div>
</div>
<h3 style="margin-top: 1.5rem; font-size: 1.1rem; color: var(--dark-color);">
<i class="fas fa-inbox"></i> Image Buffer
</h3>
<div class="image-buffer" id="image-buffer">
<!-- Images will be added here -->
</div>
</div>
</div>
<script>
// Configuration
const config = {
requiredImages: 10,
playbackFPS: 30,
apiUrl: "https://diffusers-unofficial-sdxl-turbo-i2i-t2i.hf.space" // Placeholder for actual API
};
// State
let state = {
images: [],
isPlaying: false,
currentFrame: 0,
playbackInterval: null,
isGenerating: false
};
// DOM Elements
const elements = {
generateBtn: document.getElementById('generate-btn'),
playBtn: document.getElementById('play-sequence'),
stopBtn: document.getElementById('stop-sequence'),
clearBtn: document.getElementById('clear-buffer'),
imageBuffer: document.getElementById('image-buffer'),
playerDisplay: document.getElementById('player-display'),
bufferCount: document.getElementById('buffer-count'),
requiredCount: document.getElementById('required-count'),
progressBar: document.getElementById('progress-bar'),
promptInput: document.getElementById('prompt'),
negativePromptInput: document.getElementById('negative-prompt'),
stepsInput: document.getElementById('steps'),
guidanceScaleInput: document.getElementById('guidance-scale'),
seedInput: document.getElementById('seed'),
batchSizeInput: document.getElementById('batch-size')
};
// Initialize the application
function init() {
// Set required count display
elements.requiredCount.textContent = config.requiredImages;
// Set up event listeners
elements.generateBtn.addEventListener('click', generateImage);
elements.playBtn.addEventListener('click', playSequence);
elements.stopBtn.addEventListener('click', stopSequence);
elements.clearBtn.addEventListener('click', clearBuffer);
// Update UI based on initial state
updateUI();
}
// Generate a new image
async function generateImage() {
if (state.isGenerating) return;
const prompt = elements.promptInput.value.trim();
if (!prompt) {
alert('Please enter a prompt');
return;
}
state.isGenerating = true;
updateUI();
// Simulate image generation (in a real app, this would call the actual API)
try {
// Show progress
let progress = 0;
const progressInterval = setInterval(() => {
progress += Math.random() * 10;
if (progress > 90) progress = 90;
elements.progressBar.style.width = `${progress}%`;
}, 300);
// Simulate API call delay
await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 3000));
// Generate a random image (placeholder for actual API response)
const imageData = generateRandomImageData();
// Add to buffer
addImageToBuffer(imageData);
// Complete progress
elements.progressBar.style.width = '100%';
clearInterval(progressInterval);
// Reset progress after a short delay
setTimeout(() => {
elements.progressBar.style.width = '0%';
}, 500);
} catch (error) {
console.error('Error generating image:', error);
alert('Failed to generate image. Please try again.');
} finally {
state.isGenerating = false;
updateUI();
}
}
// Generate random image data (placeholder for actual API response)
function generateRandomImageData() {
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');
// Generate random colors
const colors = [];
for (let i = 0; i < 5; i++) {
colors.push(`hsl(${Math.random() * 360}, 70%, 60%)`);
}
// Create a gradient pattern
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
colors.forEach((color, i) => {
gradient.addColorStop(i / (colors.length - 1), color);
});
// Fill with gradient
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Add some random shapes
for (let i = 0; i < 10; i++) {
ctx.fillStyle = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.7)`;
const size = 50 + Math.random() * 100;
const x = Math.random() * (canvas.width - size);
const y = Math.random() * (canvas.height - size);
if (Math.random() > 0.5) {
ctx.beginPath();
ctx.arc(x + size/2, y + size/2, size/2, 0, Math.PI * 2);
ctx.fill();
} else {
ctx.fillRect(x, y, size, size);
}
}
// Add text (the prompt)
const prompt = elements.promptInput.value.trim();
if (prompt) {
ctx.font = 'bold 24px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(prompt.substring(0, 30) + (prompt.length > 30 ? '...' : ''), canvas.width/2, canvas.height/2);
}
return canvas.toDataURL('image/png');
}
// Add image to buffer
function addImageToBuffer(imageData) {
state.images.push(imageData);
updateBufferDisplay();
updateUI();
}
// Update the buffer display
function updateBufferDisplay() {
elements.imageBuffer.innerHTML = '';
if (state.images.length === 0) {
elements.imageBuffer.innerHTML = `
<div class="empty-state" style="grid-column: 1 / -1;">
<i class="fas fa-inbox"></i>
<p>No images in buffer</p>
</div>
`;
return;
}
state.images.forEach((imageData, index) => {
const bufferItem = document.createElement('div');
bufferItem.className = 'buffer-item';
bufferItem.innerHTML = `
<img src="${imageData}" alt="Generated image ${index + 1}">
<button class="remove-btn" data-index="${index}">
<i class="fas fa-times"></i>
</button>
`;
elements.imageBuffer.appendChild(bufferItem);
});
// Add event listeners to remove buttons
document.querySelectorAll('.remove-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const index = parseInt(e.target.closest('.remove-btn').dataset.index);
removeImageFromBuffer(index);
});
});
}
// Remove image from buffer
function removeImageFromBuffer(index) {
state.images.splice(index, 1);
updateBufferDisplay();
updateUI();
}
// Clear the entire buffer
function clearBuffer() {
state.images = [];
updateBufferDisplay();
updateUI();
stopSequence();
}
// Play the image sequence
function playSequence() {
if (state.isPlaying || state.images.length < config.requiredImages) return;
state.isPlaying = true;
state.currentFrame = 0;
updateUI();
// Clear player display
elements.playerDisplay.innerHTML = '';
state.images.forEach((imageData, index) => {
const img = document.createElement('img');
img.src = imageData;
img.alt = `Frame ${index + 1}`;
if (index === 0) img.classList.add('active');
elements.playerDisplay.appendChild(img);
});
// Start playback
const frameDuration = 1000 / config.playbackFPS;
state.playbackInterval = setInterval(() => {
const images = elements.playerDisplay.querySelectorAll('img');
images.forEach(img => img.classList.remove('active'));
if (state.currentFrame >= state.images.length) {
state.currentFrame = 0;
}
images[state.currentFrame].classList.add('active');
state.currentFrame++;
}, frameDuration);
}
// Stop the image sequence
function stopSequence() {
if (!state.isPlaying) return;
clearInterval(state.playbackInterval);
state.isPlaying = false;
state.currentFrame = 0;
updateUI();
// Clear player display
elements.playerDisplay.innerHTML = `
<div class="empty-state">
<i class="fas fa-image"></i>
<p>Playback stopped</p>
</div>
`;
}
// Update UI based on current state
function updateUI() {
// Update buffer count
elements.bufferCount.textContent = state.images.length;
// Update button states
elements.playBtn.disabled = state.images.length < config.requiredImages || state.isPlaying;
elements.stopBtn.disabled = !state.isPlaying;
elements.generateBtn.disabled = state.isGenerating;
elements.generateBtn.textContent = state.isGenerating ?
'<i class="fas fa-spinner fa-spin"></i> Generating...' :
'<i class="fas fa-magic"></i> Generate Image';
// Update player display if not playing
if (!state.isPlaying && state.images.length === 0) {
elements.playerDisplay.innerHTML = `
<div class="empty-state">
<i class="fas fa-image"></i>
<p>No images in buffer yet</p>
</div>
`;
}
}
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>