import { escapeHtml } from '../utils/helpers.js';
export class ResultsManager {
constructor() {
this.currentResults = [];
this.expandedItems = new Set();
}
renderResults(data, config) {
const container = document.getElementById('results');
this.currentResults = data.matches || [];
window.splitViewManager.originalResults = data;
if (data.error) {
container.innerHTML = `<div class="error">${escapeHtml(data.error)}</div>`;
return;
}
let warningsHtml = '';
if (data.warnings && data.warnings.length > 0) {
warningsHtml = `
<div class="warning-banner">
<strong>Warning:</strong>
<ul>
${data.warnings.map(w => `<li>${escapeHtml(w)}</li>`).join('')}
</ul>
</div>
`;
}
const filteredMatches = this.currentResults.filter(match =>
match.score.composite >= config.scoreThreshold
);
let html = warningsHtml + `
<div class="stats">
<div>Contigs: <span>${data.query.contig_count}</span></div>
<div>MD5 Coverage: <span>${(data.query.md5_coverage * 100).toFixed(0)}%</span></div>
<div>Naming: <span>${escapeHtml(data.query.naming_convention)}</span></div>
<div>Matches: <span>${filteredMatches.length}</span></div>
</div>
`;
if (filteredMatches.length === 0) {
html += '<div class="error">No matches found above the score threshold</div>';
} else {
html += this.renderExpandableTable(filteredMatches);
}
container.innerHTML = html;
}
renderExpandableTable(matches) {
let html = `
<div class="results-summary">
<div class="expand-controls">
<h3 style="color: var(--accent); margin: 0;">Results Table</h3>
<button class="expand-all-btn" onclick="resultsManager.toggleExpandAll()">
<span id="expand-all-text">Expand All</span>
</button>
</div>
<table class="summary-table">
<thead>
<tr>
<th>Reference</th>
<th>Assembly</th>
<th>Source</th>
<th>Score</th>
<th>Confidence</th>
<th>Match Type</th>
</tr>
</thead>
<tbody>
`;
for (let i = 0; i < matches.length; i++) {
const match = matches[i];
const confidence = match.score.confidence.toLowerCase();
const isExpanded = this.expandedItems.has(i);
html += `
<tr onclick="resultsManager.toggleExpansion(${i})" data-result-index="${i}" class="${isExpanded ? 'selected' : ''}">
<td><strong>${escapeHtml(match.reference.display_name)}</strong></td>
<td>${escapeHtml(match.reference.assembly)}</td>
<td>${escapeHtml(match.reference.source)}</td>
<td>${(match.score.composite * 100).toFixed(1)}%</td>
<td><span class="confidence-badge confidence-${escapeHtml(confidence)}">${escapeHtml(match.score.confidence)}</span></td>
<td>${escapeHtml(match.match_type)}</td>
</tr>
`;
html += `
<tr class="expandable-row ${isExpanded ? 'expanded' : ''}" id="expandable-${i}">
<td colspan="6">
<div class="expandable-content">
<div class="result-meta">
<div class="meta-item">Assembly: <span>${escapeHtml(match.reference.assembly)}</span></div>
<div class="meta-item">Source: <span>${escapeHtml(match.reference.source)}</span></div>
<div class="meta-item">Match Type: <span>${escapeHtml(match.match_type)}</span></div>
<div class="meta-item">Score: <span>${(match.score.composite * 100).toFixed(1)}%</span></div>
<div class="meta-item">Exact Matches: <span>${match.exact_matches}</span></div>
<div class="meta-item">Renamed: <span>${match.renamed_matches}</span></div>
${match.conflicts > 0 ? `<div class="meta-item" style="color: var(--error)">Conflicts: <span>${match.conflicts}</span></div>` : ''}
${match.reordered ? `<div class="meta-item" style="color: var(--warning)">Reordered: <span>Yes</span></div>` : ''}
</div>
${this.renderSuggestions(match.suggestions)}
<div style="margin-top: 1rem;">
<button class="compare-button" onclick="splitViewManager.openComparison(${i})" style="
background: var(--success);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.2s ease;
" onmouseover="this.style.background='#27ae60'" onmouseout="this.style.background='var(--success)'">
Compare Contigs
</button>
</div>
${match.reference.download_url ? `<div style="margin-top: 1rem; font-size: 0.85rem;"><a href="${escapeHtml(match.reference.download_url)}" target="_blank" style="color: var(--accent);">Download Reference</a></div>` : ''}
</div>
</td>
</tr>
`;
}
html += '</tbody></table></div>';
return html;
}
renderSuggestions(suggestions) {
if (!suggestions || suggestions.length === 0) return '';
let html = '<div class="suggestions"><h4>Suggestions</h4>';
for (const s of suggestions) {
switch (s.type) {
case 'rename':
html += `<p><strong>Rename contigs:</strong></p><div class="suggestion-code">${escapeHtml(s.command)}</div>`;
break;
case 'reorder':
html += `<p><strong>Reorder contigs:</strong></p><div class="suggestion-code">${escapeHtml(s.command)}</div>`;
break;
case 'replace':
html += `<p><strong>Replace ${escapeHtml(s.contig)}:</strong> ${escapeHtml(s.reason)}</p>`;
break;
case 'use_as_is':
html += `<p style="color: var(--success)"><strong>Safe to use as-is</strong></p>`;
break;
case 'realign':
html += `<p style="color: var(--error)"><strong>Realignment needed:</strong> ${escapeHtml(s.reason)}</p>`;
break;
}
}
html += '</div>';
return html;
}
toggleExpansion(index) {
if (this.expandedItems.has(index)) {
this.expandedItems.delete(index);
} else {
this.expandedItems.add(index);
}
const expandableRow = document.getElementById(`expandable-${index}`);
const summaryRow = document.querySelector(`tr[data-result-index="${index}"]`);
if (this.expandedItems.has(index)) {
expandableRow?.classList.add('expanded');
summaryRow?.classList.add('selected');
} else {
expandableRow?.classList.remove('expanded');
summaryRow?.classList.remove('selected');
}
this.updateExpandAllButton();
}
toggleExpandAll() {
const allExpanded = this.expandedItems.size === this.currentResults.length;
if (allExpanded) {
this.expandedItems.clear();
document.querySelectorAll('.expandable-row').forEach(row => {
row.classList.remove('expanded');
});
document.querySelectorAll('tr[data-result-index]').forEach(row => {
row.classList.remove('selected');
});
} else {
for (let i = 0; i < this.currentResults.length; i++) {
this.expandedItems.add(i);
}
document.querySelectorAll('.expandable-row').forEach(row => {
row.classList.add('expanded');
});
document.querySelectorAll('tr[data-result-index]').forEach(row => {
row.classList.add('selected');
});
}
this.updateExpandAllButton();
}
updateExpandAllButton() {
const expandAllText = document.getElementById('expand-all-text');
if (!expandAllText) return;
const allExpanded = this.expandedItems.size === this.currentResults.length;
expandAllText.textContent = allExpanded ? 'Collapse All' : 'Expand All';
}
}
export class HelpSystem {
constructor() {
this.isExpanded = false;
this.activeTab = 'overview';
}
toggle() {
this.isExpanded = !this.isExpanded;
const helpContent = document.getElementById('help-content');
const helpToggle = document.querySelector('.help-toggle');
const arrow = document.getElementById('help-arrow');
if (this.isExpanded) {
helpContent.classList.add('expanded');
helpToggle.classList.add('expanded');
arrow.textContent = '▲';
} else {
helpContent.classList.remove('expanded');
helpToggle.classList.remove('expanded');
arrow.textContent = '▼';
}
}
showTab(tabId) {
document.querySelectorAll('.help-tab').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.help-tab-content').forEach(content => {
content.classList.remove('active');
});
event.target.classList.add('active');
document.getElementById('help-' + tabId).classList.add('active');
this.activeTab = tabId;
}
}