Fanfiction Idea Generator

Fanfiction Idea Generator

Fanfiction Idea Generator

Generate complex plot ideas based on chosen characters, fandom, and tropes.

Your customized plot idea is outlined below, featuring a logline and three major story beats. Generate a new one anytime!

Generated Story Outline

Generated: ${date} | Fandom: ${fgig_escapeHTML(fgig_data.fandom)}

${document.getElementById('fgig-results-output').innerHTML}
`; } /** * Generates and downloads a PDF of the synopsis */ async function fgig_downloadPDF() { if (!fgig_data.generatedLogline) { alert("Please generate a plot idea first before downloading."); return; } if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { alert("Error: PDF libraries failed to load."); return; } fgig_renderPdfClone(); const { jsPDF } = window.jspdf; try { const contentDiv = fgig_pdfRenderClone.querySelector('.pdf-content'); if (!contentDiv) return; const canvas = await html2canvas(contentDiv, { scale: 1.5, useCORS: true }); const imgData = canvas.toDataURL('image/png'); const imgProps = { width: canvas.width, height: canvas.height }; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const margin = 40; const contentWidth = pdfWidth - (margin * 2); const contentHeight = (contentWidth * imgProps.height) / imgProps.width; let heightLeft = contentHeight; let position = 0; pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); while (heightLeft > 0) { position -= (pdfHeight - margin * 2); pdf.addPage(); pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight); heightLeft -= (pdfHeight - margin * 2); } const safeName = (fgig_data.fandom + '_' + fgig_data.trope1).replace(/[^a-z0-9]/gi, '_').toLowerCase(); pdf.save(`${safeName}_Plot_Idea.pdf`); } catch (error) { console.error("PDF generation failed:", error); alert("An error occurred while generating the PDF."); } } // --- EVENT LISTENERS --- // Tab link clicks fgig_tabLinks.forEach((link, index) => { link.addEventListener('click', () => fgig_switchTab(index)); }); // Next/Prev button clicks if (fgig_prevButton) { fgig_prevButton.addEventListener('click', () => { if (fgig_currentTab > 0) fgig_switchTab(fgig_currentTab - 1); }); } if (fgig_nextButton) { fgig_nextButton.addEventListener('click', () => { if (fgig_currentTab === fgig_tabLinks.length - 1) { fgig_updateDataFromConfig(); fgig_switchTab(0); } else { if (fgig_currentTab < fgig_tabLinks.length - 1) fgig_switchTab(fgig_currentTab + 1); } }); } // PDF download if (fgig_downloadPdfButton) { fgig_downloadPdfButton.addEventListener('click', fgig_downloadPDF); } // Generation trigger if (fgig_generateButton) { fgig_generateButton.addEventListener('click', () => { fgig_updateDataFromConfig(); fgig_generatePlot(fgig_data); fgig_renderDashboard(true); }); } // --- INITIALIZATION --- fgig_renderConfig(); fgig_renderDashboard(); // Set initial tab state fgig_tabPanes.forEach((pane, index) => { pane.classList.toggle('hidden', index !== 0); pane.classList.toggle('fgig-active', index === 0); }); fgig_tabLinks.forEach((link, index) => { TAB_CLASSES.active.forEach(cls => link.classList.remove(cls)); TAB_CLASSES.inactive.forEach(cls => link.classList.remove(cls)); if (index === 0) { TAB_CLASSES.active.forEach(cls => link.classList.add(cls)); } else { TAB_CLASSES.inactive.forEach(cls => link.classList.add(cls)); } }); });
Scroll to Top