Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Animai | AI Image to Video Generator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <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=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> | |
| <script src="https://unpkg.com/@phosphor-icons/web"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| mono: ['JetBrains Mono', 'monospace'], | |
| }, | |
| colors: { | |
| surface: { | |
| 900: '#0a0a0f', | |
| 800: '#13131f', | |
| 700: '#1c1c2e', | |
| 600: '#252540', | |
| 500: '#353560', | |
| }, | |
| accent: { | |
| 400: '#a78bfa', | |
| 500: '#8b5cf6', | |
| 600: '#7c3aed', | |
| }, | |
| glow: { | |
| purple: 'rgba(139, 92, 246, 0.5)', | |
| cyan: 'rgba(34, 211, 238, 0.5)', | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| :root { | |
| --glass-bg: rgba(19, 19, 31, 0.7); | |
| --glass-border: rgba(255, 255, 255, 0.08); | |
| --glass-highlight: rgba(255, 255, 255, 0.03); | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| html { | |
| scroll-behavior: smooth; | |
| } | |
| body { | |
| background-color: #0a0a0f; | |
| color: #e2e8f0; | |
| font-family: 'Inter', sans-serif; | |
| overflow-x: hidden; | |
| min-height: 100vh; | |
| } | |
| /* Background ambient mesh */ | |
| .ambient-bg { | |
| position: fixed; | |
| inset: 0; | |
| z-index: -1; | |
| background: | |
| radial-gradient(ellipse 80% 50% at 20% 40%, rgba(139, 92, 246, 0.12), transparent), | |
| radial-gradient(ellipse 60% 40% at 80% 60%, rgba(34, 211, 238, 0.08), transparent), | |
| radial-gradient(ellipse 50% 50% at 50% 50%, rgba(139, 92, 246, 0.05), transparent); | |
| filter: blur(60px); | |
| } | |
| .ambient-bg::after { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.02'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); | |
| opacity: 0.4; | |
| } | |
| /* Glass morphism */ | |
| .glass-panel { | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid var(--glass-border); | |
| box-shadow: | |
| 0 8px 32px rgba(0, 0, 0, 0.4), | |
| inset 0 1px 0 var(--glass-highlight); | |
| } | |
| .glass-card { | |
| background: rgba(255, 255, 255, 0.03); | |
| border: 1px solid rgba(255, 255, 255, 0.06); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .glass-card:hover { | |
| background: rgba(255, 255, 255, 0.06); | |
| border-color: rgba(139, 92, 246, 0.3); | |
| transform: translateY(-2px); | |
| box-shadow: 0 12px 40px rgba(139, 92, 246, 0.15); | |
| } | |
| /* Upload zone */ | |
| .upload-zone { | |
| border: 2px dashed rgba(255, 255, 255, 0.15); | |
| background: rgba(255, 255, 255, 0.02); | |
| transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .upload-zone:hover, .upload-zone.drag-over { | |
| border-color: rgba(139, 92, 246, 0.6); | |
| background: rgba(139, 92, 246, 0.08); | |
| transform: scale(1.01); | |
| } | |
| .upload-zone.drag-over { | |
| box-shadow: 0 0 40px rgba(139, 92, 246, 0.2); | |
| } | |
| /* Inputs */ | |
| .input-field { | |
| background: rgba(0, 0, 0, 0.3); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .input-field:focus { | |
| outline: none; | |
| border-color: rgba(139, 92, 246, 0.5); | |
| box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1), 0 0 20px rgba(139, 92, 246, 0.1); | |
| } | |
| /* Range slider */ | |
| input[type="range"] { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 100%; | |
| height: 6px; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 3px; | |
| outline: none; | |
| } | |
| input[type="range"]::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 18px; | |
| height: 18px; | |
| background: #8b5cf6; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| box-shadow: 0 0 10px rgba(139, 92, 246, 0.5); | |
| transition: all 0.2s ease; | |
| } | |
| input[type="range"]::-webkit-slider-thumb:hover { | |
| transform: scale(1.2); | |
| box-shadow: 0 0 20px rgba(139, 92, 246, 0.7); | |
| } | |
| input[type="range"]::-moz-range-thumb { | |
| width: 18px; | |
| height: 18px; | |
| background: #8b5cf6; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| border: none; | |
| box-shadow: 0 0 10px rgba(139, 92, 246, 0.5); | |
| } | |
| /* Buttons */ | |
| .btn-primary { | |
| background: linear-gradient(135deg, #8b5cf6, #7c3aed); | |
| position: relative; | |
| overflow: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .btn-primary::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| background: linear-gradient(135deg, rgba(255,255,255,0.2), transparent); | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| .btn-primary:hover::before { | |
| opacity: 1; | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-1px); | |
| box-shadow: 0 8px 30px rgba(139, 92, 246, 0.4); | |
| } | |
| .btn-primary:active { | |
| transform: translateY(0); | |
| } | |
| .btn-primary:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| /* Tag pills */ | |
| .tag-pill { | |
| transition: all 0.2s ease; | |
| cursor: pointer; | |
| user-select: none; | |
| } | |
| .tag-pill:hover { | |
| background: rgba(139, 92, 246, 0.2); | |
| border-color: rgba(139, 92, 246, 0.4); | |
| transform: translateY(-1px); | |
| } | |
| .tag-pill.active { | |
| background: rgba(139, 92, 246, 0.25); | |
| border-color: rgba(139, 92, 246, 0.6); | |
| color: #c4b5fd; | |
| } | |
| /* Animations */ | |
| @keyframes pulse-glow { | |
| 0%, 100% { box-shadow: 0 0 20px rgba(139, 92, 246, 0.3); } | |
| 50% { box-shadow: 0 0 40px rgba(139, 92, 246, 0.5); } | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-6px); } | |
| } | |
| @keyframes shimmer { | |
| 0% { background-position: -200% 0; } | |
| 100% { background-position: 200% 0; } | |
| } | |
| .animate-pulse-glow { | |
| animation: pulse-glow 2s ease-in-out infinite; | |
| } | |
| .animate-float { | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| .shimmer-bg { | |
| background: linear-gradient(90deg, transparent 0%, rgba(139, 92, 246, 0.1) 50%, transparent 100%); | |
| background-size: 200% 100%; | |
| animation: shimmer 2s infinite; | |
| } | |
| /* Progress bar */ | |
| .progress-track { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .progress-fill { | |
| background: linear-gradient(90deg, #8b5cf6, #a78bfa); | |
| transition: width 0.3s ease; | |
| box-shadow: 0 0 10px rgba(139, 92, 246, 0.4); | |
| } | |
| /* Video container */ | |
| .video-container { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .video-container::before { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| border: 1px solid rgba(139, 92, 246, 0.2); | |
| border-radius: inherit; | |
| pointer-events: none; | |
| z-index: 10; | |
| } | |
| /* Scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(0, 0, 0, 0.2); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(139, 92, 246, 0.3); | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(139, 92, 246, 0.5); | |
| } | |
| /* Loading dots */ | |
| @keyframes loading-dot { | |
| 0%, 80%, 100% { transform: scale(0); opacity: 0.5; } | |
| 40% { transform: scale(1); opacity: 1; } | |
| } | |
| .loading-dot { | |
| animation: loading-dot 1.4s infinite ease-in-out both; | |
| } | |
| .loading-dot:nth-child(1) { animation-delay: -0.32s; } | |
| .loading-dot:nth-child(2) { animation-delay: -0.16s; } | |
| /* Particle canvas */ | |
| #particleCanvas { | |
| position: fixed; | |
| inset: 0; | |
| z-index: -1; | |
| pointer-events: none; | |
| } | |
| /* Result video styling */ | |
| video { | |
| max-width: 100%; | |
| border-radius: 12px; | |
| } | |
| /* Modal */ | |
| .modal-overlay { | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: all 0.3s ease; | |
| } | |
| .modal-overlay.active { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .modal-content { | |
| transform: scale(0.95) translateY(10px); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .modal-overlay.active .modal-content { | |
| transform: scale(1) translateY(0); | |
| } | |
| /* Toast */ | |
| .toast { | |
| transform: translateX(120%); | |
| transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .toast.show { | |
| transform: translateX(0); | |
| } | |
| </style> | |
| </head> | |
| <body class="antialiased"> | |
| <!-- Ambient Background --> | |
| <div class="ambient-bg"></div> | |
| <canvas id="particleCanvas"></canvas> | |
| <!-- Toast Container --> | |
| <div id="toastContainer" class="fixed top-24 right-6 z-[100] flex flex-col gap-3 pointer-events-none"></div> | |
| <!-- Header --> | |
| <header class="sticky top-0 z-50 glass-panel border-b border-white/5"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between"> | |
| <div class="flex items-center gap-3"> | |
| <div class="w-9 h-9 rounded-xl bg-gradient-to-br from-accent-500 to-accent-600 flex items-center justify-center shadow-lg shadow-accent-600/20"> | |
| <i class="ph ph-film-strip text-white text-lg"></i> | |
| </div> | |
| <span class="text-xl font-bold tracking-tight bg-gradient-to-r from-white to-white/60 bg-clip-text text-transparent">Animai</span> | |
| </div> | |
| <nav class="hidden md:flex items-center gap-1"> | |
| <a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">Gallery</a> | |
| <a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">API</a> | |
| <a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">Docs</a> | |
| <div class="w-px h-5 bg-white/10 mx-2"></div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="px-3 py-1.5 rounded-lg text-sm font-medium text-accent-400 hover:text-accent-300 hover:bg-accent-500/10 transition-all flex items-center gap-1.5"> | |
| <i class="ph ph-code text-xs"></i> | |
| Built with anycoder | |
| </a> | |
| </nav> | |
| <div class="flex items-center gap-3"> | |
| <button id="historyBtn" class="p-2 rounded-lg hover:bg-white/5 transition-colors relative"> | |
| <i class="ph ph-clock-counter-clockwise text-white/60 text-lg"></i> | |
| <span id="historyBadge" class="absolute -top-0.5 -right-0.5 w-4 h-4 bg-accent-500 rounded-full text-[10px] font-bold flex items-center justify-center text-white opacity-0 transition-opacity">0</span> | |
| </button> | |
| <button class="hidden sm:flex items-center gap-2 px-4 py-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all text-sm font-medium"> | |
| <i class="ph ph-user-circle text-lg"></i> | |
| Sign In | |
| </button> | |
| <button class="md:hidden p-2 rounded-lg hover:bg-white/5 transition-colors"> | |
| <i class="ph ph-list text-white/60 text-xl"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12"> | |
| <!-- Hero --> | |
| <div class="text-center mb-10 lg:mb-14"> | |
| <div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent-500/10 border border-accent-500/20 text-accent-400 text-xs font-medium mb-4"> | |
| <span class="w-1.5 h-1.5 rounded-full bg-accent-400 animate-pulse"></span> | |
| AI-Powered Image to Video | |
| </div> | |
| <h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight mb-4 leading-tight"> | |
| Bring your images <br class="hidden sm:block"> | |
| <span class="bg-gradient-to-r from-accent-400 via-purple-300 to-cyan-300 bg-clip-text text-transparent">to life with motion</span> | |
| </h1> | |
| <p class="text-white/50 text-lg max-w-2xl mx-auto leading-relaxed"> | |
| Transform static images into stunning cinematic videos using state-of-the-art diffusion models. Describe the motion, adjust the mood, and watch the magic happen. | |
| </p> | |
| </div> | |
| <div class="grid lg:grid-cols-5 gap-6 lg:gap-8"> | |
| <!-- Left Panel - Controls --> | |
| <aside class="lg:col-span-2 space-y-5"> | |
| <!-- Upload --> | |
| <div class="glass-panel rounded-2xl p-5"> | |
| <label class="block text-sm font-semibold text-white/90 mb-3 flex items-center gap-2"> | |
| <i class="ph ph-image text-accent-400"></i> | |
| Source Image | |
| </label> | |
| <div id="uploadZone" class="upload-zone rounded-xl p-8 text-center cursor-pointer relative group"> | |
| <input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10"> | |
| <div id="uploadPlaceholder" class="transition-all duration-300"> | |
| <div class="w-14 h-14 rounded-2xl bg-accent-500/10 flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform"> | |
| <i class="ph ph-upload-simple text-accent-400 text-2xl"></i> | |
| </div> | |
| <p class="text-white/80 font-medium text-sm mb-1">Drop an image or click to browse</p> | |
| <p class="text-white/30 text-xs">JPG, PNG, WebP up to 10MB</p> | |
| </div> | |
| <div id="uploadPreview" class="hidden relative"> | |
| <img id="previewImg" src="" alt="Preview" class="w-full max-h-48 object-contain rounded-lg"> | |
| <button id="removeImage" class="absolute -top-2 -right-2 w-7 h-7 bg-red-500/90 hover:bg-red-500 rounded-full flex items-center justify-center transition-colors z-20 shadow-lg"> | |
| <i class="ph ph-x text-white text-xs"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Prompt --> | |
| <div class="glass-panel rounded-2xl p-5"> | |
| <label class="block text-sm font-semibold text-white/90 mb-3 flex items-center gap-2"> | |
| <i class="ph ph-text-t text-accent-400"></i> | |
| Motion Prompt | |
| </label> | |
| <textarea id="promptInput" rows="3" class="input-field w-full rounded-xl px-4 py-3 text-sm text-white/90 placeholder-white/25 resize-none" placeholder="Describe the movement and atmosphere... e.g., 'Slow cinematic pan to the right with gentle snowfall and warm golden hour lighting'"></textarea> | |
| <div class="mt-3 flex flex-wrap gap-2"> | |
| <button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Slow cinematic zoom in with dramatic lighting">Cinematic Zoom</button> | |
| <button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Gentle parallax pan from left to right with depth of field">Parallax Pan</button> | |
| <button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Dreamy floating particles with soft bokeh and ethereal glow">Dreamy Bokeh</button> | |
| <button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Fast motion timelapse with clouds rushing overhead">Timelapse</button> | |
| </div> | |
| </div> | |
| <!-- Settings --> | |
| <div class="glass-panel rounded-2xl p-5"> | |
| <label class="block text-sm font-semibold text-white/90 mb-4 flex items-center gap-2"> | |
| <i class="ph ph-sliders-horizontal text-accent-400"></i> | |
| Motion Settings | |
| </label> | |
| <div class="space-y-5"> | |
| <div> | |
| <div class="flex justify-between mb-2"> | |
| <span class="text-xs text-white/50">Motion Intensity</span> | |
| <span id="intensityVal" class="text-xs text-accent-400 font-mono">65%</span> | |
| </div> | |
| <input type="range" id="intensitySlider" min="0" max="100" value="65"> | |
| </div> | |
| <div> | |
| <div class="flex justify-between mb-2"> | |
| <span class="text-xs text-white/50">Duration</span> | |
| <span id="durationVal" class="text-xs text-accent-400 font-mono">4s</span> | |
| </div> | |
| <input type="range" id="durationSlider" min="2" max="10" value="4" step="0.5"> | |
| </div> | |
| <div> | |
| <div class="flex justify-between mb-2"> | |
| <span class="text-xs text-white/50">Frame Rate</span> | |
| <span id="fpsVal" class="text-xs text-accent-400 font-mono">30fps</span> | |
| </div> | |
| <input type="range" id="fpsSlider" min="15" max="60" value="30" step="15"> | |
| </div> | |
| </div> | |
| <div class="mt-5 pt-4 border-t border-white/5 grid grid-cols-2 gap-3"> | |
| <label class="flex items-center gap-2 cursor-pointer group"> | |
| <div class="relative"> | |
| <input type="checkbox" id="enhanceToggle" class="peer sr-only" checked> | |
| <div class="w-9 h-5 rounded-full bg-white/10 peer-checked:bg-accent-600 transition-colors"></div> | |
| <div class="absolute left-0.5 top-0.5 w-4 h-4 rounded-full bg-white transition-transform peer-checked:translate-x-4"></div> | |
| </div> | |
| <span class="text-xs text-white/50 group-hover:text-white/70 transition-colors">AI Enhance</span> | |
| </label> | |
| <label class="flex items-center gap-2 cursor-pointer group"> | |
| <div class="relative"> | |
| <input type="checkbox" id="loopToggle" class="peer sr-only"> | |
| <div class="w-9 h-5 rounded-full bg-white/10 peer-checked:bg-accent-600 transition-colors"></div> | |
| <div class="absolute left-0.5 top-0.5 w-4 h-4 rounded-full bg-white transition-transform peer-checked:translate-x-4"></div> | |
| </div> | |
| <span class="text-xs text-white/50 group-hover:text-white/70 transition-colors">Seamless Loop</span> | |
| </label> | |
| </div> | |
| </div> | |
| <!-- Generate Button --> | |
| <button id="generateBtn" class="btn-primary w-full py-4 rounded-xl font-semibold text-white shadow-lg shadow-accent-600/20 flex items-center justify-center gap-2 text-sm tracking-wide disabled:shadow-none"> | |
| <i class="ph ph-magic-wand text-lg"></i> | |
| <span id="generateText">Generate Video</span> | |
| </button> | |
| </aside> | |
| <!-- Right Panel - Preview --> | |
| <section class="lg:col-span-3 space-y-5"> | |
| <!-- Preview Area --> | |
| <div class="glass-panel rounded-2xl p-1 overflow-hidden"> | |
| <div class="bg-surface-900/80 rounded-xl overflow-hidden min-h-[400px] lg:min-h-[500px] flex flex-col relative"> | |
| <!-- Empty State --> | |
| <div id="emptyState" class="flex-1 flex flex-col items-center justify-center p-10 text-center"> | |
| <div class="w-24 h-24 rounded-3xl bg-white/5 border border-white/5 flex items-center justify-center mb-5 animate-float"> | |
| <i class="ph ph-video-camera text-white/20 text-4xl"></i> | |
| </div> | |
| <h3 class="text-white/40 font-semibold text-lg mb-1">Ready to create</h3> | |
| <p class="text-white/25 text-sm max-w-xs">Upload an image and describe the motion to generate your first AI video</p> | |
| </div> | |
| <!-- Processing State --> | |
| <div id="processingState" class="hidden flex-1 flex flex-col items-center justify-center p-10"> | |
| <div class="relative w-28 h-28 mb-6"> | |
| <svg class="w-full h-full -rotate-90" viewBox="0 0 100 100"> | |
| <circle cx="50" cy="50" r="44" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="6"/> | |
| <circle id="progressCircle" cx="50" cy="50" r="44" fill="none" stroke="url(#grad1)" stroke-width="6" stroke-linecap="round" stroke-dasharray="276.46" stroke-dashoffset="276.46" class="transition-all duration-300"/> | |
| <defs> | |
| <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" style="stop-color:#8b5cf6;stop-opacity:1" /> | |
| <stop offset="100%" style="stop-color:#a78bfa;stop-opacity:1" /> | |
| </linearGradient> | |
| </defs> | |
| </svg> | |
| <div class="absolute inset-0 flex items-center justify-center"> | |
| <span id="progressPercent" class="text-2xl font-bold text-white/80">0%</span> | |
| </div> | |
| </div> | |
| <h3 id="processStatus" class="text-white/60 font-medium mb-2">Analyzing image...</h3> | |
| <p id="processSubstatus" class="text-white/30 text-sm">Preparing diffusion model</p> | |
| <!-- Progress log --> | |
| <div id="progressLog" class="mt-6 w-full max-w-xs space-y-1.5"> | |
| <div class="progress-track rounded-full h-1.5 overflow-hidden"> | |
| <div id="progressBar" class="progress-fill h-full rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Result State --> | |
| <div id="resultState" class="hidden flex-1 flex flex-col p-4"> | |
| <div class="flex items-center justify-between mb-3 px-1"> | |
| <div class="flex items-center gap-2"> | |
| <div class="w-2 h-2 rounded-full bg-green-400 animate-pulse"></div> | |
| <span class="text-xs text-white/50 font-mono">Generation Complete</span> | |
| </div> | |
| <div class="flex items-center gap-2"> | |
| <button id="downloadBtn" class="p-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all" title="Download Video"> | |
| <i class="ph ph-download-simple text-white/60"></i> | |
| </button> | |
| <button id="shareBtn" class="p-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all" title="Share"> | |
| <i class="ph ph-share-network text-white/60"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="flex-1 video-container rounded-xl bg-black flex items-center justify-center overflow-hidden"> | |
| <video id="resultVideo" controls class="max-w-full max-h-full" poster=""></video> | |
| </div> | |
| <div class="mt-3 flex items-center gap-3 px-1"> | |
| <div class="flex-1"> | |
| <p id="resultPrompt" class="text-xs text-white/40 truncate"></p> | |
| </div> | |
| <div class="flex items-center gap-1.5 text-xs text-white/30"> | |
| <i class="ph ph-clock"></i> | |
| <span id="resultDuration">4s</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- History Grid --> | |
| <div id="historySection" class="hidden"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="text-sm font-semibold text-white/70 flex items-center gap-2"> | |
| <i class="ph ph-clock-counter-clockwise"></i> | |
| Recent Generations | |
| </h3> | |
| <button id="clearHistory" class="text-xs text-white/30 hover:text-white/60 transition-colors">Clear All</button> | |
| </div> | |
| <div id="historyGrid" class="grid grid-cols-2 sm:grid-cols-3 gap-3"> | |
| <!-- History items injected here --> | |
| </div> | |
| </div> | |
| </section> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="border-t border-white/5 mt-16 py-8"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row items-center justify-between gap-4"> | |
| <p class="text-white/25 text-xs">Animai is a demonstration of image-to-video synthesis interfaces.</p> | |
| <div class="flex items-center gap-4"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-xs text-accent-400/60 hover:text-accent-400 transition-colors">Built with anycoder</a> | |
| <a href="#" class="text-xs text-white/25 hover:text-white/50 transition-colors">Privacy</a> | |
| <a href="#" class="text-xs text-white/25 hover:text-white/50 transition-colors">Terms</a> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- Share Modal --> | |
| <div id="shareModal" class="modal-overlay fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm"> | |
| <div class="modal-content glass-panel rounded-2xl p-6 max-w-sm w-full"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h3 class="font-semibold text-white/90">Share Video</h3> | |
| <button id="closeModal" class="p-1 rounded-lg hover:bg-white/5 transition-colors"> | |
| <i class="ph ph-x text-white/50"></i> | |
| </button> | |
| </div> | |
| <div class="space-y-3"> | |
| <button class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left"> | |
| <div class="w-9 h-9 rounded-lg bg-[#1DA1F2]/20 flex items-center justify-center"> | |
| <i class="ph ph-twitter-logo text-[#1DA1F2]"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-white/80 font-medium">Twitter</p> | |
| <p class="text-xs text-white/30">Share to your timeline</p> | |
| </div> | |
| </button> | |
| <button class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left"> | |
| <div class="w-9 h-9 rounded-lg bg-[#5865F2]/20 flex items-center justify-center"> | |
| <i class="ph ph-discord-logo text-[#5865F2]"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-white/80 font-medium">Discord</p> | |
| <p class="text-xs text-white/30">Share in a channel</p> | |
| </div> | |
| </button> | |
| <button id="copyLinkBtn" class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left"> | |
| <div class="w-9 h-9 rounded-lg bg-accent-500/20 flex items-center justify-center"> | |
| <i class="ph ph-link text-accent-400"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm text-white/80 font-medium">Copy Link</p> | |
| <p class="text-xs text-white/30">Copy to clipboard</p> | |
| </div> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ==================== STATE ==================== | |
| const state = { | |
| image: null, | |
| prompt: '', | |
| intensity: 65, | |
| duration: 4, | |
| fps: 30, | |
| enhance: true, | |
| loop: false, | |
| isGenerating: false, | |
| history: [] | |
| }; | |
| // ==================== DOM ==================== | |
| const els = { | |
| imageInput: document.getElementById('imageInput'), | |
| uploadZone: document.getElementById('uploadZone'), | |
| uploadPlaceholder: document.getElementById('uploadPlaceholder'), | |
| uploadPreview: document.getElementById('uploadPreview'), | |
| previewImg: document.getElementById('previewImg'), | |
| removeImage: document.getElementById('removeImage'), | |
| promptInput: document.getElementById('promptInput'), | |
| intensitySlider: document.getElementById('intensitySlider'), | |
| intensityVal: document.getElementById('intensityVal'), | |
| durationSlider: document.getElementById('durationSlider'), | |
| durationVal: document.getElementById('durationVal'), | |
| fpsSlider: document.getElementById('fpsSlider'), | |
| fpsVal: document.getElementById('fpsVal'), | |
| enhanceToggle: document.getElementById('enhanceToggle'), | |
| loopToggle: document.getElementById('loopToggle'), | |
| generateBtn: document.getElementById('generateBtn'), | |
| generateText: document.getElementById('generateText'), | |
| emptyState: document.getElementById('emptyState'), | |
| processingState: document.getElementById('processingState'), | |
| resultState: document.getElementById('resultState'), | |
| progressCircle: document.getElementById('progressCircle'), | |
| progressPercent: document.getElementById('progressPercent'), | |
| progressBar: document.getElementById('progressBar'), | |
| processStatus: document.getElementById('processStatus'), | |
| processSubstatus: document.getElementById('processSubstatus'), | |
| resultVideo: document.getElementById('resultVideo'), | |
| resultPrompt: document.getElementById('resultPrompt'), | |
| resultDuration: document.getElementById('resultDuration'), | |
| downloadBtn: document.getElementById('downloadBtn'), | |
| shareBtn: document.getElementById('shareBtn'), | |
| historySection: document.getElementById('historySection'), | |
| historyGrid: document.getElementById('historyGrid'), | |
| historyBadge: document.getElementById('historyBadge'), | |
| clearHistory: document.getElementById('clearHistory'), | |
| shareModal: document.getElementById('shareModal'), | |
| closeModal: document.getElementById('closeModal'), | |
| copyLinkBtn: document.getElementById('copyLinkBtn'), | |
| toastContainer: document.getElementById('toastContainer'), | |
| tagPills: document.querySelectorAll('.tag-pill') | |
| }; | |
| // ==================== UTILS ==================== | |
| function showToast(message, type = 'info') { | |
| const toast = document.createElement('div'); | |
| const colors = { | |
| info: 'border-accent-500/30 bg-s |