anycoder-8487c710 / index.html
Moustached's picture
Upload folder using huggingface_hub
0d3e698 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 | Transform Ideas into Visuals</title>
<!-- 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=Outfit:wght@300;400;600;700&family=Space+Grotesk:wght@300;500;700&display=swap" rel="stylesheet">
<!-- FontAwesome Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/*
* Z-Image Turbo - Modern CSS Styling
* Using CSS Variables for theming and Flexbox/Grid for layout
*/
:root {
--bg-dark: #0b0c15;
--bg-panel: rgba(255, 255, 255, 0.03);
--bg-panel-hover: rgba(255, 255, 255, 0.07);
--primary: #6c5ce7;
--primary-glow: rgba(108, 92, 231, 0.5);
--accent: #00cec9;
--text-main: #ffffff;
--text-muted: #a0a0b0;
--border: rgba(255, 255, 255, 0.1);
--radius-lg: 24px;
--radius-md: 16px;
--radius-sm: 8px;
--font-main: 'Outfit', sans-serif;
--font-display: 'Space Grotesk', sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
outline: none;
}
body {
background-color: var(--bg-dark);
background-image:
radial-gradient(circle at 10% 20%, rgba(108, 92, 231, 0.15) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(0, 206, 201, 0.15) 0%, transparent 40%);
color: var(--text-main);
font-family: var(--font-main);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
/* --- Header --- */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 5%;
border-bottom: 1px solid var(--border);
backdrop-filter: blur(10px);
position: sticky;
top: 0;
z-index: 100;
background: rgba(11, 12, 21, 0.8);
}
.logo {
font-family: var(--font-display);
font-size: 1.5rem;
font-weight: 700;
display: flex;
align-items: center;
gap: 10px;
text-transform: uppercase;
letter-spacing: 1px;
background: linear-gradient(90deg, #fff, var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.built-with {
color: var(--text-muted);
font-size: 0.85rem;
text-decoration: none;
display: flex;
align-items: center;
gap: 5px;
transition: color 0.3s;
}
.built-with:hover {
color: var(--accent);
}
/* --- Main Layout --- */
main {
flex: 1;
display: grid;
grid-template-columns: 1fr 1.5fr;
gap: 30px;
padding: 40px 5%;
max-width: 1600px;
width: 100%;
margin: 0 auto;
}
@media (max-width: 900px) {
main {
grid-template-columns: 1fr;
}
}
/* --- Control Panel (Left) --- */
.controls-panel {
display: flex;
flex-direction: column;
gap: 24px;
height: fit-content;
}
.input-group {
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 20px;
position: relative;
transition: all 0.3s ease;
}
.input-group:focus-within {
border-color: var(--primary);
box-shadow: 0 0 20px rgba(108, 92, 231, 0.2);
background: var(--bg-panel-hover);
}
label {
display: block;
font-size: 0.85rem;
color: var(--accent);
margin-bottom: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
textarea {
width: 100%;
background: transparent;
border: none;
color: var(--text-main);
font-family: var(--font-main);
font-size: 1.1rem;
resize: none;
height: 120px;
line-height: 1.6;
}
textarea::placeholder {
color: rgba(255, 255, 255, 0.3);
}
/* Aspect Ratio Selector */
.aspect-ratio {
display: flex;
gap: 10px;
margin-top: 10px;
}
.ratio-btn {
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
color: var(--text-muted);
cursor: pointer;
font-size: 0.8rem;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.ratio-btn:hover, .ratio-btn.active {
background: var(--primary);
color: white;
border-color: var(--primary);
}
/* Style Selector */
.style-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 10px;
}
.style-chip {
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 10px;
text-align: center;
cursor: pointer;
transition: all 0.2s;
font-size: 0.8rem;
color: var(--text-muted);
}
.style-chip i {
display: block;
font-size: 1.2rem;
margin-bottom: 5px;
color: var(--text-main);
}
.style-chip:hover, .style-chip.active {
background: linear-gradient(135deg, rgba(108, 92, 231, 0.3), rgba(0, 206, 201, 0.2));
border-color: var(--accent);
color: white;
transform: translateY(-2px);
}
/* Generate Button */
.generate-btn {
background: linear-gradient(135deg, var(--primary), var(--accent));
color: white;
border: none;
padding: 18px;
border-radius: var(--radius-lg);
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
box-shadow: 0 10px 30px -10px var(--primary-glow);
position: relative;
overflow: hidden;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 15px 40px -10px var(--primary-glow);
}
.generate-btn:active {
transform: translateY(1px);
}
.generate-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.btn-icon {
font-size: 1.2rem;
}
/* --- Preview Panel (Right) --- */
.preview-panel {
position: relative;
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
min-height: 500px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.image-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.generated-image {
max-width: 100%;
max-height: 100%;
border-radius: var(--radius-md);
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
display: none; /* Hidden by default */
animation: fadeIn 0.8s ease;
}
/* Loading State */
.loader-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(11, 12, 21, 0.9);
backdrop-filter: blur(5px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
}
.loader-overlay.active {
opacity: 1;
pointer-events: all;
}
.spinner {
width: 60px;
height: 60px;
border: 4px solid rgba(255, 255, 255, 0.1);
border-left-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
.loading-text {
font-family: var(--font-display);
font-size: 1.2rem;
letter-spacing: 2px;
color: var(--accent);
margin-bottom: 10px;
}
.progress-bar {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
}
.progress-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--primary), var(--accent));
transition: width 0.2s linear;
}
/* Empty State */
.empty-state {
text-align: center;
color: var(--text-muted);
}
.empty-state i {
font-size: 4rem;
margin-bottom: 20px;
opacity: 0.3;
}
.empty-state h3 {
font-size: 1.5rem;
margin-bottom: 10px;
color: var(--text-main);
}
/* --- Footer --- */
footer {
text-align: center;
padding: 30px;
color: var(--text-muted);
font-size: 0.9rem;
border-top: 1px solid var(--border);
margin-top: auto;
}
/* --- Toast Notification --- */
.toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: #fff;
color: #000;
padding: 12px 24px;
border-radius: 50px;
font-weight: 600;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
opacity: 0;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
z-index: 1000;
display: flex;
align-items: center;
gap: 10px;
}
.toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
/* --- Animations --- */
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.98); }
to { opacity: 1; transform: scale(1); }
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(108, 92, 231, 0.4); }
70% { box-shadow: 0 0 0 10px rgba(108, 92, 231, 0); }
100% { box-shadow: 0 0 0 0 rgba(108, 92, 231, 0); }
}
/* --- History Strip --- */
.history-strip {
margin-top: 20px;
display: flex;
gap: 15px;
overflow-x: auto;
padding-bottom: 10px;
scrollbar-width: thin;
scrollbar-color: var(--primary) transparent;
}
.history-item {
width: 80px;
height: 80px;
border-radius: var(--radius-md);
overflow: hidden;
border: 2px solid transparent;
cursor: pointer;
transition: all 0.2s;
flex-shrink: 0;
position: relative;
}
.history-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.history-item:hover, .history-item.active {
border-color: var(--accent);
transform: scale(1.05);
}
</style>
</head>
<body>
<!-- Header -->
<header>
<div class="logo">
<i class="fa-solid fa-bolt"></i> Z-Image Turbo
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i>
</a>
</header>
<!-- Main Content -->
<main>
<!-- Controls Section -->
<section class="controls-panel">
<div class="input-group">
<label for="prompt"><i class="fa-solid fa-wand-magic-sparkles"></i> Describe your vision</label>
<textarea id="prompt" placeholder="A futuristic city with neon lights, cyberpunk style, highly detailed..."></textarea>
</div>
<div class="input-group">
<label>Style</label>
<div class="style-grid">
<div class="style-chip active" data-style="realistic">
<i class="fa-solid fa-camera"></i> Real
</div>
<div class="style-chip" data-style="anime">
<i class="fa-solid fa-star"></i> Anime
</div>
<div class="style-chip" data-style="cyberpunk">
<i class="fa-solid fa-robot"></i> Cyber
</div>
<div class="style-chip" data-style="oil">
<i class="fa-solid fa-palette"></i> Oil
</div>
<div class="style-chip" data-style="3d">
<i class="fa-solid fa-cube"></i> 3D Render
</div>
<div class="style-chip" data-style="sketch">
<i class="fa-solid fa-pencil"></i> Sketch
</div>
</div>
</div>
<div class="input-group">
<label>Aspect Ratio</label>
<div class="aspect-ratio">
<div class="ratio-btn active" data-ratio="1:1">
<i class="fa-regular fa-square"></i> 1:1
</div>
<div class="ratio-btn" data-ratio="16:9">
<i class="fa-solid fa-video"></i> 16:9
</div>
<div class="ratio-btn" data-ratio="9:16">
<i class="fa-solid fa-mobile-screen"></i> 9:16
</div>
<div class="ratio-btn" data-ratio="4:3">
<i class="fa-solid fa-image"></i> 4:3
</div>
</div>
</div>
<button class="generate-btn" id="generateBtn">
<span class="btn-text">Generate Image</span>
<i class="fa-solid fa-arrow-right btn-icon"></i>
</button>
</section>
<!-- Preview Section -->
<section class="preview-panel" id="previewPanel">
<!-- Empty State -->
<div class="empty-state" id="emptyState">
<i class="fa-solid fa-image"></i>
<h3>Ready to create?</h3>
<p>Enter a prompt and let Z-Image Turbo work its magic.</p>
</div>
<!-- Loader -->
<div class="loader-overlay" id="loader">
<div class="spinner"></div>
<div class="loading-text" id="loadingText">Initializing...</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
</div>
<!-- Result Image -->
<div class="image-container">
<img src="" alt="Generated AI Art" class="generated-image" id="resultImage">
</div>
<!-- History Strip -->
<div class="history-strip" id="historyStrip">
<!-- History items will be injected here -->
</div>
</section>
</main>
<!-- Footer -->
<footer>
<p>&copy; 2023 Z-Image Turbo. Powered by AI.</p>
</footer>
<!-- Toast Notification -->
<div class="toast" id="toast">
<i class="fa-solid fa-circle-info"></i>
<span id="toastMessage">Message here</span>
</div>
<!-- JavaScript Logic -->
<script>
document.addEventListener('DOMContentLoaded', () => {
// Elements
const promptInput = document.getElementById('prompt');
const generateBtn = document.getElementById('generateBtn');
const resultImage = document.getElementById('resultImage');
const emptyState = document.getElementById('emptyState');
const loader = document.getElementById('loader');
const progressFill = document.getElementById('progressFill');
const loadingText = document.getElementById('loadingText');
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
const styleChips = document.querySelectorAll('.style-chip');
const ratioBtns = document.querySelectorAll('.ratio-btn');
const historyStrip = document.getElementById('historyStrip');
// State
let currentStyle = 'realistic';
let currentRatio = '1:1';
const history = [];
// --- Event Listeners for UI Controls ---
// Style Selection
styleChips.forEach(chip => {
chip.addEventListener('click', () => {
styleChips.forEach(c => c.classList.remove('active'));
chip.classList.add('active');
currentStyle = chip.dataset.style;
});
});
// Ratio Selection
ratioBtns.forEach(btn => {
btn.addEventListener('click', () => {
ratioBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentRatio = btn.dataset.ratio;
});
});
// Generate Action
generateBtn.addEventListener('click', startGeneration);
// --- Core Functions ---
function startGeneration() {
const prompt = promptInput.value.trim();
// Validation
if (!prompt) {
showToast('Please enter a description first!', true);
promptInput.focus();
return;
}
// Lock UI
setLoading(true);
// Simulation Variables
let progress = 0;
const steps = [
"Analyzing prompt...",
"Connecting to neural network...",
"Diffusing pixels...",
"Refining details...",
"Finalizing output..."
];
let stepIndex = 0;
// Simulate Progress Loop
const interval = setInterval(() => {
progress += Math.floor(Math.random() * 5) + 2; // Random increment
if (progress > 100) progress = 100;
progressFill.style.width = `${progress}%`;
// Update text based on progress thresholds
if (progress > 20 && stepIndex === 0) { loadingText.innerText = steps[1]; stepIndex++; }
if (progress > 50 && stepIndex === 1) { loadingText.innerText = steps[2]; stepIndex++; }
if (progress > 80 && stepIndex === 2) { loadingText.innerText = steps[3]; stepIndex++; }
if (progress > 95 && stepIndex === 3) { loadingText.innerText = steps[4]; stepIndex++; }
if (progress === 100) {
clearInterval(interval);
finishGeneration(prompt);
}
}, 100); // Speed of simulation
}
function finishGeneration(prompt) {
// Generate a consistent image based on prompt + style
// Using picsum.photos with a seed creates a deterministic "random" image
const seed = encodeURIComponent(prompt + currentStyle + Date.now());
let width = 1024;
let height = 1024;
// Adjust ratio for image dimensions
if (currentRatio === '16:9') { width = 1280; height = 720; }
if (currentRatio === '9:16') { width = 720; height = 1280; }
if (currentRatio === '4:3') { width = 1024; height = 768; }
// Construct URL (Unsplash Source API for variety, seeded)
// Note: In a real app, this would be your API endpoint.
const imageUrl = `https://picsum.photos/seed/${seed}/${width}/${height}.jpg`;
// Preload image to ensure smooth display
const tempImg = new Image();
tempImg.onload = () => {
resultImage.src = imageUrl;
emptyState.style.display = 'none';
resultImage.style.display = 'block';
setLoading(false);
// Add to history
addToHistory(imageUrl);
showToast('Image generated successfully!');
};
tempImg.src = imageUrl;
}
function setLoading(isLoading) {
if (isLoading) {
loader.classList.add('active');
generateBtn.disabled = true;
generateBtn.querySelector('.btn-text').innerText = 'Generating...';
resultImage.style.display = 'none';
historyStrip.innerHTML = ''; // Clear history while generating
} else {
loader.classList.remove('active');
generateBtn.disabled = false;
generateBtn.querySelector('.btn-text').innerText = 'Generate Image';
progressFill.style.width = '0%';
}
}
function addToHistory(url) {
const item = document.createElement('div');
item.className = 'history-item';
item.innerHTML = `<img src="${url}" alt="History">`;
item.addEventListener('click', () => {
// Remove active class from others
document.querySelectorAll('.history-item').forEach(i => i.classList.remove('active'));
item.classList.add('active');
// Show image in main view
emptyState.style.display = 'none';
resultImage.style.display = 'block';
resultImage.src = url;
});
history.prepend(item);
// Limit history to 5 items
if (history.length > 5) {
historyStrip.lastElementChild.remove();
}
}
function showToast(msg, isError = false) {
toastMessage.innerText = msg;
toast.style.background = isError ? '#ff4757' : '#fff';
toast.style.color = isError ? '#fff' : '#000';
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
});
</script>
</body>
</html>