Newsletter Writing Assistant
Craft compelling newsletters in a few simple steps.
Define Your Audience & Goal
Outline Your Content
Generate & Refine Your Content
Preview & Export
Your Subject Line Will Appear Here
Your generated newsletter content will appear here once you've generated and refined it.
Your generated newsletter content will appear here once you\'ve generated and refined it.
'; previewSubjectEl.textContent = subject; // Simple Markdown to HTML conversion let bodyHtml = bodyRaw .replace(/\*\*(.*?)\*\*/g, '$1') // Bold .replace(/^\* (.*?)$/gm, '- $1
$1
\n') // Headings .replace(/\n/g, ''); // Newlines // Fix for multiple UL tags bodyHtml = bodyHtml.replace(/<\/ul>
- /g, '');
previewBodyEl.innerHTML = bodyHtml;
};
const handleDownloadPdf = () => {
// Ensure jsPDF is loaded.
if (!window.jspdf) {
console.error('PDF generation library (jsPDF) is not available.');
// Gracefully notify user without using alert()
const downloadBtn = document.getElementById('download-pdf-btn');
if(downloadBtn) {
const originalText = downloadBtn.innerHTML;
downloadBtn.innerText = 'Error: PDF Library Missing';
setTimeout(() => { downloadBtn.innerHTML = originalText; lucide.createIcons(); }, 3000);
}
return;
}
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'pt', // Use points for better control
format: 'a4'
});
// --- Document constants and state ---
const pageW = pdf.internal.pageSize.getWidth();
const pageH = pdf.internal.pageSize.getHeight();
const margin = 40;
const maxLineWidth = pageW - margin * 2;
let cursorY = margin;
// --- Helper function for page breaks ---
const checkPageBreak = (elementHeight) => {
if (cursorY + elementHeight > pageH - margin) {
pdf.addPage();
cursorY = margin;
// Add headers to new pages for continuity
pdf.setFont('helvetica', 'italic').setFontSize(9).setTextColor(150);
pdf.text('Newsletter Content Report (continued)', margin, cursorY);
cursorY += 20;
pdf.setTextColor(0);
}
};
// --- PDF Header ---
pdf.setFont('helvetica', 'bold').setFontSize(22);
pdf.text('Newsletter Content Report', pageW / 2, cursorY, { align: 'center' });
cursorY += 20;
pdf.setFont('helvetica', 'normal').setFontSize(10);
pdf.setTextColor(100);
pdf.text(`Generated on: ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`, pageW / 2, cursorY, { align: 'center' });
cursorY += 20;
pdf.setDrawColor(220, 220, 220);
pdf.line(margin, cursorY, pageW - margin, cursorY);
cursorY += 30;
pdf.setTextColor(0);
// --- PDF Content: Subject ---
const subjectText = document.getElementById('generated-subject').value || 'No subject provided.';
pdf.setFont('helvetica', 'bold').setFontSize(16);
checkPageBreak(20);
pdf.text('Subject Line', margin, cursorY);
cursorY += 20;
pdf.setFont('helvetica', 'normal').setFontSize(12);
const subjectLines = pdf.splitTextToSize(subjectText, maxLineWidth);
checkPageBreak(subjectLines.length * 14);
pdf.text(subjectLines, margin, cursorY);
cursorY += subjectLines.length * 14 + 25; // Add extra space after
// --- PDF Content: Body ---
checkPageBreak(40); // Check space for the title
pdf.setFont('helvetica', 'bold').setFontSize(16);
pdf.text('Newsletter Body', margin, cursorY);
cursorY += 25;
const bodyRawText = document.getElementById('generated-body').value || 'No body content provided.';
const bodyLines = bodyRawText.split('\n');
bodyLines.forEach(line => {
const trimmedLine = line.trim();
if (trimmedLine.startsWith('### ')) {
const headingText = trimmedLine.substring(4);
checkPageBreak(30); // Extra space for heading
cursorY += 10;
pdf.setFont('helvetica', 'bold').setFontSize(14);
pdf.text(headingText, margin, cursorY);
cursorY += 16;
} else if (trimmedLine.startsWith('* ')) {
const itemText = trimmedLine.substring(2);
pdf.setFont('helvetica', 'normal').setFontSize(11);
const textLines = pdf.splitTextToSize(itemText, maxLineWidth - 20); // Indented line width
checkPageBreak(textLines.length * 13);
pdf.text('\u2022', margin + 10, cursorY); // Bullet point
pdf.text(textLines, margin + 25, cursorY);
cursorY += textLines.length * 13 + 3;
} else if (trimmedLine) {
pdf.setFont('helvetica', 'normal').setFontSize(11);
const paraLines = pdf.splitTextToSize(trimmedLine, maxLineWidth);
checkPageBreak(paraLines.length * 13);
pdf.text(paraLines, margin, cursorY);
cursorY += paraLines.length * 13 + 10;
} else {
checkPageBreak(10);
cursorY += 10;
}
});
// --- PDF Footer: Page Numbers ---
const pageCount = pdf.internal.getNumberOfPages();
for(let i = 1; i <= pageCount; i++) {
pdf.setPage(i);
pdf.setFont('helvetica', 'italic').setFontSize(9).setTextColor(150);
pdf.text(`Page ${i} of ${pageCount}`, pageW / 2, pageH - 20, { align: 'center' });
}
// --- Save the PDF ---
const toolName = "Newsletter";
const date = new Date().toISOString().slice(0, 10);
pdf.save(`${toolName}-Report-${date}.pdf`);
};
// --- Event Listeners ---
if (generateBtn) {
generateBtn.addEventListener('click', handleGenerateClick);
}
if (downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
}
});
