waddling-errors 0.7.3

Structured, secure-by-default diagnostic codes for distributed systems with no_std and role-based documentation
Documentation
/* ============================================
   APPLICATION INITIALIZATION
   Main entry point and setup
   ============================================ */

// Initialize the application when DOM is ready
document.addEventListener('DOMContentLoaded', initApp);

function initApp() {
    // Initialize DOM references
    initDOMReferences();
    
    // Load error data from the global DATA object (set by template.rs)
    loadErrorData();
    
    // Initialize modules
    initTheme();
    initVisibility();
    initSearch();
    initSeverityFilters();
    initFilterChips();
    initQueryBuilder();
    initSidebar();
    
    // Initial render
    filterErrors();
    renderResults();
    updateSidebar();
    
    // Handle URL hash on page load (deep link to specific error)
    handleUrlHash();
    
    // Listen for hash changes (browser back/forward)
    window.addEventListener('hashchange', handleUrlHash);
    
    // Theme toggle
    if (DOM.themeToggle) {
        DOM.themeToggle.addEventListener('click', toggleTheme);
    }
    
    // Format toggle (sequence display: numeric vs named)
    if (DOM.formatToggle) {
        DOM.formatToggle.addEventListener('click', toggleSequenceFormat);
    }
    
    // Hamburger menu toggle (mobile sidebar)
    if (DOM.hamburgerBtn) {
        DOM.hamburgerBtn.addEventListener('click', toggleMobileSidebar);
    }
}

// Toggle mobile sidebar
function toggleMobileSidebar() {
    if (DOM.sidebar && DOM.hamburgerBtn) {
        DOM.sidebar.classList.toggle('mobile-open');
        DOM.hamburgerBtn.classList.toggle('active');
        
        // Update aria-expanded
        const isOpen = DOM.sidebar.classList.contains('mobile-open');
        DOM.hamburgerBtn.setAttribute('aria-expanded', isOpen);
    }
}

// Handle URL hash to navigate to specific error
function handleUrlHash() {
    const hash = window.location.hash.slice(1); // Remove '#'
    if (!hash) return;
    
    // Find error by hash
    const error = AppState.errors.find(e => e.hash === hash);
    if (!error) {
        console.warn('[WaddlingErrors] Error not found for hash:', hash);
        return;
    }
    
    // Clear search to show all errors (so the target is visible)
    if (DOM.searchInput) {
        DOM.searchInput.value = '*.*.*.*';
        AppState.searchQuery = '*.*.*.*';
        filterErrors();
        renderResults();
    }
    
    // Wait for render, then scroll to and highlight the error
    requestAnimationFrame(() => {
        const card = document.getElementById(`error-${hash}`);
        if (card) {
            // Scroll into view
            card.scrollIntoView({ behavior: 'smooth', block: 'center' });
            
            // Add highlight effect
            card.classList.add('highlight');
            
            // Expand the card sections
            const sections = card.querySelectorAll('.error-section');
            sections.forEach(section => section.classList.add('open'));
            
            // Remove highlight class after animation
            setTimeout(() => card.classList.remove('highlight'), 2500);
        }
    });
}

function loadErrorData() {
    // Error data is set by template.rs as global DATA object
    if (typeof DATA !== 'undefined') {
        console.log('[WaddlingErrors] Loading DATA object:', DATA);
        
        // Convert HashMap/object to array of values for compatibility
        const errorsArray = DATA.errors 
            ? (Array.isArray(DATA.errors) ? DATA.errors : Object.values(DATA.errors))
            : [];
        AppState.errors = errorsArray.map(normalizeError);
        console.log('[WaddlingErrors] Loaded', AppState.errors.length, 'errors');
        
        // Store hash lookup map for reverse search (hash → code)
        AppState.hashLookup = DATA['#'] || {};
        console.log('[WaddlingErrors] Hash lookup entries:', Object.keys(AppState.hashLookup).length);
        
        // Also store component/primary/sequence metadata if available
        AppState.components = DATA.components || {};
        AppState.primaries = DATA.primaries || {};
        AppState.sequences = DATA.sequences || {};
        AppState.severities = DATA.severities || {};
        
        console.log('[WaddlingErrors] Loaded components:', Object.keys(AppState.components).length);
        console.log('[WaddlingErrors] Loaded primaries:', Object.keys(AppState.primaries).length);
        console.log('[WaddlingErrors] Loaded severities:', Object.keys(AppState.severities).length);
    } else if (typeof errorDatabase !== 'undefined') {
        // Fallback to older variable names
        console.log('[WaddlingErrors] Using errorDatabase fallback');
        AppState.errors = errorDatabase.map(normalizeError);
    } else {
        console.warn('[WaddlingErrors] No error data found');
        AppState.errors = [];
    }
}

function normalizeError(error) {
    // Get severity char from code (first character like 'E', 'W', 'C')
    const sevChar = error.code?.charAt(0)?.toUpperCase() || 'I';
    const severityMap = {
        'E': 'error',
        'B': 'blocked', 
        'C': 'critical',
        'W': 'warning',
        'H': 'help',
        'S': 'success',
        'K': 'completed',
        'I': 'info',
        'T': 'trace'
    };
    
    // Spread original error first, then override with normalized values
    return {
        ...error,
        code: error.code || 'UNKNOWN',
        message: error.message || error.msg || '',
        description: error.description || error.desc || '',
        severity: severityMap[sevChar] || 'info',
        visibility: (error.visibility || 'public').toLowerCase(),
        hash: error.hash || null,
        hints: Array.isArray(error.hints) ? error.hints : [],
        related: Array.isArray(error.related) || Array.isArray(error.see_also) 
            ? (error.related || error.see_also) 
            : [],
        tags: Array.isArray(error.tags) ? error.tags : [],
        code_example: error.code_example || error.snippet || null,
        language: error.language || 'rust',
        component: error.component || null,
        primary: error.primary || null,
        sequence: error.sequence || null,
    };
}

// Export functions that might be needed globally
window.WaddlingErrors = {
    filterErrors,
    renderResults,
    toggleCard,
    copyToClipboard,
    showToast,
    setCategory,
    setTag,
    clearFilters,
    selectError,
    getState: () => AppState
};