Character Backstory Generator
Craft rich, detailed histories for your fictional characters.
Character Blueprint
Your character's story awaits
Fill in the blueprint and bring your character to life.
Weaving a tale...
The Inkpot Ran Dry!
Could not generate the story. Please check the console and try again.
${data.early_life || 'N/A'}
`; case 'key_events': return `- ${data.key_events?.map(event => `
- ${event} `).join('') || '
- N/A '}
${data.motivations_and_flaws || 'N/A'}
`; case 'appearance': return `${data.appearance || 'N/A'}
`; default: return 'No content available.
'; } } // --- Tab & PDF Functions --- function showTab(index) { const tabs = tabsContainer.querySelectorAll('.tab'); const contents = tabContentsContainer.querySelectorAll('.tab-content'); tabs.forEach(tab => tab.classList.remove('active')); contents.forEach(content => content.classList.remove('active')); if(tabs[index]) tabs[index].classList.add('active'); if(contents[index]) contents[index].classList.add('active'); currentTabIndex = index; updateNavButtons(); } function navigateTabs(direction) { const newIndex = currentTabIndex + direction; if (newIndex >= 0 && newIndex < tabConfig.length) showTab(newIndex); } function updateNavButtons() { prevTabBtn.disabled = currentTabIndex === 0; nextTabBtn.disabled = currentTabIndex === tabConfig.length - 1; } function generatePdf() { if (!window.jspdf || !latestGeneratedData) { alert("Please generate a backstory before downloading."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' }); const pageHeight = doc.internal.pageSize.getHeight(); const pageWidth = doc.internal.pageSize.getWidth(); const margin = 50; const contentWidth = pageWidth - margin * 2; let cursorY = 0; let pageCount = 1; // Helper to add a footer with page number const addFooter = () => { doc.setFontSize(9); doc.setFont("times", "italic"); doc.setTextColor(150); doc.text(`Page ${pageCount}`, pageWidth / 2, pageHeight - 20, { align: 'center' }); }; // Helper to check for page breaks const checkPageBreak = (neededHeight) => { if (cursorY + neededHeight > pageHeight - margin) { addFooter(); doc.addPage(); pageCount++; cursorY = margin; addHeader(false); // Add header without the main title on new pages } }; // Helper to add headers const addHeader = (isFirstPage = true) => { doc.setFont("helvetica", "normal"); doc.setTextColor(100); doc.setFontSize(10); doc.text("CHARACTER DOSSIER: CONFIDENTIAL", margin, margin - 10); doc.setDrawColor(200); doc.line(margin, margin, pageWidth - margin, margin); cursorY = margin + 10; if(isFirstPage) { doc.setFont("times", "bold"); doc.setFontSize(26); doc.setTextColor(40, 40, 40); const titleLines = doc.splitTextToSize(latestGeneratedData.name || "Unnamed Character", contentWidth); doc.text(titleLines, margin, cursorY + 20); cursorY += (titleLines.length * 26) + 10; doc.setFont("times", "italic"); doc.setFontSize(14); doc.setTextColor(120); doc.text(charArchetypeEl.value, margin, cursorY); cursorY += 35; } }; // Helper to draw a section title const drawSectionTitle = (title) => { checkPageBreak(40); doc.setFont("helvetica", "bold"); doc.setFontSize(12); doc.setTextColor(88, 28, 135); // Purple-800 doc.text(title.toUpperCase(), margin, cursorY); cursorY += 8; doc.setDrawColor(220); doc.line(margin, cursorY, margin + 80, cursorY); cursorY += 15; }; // Helper to draw paragraph text const drawParagraph = (text) => { doc.setFont("times", "normal"); doc.setFontSize(11); doc.setTextColor(50, 50, 50); const lines = doc.splitTextToSize(text || 'N/A', contentWidth); checkPageBreak(lines.length * 12 + 10); doc.text(lines, margin, cursorY); cursorY += lines.length * 12 + 15; }; addHeader(true); // Add content sections drawSectionTitle("Summary"); drawParagraph(latestGeneratedData.summary); drawSectionTitle("Appearance"); drawParagraph(latestGeneratedData.appearance); drawSectionTitle("Early Life & Upbringing"); drawParagraph(latestGeneratedData.early_life); drawSectionTitle("Pivotal Life Events"); latestGeneratedData.key_events.forEach(event => { drawParagraph(`• ${event}`); }); drawSectionTitle("Motivations & Flaws"); drawParagraph(latestGeneratedData.motivations_and_flaws); addFooter(); const safeFileName = (latestGeneratedData.name || 'character').replace(/[^a-z0-9]/gi, '_').toLowerCase(); doc.save(`${safeFileName}_dossier.pdf`); } setUIState('placeholder'); });