Political Speech Template Generator
Step 1: Define the Speech Framework
Step 2: Enter Your Core Content
Step 3: Your Generated Speech Template
My fellow citizens, friends, supporters – thank you for being here today.
`; pdfText += `Opening Remarks\n- Thank the audience (${audience || 'General Audience'}).\n\n`; // Introduction Hook speechHtml += openingHtml + "Introduction
" + pstg_placeholder(hook, "Insert compelling opening story or statistic here."); pdfText += `Introduction\n${hook || "[Insert compelling opening story or statistic here.]"}\n\n`; // Body of Speech (Varies by type) let bodyHtml = "The Core Message
"; let pdfBody = "The Core Message\n"; switch(type) { case 'concession': bodyHtml += `(Maintain a ${tone} and gracious tone)
`; bodyHtml += `A short time ago, I called my opponent to congratulate them on their victory. While this is not the result we hoped for, the people have spoken, and our democracy depends on a peaceful transition of power.
`; bodyHtml += pstg_placeholder(problem, "Briefly state the challenges the country/state still faces."); bodyHtml += `Though we may be on different teams, we are not adversaries. We are all Americans. Now is the time to come together and work for the good of our nation.
`; pdfBody += `- Congratulate opponent.\n- Acknowledge result and respect for democracy.\n- ${problem || "[State remaining challenges.]"}\n- Call for unity.\n\n`; break; default: // Default structure for rally, policy, etc. bodyHtml += `(Frame this section with a ${tone} tone)
`; bodyHtml += "The Challenge We Face
"; bodyHtml += pstg_placeholder(problem, "Describe the problem in detail, connecting it to the lives of the audience."); bodyHtml += "A New Path Forward
"; bodyHtml += pstg_placeholder(solution, "Detail your solution, explaining how it benefits the audience directly."); pdfBody += `The Challenge We Face:\n${problem || "[Describe the problem in detail]"}\n\nA New Path Forward:\n${solution || "[Detail your solution]"}\n\n`; } speechHtml += bodyHtml; pdfText += pdfBody; // Call to Action speechHtml += "Call to Action
" + pstg_placeholder(cta, "Clearly state what you want the audience to do."); pdfText += `Call to Action\n${cta || "[Clearly state what you want the audience to do.]"}\n\n`; // Closing speechHtml += "Closing
"; speechHtml += `This is our moment. This is our choice. Let's choose a brighter future, together. Thank you, God bless you, and God bless the United States of America.
`; pdfText += `Closing\n- Deliver a powerful, unifying closing statement.\n- Thank the audience.\n- God bless America.`; speechOutput.innerHTML = speechHtml; lastGeneratedText = pdfText; // Store clean text for PDF } /** * Generates and triggers a download for a PDF of the result. (Spec II.C) */ function pstg_downloadPdf() { if (!lastGeneratedText) { alert("Please generate a speech first by navigating to the final tab."); return; } if (typeof jspdf === 'undefined') { alert('Error: PDF library could not be loaded. Please refresh the page.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const audience = pstg_getValue("pstg-audience"); const typeSelect = document.getElementById("pstg-speech-type"); const speechTypeName = typeSelect ? typeSelect.options[typeSelect.selectedIndex].text : "Speech"; // PDF Content & Formatting (Spec II.C.2, II.C.3) doc.setFontSize(18); doc.setTextColor('#003366'); // Consistent color doc.text(`${speechTypeName} Template`, 105, 20, { align: 'center' }); doc.setFontSize(11); doc.setTextColor('#212529'); doc.text(`For Audience: ${audience || 'General'}`, 20, 30); // Use splitTextToSize to handle wrapping for the main content const textLines = doc.splitTextToSize(lastGeneratedText, 170); // 170mm width doc.text(textLines, 20, 45); // Save the PDF (Spec II.C.4) doc.save("Political-Speech-Template.pdf"); } // --- Tab Navigation Logic (Global Scope for onclick) --- window.pstg_openTab = function (evt, tabName) { const container = document.getElementById("pstg-container"); if (!container) return; const tabcontent = container.getElementsByClassName("pstg-tab-content"); for (let i = 0; i < tabcontent.length; i++) { tabcontent[i].style.display = "none"; tabcontent[i].classList.remove("pstg-active"); } const tablinks = container.getElementsByClassName("pstg-tab-link"); for (let i = 0; i < tablinks.length; i++) { tablinks[i].classList.remove("pstg-active"); } const activeTab = document.getElementById(tabName); if (activeTab) { activeTab.style.display = "block"; activeTab.classList.add("pstg-active"); } if (evt && evt.currentTarget) { evt.currentTarget.classList.add("pstg-active"); } // Generate speech when navigating to the final tab (Spec II.B.4) if (tabName === 'pstg-tab-3') { pstg_generateSpeech(); } }; window.pstg_navTab = function (direction) { const container = document.getElementById("pstg-container"); if (!container) return; const tabs = container.querySelectorAll(".pstg-tab-link"); let currentIndex = -1; for (let i = 0; i < tabs.length; i++) { if (tabs[i].classList.contains("pstg-active")) { currentIndex = i; break; } } if (currentIndex === -1) return; let newIndex = currentIndex + direction; if (newIndex >= tabs.length || newIndex < 0) { return; // Don't loop } // Simulate click (this will also trigger generation on tab 3) tabs[newIndex].click(); }; // --- Initializer --- // Attach listener to PDF button const pdfBtn = document.getElementById("pstg-download-pdf"); if(pdfBtn) { pdfBtn.addEventListener("click", pstg_downloadPdf); } else { console.error("Generator: PDF button not found."); } });