anycoder-897410b8 / index.html
Abhi1907's picture
Upload folder using huggingface_hub
2998a40 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Z-Image Turbo | AI Generator</title>
<!-- Import Remix Icon -->
<link href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css" rel="stylesheet">
<!-- Import Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
/* --- CSS VARIABLES & RESET --- */
:root {
--bg-dark: #0f1115;
--bg-panel: rgba(23, 25, 30, 0.75);
--bg-input: rgba(0, 0, 0, 0.3);
--primary: #6366f1;
--primary-glow: rgba(99, 102, 241, 0.4);
--accent: #ec4899;
--text-main: #ffffff;
--text-muted: #9ca3af;
--border: rgba(255, 255, 255, 0.1);
--radius-lg: 16px;
--radius-md: 8px;
--radius-sm: 4px;
--font-display: 'Space Grotesk', sans-serif;
--font-body: 'Inter', sans-serif;
--transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-body);
background-color: var(--bg-dark);
color: var(--text-main);
min-height: 100vh;
overflow-x: hidden;
background-image:
radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.15) 0%, transparent 40%);
background-attachment: fixed;
}
/* --- UTILITIES --- */
.glass {
background: var(--bg-panel);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--border);
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 12px 24px;
border-radius: var(--radius-md);
font-weight: 600;
font-family: var(--font-display);
border: none;
cursor: pointer;
transition: var(--transition);
font-size: 0.95rem;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), #4f46e5);
color: white;
box-shadow: 0 4px 20px var(--primary-glow);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px var(--primary-glow);
}
.btn-primary:active {
transform: translateY(0);
}
.btn-icon {
padding: 8px;
background: rgba(255, 255, 255, 0.1);
color: var(--text-main);
border-radius: var(--radius-sm);
}
.btn-icon:hover {
background: rgba(255, 255, 255, 0.2);
}
/* --- HEADER --- */
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
padding: 16px 0;
border-bottom: 1px solid var(--border);
background: rgba(15, 17, 21, 0.8);
backdrop-filter: blur(10px);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-family: var(--font-display);
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(to right, #fff, #a5b4fc);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 10px;
}
.logo i {
color: var(--primary);
-webkit-text-fill-color: var(--primary);
}
.anycoder-link {
font-size: 0.85rem;
color: var(--text-muted);
text-decoration: none;
transition: var(--transition);
display: flex;
align-items: center;
gap: 6px;
}
.anycoder-link:hover {
color: var(--primary);
}
/* --- MAIN LAYOUT --- */
main {
padding-top: 100px;
padding-bottom: 40px;
display: grid;
grid-template-columns: 350px 1fr;
gap: 30px;
min-height: calc(100vh - 80px);
}
/* --- SIDEBAR CONTROLS --- */
.controls-panel {
border-radius: var(--radius-lg);
padding: 24px;
height: fit-content;
position: sticky;
top: 100px;
display: flex;
flex-direction: column;
gap: 24px;
}
.input-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.input-label {
font-size: 0.85rem;
font-weight: 600;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
display: flex;
justify-content: space-between;
}
textarea, input[type="text"], select {
width: 100%;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 12px 16px;
color: var(--text-main);
font-family: var(--font-body);
font-size: 0.95rem;
transition: var(--transition);
resize: vertical;
}
textarea:focus, input[type="text"]:focus, select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
textarea {
min-height: 120px;
}
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.range-wrap {
display: flex;
flex-direction: column;
gap: 10px;
}
input[type="range"] {
width: 100%;
height: 4px;
background: rgba(255,255,255,0.1);
border-radius: 2px;
appearance: none;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--primary);
cursor: pointer;
transition: var(--transition);
}
/* --- PREVIEW AREA --- */
.preview-area {
display: flex;
flex-direction: column;
gap: 24px;
}
.main-image-container {
width: 100%;
min-height: 500px;
border-radius: var(--radius-lg);
border: 1px solid var(--border);
background: rgba(0,0,0,0.2);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0,0,0,0.3);
}
.main-image {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
opacity: 0;
transition: opacity 0.5s ease;
}
.main-image.loaded {
opacity: 1;
}
.placeholder-state {
text-align: center;
color: var(--text-muted);
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.placeholder-state i {
font-size: 3rem;
opacity: 0.5;
}
/* Loading Spinner */
.loader {
display: none;
width: 48px;
height: 48px;
border: 5px solid #FFF;
border-bottom-color: var(--primary);
border-radius: 50%;
animation: rotation 1s linear infinite;
position: absolute;
z-index: 10;
}
@keyframes rotation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Image Actions */
.image-actions {
position: absolute;
bottom: 20px;
right: 20px;
display: flex;
gap: 10px;
opacity: 0;
transform: translateY(10px);
transition: var(--transition);
}
.main-image-container:hover .image-actions {
opacity: 1;
transform: translateY(0);
}
/* History Strip */
.history-section {
margin-top: auto;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.history-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 12px;
}
.history-item {
aspect-ratio: 1;
border-radius: var(--radius-md);
overflow: hidden;
border: 1px solid transparent;
cursor: pointer;
transition: var(--transition);
position: relative;
}
.history-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.history-item:hover {
border-color: var(--primary);
transform: scale(1.05);
}
/* --- TOAST --- */
.toast-container {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 10px;
}
.toast {
background: var(--bg-panel);
border: 1px solid var(--border);
backdrop-filter: blur(10px);
padding: 16px 20px;
border-radius: var(--radius-md);
color: white;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
animation: slideIn 0.3s ease;
max-width: 300px;
}
.toast i {
color: var(--primary);
font-size: 1.2rem;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* --- RESPONSIVE --- */
@media (max-width: 900px) {
main {
grid-template-columns: 1fr;
}
.controls-panel {
position: relative;
top: 0;
}
}
</style>
</head>
<body>
<!-- Header -->
<header class="glass">
<div class="container header-content">
<div class="logo">
<i class="ri-bolt-flash-fill"></i>
Z-Image Turbo
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder <i class="ri-external-link-line"></i>
</a>
</div>
</header>
<!-- Main Application -->
<main class="container">
<!-- Sidebar Controls -->
<aside class="controls-panel glass">
<div class="input-group">
<label class="input-label">Prompt</label>
<textarea id="promptInput" placeholder="Describe the image you want to generate... e.g., A futuristic cyberpunk city with neon lights, 4k, highly detailed"></textarea>
</div>
<div class="input-group">
<label class="input-label">Negative Prompt</label>
<textarea id="negativeInput" placeholder="What to avoid? e.g., blurry, low quality, distorted" style="min-height: 60px;"></textarea>
</div>
<div class="settings-grid">
<div class="input-group">
<label class="input-label">Aspect Ratio</label>
<select id="aspectRatio">
<option value="1:1">Square (1:1)</option>
<option value="16:9">Landscape (16:9)</option>
<option value="9:16">Portrait (9:16)</option>
<option value="4:3">Classic (4:3)</option>
</select>
</div>
<div class="input-group">
<label class="input-label">Style Seed</label>
<input type="text" id="seedInput" placeholder="Random">
</div>
</div>
<div class="input-group">
<label class="input-label">Turbo Speed</label>
<div class="range-wrap">
<input type="range" min="1" max="10" value="5" id="speedRange">
</div>
</div>
<button class="btn btn-primary" id="generateBtn">
<i class="ri-magic-line"></i> Generate Image
</button>
</aside>
<!-- Preview Area -->
<section class="preview-area">
<div class="main-image-container glass" id="mainImageContainer">
<span class="loader" id="loader"></span>
<div class="placeholder-state" id="placeholderState">
<i class="ri-image-add-line"></i>
<p>Enter a prompt and hit Generate to start.</p>
</div>
<img src="" alt="Generated AI Art" class="main-image" id="mainImage">
<div class="image-actions" id="imageActions" style="display:none;">
<button class="btn btn-primary glass" id="downloadBtn" title="Download Image">
<i class="ri-download-line"></i>
</button>
<button class="btn btn-icon glass" id="fullscreenBtn" title="View Fullscreen">
<i class="ri-fullscreen-line"></i>
</button>
</div>
</div>
<!-- History Grid -->
<div class="history-section">
<div class="history-header">
<h3 style="font-family: var(--font-display);">Recent Generations</h3>
<button class="btn-icon" id="clearHistoryBtn" title="Clear History">
<i class="ri-delete-bin-line"></i>
</button>
</div>
<div class="history-grid" id="historyGrid">
<!-- History items injected here -->
</div>
</div>
</section>
</main>
<!-- Toast Container -->
<div class="toast-container" id="toastContainer"></div>
<script>
/**
* Z-Image Turbo Logic
* Handles API calls to Pollinations.ai (SDXL Turbo backend simulation)
* and manages UI state.
*/
// Elements
const promptInput = document.getElementById('promptInput');
const negativeInput = document.getElementById('negativeInput');
const aspectRatioSelect = document.getElementById('aspectRatio');
const seedInput = document.getElementById('seedInput');
const generateBtn = document.getElementById('generateBtn');
const mainImage = document.getElementById('mainImage');
const loader = document.getElementById('loader');
const placeholderState = document.getElementById('placeholderState');
const imageActions = document.getElementById('imageActions');
const historyGrid = document.getElementById('historyGrid');
const downloadBtn = document.getElementById('downloadBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
// State
let isGenerating = false;
let currentImageUrl = '';
let history = [];
// Aspect Ratio Map (Width x Height)
const dimensions = {
'1:1': { w: 1024, h: 1024 },
'16:9': { w: 1280, h: 720 },
'9:16': { w: 720, h: 1280 },
'4:3': { w: 1024, h: 768 }
};
// --- Core Functions ---
/**
* Generates the random seed if not provided
*/
const getSeed = () => {
const val = seedInput.value.trim();
return val ? val : Math.floor(Math.random() * 1000000);
};
/**
* Constructs the API URL
*/
const buildApiUrl = () => {
const prompt = promptInput.value.trim();
const negative = negativeInput.value.trim();
const ratio = aspectRatioSelect.value;
const seed = getSeed();
const { w, h } = dimensions[ratio];
if (!prompt) {
showToast("Please enter a prompt first", "error");
return null;
}
// Using Pollinations.ai API (Free, No Key, SDXL Turbo compatible)
// We encode the prompt to handle special characters
const encodedPrompt = encodeURIComponent(prompt + (negative ? `, negative: ${negative}` : ''));
// Construct URL
const url = `https://image.pollinations.ai/prompt/${encodedPrompt}?width=${w}&height=${h}&seed=${seed}&nologo=true&model=flux`; // Using flux or turbo model
return url;
};
/**
* Triggers the Image Generation
*/
const handleGenerate = async () => {
if (isGenerating) return;
const url = buildApiUrl();
if (!url) return;
// UI Updates for loading state
isGenerating = true;
generateBtn.innerHTML = '<i class="ri-loader-4-line ri-spin"></i> Generating...';
generateBtn.style.opacity = '0.7';
loader.style.display = 'block';
placeholderState.style.display = 'none';
mainImage.classList.remove('loaded');
imageActions.style.display = 'none';
// Preload Image
const img = new Image();
img.src = url;
img.onload = () => {
// Success
mainImage.src = url;
currentImageUrl = url;
mainImage.classList.add('loaded');
// UI Reset
loader.style.display = 'none';
imageActions.style.display = 'flex';
generateBtn.innerHTML = '<i class="ri-magic-line"></i> Generate Image';
generateBtn.style.opacity = '1';
isGenerating = false;
// Add to history
addToHistory(url, promptInput.value);
showToast("Image generated successfully!", "success");
};
img.onerror = () => {
// Error
loader.style.display = 'none';
placeholderState.style.display = 'flex';
placeholderState.innerHTML = '<i class="ri-error-warning-line" style="color:var(--accent)"></i><p>Failed to generate image. Try again.</p>';
generateBtn.innerHTML = '<i class="ri-magic-line"></i> Generate Image';
generateBtn.style.opacity = '1';
isGenerating = false;
showToast("Generation failed. Check your connection.", "error");
};
};
/**
* Adds generated image to the history grid
*/
const addToHistory = (url, prompt) => {
// Prevent duplicates at the top
if (history.length > 0 && history[0].url === url) return;
history.unshift({ url, prompt });
if (history.length > 8) history.pop(); // Keep last 8
renderHistory();
};
/**
* Renders the history grid
*/
const renderHistory = () => {
historyGrid.innerHTML = '';
history.forEach((item) => {
const div = document.createElement('div');
div.className = 'history-item';
div.innerHTML = `<img src="${item.url}" alt="History Item">`;
div.title = item.prompt;
div.onclick = () => {
// Load history item to main view
mainImage.src = item.url;
currentImageUrl = item.url;
mainImage.classList.add('loaded');
placeholderState.style.display = 'none';
imageActions.style.display = 'flex';
promptInput.value = item.prompt;
};
historyGrid.appendChild(div);
});
};
/**
* Downloads the current image
*/
const handleDownload = async () => {
if (!currentImageUrl) return;
try {
showToast("Downloading...", "success");
const response = await fetch(currentImageUrl);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `z-turbo-${Date.now()}.jpg`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (err) {
showToast("Download failed", "error");
}
};
/**
* Displays a toast notification
*/
const showToast = (message, type = 'success') => {
const toast = document.createElement('div');
toast.className = 'toast';
const icon = type === 'success' ? 'ri-checkbox-circle-line' : 'ri-error-warning-line';
toast.innerHTML = `<i class="${icon}"></i> <span>${message}</span>`;
const container = document.getElementById('toastContainer');
container.appendChild(toast);
// Remove after 3 seconds
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(20px)';
setTimeout(() => toast.remove(), 300);
}, 3000);
};
// --- Event Listeners ---
generateBtn.addEventListener('click', handleGenerate);
// Allow Ctrl+Enter to generate
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
handleGenerate();
}
});
downloadBtn.addEventListener('click', handleDownload);
fullscreenBtn.addEventListener('click', () => {
if (mainImage.src) {
window.open(currentImageUrl, '_blank');
}
});
clearHistoryBtn.addEventListener('click', () => {
history = [];
renderHistory();
showToast("History cleared", "success");
});
// Initialize empty state
renderHistory();
</script>
</body>
</html>