Real Estate Blog Post Generator
Craft compelling property listings and market updates.
1. Property & Topic
2. Post Outline
3. Generated Post
Review and edit the proposed post structure.
Your generated blog post will appear here.
${generateParagraph(title)}
`; postHtml += `Beyond the primary features, residents will appreciate the subtle touches of luxury and convenience that define this exceptional property.
`; }); const conclusionTitle = outlineTitles[outlineTitles.length - 1]; postHtml += `${conclusionTitle}
`; postHtml += `To experience the allure of ${topic} firsthand, please contact us to schedule a private tour. This exclusive opportunity is available by appointment only for qualified buyers. We look forward to assisting you.
`; elements.generatedPostContainer.innerHTML = postHtml; }; // --- UTILITY FUNCTIONS --- const copyToClipboard = () => { const content = elements.generatedPostContainer.innerText; const button = elements.copyHtmlBtn; navigator.clipboard.writeText(content).then(() => { button.textContent = 'Copied!'; setTimeout(() => { button.textContent = 'Copy Post'; }, 2000); }).catch(err => { const textArea = document.createElement("textarea"); textArea.value = content; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); button.textContent = 'Copied!'; setTimeout(() => { button.textContent = 'Copy Post'; }, 2000); } catch (err) { console.error('Failed to copy text: ', err); button.textContent = 'Error'; } document.body.removeChild(textArea); }); }; const downloadPDF = () => { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const title = document.querySelector('#generatedPostContainer h1')?.innerText || "Real Estate Listing"; const contentElements = elements.generatedPostContainer.children; const PAGE_WIDTH = pdf.internal.pageSize.getWidth(); const MARGIN = 50; let y = 0; // PDF Header pdf.setFillColor(30, 41, 59); // slate-800 pdf.rect(0, 0, PAGE_WIDTH, 80, 'F'); pdf.setFont('helvetica', 'bold'); pdf.setFontSize(22); pdf.setTextColor(255, 255, 255); const titleLines = pdf.splitTextToSize(title, PAGE_WIDTH - MARGIN*2); pdf.text(titleLines, MARGIN, 50); y = 120; // Starting Y for content // Content Array.from(contentElements).forEach(el => { const text = el.innerText; let fontSize = 11; let fontStyle = 'normal'; let color = [47, 79, 79]; // Dark Slate Gray let spacing = 15; if (el.tagName === 'H1') { // H1 is already in the header, so skip it in the body return; } if (el.tagName === 'H2') { fontSize = 14; fontStyle = 'bold'; color = [202, 138, 4]; // yellow-600 spacing = 20; // Add a decorative line before H2 if (y + 30 > pdf.internal.pageSize.getHeight() - MARGIN) { pdf.addPage(); y = MARGIN; } pdf.setDrawColor(202, 138, 4); pdf.setLineWidth(0.5); pdf.line(MARGIN, y - 5, PAGE_WIDTH - MARGIN, y - 5); y += 5; } pdf.setFont('helvetica', fontStyle); pdf.setFontSize(fontSize); pdf.setTextColor(color[0], color[1], color[2]); const lines = pdf.splitTextToSize(text, PAGE_WIDTH - MARGIN*2); const textHeight = lines.length * fontSize * 1.15; if (y + textHeight > pdf.internal.pageSize.getHeight() - MARGIN) { pdf.addPage(); y = MARGIN; } pdf.text(lines, MARGIN, y); y += textHeight + spacing; }); pdf.save(`${title.replace(/ /g, '_').substring(0, 30)}.pdf`); }; // --- EVENT LISTENERS --- elements.prevBtn.addEventListener('click', () => nextPrev(-1)); elements.nextBtn.addEventListener('click', () => nextPrev(1)); elements.tabs.forEach((tab, i) => { tab.addEventListener('click', () => { console.warn("Direct tab navigation is disabled. Please use Next/Previous buttons."); }); }); elements.copyHtmlBtn.addEventListener('click', copyToClipboard); elements.downloadPdfBtn.addEventListener('click', downloadPDF); // Initialize showTab(currentTab); });