// ============================================================ // RushPilot AI — Application Logic // ============================================================ // ---- Initialize Lucide icons ---- function refreshIcons() { if (window.lucide) lucide.createIcons(); } refreshIcons(); // ============================================================ // DATA // ============================================================ const mediaAssets = [ { name: 'interview_take_04', dur: '00:18', badge: '4K', cat: 'people', seed: 22 }, { name: 'broll_citywide_4k', dur: '00:42', badge: 'AI', cat: 'cityscape', seed: 11 }, { name: 'product_hero_shot', dur: '00:24', cat: 'technology', seed: 42 }, { name: 'demo_sequence_v2', dur: '01:12', badge: 'AI', cat: 'office', seed: 9 }, { name: 'team_working_01', dur: '00:36', cat: 'workspace', seed: 3 }, { name: 'outro_logo_anim', dur: '00:08', cat: 'abstract', seed: 5 }, { name: 'interview_take_07', dur: '00:22', cat: 'people', seed: 15 }, { name: 'broll_product_macro', dur: '00:30', badge: 'AI', cat: 'technology', seed: 7 }, ]; const stylePresets = [ { name: 'Cinematic', cat: 'technology', seed: 42, selected: true }, { name: 'Documentary', cat: 'people', seed: 22 }, { name: 'Vlog / Social', cat: 'office', seed: 9 }, { name: 'Commercial', cat: 'minimal', seed: 8 }, { name: 'Podcast', cat: 'workspace', seed: 3 }, { name: 'Product Reveal', cat: 'abstract', seed: 5 }, { name: 'Drone Aerial', cat: 'aerial', seed: 12 }, { name: 'Anime / Stylized', cat: 'gradient', seed: 33 }, ]; const colorPresets = [ { name: 'Teal/Orange', grad: 'linear-gradient(135deg,#0f766e,#f97316)' }, { name: 'Noir B&W', grad: 'linear-gradient(135deg,#27272a,#71717a)' }, { name: 'Golden Hour', grad: 'linear-gradient(135deg,#f59e0b,#dc2626)' }, { name: 'Cyberpunk', grad: 'linear-gradient(135deg,#6366f1,#ec4899)' }, { name: 'Vintage Film', grad: 'linear-gradient(135deg,#92400e,#fcd34d)' }, { name: 'Cool Blue', grad: 'linear-gradient(135deg,#0ea5e9,#1e3a8a)' }, ]; const transitions = [ { name: 'Cross Dissolve', dur: '0.5s' }, { name: 'Dip to Black', dur: '0.8s' }, { name: 'Whip Pan', dur: '0.3s' }, { name: 'Zoom Blur', dur: '0.4s' }, { name: 'Glitch Cut', dur: '0.2s' }, { name: 'Slide Push', dur: '0.5s' }, ]; const historyItems = [ { prompt: 'Cinematic product reveal', cat: 'technology', seed: 42, time: '2m ago', badge: '4K' }, { prompt: 'Aerial city flythrough', cat: 'aerial', seed: 12, time: '8m ago' }, { prompt: 'Podcast intro animation', cat: 'workspace', seed: 3, time: '14m ago' }, { prompt: 'Abstract logo motion', cat: 'abstract', seed: 5, time: '32m ago' }, { prompt: 'Macro product detail', cat: 'technology', seed: 7, time: '1h ago', badge: 'AI' }, { prompt: 'Team b-roll sequence', cat: 'workspace', seed: 3, time: '2h ago' }, { prompt: 'Documentary interview', cat: 'people', seed: 22, time: '3h ago' }, { prompt: 'Social vertical cut', cat: 'office', seed: 9, time: '5h ago' }, ]; // ============================================================ // RENDER MEDIA GRID // ============================================================ function renderMediaGrid() { const grid = document.getElementById('mediaGrid'); grid.innerHTML = mediaAssets.map(a => `
${a.name} ${a.badge ? `
${a.badge}
` : ''}
${a.dur}
${a.name}
MOV · 4K · 23.976
`).join(''); grid.querySelectorAll('.media-item').forEach(el => { el.addEventListener('click', () => { grid.querySelectorAll('.media-item').forEach(m => m.style.borderColor = ''); el.style.borderColor = '#818cf8'; showToast(`Added "${el.dataset.name}" to timeline`, 'info'); }); }); } // ============================================================ // RENDER STYLE PRESETS // ============================================================ function renderStylePresets() { const container = document.getElementById('stylePresets'); container.innerHTML = stylePresets.map((s, i) => `
${s.name}
`).join(''); container.querySelectorAll('.style-card').forEach(el => { el.addEventListener('click', () => { container.querySelectorAll('.style-card').forEach(c => c.classList.remove('selected')); el.classList.add('selected'); }); }); refreshIcons(); } // ============================================================ // RENDER COLOR PRESETS // ============================================================ function renderColorPresets() { const container = document.getElementById('effectsColor'); container.innerHTML = colorPresets.map((p, i) => `
${p.name}
`).join(''); container.querySelectorAll('.effect-preset').forEach(el => { el.addEventListener('click', () => { container.querySelectorAll('.effect-preset').forEach(e => e.classList.remove('selected')); el.classList.add('selected'); }); }); } // ============================================================ // RENDER TRANSITIONS // ============================================================ function renderTransitions() { const container = document.getElementById('effectsTrans'); container.innerHTML = transitions.map(t => `
${t.name} ${t.dur}
`).join(''); } // ============================================================ // RENDER STOCK GRID // ============================================================ function renderStockGrid() { const grid = document.getElementById('stockGrid'); const cats = ['nature','cityscape','technology','people','workspace','abstract','office','minimal']; grid.innerHTML = cats.map((c, i) => `
stock
4K
${c.charAt(0).toUpperCase()+c.slice(1)} clip
Royalty-free
`).join(''); } // ============================================================ // RENDER HISTORY STRIP // ============================================================ function renderHistory() { const strip = document.getElementById('historyStrip'); strip.innerHTML = historyItems.map(h => `
${h.prompt} ${h.badge ? `
${h.badge}
` : ''}
${h.prompt}
${h.time}
`).join(''); } // ============================================================ // TIMELINE RULER // ============================================================ function renderTimelineRuler() { const ruler = document.getElementById('timelineRuler'); const rulerWidth = 1040; // matches track content let html = ''; const totalSec = 32; // 32 seconds shown for (let s = 0; s <= totalSec; s++) { const x = (s / totalSec) * rulerWidth; const major = s % 5 === 0; html += `
`; if (major) { const mm = String(Math.floor(s/60)).padStart(2,'0'); const ss = String(s%60).padStart(2,'0'); html += `
${mm}:${ss}:00
`; } } ruler.innerHTML = html; } // ============================================================ // WAVEFORMS // ============================================================ function renderWaveforms() { document.querySelectorAll('.waveform').forEach(wf => { const seed = parseInt(wf.dataset.seed) || 1; const bars = wf.classList.contains('dense') ? 120 : 60; let rng = seed; const rand = () => { rng = (rng * 9301 + 49297) % 233280; return rng / 233280; }; let html = ''; for (let i = 0; i < bars; i++) { const base = 0.3 + 0.7 * Math.abs(Math.sin(i * 0.3 + seed)); const noise = rand() * 0.4; const h = Math.max(8, Math.min(100, (base + noise) * 100)); html += `
`; } wf.innerHTML = html; }); } // ============================================================ // MODE SWITCHING // ============================================================ const modeBtns = document.querySelectorAll('.mode-btn'); const modeIndicator = document.getElementById('modeIndicator'); const editorView = document.getElementById('editorView'); const generatorView = document.getElementById('generatorView'); const statusMode = document.getElementById('statusMode'); modeBtns.forEach(btn => { btn.addEventListener('click', () => { const mode = btn.dataset.mode; modeBtns.forEach(b => { b.classList.remove('text-white'); b.classList.add('text-zinc-400'); }); btn.classList.remove('text-zinc-400'); btn.classList.add('text-white'); modeIndicator.style.transform = mode === 'generator' ? 'translateX(100%)' : 'translateX(0)'; if (mode === 'editor') { editorView.classList.remove('hidden'); editorView.classList.add('flex'); generatorView.classList.add('hidden'); generatorView.classList.remove('flex'); statusMode.textContent = 'Editor'; } else { generatorView.classList.remove('hidden'); generatorView.classList.add('flex'); editorView.classList.add('hidden'); editorView.classList.remove('flex'); statusMode.textContent = 'Generator'; } refreshIcons(); }); }); // ============================================================ // LEFT PANEL TABS // ============================================================ document.querySelectorAll('.left-tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.left-tab').forEach(t => t.classList.remove('active')); tab.classList.add('active'); const target = tab.dataset.leftTab; document.querySelectorAll('.left-panel').forEach(p => { p.classList.toggle('hidden', p.dataset.leftPanel !== target); }); }); }); // ============================================================ // INSPECTOR COLLAPSE // ============================================================ document.querySelectorAll('.inspector-header').forEach(h => { h.addEventListener('click', () => { h.parentElement.classList.toggle('collapsed'); }); }); // ============================================================ // PLAYBACK SIMULATION // ============================================================ let isPlaying = false; let playInterval = null; let currentTime = 0; const playBtn = document.getElementById('playBtn'); const playIcon = document.getElementById('playIcon'); const tcDisplay = document.getElementById('timecodeDisplay'); const playhead = document.getElementById('playhead'); function formatTC(frames) { const fps = 24; const totalFrames = Math.floor(frames); const f = totalFrames % fps; const s = Math.floor(totalFrames / fps) % 60; const m = Math.floor(totalFrames / (fps*60)) % 60; const h = Math.floor(totalFrames / (fps*3600)); return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}:${String(f).padStart(2,'0')}`; } playBtn.addEventListener('click', () => { isPlaying = !isPlaying; if (isPlaying) { playIcon.setAttribute('data-lucide', 'square'); playBtn.style.background = '#ef4444'; playBtn.style.color = '#fff'; playInterval = setInterval(() => { currentTime += 1; tcDisplay.textContent = formatTC(84*24 + currentTime); const px = 320 + (currentTime / 24) * 40; if (px < 1040) playhead.style.left = px + 'px'; else { currentTime = 0; playhead.style.left = '320px'; } }, 42); } else { playIcon.setAttribute('data-lucide', 'play'); playBtn.style.background = '#fff'; playBtn.style.color = '#000'; clearInterval(playInterval); } refreshIcons(); }); // Spacebar shortcut document.addEventListener('keydown', (e) => { if (e.code === 'Space' && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') { e.preventDefault(); playBtn.click(); } }); // ============================================================ // CLIP SELECTION // ============================================================ document.querySelectorAll('.clip').forEach(clip => { clip.addEventListener('click', (e) => { e.stopPropagation(); document.querySelectorAll('.clip').forEach(c => c.classList.remove('selected')); clip.classList.add('selected'); const label = clip.querySelector('.clip-label')?.textContent || 'clip'; showToast(`Selected: ${label}`, 'info'); }); }); // ============================================================ // TIMELINE ZOOM // ============================================================ const timelineZoom = document.getElementById('timelineZoom'); const timelineTracks = document.getElementById('timelineTracks'); const timelineRuler = document.getElementById('timelineRuler'); timelineZoom.addEventListener('input', () => { const scale = timelineZoom.value / 100; timelineTracks.style.transform = `scaleX(${scale})`; timelineTracks.style.transformOrigin = 'left center'; timelineRuler.style.transform = `scaleX(${scale})`; timelineRuler.style.transformOrigin = 'left center'; }); // Timeline scroll sync const timelineScroll = document.getElementById('timelineScroll'); timelineScroll.addEventListener('scroll', () => { timelineRuler.style.transform = `translateX(${-timelineScroll.scrollLeft}px)`; }); // ============================================================ // FILTER CHIPS // ============================================================ document.querySelectorAll('.filter-chip').forEach(chip => { chip.addEventListener('click', () => { document.querySelectorAll('.filter-chip').forEach(c => c.classList.remove('active')); chip.classList.add('active'); }); }); // ============================================================ // GENERATOR: PARAMETERS // ============================================================ // Model select document.querySelectorAll('.model-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('.model-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); }); }); // Ratio select const genResBadge = document.getElementById('genResBadge'); document.querySelectorAll('.ratio-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('.ratio-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); const fps = document.querySelector('#fpsSelect .active')?.dataset.fps || 30; const dur = document.getElementById('durationSlider').value; genResBadge.textContent = `${opt.dataset.w}×${opt.dataset.h} · ${fps}fps · ${dur}s`; }); }); // FPS document.querySelectorAll('#fpsSelect .seg-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('#fpsSelect .seg-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); const ratio = document.querySelector('#ratioSelect .active'); const dur = document.getElementById('durationSlider').value; genResBadge.textContent = `${ratio.dataset.w}×${ratio.dataset.h} · ${opt.dataset.fps}fps · ${dur}s`; }); }); // Resolution document.querySelectorAll('#resSelect .seg-opt').forEach(opt => { opt.addEventListener('click', () => { document.querySelectorAll('#resSelect .seg-opt').forEach(o => o.classList.remove('active')); opt.classList.add('active'); }); }); // Duration const durationSlider = document.getElementById('durationSlider'); const durationValue = document.getElementById('durationValue'); durationSlider.addEventListener('input', () => { durationValue.textContent = durationSlider.value + 's'; const ratio = document.querySelector('#ratioSelect .active'); const fps = document.querySelector('#fpsSelect .active')?.dataset.fps || 30; genResBadge.textContent = `${ratio.dataset.w}×${ratio.dataset.h} · ${fps}fps · ${durationSlider.value}s`; }); // Motion const motionSlider = document.getElementById('motionSlider'); const motionValue = document.getElementById('motionValue'); const motionLabels = ['Static','Subtle','Low','Medium','High','Intense']; motionSlider.addEventListener('input', () => { const idx = Math.floor(motionSlider.value / 100 * (motionLabels.length-1)); motionValue.textContent = motionLabels[idx]; }); // Prompt chips document.querySelectorAll('.prompt-chip').forEach(chip => { chip.addEventListener('click', () => { const input = document.getElementById('promptInput'); const insert = chip.dataset.insert; input.value = input.value.trim() + ' ' + insert; }); }); // Enhance prompt document.getElementById('enhancePrompt').addEventListener('click', () => { const input = document.getElementById('promptInput'); const additions = ' Shot on ARRI Alexa, 35mm anamorphic lens, color graded teal-and-orange, volumetric haze, film grain, high dynamic range, 8k detail.'; if (!input.value.includes('ARRI')) { input.value = input.value.replace(/\.$/, '') + additions; showToast('Prompt enhanced with cinematic tokens', 'success'); } else { showToast('Prompt already enhanced', 'info'); } }); // ============================================================ // GENERATION SIMULATION // ============================================================ const generateBtn = document.getElementById('generateBtn'); const genOverlay = document.getElementById('genOverlay'); const genResultBar = document.getElementById('genResultBar'); const progressRing = document.getElementById('progressRing'); const progressPct = document.getElementById('progressPct'); const genStageText = document.getElementById('genStageText'); const genStageSub = document.getElementById('genStageSub'); const stageDots = document.querySelectorAll('#genStages .stage-dot'); const stages = [ { text: 'Initializing engine…', sub: 'Allocating GPU resources' }, { text: 'Analyzing prompt semantics…', sub: 'Parsing scene graph & intent' }, { text: 'Generating keyframes…', sub: 'Diffusion sampling · 40 steps' }, { text: 'Synthesizing motion…', sub: 'Temporal coherence & flow' }, { text: 'Upscaling & color grading…', sub: 'AI super-resolution · 4K' }, ]; generateBtn.addEventListener('click', () => { genOverlay.classList.remove('hidden'); genOverlay.classList.add('flex'); genResultBar.style.opacity = '0'; generateBtn.disabled = true; generateBtn.style.opacity = '0.5'; let progress = 0; let stageIdx = 0; stageDots.forEach(d => d.classList.remove('active','done')); stageDots[0].classList.add('