UNPKG

git-changelog-tool

Version:

A comprehensive collection of tools to generate, format, and visualize changelogs from git commit history

1,274 lines (1,085 loc) 142 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Changelog JSON Parser</title> <style> body { font-family: Arial, sans-serif; font-size: 14px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; } .container { background: white; border-radius: 8px; padding: 20px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .main-layout { display: flex; gap: 20px; align-items: flex-start; } .content-area { flex: 1; min-width: 0; } .sidebar { width: 300px; font-size: 14px; flex-shrink: 0; } h1 { color: #333; text-align: center; margin-bottom: 30px; } .file-input-section { border: 2px dashed #ccc; border-radius: 8px; font-size: 14px; padding: 40px; text-align: center; margin-bottom: 30px; transition: all 0.3s ease; position: relative; } .file-input-section.dragover { border-color: #007bff; background-color: #f8f9fa; } .file-input-section.collapsed { padding: 15px 40px; border-style: solid; background-color: #f8f9fa; } .file-input-section.collapsed .file-input-content { display: none; } .file-input-section.collapsed .file-input-collapsed { display: block; } .file-input-collapsed { display: none; font-size: 14px; color: #6c757d; } .loaded-file-item { display: flex; justify-content: space-between; align-items: center; background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px; padding: 8px 12px; margin-bottom: 5px; font-size: 0.9em; } .loaded-file-info { flex: 1; margin-right: 10px; } .loaded-file-name { font-weight: bold; color: #495057; } .loaded-file-details { font-size: 0.8em; color: #6c757d; margin-top: 2px; } .remove-file-btn { background: #dc3545; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 0.8em; flex-shrink: 0; } .remove-file-btn:hover { background: #c82333; } .collapse-toggle { position: absolute; top: 10px; right: 15px; background: none; border: none; font-size: 18px; cursor: pointer; color: #6c757d; padding: 5px; border-radius: 3px; transition: all 0.2s ease; } .collapse-toggle:hover { background-color: #e9ecef; color: #495057; } .file-input { margin: 10px; padding: 8px 16px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; } .changelog-info { background: #e9ecef; padding: 15px; border-radius: 4px; margin-bottom: 20px; } .component-section { margin-bottom: 30px; } .component-title { background: #007bff; color: white; padding: 10px 15px; border-radius: 4px 4px 0 0; font-weight: bold; font-size: 1.1em; } .component-title.type-fix { background: #dc3545; } .component-title.type-feat { background: #28a745; } .component-title.type-style { background: #6f42c1; } .component-title.type-refactor { background: #fd7e14; } .component-title.type-other { background: #6c757d; } .commits-table { width: 100%; border-collapse: collapse; border: 1px solid #ddd; border-top: none; table-layout: fixed; } .commits-table th, .commits-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; word-wrap: break-word; overflow-wrap: break-word; } /* Fixed column widths - default (component grouping) */ .commits-table th:nth-child(1), .commits-table td:nth-child(1) { width: 120px; /* Type/Component column */ } .commits-table th:nth-child(2), .commits-table td:nth-child(2) { width: auto; /* Commit message - takes remaining space */ min-width: 200px; } .commits-table th:nth-child(3), .commits-table td:nth-child(3) { width: 100px; /* Hash column */ } .commits-table th:nth-child(4), .commits-table td:nth-child(4) { width: 180px; /* Date column */ } .commits-table th:nth-child(5), .commits-table td:nth-child(5) { width: 150px; /* Author column */ } .commits-table th:nth-child(6), .commits-table td:nth-child(6) { width: 200px; /* Branches column */ } /* Column widths when grouping by type (with bugs column) */ .commits-table.type-grouping th:nth-child(1), .commits-table.type-grouping td:nth-child(1) { width: 120px; /* Component column */ } .commits-table.type-grouping th:nth-child(2), .commits-table.type-grouping td:nth-child(2) { width: 150px; /* Bugs column */ } .commits-table.type-grouping th:nth-child(3), .commits-table.type-grouping td:nth-child(3) { width: auto; /* Commit message - takes remaining space */ min-width: 200px; } .commits-table.type-grouping th:nth-child(4), .commits-table.type-grouping td:nth-child(4) { width: 100px; /* Hash column */ } .commits-table.type-grouping th:nth-child(5), .commits-table.type-grouping td:nth-child(5) { width: 180px; /* Date column */ } .commits-table.type-grouping th:nth-child(6), .commits-table.type-grouping td:nth-child(6) { width: 150px; /* Author column */ } .commits-table.type-grouping th:nth-child(7), .commits-table.type-grouping td:nth-child(7) { width: 200px; /* Branches column */ } /* Text overflow handling for specific columns - default (component grouping) */ .commits-table td:nth-child(5) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .commits-table td:nth-child(6) { white-space: nowrap; overflow: hidden; line-clamp: 2; -webkit-line-clamp: 2; text-overflow: ellipsis; } /* Commit message column styling - default */ .commits-table td:nth-child(2) { line-height: 1.4; } /* Text overflow handling when grouping by type */ .commits-table.type-grouping td:nth-child(6) { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .commits-table.type-grouping td:nth-child(7) { white-space: nowrap; overflow: hidden; line-clamp: 2; -webkit-line-clamp: 2; text-overflow: ellipsis; } /* Commit message column styling when grouping by type */ .commits-table.type-grouping td:nth-child(3) { line-height: 1.4; } /* Bugs column styling */ .commits-table.type-grouping td:nth-child(2) { line-height: 1.2; overflow: hidden; } .commits-table th { background-color: #f8f9fa; font-weight: bold; } .commits-table tr:hover { background-color: #f8f9fa; } .commit-type { padding: 4px 8px; border-radius: 4px; font-size: 0.85em; font-weight: bold; text-transform: uppercase; } .commit-type.fix { background-color: #dc3545; color: white; } .commit-type.feat { background-color: #28a745; color: white; } .commit-type.style { background-color: #6f42c1; color: white; } .commit-type.refactor { background-color: #fd7e14; color: white; } .commit-type.other { background-color: #6c757d; color: white; } .commit-type.bugs { background-color: #e74c3c; color: white; } .bug-tag { display: inline-block; background-color: #f8f9fa; color: #495057; border: 1px solid #dee2e6; border-radius: 3px; padding: 2px 6px; font-size: 0.7em; margin-right: 4px; margin-bottom: 2px; } .editable { cursor: pointer; position: relative; } .editable:hover { background-color: #f8f9fa; border-radius: 3px; } .editable-input { width: 100%; border: 2px solid #007bff; border-radius: 3px; padding: 4px; font-size: inherit; font-family: inherit; } .editable-textarea { width: 100%; min-height: 40px; border: 2px solid #007bff; border-radius: 3px; padding: 4px; font-size: inherit; font-family: inherit; resize: vertical; } .edit-buttons { margin-top: 5px; } .edit-btn { background: #007bff; color: white; border: none; padding: 4px 8px; border-radius: 3px; cursor: pointer; font-size: 0.8em; margin-right: 5px; } .edit-btn:hover { background: #0056b3; } .edit-btn.cancel { background: #6c757d; } .edit-btn.cancel:hover { background: #545b62; } .add-bug-btn { background: #28a745; color: white; border: none; padding: 2px 6px; border-radius: 3px; cursor: pointer; font-size: 0.7em; margin-left: 5px; } .add-bug-btn:hover { background: #1e7e34; } .bug-tag.editable { background-color: #e9ecef; cursor: pointer; } .bug-tag.editable:hover { background-color: #dee2e6; } .bug-tag.readonly { background-color: #e9ecef; color: #6c757d; border-color: #ced4da; opacity: 0.7; } .additional-commit-log { margin-top: 8px; padding: 6px 8px; background-color: #e3f2fd; border-left: 3px solid #2196f3; border-radius: 3px; font-size: 0.9em; font-style: italic; } .additional-commit-log strong { color: #1976d2; font-weight: bold; } .commit-log-container { position: relative; } .commit-log-preview { max-height: 4.2em; /* Roughly 3 lines */ overflow: hidden; line-height: 1.4; position: relative; } .commit-log-preview.has-overflow::after { content: ""; position: absolute; bottom: 0; right: 0; width: 50px; height: 1.4em; background: linear-gradient(to right, transparent, white 50%); pointer-events: none; } .commit-log-full { line-height: 1.4; } .commit-log-toggle { display: inline-block; margin-top: 5px; color: #007bff; cursor: pointer; font-size: 0.9em; text-decoration: underline; user-select: none; } .commit-log-toggle:hover { color: #0056b3; } .commit-body { margin-top: 8px; padding-top: 8px; border-top: 1px solid #e9ecef; color: #6c757d; font-size: 0.95em; line-height: 1.4; } .commit-body-container { position: relative; } .commit-body-preview { max-height: 3.6em; /* Roughly 3 lines for body content */ overflow: hidden; position: relative; } .commit-body-preview.has-overflow::after { content: ""; position: absolute; bottom: 0; right: 0; width: 50px; height: 1.4em; background: linear-gradient(to right, transparent, white 50%); pointer-events: none; } .commit-body-full { /* No height restrictions for full view */ } .original-commit-log { padding: 6px 8px; background-color: #f8f9fa; border-left: 3px solid #6c757d; border-radius: 3px; font-size: 0.9em; margin-bottom: 8px; } .original-commit-log strong { color: #495057; font-weight: bold; } .remove-bug { margin-left: 4px; color: #dc3545; cursor: pointer; font-weight: bold; } .remove-bug:hover { color: #c82333; } .commit-hash { font-family: monospace; background: #f8f9fa; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .error { color: #dc3545; background: #f8d7da; padding: 10px; border-radius: 4px; margin: 10px 0; } .no-data { text-align: center; color: #6c757d; font-style: italic; padding: 40px; } .filters-section { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 20px; display: none; position: sticky; top: 20px; overflow-y: auto; } .filters-section.show { display: block; } .filters-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .filters-header h4 { margin: 0; color: #495057; } .clear-filters-btn { background: #6c757d; color: white; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-size: 0.9em; } .clear-filters-btn:hover { background: #5a6268; } .layout-section { border-bottom: 1px solid #dee2e6; padding-bottom: 15px; margin-bottom: 15px; } .layout-section h5 { margin: 0 0 10px 0; color: #495057; font-size: 0.9em; } .layout-options { display: flex; flex-direction: column; gap: 8px; } .layout-option { display: flex; align-items: center; gap: 8px; } .layout-option input[type="radio"] { margin: 0; } .layout-option label { margin: 0; font-weight: normal; cursor: pointer; font-size: 0.9em; } .search-section { border-bottom: 1px solid #dee2e6; padding-bottom: 15px; margin-bottom: 15px; } .search-section h5 { margin: 0 0 10px 0; color: #495057; font-size: 0.9em; } .search-input { width: 100%; padding: 8px 12px; border: 1px solid #ced4da; border-radius: 4px; font-size: 0.9em; box-sizing: border-box; } .search-input:focus { outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } .search-clear { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #6c757d; cursor: pointer; font-size: 1.2em; padding: 0; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; } .search-container { position: relative; } .search-highlight { background-color: yellow; padding: 1px 2px; border-radius: 2px; } .filters-grid { display: flex; flex-direction: column; gap: 20px; } .filter-group { display: flex; flex-direction: column; } .filter-group label { font-weight: bold; margin-bottom: 5px; color: #495057; } .filter-group select, .filter-group input { padding: 8px; border: 1px solid #ced4da; font-size: 0.9em; } .filter-group select[multiple] { min-height: 80px; } .filter-container { border: 1px solid #ced4da; border-radius: 4px; background: white; font-size: 0.9em; } .filter-search { width: 100%; padding: 6px 8px; border: none; border-bottom: 1px solid #e9ecef; font-size: 0.85em; box-sizing: border-box; background: #f8f9fa; } .filter-search:focus { outline: none; background: white; border-bottom-color: #007bff; } .filter-search::placeholder { color: #6c757d; font-style: italic; } .filter-items { max-height: 160px; overflow-y: auto; padding: 8px; } .filter-item { display: flex; align-items: center; padding: 4px 0; cursor: pointer; border-radius: 3px; transition: background-color 0.2s; } .filter-item:hover { background-color: #f8f9fa; } .filter-item input[type="checkbox"] { margin-right: 8px; cursor: pointer; } .filter-item label { cursor: pointer; margin: 0; flex: 1; font-size: 0.9em; word-break: break-word; } /* Special styling for files filter */ #filesFilter .filter-item label { font-family: monospace; font-size: 0.85em; word-break: break-all; } .date-range-inputs { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } .active-filters { margin-top: 15px; padding-top: 15px; border-top: 1px solid #dee2e6; } .active-filters h5 { margin: 0 0 10px 0; color: #495057; font-size: 0.9em; } .filter-tags { display: flex; flex-wrap: wrap; gap: 8px; } .filter-tag { background: #007bff; color: white; padding: 4px 8px; border-radius: 12px; font-size: 0.8em; display: flex; align-items: center; gap: 5px; } .filter-tag .remove { cursor: pointer; font-weight: bold; } .filter-tag .remove:hover { color: #ffcccb; } .results-summary { background: #e3f2fd; padding: 10px 15px; border-radius: 4px; margin-bottom: 20px; font-size: 0.9em; color: #1565c0; } /* Responsive design */ @media (max-width: 768px) { .main-layout { flex-direction: column; } .sidebar { width: 100%; order: -1; } .filters-section { position: static; max-height: none; margin-bottom: 20px; } .filters-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; } } </style> </head> <body> <div class="container"> <h1>Changelog JSON Parser</h1> <div class="file-input-section" id="dropZone"> <button class="collapse-toggle" id="collapseToggle" title="Collapse file input" style="display: none">−</button> <div class="file-input-content"> <p>Drag and drop JSON files here or select files:</p> <input type="file" id="fileInput" class="file-input" accept=".json" multiple /> <p><small>Supports multiple changelog JSON files. Commits will be merged by hash.</small></p> <!-- Loaded Files List --> <div id="loadedFilesList" style="display: none; margin-top: 15px; border-top: 1px solid #ddd; padding-top: 15px"> <h4 style="margin: 0 0 10px 0; color: #495057; font-size: 0.9em">Loaded Files:</h4> <div id="loadedFilesContainer"></div> <div style="margin-top: 10px; text-align: center"> <button id="resetDataBtn" style="background: #dc3545; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; font-size: 0.9em">Reset All Data</button> </div> </div> </div> <div class="file-input-collapsed"> <span id="loadedFileName">No file loaded</span> • <button style="background: none; border: none; color: #007bff; cursor: pointer; text-decoration: underline" onclick="document.getElementById('fileInput').click()">Load new file</button> </div> </div> <div id="error" class="error" style="display: none"></div> <div id="content" style="display: none"> <div id="changelogInfo" class="changelog-info"></div> <div class="main-layout"> <div class="content-area"> <div id="resultsSummary" class="results-summary" style="display: none"></div> <div id="componentsContainer" style="height: 100%; overflow-y: auto"></div> </div> <div class="sidebar"> <div id="filtersSection" class="filters-section"> <div class="filters-header"> <h4>Controls</h4> <div> <div style="display: flex; flex-direction: column; gap: 5px; margin-right: 10px"> <button id="exportJsonBtn" class="clear-filters-btn" style="background: #28a745">Export All</button> <button id="exportModificationsBtn" class="clear-filters-btn" style="background: #17a2b8; font-size: 0.8em">Export Modifications Only</button> </div> </div> </div> <div class="search-section"> <h5>Search:</h5> <div class="search-container"> <input type="text" id="searchInput" class="search-input" placeholder="Search commits, authors, components..." /> <button id="searchClear" class="search-clear" style="display: none" title="Clear search">×</button> </div> </div> <div class="layout-section"> <h5>Group By:</h5> <div class="layout-options"> <div class="layout-option"> <input type="radio" id="groupByComponent" name="groupBy" value="component" checked /> <label for="groupByComponent">Component</label> </div> <div class="layout-option"> <input type="radio" id="groupByType" name="groupBy" value="type" /> <label for="groupByType">Commit Type</label> </div> </div> </div> <div class="filters-grid"> <div class="filter-group"> <label>Filters:</label> </div> <div id="activeFilters" class="active-filters" style="display: none"> <div style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center"> <h5>Active Filters:</h5> <button id="clearFiltersBtn" class="clear-filters-btn">Clear All</button> </div> <div id="filterTags" class="filter-tags"></div> </div> <div class="filter-group"> <label>Date Range:</label> <div class="date-range-inputs"> <input type="date" id="dateFromFilter" placeholder="From" /> <input type="date" id="dateToFilter" placeholder="To" /> </div> </div> <div class="filter-group"> <label for="typeFilter">Commit Types:</label> <div id="typeFilter" class="filter-container"> <!-- Type checkboxes will be populated dynamically --> </div> </div> <div class="filter-group"> <label for="authorFilter">Authors:</label> <div id="authorFilter" class="filter-container"> <!-- Author checkboxes will be populated dynamically --> </div> </div> <div class="filter-group"> <label for="branchFilter">Include Branches:</label> <div id="branchFilter" class="filter-container"> <!-- Branch checkboxes will be populated dynamically --> </div> </div> <div class="filter-group"> <label for="excludeBranchFilter">Exclude Branches:</label> <div id="excludeBranchFilter" class="filter-container"> <!-- Branch checkboxes will be populated dynamically --> </div> </div> <div class="filter-group"> <label for="bugsFilter">Bug Types:</label> <div id="bugsFilter" class="filter-container"> <!-- Bug type checkboxes will be populated dynamically --> </div> </div> <div class="filter-group"> <label for="filesFilter">Source Files:</label> <div id="filesFilter" class="filter-container"> <!-- File checkboxes will be populated dynamically --> </div> </div> </div> </div> </div> </div> </div> </div> <script> class ChangelogParser { constructor() { this.fileInput = document.getElementById("fileInput"); this.dropZone = document.getElementById("dropZone"); this.errorDiv = document.getElementById("error"); this.contentDiv = document.getElementById("content"); this.changelogInfoDiv = document.getElementById("changelogInfo"); this.componentsContainer = document.getElementById("componentsContainer"); // Filter elements this.filtersSection = document.getElementById("filtersSection"); this.typeFilter = document.getElementById("typeFilter"); this.authorFilter = document.getElementById("authorFilter"); this.branchFilter = document.getElementById("branchFilter"); this.excludeBranchFilter = document.getElementById("excludeBranchFilter"); this.bugsFilter = document.getElementById("bugsFilter"); this.filesFilter = document.getElementById("filesFilter"); this.dateFromFilter = document.getElementById("dateFromFilter"); this.dateToFilter = document.getElementById("dateToFilter"); this.clearFiltersBtn = document.getElementById("clearFiltersBtn"); this.exportJsonBtn = document.getElementById("exportJsonBtn"); this.exportModificationsBtn = document.getElementById("exportModificationsBtn"); this.resetDataBtn = document.getElementById("resetDataBtn"); this.activeFiltersDiv = document.getElementById("activeFilters"); this.filterTagsDiv = document.getElementById("filterTags"); this.resultsSummaryDiv = document.getElementById("resultsSummary"); // Layout elements this.groupByComponent = document.getElementById("groupByComponent"); this.groupByType = document.getElementById("groupByType"); // Search elements this.searchInput = document.getElementById("searchInput"); this.searchClear = document.getElementById("searchClear"); // Collapse elements this.collapseToggle = document.getElementById("collapseToggle"); this.loadedFileName = document.getElementById("loadedFileName"); // Loaded files elements this.loadedFilesList = document.getElementById("loadedFilesList"); this.loadedFilesContainer = document.getElementById("loadedFilesContainer"); // Data storage this.originalData = null; this.allCommits = []; this.loadedFiles = []; this.commitMap = new Map(); // For merging commits by hash this.currentFilters = { types: [], authors: [], branches: [], excludeBranches: [], bugs: [], files: [], dateFrom: null, dateTo: null, }; this.currentGroupBy = "component"; this.currentSearchTerm = ""; this.initializeEventListeners(); } initializeEventListeners() { // File input change this.fileInput.addEventListener("change", (e) => { if (e.target.files.length > 0) { this.handleMultipleFiles(Array.from(e.target.files)); } }); // Drag and drop events this.dropZone.addEventListener("dragover", (e) => { e.preventDefault(); this.dropZone.classList.add("dragover"); }); this.dropZone.addEventListener("dragleave", (e) => { e.preventDefault(); this.dropZone.classList.remove("dragover"); }); this.dropZone.addEventListener("drop", (e) => { e.preventDefault(); this.dropZone.classList.remove("dragover"); const files = Array.from(e.dataTransfer.files); if (files.length > 0) { this.handleMultipleFiles(files); } }); // Filter event listeners are now handled in populateFilter method this.dateFromFilter.addEventListener("change", () => this.handleFilterChange()); this.dateToFilter.addEventListener("change", () => this.handleFilterChange()); this.clearFiltersBtn.addEventListener("click", () => this.clearAllFilters()); this.exportJsonBtn.addEventListener("click", () => this.exportModifiedJson()); this.exportModificationsBtn.addEventListener("click", () => this.exportModificationsOnly()); // Layout event listeners this.groupByComponent.addEventListener("change", () => this.handleLayoutChange()); this.groupByType.addEventListener("change", () => this.handleLayoutChange()); // Search event listeners this.searchInput.addEventListener("input", () => this.handleSearchChange()); this.searchInput.addEventListener("keyup", (e) => { if (e.key === "Escape") { this.clearSearch(); } }); this.searchClear.addEventListener("click", () => this.clearSearch()); // Collapse event listener this.collapseToggle.addEventListener("click", () => this.toggleFileInputCollapse()); } handleMultipleFiles(files) { // Don't reset data - add to existing data instead // this.loadedFiles = []; // Keep existing files // this.commitMap.clear(); // Keep existing commits // this.allCommits = []; // Keep existing commits // Filter for JSON files only const jsonFiles = files.filter((file) => file.name.toLowerCase().endsWith(".json")); if (jsonFiles.length === 0) { this.showError("Please select at least one JSON file."); return; } if (jsonFiles.length !== files.length) { console.warn(`${files.length - jsonFiles.length} non-JSON files were ignored.`); } this.processFiles(jsonFiles); } async processFiles(files) { let processedCount = 0; let modificationsCount = 0; let hasErrors = false; const errors = []; for (const file of files) { try { const fileData = await this.readFile(file); const jsonData = JSON.parse(fileData); if (jsonData.type === "changelog_modifications") { // This is a modifications file this.storeModificationsFile(jsonData, file.name); modificationsCount++; } else { // This is a regular changelog file this.mergeFileData(jsonData, file.name); processedCount++; } } catch (error) { hasErrors = true; errors.push(`${file.name}: ${error.message}`); } } // Allow loading of modifications files even without changelog files if (processedCount === 0 && modificationsCount === 0) { this.showError(`No valid files could be processed:\n${errors.join("\n")}`); return; } if (hasErrors) { console.warn("Some files had errors:", errors); } // Only finalize data if we have actual changelog data if (processedCount > 0) { this.finalizeData(); } else if (modificationsCount > 0) { // Check if we already have changelog data and just added modifications if (this.allCommits && this.allCommits.length > 0) { // We already have data, just update the loaded files list this.updateLoadedFilesList(); } else { // Only modifications loaded - update the UI to show pending modifications this.updateLoadedFilesList(); this.showPendingModificationsInfo(); } } // Show loading feedback const loadingMsg = document.createElement("div"); loadingMsg.style.cssText = ` position: fixed; top: 10px; left: 50%; transform: translateX(-50%); background: #28a745; color: white; padding: 10px 20px; border-radius: 4px; z-index: 1001; font-size: 0.9em; `; if (processedCount > 0 && modificationsCount > 0) { loadingMsg.textContent = `✅ Added ${processedCount} changelog files + ${modificationsCount} modification files (auto-applying...)`; } else if (processedCount > 0) { const hasPending = this.pendingModifications && this.pendingModifications.modifications && this.pendingModifications.modifications.length > 0; if (hasPending) { loadingMsg.textContent = `✅ Added ${processedCount} changelog files (auto-applying pending modifications...)`; } else { loadingMsg.textContent = `✅ Added ${processedCount} changelog files`; } } else if (modificationsCount > 0) { // Check if we already have changelog data loaded if (this.allCommits && this.allCommits.length > 0) { loadingMsg.textContent = `✅ Added ${modificationsCount} modification files (auto-applying...)`; } else { loadingMsg.textContent = `✅ Added ${modificationsCount} modification files (waiting for changelog data)`; } } if (processedCount > 0 || modificationsCount > 0) {