Comic Script Writer

Comic Script Writer

Script Preview

Your script preview will appear here.

'; return; } scriptElements.forEach(item => { let element; // Sanitize content for HTML output const safeContent = item.content.toString() .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'") .replace(/\n/g, '
'); switch(item.type) { case 'Page': element = document.createElement('h3'); element.innerHTML = `PAGE ${safeContent}`; break; case 'Panel': element = document.createElement('h4'); element.innerHTML = `PANEL ${safeContent}`; break; case 'Description': element = document.createElement('p'); element.className = 'script-description'; element.innerHTML = safeContent; break; case 'Character': element = document.createElement('p'); element.className = 'script-character'; element.innerHTML = safeContent; break; case 'Dialogue': element = document.createElement('p'); element.className = 'script-dialogue'; element.innerHTML = safeContent; break; case 'Caption': element = document.createElement('p'); element.className = 'script-caption'; element.innerHTML = `CAPTION: ${safeContent}`; break; case 'SFX': element = document.createElement('p'); element.className = 'script-sfx'; element.innerHTML = `SFX: ${safeContent}`; break; } if(element) previewArea.appendChild(element); }); }; // --- PDF Generation --- const downloadPdf = () => { if (scriptElements.length === 0) { alert("Your script is empty."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'a4'); doc.setFont('Courier'); const pageHeight = doc.internal.pageSize.height; const margin = 50; const maxWidth = doc.internal.pageSize.width - (margin * 2); const lineHeight = 14; let currentY = margin; const checkPageBreak = (spaceNeeded) => { if (currentY + spaceNeeded > pageHeight - margin) { doc.addPage(); currentY = margin; } }; scriptElements.forEach(item => { const content = item.content.toString(); let lines = []; switch(item.type) { case 'Page': checkPageBreak(lineHeight * 3); currentY += 15; // Extra space before page doc.setFont('Courier', 'bold'); doc.setFontSize(12); lines = doc.splitTextToSize(`PAGE ${content}`.toUpperCase(), maxWidth); doc.text(lines, (doc.internal.pageSize.width / 2), currentY, { align: 'center' }); currentY += (lines.length * lineHeight) + 15; break; case 'Panel': checkPageBreak(lineHeight * 2); doc.setFont('Courier', 'bold'); doc.setFontSize(11); lines = doc.splitTextToSize(`PANEL ${content}`.toUpperCase(), maxWidth - 20); doc.text(lines, margin + 20, currentY); currentY += (lines.length * lineHeight) + 5; break; case 'Description': checkPageBreak(lineHeight); doc.setFont('Courier', 'normal'); doc.setFontSize(11); lines = doc.splitTextToSize(content, maxWidth - 40); doc.text(lines, margin + 40, currentY); currentY += (lines.length * lineHeight) + 5; break; case 'Character': checkPageBreak(lineHeight * 2); doc.setFont('Courier', 'bold'); doc.setFontSize(11); lines = doc.splitTextToSize(content.toUpperCase(), maxWidth - 120); doc.text(lines, margin + 120, currentY); currentY += (lines.length * lineHeight); break; case 'Dialogue': checkPageBreak(lineHeight); doc.setFont('Courier', 'normal'); doc.setFontSize(11); lines = doc.splitTextToSize(content, maxWidth - 160); doc.text(lines, margin + 100, currentY); currentY += (lines.length * lineHeight) + 5; break; case 'Caption': checkPageBreak(lineHeight); doc.setFont('Courier', 'bold'); doc.setFontSize(11); lines = doc.splitTextToSize(`CAPTION: ${content}`, maxWidth - 60); doc.text(lines, margin + 60, currentY); currentY += (lines.length * lineHeight) + 5; break; case 'SFX': checkPageBreak(lineHeight); doc.setFont('Courier', 'bold'); doc.setFontSize(11); lines = doc.splitTextToSize(`SFX: ${content}`.toUpperCase(), maxWidth - 60); doc.text(lines, margin + 60, currentY); currentY += (lines.length * lineHeight) + 5; break; } }); doc.save('comic-script.pdf'); }; // --- Event Listeners --- // Editor Toolbar document.getElementById('comic-add-page-btn').addEventListener('click', () => addScriptItem('Page')); document.getElementById('comic-add-panel-btn').addEventListener('click', () => addScriptItem('Panel')); document.getElementById('comic-add-desc-btn').addEventListener('click', () => addScriptItem('Description')); document.getElementById('comic-add-dialogue-btn').addEventListener('click', () => { // Smart Button: Add Character, then Dialogue addScriptItem('Character', ''); addScriptItem('Dialogue', ''); }); document.getElementById('comic-add-caption-btn').addEventListener('click', () => addScriptItem('Caption')); document.getElementById('comic-add-sfx-btn').addEventListener('click', () => addScriptItem('SFX')); // PDF Download downloadPdfBtn.addEventListener('click', downloadPdf); // Live Updating & Auto-sizing editorArea.addEventListener('input', (e) => { if (e.target.tagName === 'TEXTAREA' || e.target.tagName === 'INPUT') { const id = e.target.closest('.comic-script-editor-item').dataset.id; updateScriptData(id, e.target.value); if (e.target.tagName === 'TEXTAREA') { autoSizeTextarea(e.target); } } }); // Drag and Drop editorArea.addEventListener('dragstart', (e) => { if (e.target.classList.contains('comic-script-editor-item')) { draggedItem = e.target; setTimeout(() => e.target.classList.add('dragging'), 0); } }); editorArea.addEventListener('dragend', () => { if (draggedItem) { draggedItem.classList.remove('dragging'); draggedItem = null; // Clear all drop indicators editorArea.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over')); // Rebuild scriptElements array from DOM order scriptElements = []; editorArea.querySelectorAll('.comic-script-editor-item').forEach(itemDiv => { const id = itemDiv.dataset.id; const type = itemDiv.dataset.type; const content = itemDiv.querySelector('input, textarea').value; scriptElements.push({ id, type, content }); }); renderPreview(); // Update preview after reorder } }); editorArea.addEventListener('dragover', (e) => { e.preventDefault(); const target = e.target.closest('.comic-script-editor-item'); if (target && target !== draggedItem) { // Clear previous indicators editorArea.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over')); // Add new indicator target.classList.add('drag-over'); // Reorder DOM editorArea.insertBefore(draggedItem, target); } }); // Mobile Toggles mobileNavEditor.addEventListener('click', () => { editorPane.style.display = 'flex'; previewPane.style.display = 'none'; mobileNavEditor.classList.add('active'); mobileNavPreview.classList.remove('active'); }); mobileNavPreview.addEventListener('click', () => { renderPreview(); // Ensure preview is up to date editorPane.style.display = 'none'; previewPane.style.display = 'flex'; mobileNavEditor.classList.remove('active'); mobileNavPreview.classList.add('active'); }); // --- Initial Setup --- // Add sample data addScriptItem('Page', '1'); addScriptItem('Panel', '1'); addScriptItem('Description', 'A sprawling, futuristic city at night. Flying cars zip between neon-lit skyscrapers. Rain streaks the window of a high-rise office.'); addScriptItem('Panel', '2'); addScriptItem('Description', 'INT. OFFICE - NIGHT. Close on DETECTIVE MILES, 50s, trench coat dripping. He looks exhausted. He\'s staring at a holographic evidence board.'); addScriptItem('Character', 'MILES (V.O.)'); addScriptItem('Dialogue', 'The rain in this city never washes anything clean. It just... moves the dirt around.'); // Render initial preview renderPreview(); });