feat: 持久化 AI 审查结果,文件级内联展示与页面集成
This commit is contained in:
@@ -780,6 +780,94 @@ const DiffViewer = {
|
||||
</td>`;
|
||||
|
||||
row.parentNode.insertBefore(suggestionRow, row.nextSibling);
|
||||
},
|
||||
|
||||
// ── Insert file-level suggestion (no line number required) ─────
|
||||
|
||||
insertFileSuggestion(filename, severity, content, suggestionId) {
|
||||
const diffContent = this.container.querySelector('#diff-content');
|
||||
if (!diffContent) return;
|
||||
|
||||
// Find or index the file
|
||||
const fileEntry = this._ensureFileIndexed(filename);
|
||||
if (!fileEntry) return;
|
||||
|
||||
const wrapper = fileEntry.wrapper;
|
||||
|
||||
// Expand the file if collapsed
|
||||
const fileContent = wrapper.querySelector('.d2h-file-diff');
|
||||
if (fileContent && fileContent.style.display === 'none') {
|
||||
fileContent.style.display = 'block';
|
||||
wrapper.classList.remove('d2h-collapsed');
|
||||
}
|
||||
|
||||
// Find or create the file-level suggestions container
|
||||
let sugContainer = wrapper.querySelector('.file-suggestions-container');
|
||||
if (!sugContainer) {
|
||||
sugContainer = document.createElement('div');
|
||||
sugContainer.className = 'file-suggestions-container';
|
||||
// Insert after the file header, before the diff content
|
||||
const header = wrapper.querySelector('.d2h-file-header');
|
||||
if (header) {
|
||||
header.parentNode.insertBefore(sugContainer, header.nextSibling);
|
||||
} else {
|
||||
wrapper.prepend(sugContainer);
|
||||
}
|
||||
}
|
||||
|
||||
const severityStyles = {
|
||||
critical: 'border-l-4 border-red-500 bg-red-50',
|
||||
warning: 'border-l-4 border-yellow-500 bg-yellow-50',
|
||||
info: 'border-l-4 border-green-500 bg-green-50',
|
||||
};
|
||||
const severityLabels = {
|
||||
critical: '🔴 严重',
|
||||
warning: '🟡 建议',
|
||||
info: '🟢 提示',
|
||||
};
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = `review-file-suggestion ${severityStyles[severity] || severityStyles.info} p-3 mx-2 my-1 rounded`;
|
||||
card.setAttribute('data-suggestion-id', suggestionId || '');
|
||||
card.innerHTML = `
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="text-xs font-medium">${severityLabels[severity] || severityLabels.info}</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-700 review-content">${typeof renderMarkdown === 'function' ? renderMarkdown(content) : content}</div>
|
||||
<div class="mt-2 note-editor-placeholder" data-scope="suggestion" data-scope-key="${suggestionId || ''}"></div>`;
|
||||
|
||||
sugContainer.appendChild(card);
|
||||
},
|
||||
|
||||
// ── Mark file severity in sidebar ──────────────────────────────
|
||||
|
||||
markFileSeverity(filename, severity) {
|
||||
const treeFiles = this.container.querySelectorAll('.file-tree-file');
|
||||
const severityColors = {
|
||||
critical: 'bg-red-500',
|
||||
warning: 'bg-yellow-500',
|
||||
info: 'bg-green-500',
|
||||
};
|
||||
const severityOrder = { critical: 3, warning: 2, info: 1 };
|
||||
|
||||
for (const item of treeFiles) {
|
||||
const treeName = item.getAttribute('data-filename');
|
||||
if (treeName && (treeName === filename || treeName.endsWith('/' + filename) || filename.endsWith('/' + treeName))) {
|
||||
let badge = item.querySelector('.file-tree-severity');
|
||||
if (!badge) {
|
||||
badge = document.createElement('span');
|
||||
badge.className = 'file-tree-severity inline-block w-2 h-2 rounded-full ml-1';
|
||||
item.querySelector('.file-tree-name').appendChild(badge);
|
||||
}
|
||||
// Only upgrade severity, never downgrade
|
||||
const currentSev = badge.getAttribute('data-severity') || '';
|
||||
if (!currentSev || (severityOrder[severity] || 0) > (severityOrder[currentSev] || 0)) {
|
||||
badge.className = `file-tree-severity inline-block w-2 h-2 rounded-full ml-1 ${severityColors[severity] || severityColors.info}`;
|
||||
badge.setAttribute('data-severity', severity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user