Cyberpunk Story Generator
Generate unique sci-fi story prompts.
Your next mission awaits. Hit 'Generate Story' to get your briefing.
Modify the data pools below. Each entry must be on a new line.
Your protagonist is: ${protagonist}.
Their mission is to: ${goal} in the sprawling, dangerous setting of ${setting}.
Standing in their way is: ${antagonist}.
The complication: ${twist}.
`; storyOutput.innerHTML = storyHtml; pdfBtn.disabled = false; }; // --- DATA MANAGEMENT --- const saveCustomLists = () => { const customLists = {}; for (const key in listTextareas) { const lines = listTextareas[key].value.split('\n').map(line => line.trim()).filter(line => line.length > 0); if (lines.length > 0) { customLists[key] = lines; } } localStorage.setItem('cyberpunkStoryLists', JSON.stringify(customLists)); loadLists(); alert('Custom lists saved!'); switchTab('dashboard'); }; const resetDefaults = () => { if (confirm('Are you sure you want to reset all lists to their default values? Your custom entries will be lost.')) { localStorage.removeItem('cyberpunkStoryLists'); loadLists(); populateTextareas(); } }; const loadLists = () => { const savedLists = localStorage.getItem('cyberpunkStoryLists'); if (savedLists) { const parsedLists = JSON.parse(savedLists); // Ensure all categories are present, falling back to default if a custom one is empty for (const key in defaultLists) { storyElements[key] = (parsedLists[key] && parsedLists[key].length > 0) ? parsedLists[key] : defaultLists[key]; } } else { storyElements = { ...defaultLists }; } }; const populateTextareas = () => { for (const key in listTextareas) { listTextareas[key].value = storyElements[key].join('\n'); } }; // --- UI/NAVIGATION --- const switchTab = (tabName) => { if (!tabs.includes(tabName)) return; currentTab = tabName; Object.values(tabContent).forEach(content => content.classList.add('hidden')); if(tabContent[tabName]) tabContent[tabName].classList.remove('hidden'); Object.values(tabButtons).forEach(button => button.classList.remove('active')); if(tabButtons[tabName]) tabButtons[tabName].classList.add('active'); if (tabName === 'config') { populateTextareas(); } updateNavButtons(); }; const updateNavButtons = () => { const currentIndex = tabs.indexOf(currentTab); prevBtn.disabled = currentIndex === 0; nextBtn.disabled = currentIndex === tabs.length - 1; }; const navigateTabs = (direction) => { const currentIndex = tabs.indexOf(currentTab); let newIndex = direction === 'next' ? Math.min(currentIndex + 1, tabs.length - 1) : Math.max(currentIndex - 1, 0); switchTab(tabs[newIndex]); }; // --- PDF GENERATION --- const generatePDF = () => { if (!currentStory) return; const { jsPDF } = window.jspdf; const doc = new jsPDF(); const pageHeight = doc.internal.pageSize.height; const pageWidth = doc.internal.pageSize.width; const margin = 15; const maxLineWidth = pageWidth - margin * 2; let y = margin; // Add a border doc.setDrawColor(229, 231, 235); // Light Gray doc.rect(5, 5, pageWidth - 10, pageHeight - 10); // Header doc.setFont('helvetica', 'bold'); doc.setFontSize(20); doc.setTextColor(37, 99, 235); // Blue doc.text('Story Prompt', pageWidth / 2, y, { align: 'center' }); y += 10; doc.setFontSize(10); doc.setTextColor(107, 114, 128); // Gray doc.text('Generated by the Cyberpunk Story Generator', pageWidth / 2, y, { align: 'center'}); y += 15; // Content const addSection = (title, text) => { doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(17, 24, 39); // Dark Gray doc.text(`${title}:`, margin, y); y += 7; doc.setFontSize(11); doc.setFont('helvetica', 'normal'); doc.setTextColor(55, 65, 81); // Medium Gray const lines = doc.splitTextToSize(text, maxLineWidth); doc.text(lines, margin, y); y += (lines.length * 5) + 10; }; addSection('PROTAGONIST', currentStory.protagonist); addSection('OBJECTIVE', currentStory.goal); addSection('SETTING', currentStory.setting); addSection('ANTAGONIST', currentStory.antagonist); addSection('COMPLICATION', currentStory.twist); // Footer doc.setDrawColor(229, 231, 235); // Light Gray doc.line(margin, pageHeight - 20, pageWidth - margin, pageHeight - 20); doc.setFontSize(8); doc.setTextColor(156, 163, 175); const today = new Date().toLocaleDateString(); doc.text(`Generated on ${today}`, pageWidth / 2, pageHeight - 15, { align: 'center' }); doc.save('Story-Prompt.pdf'); }; // --- EVENT LISTENERS & INITIALIZATION --- generateBtn.addEventListener('click', generateStory); pdfBtn.addEventListener('click', generatePDF); // --- INITIALIZE APP --- window.app = { switchTab, navigateTabs, saveCustomLists, resetDefaults, }; loadLists(); updateNavButtons(); });