lab-v5wla9tc / assets /js /app.js
fullstackufo's picture
Upload folder using huggingface_hub
742adcd verified
// DOM Elements
const apiKeyInput = document.getElementById('apiKey');
const toggleApiKeyBtn = document.getElementById('toggleApiKey');
const scriptInput = document.getElementById('scriptInput');
const themeInput = document.getElementById('themeInput');
const toolButtons = document.querySelectorAll('.tool-btn');
const outputSection = document.getElementById('outputSection');
const outputTitle = document.getElementById('outputTitle');
const outputContent = document.getElementById('outputContent');
const copyBtn = document.getElementById('copyBtn');
const downloadBtn = document.getElementById('downloadBtn');
const loadingOverlay = document.getElementById('loadingOverlay');
// State
let currentGenerationType = '';
let currentOutput = '';
// API Key Toggle
toggleApiKeyBtn.addEventListener('click', () => {
const type = apiKeyInput.type === 'password' ? 'text' : 'password';
apiKeyInput.type = type;
toggleApiKeyBtn.textContent = type === 'password' ? '👁️' : '🙈';
});
// Tool Button Handlers
toolButtons.forEach(button => {
button.addEventListener('click', async () => {
const tool = button.dataset.tool;
const apiKey = apiKeyInput.value.trim();
const script = scriptInput.value.trim();
// Validation
if (!apiKey) {
showNotification('Please enter your Google Gemini API key', 'error');
apiKeyInput.focus();
return;
}
if (!script) {
showNotification('Please enter a script or scene', 'error');
scriptInput.focus();
return;
}
if (tool === 'suno' && !themeInput.value.trim()) {
showNotification('Please enter a theme for audio generation', 'error');
themeInput.focus();
return;
}
currentGenerationType = tool;
await generateContent(tool, apiKey, script);
});
});
// Generate Content
async function generateContent(tool, apiKey, script) {
showLoading(true);
const prompts = {
blueprint: `You are an expert cinematographer and director. Analyze the following script and create a detailed cinematic blueprint.
Include:
1. Shot-by-shot breakdown with timestamps
2. Camera angles, movements, and framing (wide, medium, close-up, etc.)
3. Lighting and color palette
4. Sound design and audio cues
5. Pacing and rhythm notes
6. Strategic context and emotional beats
Format the output as a structured, professional production document.
Script:
${script}`,
suno: `You are a music supervisor creating audio prompts for the Suno AI music generator. Based on the script and theme provided, generate a detailed JSON prompt structure.
Include:
1. Style tags (genre, mood, instrumentation)
2. Lyrics or vocal direction
3. Tempo and energy level
4. Musical progression notes
Theme: ${themeInput.value.trim()}
Script:
${script}
Output as valid JSON with fields: style_tags (array), lyrics (string), tempo (string), mood (string), description (string)`,
storyboard: `You are a professional storyboard artist. Create detailed image generation prompts for each key shot in this script.
For each shot, provide:
1. Shot number and description
2. Detailed visual prompt optimized for Midjourney/DALL-E
3. Camera angle and composition notes
4. Lighting and atmosphere
5. Key visual elements and subjects
Format as a numbered list of prompts, each ready to copy into an image generator.
Script:
${script}`,
feedback: `You are an experienced script doctor and creative consultant. Provide concise, actionable feedback on this script excerpt.
Focus on:
1. Strongest elements
2. One key improvement opportunity
3. Cinematic potential
4. Emotional impact
Keep feedback brief (3-4 sentences) and constructive.
Script:
${script}`
};
const prompt = prompts[tool];
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
contents: [{
parts: [{
text: prompt
}]
}],
generationConfig: {
temperature: 0.7,
topK: 40,
topP: 0.95,
maxOutputTokens: 8192,
}
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'API request failed');
}
const data = await response.json();
const generatedText = data.candidates[0]?.content?.parts[0]?.text;
if (!generatedText) {
throw new Error('No content generated');
}
currentOutput = generatedText;
displayOutput(tool, generatedText);
showNotification('Content generated successfully!', 'success');
} catch (error) {
console.error('Generation error:', error);
showNotification(`Error: ${error.message}`, 'error');
} finally {
showLoading(false);
}
}
// Display Output
function displayOutput(tool, content) {
const titles = {
blueprint: 'Cinematic Blueprint',
suno: 'Suno Audio Prompts',
storyboard: 'Storyboard Image Prompts',
feedback: 'Script Feedback'
};
outputTitle.textContent = titles[tool] || 'Generated Content';
outputContent.textContent = content;
outputSection.style.display = 'block';
outputSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// Copy to Clipboard
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(currentOutput);
showNotification('Copied to clipboard!', 'success');
// Visual feedback
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<span class="btn-icon">✓</span>Copied!';
setTimeout(() => {
copyBtn.innerHTML = originalText;
}, 2000);
} catch (error) {
showNotification('Failed to copy', 'error');
}
});
// Download as File
downloadBtn.addEventListener('click', () => {
const fileNames = {
blueprint: 'cinematic-blueprint.txt',
suno: 'suno-prompts.json',
storyboard: 'storyboard-prompts.txt',
feedback: 'script-feedback.txt'
};
const fileName = fileNames[currentGenerationType] || 'generated-content.txt';
const blob = new Blob([currentOutput], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification('File downloaded!', 'success');
});
// Show/Hide Loading
function showLoading(show) {
loadingOverlay.style.display = show ? 'flex' : 'none';
// Disable all buttons while loading
toolButtons.forEach(btn => {
btn.disabled = show;
});
}
// Notification System
function showNotification(message, type = 'info') {
// Remove existing notifications
const existing = document.querySelector('.notification');
if (existing) {
existing.remove();
}
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
const styles = {
position: 'fixed',
top: '20px',
right: '20px',
padding: '1rem 1.5rem',
borderRadius: '0.75rem',
fontSize: '0.875rem',
fontWeight: '500',
zIndex: '10000',
maxWidth: '400px',
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.5)',
animation: 'slideIn 0.3s ease-out',
transition: 'all 0.3s ease'
};
const colors = {
success: {
background: '#10b981',
color: 'white'
},
error: {
background: '#ef4444',
color: 'white'
},
info: {
background: '#6366f1',
color: 'white'
}
};
Object.assign(notification.style, styles, colors[type] || colors.info);
document.body.appendChild(notification);
// Add slide-in animation
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
`;
document.head.appendChild(style);
setTimeout(() => {
notification.style.opacity = '0';
notification.style.transform = 'translateX(400px)';
setTimeout(() => notification.remove(), 300);
}, 4000);
}
// Load API key from localStorage if exists
window.addEventListener('DOMContentLoaded', () => {
const savedApiKey = localStorage.getItem('gemini_api_key');
if (savedApiKey) {
apiKeyInput.value = savedApiKey;
}
});
// Save API key to localStorage on change
apiKeyInput.addEventListener('change', () => {
const apiKey = apiKeyInput.value.trim();
if (apiKey) {
localStorage.setItem('gemini_api_key', apiKey);
} else {
localStorage.removeItem('gemini_api_key');
}
});
// Example script for demo
const exampleScript = `INT. ABANDONED WAREHOUSE - NIGHT
Moonlight streams through broken windows, casting geometric shadows across concrete floors.
DETECTIVE SARAH CHEN (35) steps through the doorway, her flashlight cutting through darkness. She moves with practiced caution, every footstep echoing.
A SOUND - metal scraping metal - from the shadows ahead.
Sarah freezes. Her hand moves to her holster.
SARAH
(whispered)
I know you're here.
Silence. Then a FIGURE emerges from behind a steel pillar - MARCUS REID (40s), disheveled but defiant.
MARCUS
(bitter laugh)
Took you long enough.
Their eyes lock. Years of history compressed into a single moment.`;
// Add example button functionality
const addExampleBtn = document.createElement('button');
addExampleBtn.textContent = '📄 Load Example Script';
addExampleBtn.className = 'tool-btn';
addExampleBtn.style.marginTop = '1rem';
addExampleBtn.style.background = 'var(--color-surface-light)';
addExampleBtn.addEventListener('click', () => {
scriptInput.value = exampleScript;
showNotification('Example script loaded!', 'info');
});
document.querySelector('.script-card').appendChild(addExampleBtn);