anycoder-3f642b92 / index.html
Mousco's picture
Upload folder using huggingface_hub
4e3d8a6 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>AI Video Generator Studio</title>
<!-- Importation de la police Inter (Moderne et clean) -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Importation des icônes FontAwesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* =========================================
VARIABLES CSS & RESET
========================================= */
:root {
--bg-body: #09090b;
--bg-sidebar: #121214;
--bg-card: #18181b;
--bg-input: #27272a;
--primary: #8b5cf6; /* Violet */
--primary-hover: #7c3aed;
--accent: #ec4899; /* Rose */
--text-main: #ffffff;
--text-secondary: #a1a1aa;
--border: #27272a;
--success: #10b981;
--radius-md: 12px;
--radius-lg: 16px;
--header-height: 70px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
outline: none;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
height: 100vh;
overflow: hidden; /* Prevent body scroll, handle inside containers */
display: flex;
}
/* Lien obligatoire "Built with anycoder" */
.anycoder-credit {
position: fixed;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 0.8rem;
color: var(--text-secondary);
z-index: 1000;
text-decoration: none;
opacity: 0.6;
transition: opacity 0.3s;
background: rgba(0,0,0,0.5);
padding: 4px 12px;
border-radius: 20px;
backdrop-filter: blur(4px);
}
.anycoder-credit:hover {
opacity: 1;
color: var(--primary);
}
/* =========================================
LAYOUT PRINCIPAL
========================================= */
.app-container {
display: flex;
width: 100%;
height: 100%;
}
/* SIDEBAR */
.sidebar {
width: 260px;
background-color: var(--bg-sidebar);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 20px;
transition: transform 0.3s ease;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 40px;
display: flex;
align-items: center;
gap: 10px;
}
.logo i {
color: var(--primary);
}
.nav-menu {
list-style: none;
flex: 1;
}
.nav-item {
margin-bottom: 8px;
}
.nav-link {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
color: var(--text-secondary);
text-decoration: none;
border-radius: var(--radius-md);
transition: all 0.2s;
font-weight: 500;
}
.nav-link:hover, .nav-link.active {
background-color: var(--bg-input);
color: var(--text-main);
}
.nav-link.active {
border-left: 3px solid var(--primary);
}
.user-profile {
display: flex;
align-items: center;
gap: 10px;
padding-top: 20px;
border-top: 1px solid var(--border);
}
.avatar-small {
width: 36px;
height: 36px;
border-radius: 50%;
background: linear-gradient(45deg, var(--primary), var(--accent));
}
/* MAIN CONTENT */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
position: relative;
}
.top-bar {
height: var(--header-height);
border-bottom: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30px;
}
.page-title {
font-size: 1.2rem;
font-weight: 600;
}
.credits {
background: rgba(139, 92, 246, 0.1);
color: var(--primary);
padding: 6px 12px;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 600;
}
/* SCROLLABLE AREA */
.content-scroll {
flex: 1;
overflow-y: auto;
padding: 30px;
}
/* =========================================
GENERATOR SECTION
========================================= */
.generator-grid {
display: grid;
grid-template-columns: 1fr 400px;
gap: 30px;
max-width: 1400px;
margin: 0 auto;
}
/* INPUT AREA */
.input-card {
background-color: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
display: flex;
flex-direction: column;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-label {
font-size: 0.9rem;
color: var(--text-secondary);
font-weight: 500;
}
textarea.prompt-input {
width: 100%;
height: 120px;
background-color: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: 16px;
color: var(--text-main);
font-family: inherit;
font-size: 1rem;
resize: none;
transition: border-color 0.2s;
}
textarea.prompt-input:focus {
border-color: var(--primary);
}
/* OPTIONS CONTROLS */
.controls-row {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.control-group {
flex: 1;
min-width: 140px;
}
select, .custom-select-trigger {
width: 100%;
background-color: var(--bg-input);
border: 1px solid var(--border);
color: var(--text-main);
padding: 10px 14px;
border-radius: var(--radius-md);
cursor: pointer;
appearance: none; /* Hide default arrow for custom styling if needed */
}
/* Style Chips for Ratios */
.ratio-options {
display: flex;
background: var(--bg-input);
padding: 4px;
border-radius: var(--radius-md);
gap: 4px;
}
.ratio-btn {
flex: 1;
background: transparent;
border: none;
color: var(--text-secondary);
padding: 8px;
border-radius: 8px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s;
}
.ratio-btn.active {
background-color: var(--bg-card);
color: var(--text-main);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.generate-btn {
background: linear-gradient(135deg, var(--primary), var(--accent));
color: white;
border: none;
padding: 16px;
border-radius: var(--radius-md);
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: transform 0.1s, opacity 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.generate-btn:hover {
opacity: 0.9;
}
.generate-btn:active {
transform: scale(0.98);
}
.generate-btn:disabled {
background: var(--bg-input);
color: var(--text-secondary);
cursor: not-allowed;
}
/* PREVIEW AREA */
.preview-card {
background-color: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 400px;
position: relative;
overflow: hidden;
}
.video-placeholder {
width: 100%;
aspect-ratio: 16/9;
background-color: #000;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
/* Animation when "video" is playing */
.video-active {
background-image: url('https://picsum.photos/seed/ai-video/800/450');
background-size: cover;
background-position: center;
}
.video-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
display: flex;
align-items: flex-end;
padding: 15px;
}
.play-btn {
width: 60px;
height: 60px;
background: rgba(255,255,255,0.2);
backdrop-filter: blur(5px);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.5rem;
border: none;
cursor: pointer;
transition: transform 0.2s;
}
.play-btn:hover {
transform: scale(1.1);
background: var(--primary);
}
/* Loading State */
.loading-overlay {
position: absolute;
inset: 0;
background: rgba(15, 23, 42, 0.9);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
display: none; /* Hidden by default */
}
.loading-overlay.active {
display: flex;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(139, 92, 246, 0.3);
border-radius: 50%;
border-top-color: var(--primary);
animation: spin 1s ease-in-out infinite;
margin-bottom: 20px;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.progress-bar {
width: 80%;
height: 6px;
background-color: var(--bg-input);
border-radius: 3px;
overflow: hidden;
margin-bottom: 10px;
}
.progress-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--primary), var(--accent));
transition: width 0.3s linear;
}
.status-text {
font-size: 0.9rem;
color: var(--text-secondary);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* HISTORY SECTION */
.history-section {
margin-top: 40px;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.history-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.history-item {
background-color: var(--bg-card);
border-radius: var(--radius-md);
overflow: hidden;
border: 1px solid var(--border);
transition: transform 0.2s;
cursor: pointer;
}
.history-item:hover {
transform: translateY(-5px);
border-color: var(--primary);
}
.history-thumb {
width: 100%;
height: 140px;
background-color: #000;
background-size: cover;
background-position: center;
position: relative;
}
.history-info {
padding: 12px;
}
.history-prompt {
font-size: 0.85rem;
color: var(--text-main);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 4px;
}
.history-meta {
font-size: 0.75rem;
color: var(--text-secondary);
display: flex;
justify-content: space-between;
}
/* =========================================
RESPONSIVE DESIGN
========================================= */
@media (max-width: 900px) {
.generator-grid {
grid-template-columns: 1fr;
}
.preview-card {
min-height: 300px;
order: -1; /* Put preview on top on mobile */
}
}
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: -100%;
top: 0;
bottom: 0;
z-index: 100;
width: 80%;
box-shadow: 10px 0 20px rgba(0,0,0,0.5);
}
.sidebar.active {
left: 0;
}
.menu-toggle {
display: block;
font-size: 1.2rem;
background: none;
border: none;
color: var(--text-main);
cursor: pointer;
margin-right: 15px;
}
.content-scroll {
padding: 15px;
}
}
@media (min-width: 769px) {
.menu-toggle {
display: none;
}
}
</style>
</head>
<body>
<!-- Lien crédit obligatoire -->
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-credit">
Built with anycoder <i class="fas fa-external-link-alt"></i>
</a>
<div class="app-container">
<!-- SIDEBAR -->
<aside class="sidebar" id="sidebar">
<div class="logo">
<i class="fas fa-video"></i>
<span>VideoGen AI</span>
</div>
<ul class="nav-menu">
<li class="nav-item">
<a href="#" class="nav-link active">
<i class="fas fa-wand-magic-sparkles"></i>
<span>Créer</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="fas fa-clock-rotate-left"></i>
<span>Historique</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="fas fa-bookmark"></i>
<span>Favoris</span>
</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">
<i class="fas fa-gear"></i>
<span>Paramètres</span>
</a>
</li>
</ul>
<div class="user-profile">
<div class="avatar-small"></div>
<div>
<div style="font-size: 0.9rem; font-weight: 600;">Mon Compte</div>
<div style="font-size: 0.75rem; color: var(--text-secondary);">Plan Pro</div>
</div>
</div>
</aside>
<!-- MAIN CONTENT -->
<main class="main-content">
<!-- Top Bar -->
<header class="top-bar">
<div style="display: flex; align-items: center;">
<button class="menu-toggle" id="menuToggle"><i class="fas fa-bars"></i></button>
<h1 class="page-title">Nouvelle Vidéo</h1>
</div>
<div class="credits">
<i class="fas fa-coins"></i> 120 Crédits restants
</div>
</header>
<!-- Scrollable Content -->
<div class="content-scroll">
<div class="generator-grid">
<!-- INPUT COLUMN -->
<div class="input-card">
<div class="form-group">
<label class="form-label">Votre prompt</label>
<textarea class="prompt-input" id="promptInput" placeholder="Décrivez la vidéo que vous souhaitez générer... (ex: Un astronaute marchant sur Mars au coucher du soleil, style cinématographique 4K)"></textarea>
</div>
<div class="controls-row">
<div class="control-group">
<label class="form-label">Style</label>
<select id="styleSelect">
<option value="cinematic">Cinématique</option>
<option value="anime">Anime</option>
<option value="3d">Rendu 3D</option>
<option value="cyberpunk">Cyberpunk</option>
<option value="realistic">Réaliste</option>
</select>
</div>
<div class="control-group">
<label class="form-label">Durée</label>
<select id="durationSelect">
<option value="5">5 secondes</option>
<option value="10">10 secondes</option>
<option value="15">15 secondes</option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">Format</label>
<div class="ratio-options">
<button class="ratio-btn active" data-ratio="16:9">16:9</button>
<button class="ratio-btn" data-ratio="9:16">9:16</button>
<button class="ratio-btn" data-ratio="1:1">1:1</button>
</div>
</div>
<button class="generate-btn" id="generateBtn">
<i class="fas fa-sparkles"></i> Générer la vidéo
</button>
</div>
<!-- PREVIEW COLUMN -->
<div class="preview-card">
<div class="loading-overlay" id="loadingOverlay">
<div class="spinner"></div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="status-text" id="statusText">Initialisation...</div>
</div>
<div class="video-placeholder" id="videoPlaceholder">
<div style="text-align: center; color: var(--text-secondary);">
<i class="fas fa-film" style="font-size: 3rem; margin-bottom: 15px; opacity: 0.5;"></i>
<p>Aperçu de la vidéo</p>
</div>
</div>
<div id="videoControls" style="width: 100%; margin-top: 20px; display: none; justify-content: space-between; align-items: center;">
<div style="font-size: 0.9rem; color: var(--text-secondary);">
<i class="fas fa-check-circle" style="color: var(--success);"></i> Généré avec succès
</div>
<button style="background: var(--bg-input); border: none; color: white; padding: 8px 16px; border-radius: 6px; cursor: pointer;">
<i class="fas fa-download"></i> Télécharger
</button>
</div>
</div>
</div>
<!-- HISTORY GRID -->
<div class="history-section">
<div class="section-header">
<h2>Générations récentes</h2>
<a href="#" style="color: var(--primary); text-decoration: none; font-size: 0.9rem;">Voir tout</a>
</div>
<div class="history-grid" id="historyGrid">
<!-- Items injectés via JS -->
</div>
</div>
</div>
</main>
</div>
<script>
// =========================================
// DOM ELEMENTS
// =========================================
const promptInput = document.getElementById('promptInput');
const generateBtn = document.getElementById('generateBtn');
const loadingOverlay = document.getElementById('loadingOverlay');
const progressFill = document.getElementById('progressFill');
const statusText = document.getElementById('statusText');
const videoPlaceholder = document.getElementById('videoPlaceholder');
const videoControls = document.getElementById('videoControls');
const historyGrid = document.getElementById('historyGrid');
const ratioBtns = document.querySelectorAll('.ratio-btn');
const menuToggle = document.getElementById('menuToggle');
const sidebar = document.getElementById('sidebar');
// =========================================
// STATE & DATA
// =========================================
let isGenerating = false;
const historyData = [
{
id: 1,
prompt: "Ville futuriste avec voitures volantes, nuit",
style: "Cyberpunk",
duration: "5s",
thumb: "https://picsum.photos/seed/cyber1/400/250"
},
{
id: 2,
prompt: "Chat buvant du café dans un café parisien",
style: "Réaliste",
duration: "10s",
thumb: "https://picsum.photos/seed/cat2/400/250"
},
{
id: 3,
prompt: "Dragon cracheur de feu sur une montagne",
style: "Cinématique",
duration: "5s",
thumb: "https://picsum.photos/seed/dragon3/400/250"
}
];
// =========================================
// FUNCTIONS
// =========================================
// 1. Gestion du menu mobile
menuToggle.addEventListener('click', () => {
sidebar.classList.toggle('active');
});
// Clic hors sidebar pour fermer
document.addEventListener('click', (e) => {
if (window.innerWidth <= 768) {
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
sidebar.classList.remove('active');
}
}
});
// 2. Gestion des boutons de ratio
ratioBtns.forEach(btn => {
btn.addEventListener('click', () => {
ratioBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Changer l'aspect ratio du placeholder visuellement
const ratio = btn.dataset.ratio;
if(ratio === '16:9') videoPlaceholder.style.aspectRatio = '16/9';
if(ratio === '9:16') videoPlaceholder.style.aspectRatio = '9/16';
if(ratio === '1:1') videoPlaceholder.style.aspectRatio = '1/1';
});
});
// 3. Rendu de l'historique
function renderHistory() {
historyGrid.innerHTML = '';
historyData.forEach(item => {
const div = document.createElement('div');
div.className = 'history-item';
div.innerHTML = `
<div class="history-thumb" style="background-image: url('${item.thumb}');">
<div style="position:absolute; bottom:8px; right:8px; background:rgba(0,0,0,0.7); color:white; font-size:0.7rem; padding:2px 6px; border-radius:4px;">${item.duration}</div>
</div>
<div class="history-info">
<div class="history-prompt">${item.prompt}</div>
<div class="history-meta">
<span>${item.style}</span>
<span>Il y a 2h</span>
</div>
</div>
`;
div.onclick = () => loadVideoToPreview(item);
historyGrid.appendChild(div);
});
}
// 4. Charger une vidéo depuis l'historique
function loadVideoToPreview(item) {
videoPlaceholder.innerHTML = `
<div class="video-overlay">
<h3 style="color:white; margin-bottom:10px;">${item.prompt}</h3>
<button class="play-btn"><i class="fas fa-play"></i></button>
</div>
`;
videoPlaceholder.style.backgroundImage = `url('${item.thumb}')`;
videoPlaceholder.classList.add('video-active');
videoControls.style.display = 'flex';
// Reset placeholder text logic
videoPlaceholder.querySelector('div[style*="text-align"]').style.display = 'none';
}
// 5. Simulation de la génération
generateBtn.addEventListener('click', () => {
const prompt = promptInput.value.trim();
if (!prompt) {
promptInput.focus();
promptInput.style.borderColor = 'var(--accent)';
setTimeout(() => promptInput.style.borderColor = 'var(--border)', 2000);
return;
}
if (isGenerating) return;
isGenerating = true;
// UI Updates
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Génération...';
loadingOverlay.classList.add('active');
videoControls.style.display = 'none';
videoPlaceholder.classList.remove('video-active');
videoPlaceholder.style.backgroundImage = 'none';
// Reset Placeholder to default but hide text while loading
videoPlaceholder.innerHTML = `
<div style="text-align: center; color: var(--text-secondary); opacity: 0;">
<i class="fas fa-film" style="font-size: 3rem; margin-bottom: 15px;"></i>
<p>Aperçu de la vidéo</p>
</div>
`;
// Simulation des étapes
const steps = [
{ pct: 0, text: "Analyse du prompt..." },
{ pct: 20, text: "Génération des keyframes..." },
{ pct: 45, text: "Interpolation des mouvements..." },
{ pct: 70, text: "Rendu des textures..." },
{ pct: 90, text: "Encodage final..." },
{ pct: 100, text: "Terminé !" }
];
let stepIndex = 0;
const interval = setInterval(() => {
if (stepIndex >= steps.length) {
clearInterval(interval);
finishGeneration(prompt);
return;
}
const step = steps[stepIndex];
progressFill.style.width = step.pct + '%';
statusText.textContent = step.text;
stepIndex++;
}, 600); // 600ms par étape (environ 3.6s total)
});
function finishGeneration(prompt) {
isGenerating = false;
// Masquer le loader
loadingOverlay.classList.remove('active');
// Réafficher le bouton
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-sparkles"></i> Générer la vidéo';
// Afficher le résultat (Simulation)
const randomSeed = Math.floor(Math.random() * 1000);
const thumbUrl = `https://picsum.photos/seed/${randomSeed}/800/450`;
videoPlaceholder.style.backgroundImage = `url('${thumbUrl}')`;
videoPlaceholder.classList.add('video-active');
videoPlaceholder.innerHTML = `
<div class="video-overlay">
<h3 style="color:white; margin-bottom:10px; font-size:0.9rem;">Résultat généré</h3>
<button class="play-btn"><i class="fas fa-play"></i></button>
</div>
`;
videoControls.style.display = 'flex';
// Ajouter à l'historique
const newItem = {
id: Date.now(),
prompt: prompt,
style: document.getElementById('styleSelect').value,
duration: document.getElementById('durationSelect').value + 's',
thumb: thumbUrl
};
historyData.unshift(newItem); // Ajouter au début
renderHistory();
}
// Init
renderHistory();
</script>
</body>
</html>