136 lines
4.6 KiB
JavaScript
136 lines
4.6 KiB
JavaScript
|
|
// Diff Viewer using diff2html
|
||
|
|
const DiffViewer = {
|
||
|
|
init(containerId) {
|
||
|
|
this.container = document.getElementById(containerId);
|
||
|
|
if (!this.container) {
|
||
|
|
console.error('Diff container not found:', containerId);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// Render unified diff string
|
||
|
|
renderDiff(diffString, options = {}) {
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
const config = {
|
||
|
|
drawFileList: options.drawFileList !== false,
|
||
|
|
fileListToggle: options.fileListToggle !== false,
|
||
|
|
fileListStartVisible: options.fileListStartVisible !== false,
|
||
|
|
fileContentToggle: options.fileContentToggle !== false,
|
||
|
|
matching: options.matching || 'lines',
|
||
|
|
outputFormat: options.outputFormat || 'side-by-side',
|
||
|
|
synchronisedScroll: true,
|
||
|
|
highlight: true,
|
||
|
|
renderNothingWhenEmpty: false,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Use diff2html to render
|
||
|
|
const diffHtml = Diff2Html.html(diffString, config);
|
||
|
|
this.container.innerHTML = diffHtml;
|
||
|
|
|
||
|
|
// Add syntax highlighting
|
||
|
|
this.container.querySelectorAll('pre code').forEach(block => {
|
||
|
|
if (window.hljs) {
|
||
|
|
hljs.highlightElement(block);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// Render per-file diffs
|
||
|
|
renderFiles(files, options = {}) {
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
if (!files || files.length === 0) {
|
||
|
|
this.container.innerHTML = '<div class="text-gray-500 p-4">No changes</div>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Combine all patches
|
||
|
|
const combinedDiff = files.map(f => f.patch).join('\n');
|
||
|
|
this.renderDiff(combinedDiff, options);
|
||
|
|
},
|
||
|
|
|
||
|
|
// Load and render diff from API
|
||
|
|
async loadDiff(repoId, base, head, options = {}) {
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
this.container.innerHTML = '<div class="text-center p-8"><div class="spinner inline-block"></div><p class="mt-2 text-gray-500">Loading diff...</p></div>';
|
||
|
|
|
||
|
|
try {
|
||
|
|
const url = `/api/repos/${repoId}/diff?base=${encodeURIComponent(base)}&head=${encodeURIComponent(head)}`;
|
||
|
|
const resp = await fetch(url);
|
||
|
|
if (!resp.ok) throw new Error('Failed to load diff');
|
||
|
|
|
||
|
|
const data = await resp.json();
|
||
|
|
|
||
|
|
if (data.diff) {
|
||
|
|
this.renderDiff(data.diff, options);
|
||
|
|
} else if (Array.isArray(data)) {
|
||
|
|
this.renderFiles(data, options);
|
||
|
|
}
|
||
|
|
} catch (err) {
|
||
|
|
this.container.innerHTML = `<div class="text-red-500 p-4">Error loading diff: ${err.message}</div>`;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// Load per-file diffs
|
||
|
|
async loadFiles(repoId, base, head) {
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
this.container.innerHTML = '<div class="text-center p-8"><div class="spinner inline-block"></div><p class="mt-2 text-gray-500">Loading files...</p></div>';
|
||
|
|
|
||
|
|
try {
|
||
|
|
const url = `/api/repos/${repoId}/diff?base=${encodeURIComponent(base)}&head=${encodeURIComponent(head)}&per_file=true`;
|
||
|
|
const resp = await fetch(url);
|
||
|
|
if (!resp.ok) throw new Error('Failed to load diff');
|
||
|
|
|
||
|
|
const files = await resp.json();
|
||
|
|
this.renderFiles(files);
|
||
|
|
} catch (err) {
|
||
|
|
this.container.innerHTML = `<div class="text-red-500 p-4">Error: ${err.message}</div>`;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// Switch view mode (side-by-side or line-by-line)
|
||
|
|
setViewMode(mode) {
|
||
|
|
if (!this.container) return;
|
||
|
|
|
||
|
|
const config = {
|
||
|
|
drawFileList: true,
|
||
|
|
outputFormat: mode === 'unified' ? 'line-by-line' : 'side-by-side',
|
||
|
|
};
|
||
|
|
|
||
|
|
// Re-render with existing diff content
|
||
|
|
const existingDiff = this.container.querySelector('.d2h-diff-table');
|
||
|
|
if (existingDiff) {
|
||
|
|
// Get the raw diff from the container's data
|
||
|
|
const rawDiff = this.container.dataset.rawDiff;
|
||
|
|
if (rawDiff) {
|
||
|
|
this.renderDiff(rawDiff, config);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// Toggle file list visibility
|
||
|
|
toggleFileList() {
|
||
|
|
const fileList = this.container.querySelector('.d2h-file-list');
|
||
|
|
if (fileList) {
|
||
|
|
fileList.style.display = fileList.style.display === 'none' ? 'block' : 'none';
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
// Expand/collapse all files
|
||
|
|
toggleAllFiles(expand) {
|
||
|
|
const fileContents = this.container.querySelectorAll('.d2h-file-wrapper');
|
||
|
|
fileContents.forEach(file => {
|
||
|
|
const content = file.querySelector('.d2h-file-diff');
|
||
|
|
if (content) {
|
||
|
|
content.style.display = expand ? 'block' : 'none';
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Export for use
|
||
|
|
window.DiffViewer = DiffViewer;
|