pyfvw's picture
Initial DeepSite commit
fc12e5d verified
Raw
History Blame Contribute Delete
19.8 kB
// ============================================================
// 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 => `
<div class="media-item" data-name="${a.name}">
<img src="http://static.photos/${a.cat}/320x240/${a.seed}" loading="lazy" alt="${a.name}"/>
${a.badge ? `<div class="mi-badge ${a.badge==='AI'?'ai':''}">${a.badge}</div>` : ''}
<div class="mi-duration">${a.dur}</div>
<div class="mi-overlay">
<div class="mi-name">${a.name}</div>
<div class="mi-meta">MOV · 4K · 23.976</div>
</div>
</div>
`).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) => `
<div class="style-card ${s.selected ? 'selected' : ''}" data-idx="${i}">
<div class="sc-bg" style="background-image:url('http://static.photos/${s.cat}/320x240/${s.seed}')"></div>
<div class="sc-overlay"></div>
<div class="sc-check"><i data-lucide="check" class="w-2.5 h-2.5 text-white"></i></div>
<div class="sc-label"><span>${s.name}</span></div>
</div>
`).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) => `
<div class="effect-preset ${i===0?'selected':''}" style="background:${p.grad}">
<div class="ep-label">${p.name}</div>
</div>
`).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 => `
<div class="trans-item">
<div class="trans-icon"></div>
<span class="trans-name">${t.name}</span>
<span class="trans-dur">${t.dur}</span>
</div>
`).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) => `
<div class="media-item">
<img src="http://static.photos/${c}/320x240/${i+1}" loading="lazy" alt="stock"/>
<div class="mi-badge">4K</div>
<div class="mi-overlay">
<div class="mi-name">${c.charAt(0).toUpperCase()+c.slice(1)} clip</div>
<div class="mi-meta">Royalty-free</div>
</div>
</div>
`).join('');
}
// ============================================================
// RENDER HISTORY STRIP
// ============================================================
function renderHistory() {
const strip = document.getElementById('historyStrip');
strip.innerHTML = historyItems.map(h => `
<div class="history-item">
<img src="http://static.photos/${h.cat}/320x240/${h.seed}" loading="lazy" alt="${h.prompt}"/>
${h.badge ? `<div class="hi-badge">${h.badge}</div>` : ''}
<div class="hi-overlay">
<div class="hi-prompt">${h.prompt}</div>
<div class="hi-time">${h.time}</div>
</div>
</div>
`).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 += `<div class="ruler-tick ${major?'major':''}" style="left:${x}px; height:${major?6:4}px"></div>`;
if (major) {
const mm = String(Math.floor(s/60)).padStart(2,'0');
const ss = String(s%60).padStart(2,'0');
html += `<div class="ruler-label" style="left:${x+3}px">${mm}:${ss}:00</div>`;
}
}
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 += `<div class="wf-bar" style="height:${h}%"></div>`;
}
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('