function initSidebar() {
document.querySelectorAll('.browse-tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabId = tab.dataset.tab;
switchBrowseTab(tabId);
});
});
updateStats();
}
function updateStats() {
const statErrors = document.getElementById('statErrors');
const statComponents = document.getElementById('statComponents');
const statDeprecated = document.getElementById('statDeprecated');
if (statErrors) {
statErrors.textContent = AppState.errors.length;
}
if (statComponents) {
statComponents.textContent = Object.keys(AppState.components || {}).length;
}
if (statDeprecated) {
const deprecatedCount = AppState.errors.filter(e => e.deprecated).length;
statDeprecated.textContent = deprecatedCount;
}
}
function switchBrowseTab(tabId) {
document.querySelectorAll('.browse-tab').forEach(tab => {
tab.classList.toggle('active', tab.dataset.tab === tabId);
});
AppState.activeCategory = null;
AppState.activeCategoryType = null;
hideDetail();
filterErrors();
renderResults();
buildBrowseList(tabId);
}
function updateSidebar() {
const activeTab = document.querySelector('.browse-tab.active');
buildBrowseList(activeTab?.dataset.tab || 'components');
updateStats();
}
function buildBrowseList(tabType = 'components') {
if (!DOM.browseList) return;
let items = [];
switch (tabType) {
case 'components':
items = buildComponentList();
break;
case 'primaries':
items = buildPrimaryList();
break;
case 'sequences':
items = buildSequenceList();
break;
default:
items = buildComponentList();
}
if (items.length === 0) {
DOM.browseList.innerHTML = '<div class="browse-empty">No items to display</div>';
return;
}
DOM.browseList.innerHTML = items.map(item => `
<div class="browse-item ${AppState.activeCategory === item.id ? 'active' : ''}"
data-type="${tabType}" data-name="${escapeHtml(item.id)}">
<span class="browse-item-name">${escapeHtml(item.displayName || item.name)}</span>
<span class="browse-item-count">${item.count}</span>
</div>
`).join('');
DOM.browseList.querySelectorAll('.browse-item').forEach(item => {
item.addEventListener('click', () => {
const id = item.dataset.name;
const type = item.dataset.type;
if (AppState.activeCategory === id && AppState.activeCategoryType === type) {
AppState.activeCategory = null;
AppState.activeCategoryType = null;
hideDetail();
} else {
AppState.activeCategory = id;
AppState.activeCategoryType = type; showComponentDetail(id, type);
}
filterErrors();
renderResults();
DOM.browseList.querySelectorAll('.browse-item').forEach(i => {
i.classList.toggle('active', i.dataset.name === AppState.activeCategory);
});
});
});
}
function buildComponentList() {
const components = new Map();
AppState.filteredErrors.forEach(error => {
const comp = error.component || getCategoryFromCode(error.code);
if (!components.has(comp)) {
components.set(comp, { id: comp, name: comp, count: 0 });
}
components.get(comp).count++;
});
Object.entries(AppState.components || {}).forEach(([id, meta]) => {
if (components.has(id)) {
components.get(id).name = meta.name || id;
}
});
return Array.from(components.values()).sort((a, b) => b.count - a.count);
}
function buildPrimaryList() {
const primaries = new Map();
AppState.filteredErrors.forEach(error => {
const prim = error.primary;
if (!prim) return;
if (!primaries.has(prim)) {
primaries.set(prim, { id: prim, name: prim, count: 0 });
}
primaries.get(prim).count++;
});
Object.entries(AppState.primaries || {}).forEach(([id, meta]) => {
if (primaries.has(id)) {
primaries.get(id).name = meta.name || id;
}
});
return Array.from(primaries.values()).sort((a, b) => b.count - a.count);
}
function buildSequenceList() {
const sequences = new Map();
AppState.filteredErrors.forEach(error => {
const seq = error.sequence;
if (seq === undefined || seq === null) return;
const seqKey = String(seq);
if (!sequences.has(seqKey)) {
const paddedNum = String(seq).padStart(3, '0');
sequences.set(seqKey, { id: seqKey, num: paddedNum, name: null, count: 0 });
}
sequences.get(seqKey).count++;
});
Object.entries(AppState.sequences || {}).forEach(([id, meta]) => {
if (sequences.has(id)) {
sequences.get(id).name = meta.name || null;
}
});
const result = Array.from(sequences.values()).map(item => ({
...item,
displayName: item.name ? `${item.num} - ${item.name}` : item.num
}));
return result.sort((a, b) => b.count - a.count);
}
const SEVERITY_INFO = {
'E': { name: 'Error', emoji: '❌', priority: 8, blocking: true, tone: 'Negative', description: 'Invalid input, logic errors, or failures that prevent an operation from completing.' },
'B': { name: 'Blocked', emoji: '🚫', priority: 7, blocking: true, tone: 'Negative', description: 'Operation blocked by an external condition (deadlock, I/O wait, resource unavailable).' },
'C': { name: 'Critical', emoji: '🔥', priority: 6, blocking: false, tone: 'Negative', description: 'Severe conditions requiring immediate attention (data corruption, resource exhaustion). Execution may continue in degraded mode.' },
'W': { name: 'Warning', emoji: '⚠️', priority: 5, blocking: false, tone: 'Negative', description: 'Potentially problematic situations that do not prevent operation (deprecated API, edge cases).' },
'H': { name: 'Help', emoji: '💡', priority: 4, blocking: false, tone: 'Neutral', description: 'Helpful suggestions, tips, or guidance for users or developers.' },
'S': { name: 'Success', emoji: '✅', priority: 3, blocking: false, tone: 'Positive', description: 'Operation completed successfully. Explicit positive feedback.' },
'K': { name: 'Completed', emoji: '✔️', priority: 2, blocking: false, tone: 'Positive', description: 'Long-running task or phase has finished (builds, migrations, batch jobs).' },
'I': { name: 'Info', emoji: 'ℹ️', priority: 1, blocking: false, tone: 'Neutral', description: 'Informational events, milestones, or status updates.' },
'T': { name: 'Trace', emoji: '🔍', priority: 0, blocking: false, tone: 'Neutral', description: 'Detailed execution traces, debugging information, profiling, and performance measurements.' }
};
function hideDetail() {
const detailSection = document.getElementById('detailSection');
if (detailSection) {
detailSection.style.display = 'none';
}
}
function showSeverityDetail(severityChar) {
const detailSection = document.getElementById('detailSection');
const detailPanel = document.getElementById('detailPanel');
if (!detailSection || !detailPanel) return;
const info = SEVERITY_INFO[severityChar] || {
name: severityChar,
emoji: '❓',
priority: '?',
blocking: false,
tone: 'Unknown',
description: 'Unknown severity level.'
};
const errors = AppState.filteredErrors.filter(e => {
const sev = e.code.split('.')[0];
return sev === severityChar;
});
AppState.activeCategory = null;
document.querySelectorAll('.browse-item').forEach(i => i.classList.remove('active'));
const toneClass = info.tone === 'Negative' ? 'tone-negative' :
info.tone === 'Positive' ? 'tone-positive' : 'tone-neutral';
detailPanel.innerHTML = `
<div class="detail-back" onclick="hideDetail();">← Back</div>
<div class="detail-type">Severity</div>
<div class="detail-title severity">
<span class="severity-emoji">${info.emoji}</span>
<span>${severityChar} - ${escapeHtml(info.name)}</span>
</div>
<div class="detail-description">${escapeHtml(info.description)}</div>
<div class="severity-meta">
<span class="severity-meta-item">
<span class="meta-label">Priority:</span>
<span class="meta-value">${info.priority}</span>
</span>
<span class="severity-meta-item">
<span class="meta-label">Blocking:</span>
<span class="meta-value ${info.blocking ? 'blocking-yes' : 'blocking-no'}">${info.blocking ? 'Yes' : 'No'}</span>
</span>
<span class="severity-meta-item">
<span class="meta-label">Tone:</span>
<span class="meta-value ${toneClass}">${info.tone}</span>
</span>
</div>
<div class="detail-section">
<div class="detail-section-title">Error Codes (${errors.length})</div>
<div class="detail-errors">
${errors.slice(0, 20).map(e => `
<span class="detail-error-link" onclick="searchFor('${e.code}')">${escapeHtml(e.code)}</span>
`).join('')}
${errors.length > 20 ? `<span class="detail-more">+${errors.length - 20} more</span>` : ''}
</div>
</div>
`;
detailSection.style.display = 'block';
}
function showComponentDetail(id, type) {
const detailSection = document.getElementById('detailSection');
const detailPanel = document.getElementById('detailPanel');
if (!detailSection || !detailPanel) return;
let meta = {};
let errors = [];
let typeLabel = 'Component';
let titleClass = 'component';
let displayTitle = id;
if (type === 'components') {
meta = AppState.components?.[id] || {};
typeLabel = 'Component';
titleClass = 'component';
errors = AppState.filteredErrors.filter(e =>
(e.component || getCategoryFromCode(e.code)) === id
);
} else if (type === 'primaries') {
meta = AppState.primaries?.[id] || {};
typeLabel = 'Primary';
titleClass = 'primary';
errors = AppState.filteredErrors.filter(e => e.primary === id);
} else if (type === 'sequences') {
meta = AppState.sequences?.[id] || {};
typeLabel = 'Sequence';
titleClass = 'sequence';
errors = AppState.filteredErrors.filter(e => e.sequence == id);
const paddedNum = String(id).padStart(3, '0');
const seqName = meta.name || null;
displayTitle = seqName ? `${paddedNum} - ${seqName}` : paddedNum;
}
const description = meta.description || '';
const examples = meta.examples || [];
const locations = meta.locations || [];
detailPanel.innerHTML = `
<div class="detail-back" onclick="hideDetail(); AppState.activeCategory = null; updateSidebar();">\u2190 Back</div>
<div class="detail-type">${typeLabel}</div>
<div class="detail-title ${titleClass}">${escapeHtml(displayTitle)}</div>
${description ? `<div class="detail-description">${escapeHtml(description)}</div>` : ''}
${examples.length ? `
<div class="detail-section">
<div class="detail-section-title">Examples</div>
<div class="detail-list">
${examples.map(ex => `<div class="detail-list-item">${escapeHtml(ex)}</div>`).join('')}
</div>
</div>
` : ''}
<div class="detail-section">
<div class="detail-section-title">Error Codes</div>
<div class="detail-errors">
${errors.map(e => `
<span class="detail-error-link" onclick="searchFor('${e.code}')">${escapeHtml(e.code)}</span>
`).join('')}
</div>
</div>
${locations.length ? `
<div class="detail-section">
<div class="detail-section-title">Locations</div>
<div class="detail-list">
${locations.map(loc => {
const path = typeof loc === 'string' ? loc : (loc.path || loc.file || '');
const role = typeof loc === 'object' && loc.role ? ` <span class="location-role">(${loc.role})</span>` : '';
return `<div class="detail-list-item location-item">${escapeHtml(path)}${role}</div>`;
}).join('')}
</div>
</div>
` : ''}
`;
detailSection.style.display = 'block';
}
function searchFor(code) {
if (DOM.searchInput) {
AppState.activeCategory = null;
AppState.activeTag = null;
hideDetail();
DOM.searchInput.value = code;
AppState.searchQuery = code;
filterErrors();
renderResults();
updateSidebar();
renderActiveFilters();
}
}
function selectError(code) {
AppState.selectedErrorCode = code;
const card = document.querySelector(`.error-card[data-code="${code}"]`);
if (card) {
if (!AppState.expandedCards.has(code)) {
toggleCard(code);
}
scrollToElement(card);
}
}
function scrollToElement(element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}