Release Notes Generator

Release Notes Generator

Release Notes Generator

Generate clean, professional release notes from your feature and bug lists.

Your Release Notes

Summary

Build Your Release

Add Items

Current Release Items

Configuration

Manage the categories for your release note items. These will appear in the dropdown when adding items and as headers in the final notes.

Manage Categories

${draft.date}

`; const grouped = db.categories.reduce((acc, cat) => { const items = draft.items.filter(item => item.category === cat); if (items.length > 0) { acc[cat] = items; } return acc; }, {}); for (const category in grouped) { html += `

${category}

    `; grouped[category].forEach(item => { html += `
  • ${item.text}
  • `; }); html += `
`; } pdfContent.innerHTML = html; pdfDownloadBtn.style.display = 'inline-block'; copyBtn.style.display = 'inline-block'; } function renderSummaryChart() { if (!summaryChartCanvas) return; const counts = db.categories.reduce((acc, cat) => { acc[cat] = 0; return acc; }, {}); draft.items.forEach(item => { if (counts.hasOwnProperty(item.category)) { counts[item.category]++; } }); if (summaryChartInstance) { summaryChartInstance.destroy(); } summaryChartInstance = new Chart(summaryChartCanvas.getContext('2d'), { type: 'doughnut', data: { labels: Object.keys(counts), datasets: [{ label: 'Item Count', data: Object.values(counts), backgroundColor: ['#3b82f6', '#10b981', '#ef4444', '#f59e0b', '#6366f1', '#8b5cf6'], borderColor: '#ffffff', borderWidth: 3 }] }, options: { responsive: true, maintainAspectRatio: false, // Per spec plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: (context) => `${context.label}: ${context.raw} items` } } } } }); } // --- Utility Functions --- function showMessageModal(message) { // A non-blocking alternative to alert() let modal = document.getElementById('rng-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'rng-modal'; modal.className = 'rng-pdf-hide fixed inset-0 bg-gray-800 bg-opacity-50 flex items-center justify-center p-4'; modal.style.zIndex = '1000'; modal.innerHTML = `

Notification

`; document.body.appendChild(modal); modal.querySelector('#rng-modal-close').addEventListener('click', () => { modal.style.display = 'none'; }); } modal.querySelector('#rng-modal-message').textContent = message; modal.style.display = 'flex'; } async function downloadPDF() { // This function is designed to be robust (Spec 9) pdfTarget.classList.add('rng-pdf-view'); try { const canvas = await html2canvas(pdfTarget, { scale: 2, logging: false, useCORS: true, onclone: (doc) => { // Copy the live chart canvas to the cloned document const originalCanvas = summaryChartCanvas; const clonedCanvas = doc.getElementById('rng-summary-chart'); if (clonedCanvas) { clonedCanvas.getContext('2d').drawImage(originalCanvas, 0, 0); } } }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF('p', 'mm', 'a4'); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = (canvas.height * pdfWidth) / canvas.width; const pageMargin = 15; const contentWidth = pdfWidth - (pageMargin * 2); const contentHeight = (canvas.height * contentWidth) / canvas.width; pdf.addImage(imgData, 'PNG', pageMargin, pageMargin, contentWidth, contentHeight); pdf.save(`Release_Notes_${draft.version || 'v1.0'}.pdf`); } catch (error) { console.error("Error generating PDF:", error); showMessageModal("An error occurred while generating the PDF. Please try again."); } finally { pdfTarget.classList.remove('rng-pdf-view'); } } function copyToClipboard() { // Use document.execCommand for iframe compatibility (Spec II.C.4) const el = document.createElement('textarea'); let text = `Release Notes: ${draft.version} (${draft.date})\n\n`; const grouped = db.categories.reduce((acc, cat) => { const items = draft.items.filter(item => item.category === cat); if (items.length > 0) acc[cat] = items; return acc; }, {}); for (const category in grouped) { text += `--- ${category} ---\n`; grouped[category].forEach(item => { text += `• ${item.text}\n`; }); text += `\n`; } el.value = text; document.body.appendChild(el); el.select(); try { document.execCommand('copy'); showMessageModal('Release notes copied to clipboard!'); } catch (err) { showMessageModal('Failed to copy text. Please try again.'); } document.body.removeChild(el); } function renderAll() { renderConfig(); populateCategoryDropdowns(); renderDraftList(); } function loadSampleData() { db.categories = ["🚀 New Features", "🐛 Bug Fixes", "✨ Improvements", "🧹 Maintenance"]; draft.items = [ { id: 1, text: "Added user profile avatars to the dashboard.", category: "🚀 New Features" }, { id: 2, text: "Fixed a bug where users in California (USA) could not log in.", category: "🐛 Bug Fixes" }, { id: 3, text: "Improved database query speed for large reports.", category: "✨ Improvements" } ]; draft.version = "v1.0.0-sample"; draft.date = "2025-10-27"; versionInput.value = draft.version; dateInput.value = draft.date; renderAll(); } // --- 4. EVENT BINDING & INITIALIZATION --- navTabs.forEach((tab, index) => { tab.addEventListener('click', () => showTab(index)); }); addCategoryForm.addEventListener('submit', handleAddCategory); addItemForm.addEventListener('submit', handleAddItem); generateBtn.addEventListener('click', handleGenerateNotes); pdfDownloadBtn.addEventListener('click', downloadPDF); copyBtn.addEventListener('click', copyToClipboard); // Set default date dateInput.value = new Date().toISOString().split('T')[0]; // Initial Load loadSampleData(); showTab(1); // Start on the build tab });
Scroll to Top