Output Format:
📝 Markdown
🌐 HTML
📄 Plain Text
📊 JSON
⚙️ YAML
🚀 Generate Changelog
🧹 Clear All
✅ Validate Input
0
Total Changes
0
Features
0
Bug Fixes
0
Breaking
📝 Download .md
🌐 Download .html
📄 Download .txt
📊 Download .json
Kloudbean Zero-Ops Managed Cloud Infrastructure and Hosting
Powerful & Cost-Effective Managed Cloud Hosting for Everyone
Start Free Trial
Advanced Changelog Generator Features
This enhanced tool offers template selection, scope parsing, customization options, multiple export formats, and intelligent categorization. Perfect for professional development workflows with support for conventional commits and advanced formatting.
Template Options & Customization
Choose from multiple professionally designed templates:
Standard: Clean format with emoji icons and modern styling
Keep a Changelog: Follows the popular keepachangelog.com specification
Minimal: Simple, distraction-free text format
GitHub Style: Optimized for GitHub releases and documentation
Advanced Parsing Capabilities
Enhanced features include:
Conventional commits with scope support (feat(auth): description)
Automatic author extraction from commit messages
Breaking change detection and highlighting
Duplicate removal and intelligent categorization
Multi-format export (Markdown, HTML, JSON, YAML, Plain Text)
Professional Development Integration
Built for modern development workflows with features like validation, statistics, and multiple export formats. Seamlessly integrate with your CI/CD pipelines and documentation systems hosted on Kloudbean's reliable infrastructure.
Frequently Asked Questions
Q. What's new in the advanced version?
Template selection, scope parsing, author detection, multiple export formats, statistics, validation, and extensive customization options.
Q. How do I use scopes in commit messages?
Use the format: type(scope): description. For example: "feat(auth): add OAuth2 support" or "fix(api): resolve timeout issue".
Q. Can I export the changelog as a file?
Yes! Use the download buttons to export in Markdown, HTML, Plain Text, or JSON formats directly to your device.
Q. What does the validation feature do?
It checks your commit messages for proper conventional commit format and provides suggestions for improvement.
Deploy your professional documentation with powerful tools? Host with Kloudbean Today!
\n';
return changelog;
}
// Generate text changelog
function generateTextChangelog(data) {
let changelog = `CHANGELOG\n\n${data.version} - ${formatDate(data.date)}\n${'='.repeat(data.version.length + formatDate(data.date).length + 3)}\n\n`;
const template = templates[selectedTemplate];
Object.keys(template.sections).forEach(categoryKey => {
const items = data.categories[categoryKey];
if (!items || items.length === 0) return;
changelog += `${template.sections[categoryKey].title.toUpperCase()}:\n`;
items.forEach(item => {
const scope = item.scope ? `[${item.scope}] ` : '';
const author = item.author ? ` (by @${item.author})` : '';
changelog += ` * ${scope}${item.description}${author}\n`;
});
changelog += '\n';
});
return changelog.trim();
}
// Generate YAML changelog
function generateYAMLChangelog(data) {
const yamlData = {
version: data.version,
date: data.date,
repository: data.repoUrl || null,
totalChanges: data.totalChanges,
changes: {}
};
Object.keys(data.categories).forEach(categoryKey => {
const items = data.categories[categoryKey];
if (items && items.length > 0) {
yamlData.changes[categoryKey] = items.map(item => ({
type: item.type,
scope: item.scope,
description: item.description,
author: item.author,
breaking: item.breaking
}));
}
});
return JSON.stringify(yamlData, null, 2).replace(/"/g, '').replace(/,/g, '');
}
// Update statistics
function updateStatistics(data) {
elements.statsContainer.style.display = 'grid';
document.getElementById('total-changes').textContent = data.totalChanges;
document.getElementById('features-count').textContent = data.categories.features.length;
document.getElementById('fixes-count').textContent = data.categories.fixes.length;
document.getElementById('breaking-count').textContent = data.categories.breaking.length;
}
// Copy output
function copyOutput() {
elements.outputChangelog.select();
document.execCommand('copy');
const button = document.getElementById('copy-output');
const original = button.textContent;
button.textContent = 'Copied!';
setTimeout(() => {
button.textContent = original;
}, 1500);
}
// Preview HTML
function previewHTML() {
if (elements.outputFormat.value === 'html') {
const newWindow = window.open('', '_blank');
newWindow.document.write(elements.outputChangelog.value);
newWindow.document.close();
} else {
showStatus('HTML preview is only available when output format is set to HTML.', 'warning');
}
}
// Download file
function downloadFile(type) {
if (!changelogData) {
showStatus('Please generate a changelog first.', 'invalid');
return;
}
let content = '';
let filename = `changelog-${changelogData.version}`;
let mimeType = 'text/plain';
switch (type) {
case 'md':
content = generateMarkdownChangelog(changelogData);
filename += '.md';
mimeType = 'text/markdown';
break;
case 'html':
content = generateHTMLChangelog(changelogData);
filename += '.html';
mimeType = 'text/html';
break;
case 'txt':
content = generateTextChangelog(changelogData);
filename += '.txt';
mimeType = 'text/plain';
break;
case 'json':
content = JSON.stringify(changelogData, null, 2);
filename += '.json';
mimeType = 'application/json';
break;
}
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showStatus(`Downloaded ${filename} successfully!`, 'valid');
}
// Format date
function formatDate(dateString) {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Update line numbers
function updateLineNumbers() {
const codeInput = elements.inputCommits;
const lineNumbers = document.getElementById('line-numbers');
const lines = codeInput.value.split('\n');
const count = Math.max(lines.length, 1);
let lineNumbersContent = '';
for (let i = 1; i <= count; i++) {
lineNumbersContent += i + '\n';
}
lineNumbers.innerText = lineNumbersContent;
codeInput.style.height = 'auto';
codeInput.style.height = (codeInput.scrollHeight) + 'px';
}
// Update output line numbers
function updateOutputLineNumbers() {
const outputArea = elements.outputChangelog;
const lineNumbers = document.getElementById('output-line-numbers');
const lines = outputArea.value.split('\n');
const count = Math.max(lines.length, 1);
let lineNumbersContent = '';
for (let i = 1; i <= count; i++) {
lineNumbersContent += i + '\n';
}
lineNumbers.innerText = lineNumbersContent;
outputArea.style.height = 'auto';
outputArea.style.height = (outputArea.scrollHeight) + 'px';
}
// Sync scroll
function syncScroll() {
document.getElementById('line-numbers').scrollTop = elements.inputCommits.scrollTop;
}
// Sync output scroll
function syncOutputScroll() {
document.getElementById('output-line-numbers').scrollTop = elements.outputChangelog.scrollTop;
}
// Show status message
function showStatus(message, status) {
elements.statusMessage.textContent = message;
elements.statusMessage.className = `tool-status tool-${status}`;
elements.statusMessage.style.display = 'block';
setTimeout(() => {
elements.statusMessage.style.display = 'none';
}, 7000);
}