function renderResults() {
if (!DOM.resultsContainer) return;
const errors = AppState.filteredErrors;
if (DOM.resultsCount) {
DOM.resultsCount.textContent = `${errors.length} ${plural(errors.length, 'error')}`;
}
if (errors.length === 0) {
DOM.resultsContainer.innerHTML = `
<div class="empty-state">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
<h3>No errors found</h3>
<p>Try adjusting your search or filter criteria</p>
</div>
`;
return;
}
DOM.resultsContainer.innerHTML = '';
errors.forEach(error => {
const card = renderErrorCard(error);
DOM.resultsContainer.appendChild(card);
});
if (typeof Prism !== 'undefined') {
Prism.highlightAllUnder(DOM.resultsContainer);
}
}
function renderErrorCard(error) {
const severity = (error.severity || 'info').toLowerCase();
const sevChar = error.code?.charAt(0)?.toUpperCase() || 'I';
const card = createElement('article', `error-card severity-${sevChar}`);
card.dataset.code = error.code;
if (error.hash) {
card.dataset.hash = error.hash;
card.id = `error-${error.hash}`;
}
card.addEventListener('click', (e) => {
if (e.target.closest('button, a, .copy-code-btn, .snippet-copy-btn')) return;
if (error.hash) {
history.replaceState(null, '', `#${error.hash}`);
}
});
const header = renderCardHeader(error, severity, sevChar);
const body = renderCardBody(error);
card.appendChild(header);
card.appendChild(body);
return card;
}
const SEVERITY_EMOJI = {
'E': '❌', 'B': '🚫', 'C': '🔥', 'W': '⚠️',
'H': '💡', 'S': '✅', 'K': '✔️', 'I': 'ℹ️', 'T': '🔍'
};
function renderCardHeader(error, severity, sevChar) {
const header = createElement('div', 'error-card-header');
const codeRow = createElement('div', 'error-code-row');
const emoji = createElement('span', 'severity-emoji');
emoji.textContent = SEVERITY_EMOJI[sevChar] || 'ℹ️';
emoji.title = severity;
codeRow.appendChild(emoji);
const copyBtn = createElement('button', 'copy-code-btn', {
'data-code': error.code,
title: 'Copy error code'
});
copyBtn.innerHTML = '📋';
copyBtn.addEventListener('click', (e) => {
e.stopPropagation();
copyToClipboard(error.code, copyBtn);
});
codeRow.appendChild(copyBtn);
const codeGroup = createElement('div', 'error-code-group');
const codeEl = renderErrorCode(error.code, error);
codeGroup.appendChild(codeEl);
codeRow.appendChild(codeGroup);
if (error.hash) {
const arrow = createElement('span', 'error-arrow');
arrow.textContent = '\u2192';
codeRow.appendChild(arrow);
const hashEl = createElement('span', 'error-hash', {
title: 'Click to copy hash'
});
hashEl.textContent = error.hash;
hashEl.addEventListener('click', (e) => {
e.stopPropagation();
copyToClipboard(error.hash, hashEl);
});
codeRow.appendChild(hashEl);
const shareBtn = createElement('button', 'share-link-btn', {
title: 'Copy shareable link'
});
shareBtn.innerHTML = '🔗';
shareBtn.addEventListener('click', (e) => {
e.stopPropagation();
const url = `${window.location.origin}${window.location.pathname}#${error.hash}`;
copyToClipboard(url, shareBtn);
showToast('Link Copied', `Shareable link for ${error.code}`, 'success');
});
codeRow.appendChild(shareBtn);
}
header.appendChild(codeRow);
const desc = createElement('p', 'error-description');
desc.innerHTML = highlightMatch(error.message || error.description, AppState.searchQuery);
header.appendChild(desc);
const meta = createElement('div', 'error-meta');
if (error.introduced) {
const verBadge = createElement('span', 'meta-badge introduced');
verBadge.textContent = `v${error.introduced}`;
meta.appendChild(verBadge);
}
if (error.visibility && error.visibility !== 'public') {
const visBadge = createElement('span', `meta-badge visibility-${error.visibility}`);
visBadge.textContent = capitalize(error.visibility);
meta.appendChild(visBadge);
}
if (error.deprecated) {
const depBadge = createElement('span', 'meta-badge deprecated');
depBadge.textContent = 'Deprecated';
meta.appendChild(depBadge);
}
if (meta.children.length > 0) {
header.appendChild(meta);
}
return header;
}
function renderCardBody(error) {
const body = createElement('div', 'error-card-body');
const sections = [];
if (error.code_snippets && error.code_snippets.length > 0) {
sections.push({
id: 'code',
title: 'Code',
icon: '\uD83D\uDCDD',
render: () => renderCodeSection(error)
});
}
if (error.hints && error.hints.length > 0) {
sections.push({
id: 'hints',
title: 'Hints',
icon: '\uD83D\uDCA1',
render: () => renderHintsSection(error)
});
}
if (error.related_codes && error.related_codes.length > 0) {
sections.push({
id: 'related',
title: 'Related',
icon: '\uD83D\uDD17',
render: () => renderRelatedSection(error)
});
}
if (error.tags && error.tags.length > 0) {
sections.push({
id: 'tags',
title: 'Tags',
icon: '\uD83C\uDFF7\uFE0F',
render: () => renderTagsSection(error)
});
}
if (sections.length === 0) {
if (error.description) {
const descEl = createElement('p', 'error-description-full');
descEl.textContent = error.description;
body.appendChild(descEl);
}
return body;
}
sections.forEach((section, index) => {
const isOpen = index < 2; const sectionEl = createElement('div', `error-section${isOpen ? ' open' : ''}`);
const toggle = createElement('button', 'error-section-toggle');
toggle.innerHTML = `
<span class="toggle-left">
<span>${section.icon}</span>
<span>${section.title}</span>
</span>
<span class="icon">\u25BC</span>
`;
toggle.addEventListener('click', () => {
sectionEl.classList.toggle('open');
});
const content = createElement('div', 'error-section-content');
content.appendChild(section.render());
sectionEl.appendChild(toggle);
sectionEl.appendChild(content);
body.appendChild(sectionEl);
});
return body;
}
function renderCodeSection(error) {
const container = createElement('div', 'code-snippets-container');
const snippets = error.code_snippets || [];
snippets.forEach(snippet => {
const wrapper = createElement('div', 'code-snippet');
const code = snippet.code || '';
const lang = snippet.language || 'rust';
const label = snippet.label || '';
const role = snippet.role || '';
wrapper.innerHTML = `
<div class="code-snippet-header">
<div class="code-snippet-address-bar">
<span class="code-snippet-lang">${escapeHtml(lang)}</span>
${label ? `<span class="code-snippet-label">${escapeHtml(label)}</span>` : ''}
</div>
<div class="code-snippet-right">
${role ? `<span class="code-snippet-role ${role.toLowerCase()}">${escapeHtml(role)}</span>` : ''}
<button class="snippet-copy-btn" data-code="${escapeHtml(code)}">
<span>\uD83D\uDCCB</span>
<span class="copy-text">Copy</span>
</button>
</div>
</div>
<div class="code-snippet-content">
<pre><code class="language-${escapeHtml(lang)}">${escapeHtml(code)}</code></pre>
</div>
`;
const copyBtn = wrapper.querySelector('.snippet-copy-btn');
copyBtn.addEventListener('click', () => copyToClipboard(code, copyBtn));
container.appendChild(wrapper);
});
return container;
}
function renderHintsSection(error) {
const list = createElement('div', 'hint-list');
(error.hints || []).forEach(hint => {
const hintText = typeof hint === 'string' ? hint : hint.message;
const item = createElement('div', 'hint-item');
item.innerHTML = `
<span class="hint-icon">\u2192</span>
${escapeHtml(hintText)}
`;
list.appendChild(item);
});
return list;
}
function renderRelatedSection(error) {
const list = createElement('div', 'related-codes');
(error.related_codes || []).forEach(related => {
const relatedCode = typeof related === 'string' ? related : related.code;
const item = createElement('span', 'related-code');
item.textContent = relatedCode;
item.style.cursor = 'pointer';
item.addEventListener('click', () => {
const targetCard = document.querySelector(`.error-card[data-code="${relatedCode}"]`);
if (targetCard) {
scrollToElement(targetCard);
targetCard.classList.add('highlight');
setTimeout(() => targetCard.classList.remove('highlight'), 2000);
} else {
DOM.searchInput.value = relatedCode;
AppState.searchQuery = relatedCode;
filterErrors();
renderResults();
}
});
list.appendChild(item);
});
return list;
}
function renderTagsSection(error) {
const list = createElement('div', 'tags');
(error.tags || []).forEach(tag => {
const item = createElement('span', 'tag');
item.textContent = tag;
item.style.cursor = 'pointer';
item.addEventListener('click', () => {
setTag(tag);
});
list.appendChild(item);
});
return list;
}
function toggleCard(code) {
const card = document.querySelector(`.error-card[data-code="${code}"]`);
if (!card) return;
if (AppState.expandedCards.has(code)) {
AppState.expandedCards.delete(code);
card.classList.remove('expanded');
} else {
AppState.expandedCards.add(code);
card.classList.add('expanded');
if (!AppState.openSections.has(code)) {
const firstSection = card.querySelector('.section');
if (firstSection) {
AppState.openSections.set(code, new Set([firstSection.dataset.section]));
}
}
}
if (AppState.selectedErrorCode === code) {
showErrorDetail(code);
}
}
function toggleSection(errorCode, sectionId, sectionEl) {
const openSections = AppState.openSections.get(errorCode) || new Set();
if (openSections.has(sectionId)) {
openSections.delete(sectionId);
sectionEl.classList.remove('open');
} else {
openSections.add(sectionId);
sectionEl.classList.add('open');
}
AppState.openSections.set(errorCode, openSections);
}
function renderErrorCode(code, error) {
const container = createElement('div', 'error-code');
const parts = code.split('.');
const partTypes = ['severity', 'component', 'primary', 'sequence'];
const partTitles = ['Severity', 'Component', 'Primary Category', 'Sequence Number'];
const displayParts = [
parts[0], error.component || parts[1], error.primary || parts[2], parts[3] ];
displayParts.forEach((part, index) => {
if (index > 0) {
const dot = createElement('span', 'dot');
dot.textContent = '.';
container.appendChild(dot);
}
const partType = partTypes[index] || 'sequence';
const partTitle = partTitles[index] || 'Sequence';
let displayText = part;
if (partType === 'sequence' && AppState.sequenceFormat === 'named') {
const seqNum = parseInt(part, 10);
const seqMeta = AppState.sequences?.[seqNum];
if (seqMeta && seqMeta.name) {
displayText = seqMeta.name;
}
}
const span = createElement('span', `part ${partType} clickable`, {
'data-type': partType,
'data-value': part,
'data-original': parts[index], title: `Click to view ${partTitle} info`
});
span.textContent = displayText;
span.addEventListener('click', (e) => {
e.stopPropagation();
showPartInfo(partType, part, error);
});
container.appendChild(span);
});
return container;
}
function showPartInfo(partType, value, error) {
if (partType === 'severity') {
showSeverityDetail(value);
return;
}
const tabMap = {
'component': 'components',
'primary': 'primaries',
'sequence': 'sequences'
};
const tabId = tabMap[partType];
if (!tabId) return;
let lookupValue = value;
if (partType === 'sequence') {
lookupValue = String(parseInt(value, 10));
} else if (partType === 'component') {
lookupValue = error.component || value;
} else if (partType === 'primary') {
lookupValue = error.primary || value;
}
const tabBtn = document.querySelector(`.browse-tab[data-tab="${tabId}"]`);
if (tabBtn) {
document.querySelectorAll('.browse-tab').forEach(t => t.classList.remove('active'));
tabBtn.classList.add('active');
AppState.activeCategory = null;
hideDetail();
if (typeof buildBrowseList === 'function') {
buildBrowseList(tabId);
}
}
setTimeout(() => {
const item = document.querySelector(`.browse-item[data-type="${tabId}"][data-name="${lookupValue}"]`);
if (item) {
item.scrollIntoView({ behavior: 'smooth', block: 'center' });
item.click();
item.classList.add('highlight-pulse');
setTimeout(() => item.classList.remove('highlight-pulse'), 2000);
}
}, 100); }