diff.added.forEach(id => { const module = SNAPSHOTS_DATA.frames[index].modules.find(m => m.id === id); if (module) { html += `
${module.name}
`; } }); } if (diff.removed.length > 0) { html += '

πŸ”΄ Removed Modules

'; diff.removed.forEach(id => { html += `
${id.replace('mod:', '')}
`; }); } if (diff.changed.length > 0) { html += '

πŸ”΅ Changed Modules

'; diff.changed.forEach(change => { const tierInfo = change.tier_change ? `
Tier: ${change.tier_change}` : ''; const roleInfo = change.role_change ? `
Role: ${change.role_change}` : ''; const scoreInfo = Math.abs(change.score_delta) > 0.01 ? `
Score: ${change.score_delta > 0 ? '+' : ''}${change.score_delta.toFixed(3)}` : ''; html += `
${change.id.replace('mod:', '')}${roleInfo}${tierInfo}${scoreInfo}
`; }); } if (diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0) { html += '

No changes detected

'; } document.getElementById('details-content').innerHTML = html; } // Render trends chart function renderTrends() { const option = { title: { text: 'Historical Trends (Click to view snapshot)', left: 'center', top: 10, textStyle: { fontSize: 14, color: '#64748B' } }, tooltip: { trigger: 'axis', formatter: function(params) { let result = params[0].name + '
'; params.forEach(p => { result += p.marker + p.seriesName + ': ' + p.value + '
'; }); result += 'Click to view this snapshot'; return result; } }, legend: { data: ['Files', 'Modules'], bottom: 10 }, grid: { left: 50, right: 50, top: 50, bottom: 50 }, xAxis: { type: 'category', data: TRENDS.dates.map((d, i) => { // Format date display return d.substring(0, 13).replace('T', ' '); }) }, yAxis: { type: 'value' }, series: [ { name: 'Files', type: 'line', data: TRENDS.file_counts, smooth: true }, { name: 'Modules', type: 'line', data: TRENDS.module_counts, smooth: true } ] }; trendsChart.setOption(option); // Add click event trendsChart.off('click'); trendsChart.on('click', function(params) { if (params.componentType === 'series') { const snapshotIndex = params.dataIndex; if (SNAPSHOTS_DATA.frames[snapshotIndex]) { renderSnapshot(snapshotIndex); renderDiffSidebar(snapshotIndex); } } }); } // Show module details in sidebar function showModuleDetails(moduleData) { const moduleDeps = INDEX_DATA.module_dependencies || {}; const myDeps = moduleDeps[moduleData.id] || []; // Get files for this module (handle both static and snapshot modes) let moduleFiles = []; if (moduleData.files && Array.isArray(moduleData.files)) { // Static mode: files are embedded in node data moduleFiles = moduleData.files; } else { // Snapshot mode: need to fetch from INDEX_DATA const allFiles = INDEX_DATA.files || {}; const fileDeps = INDEX_DATA.file_dependencies || {}; for (const [filePath, fileInfo] of Object.entries(allFiles)) { if (fileInfo.module === moduleData.id) { moduleFiles.push({ path: filePath, name: fileInfo.name || filePath.split('/').pop(), desc: fileInfo.desc || '', signatures: fileInfo.signatures || [], dependencies: fileDeps[filePath] || [] }); } } } // Breadcrumb navigation let html = `
🏠 Home β€Ί ${moduleData.name}

${moduleData.name}

${moduleData.desc || ''}

Role
${moduleData.role || moduleData.category}
Core Score
${moduleData.core_score.toFixed(3)}
Files
${moduleFiles.length}
`; // Show module dependencies if (myDeps.length > 0) { html += `

Dependencies (${myDeps.length})

`; myDeps.forEach(dep => { const targetName = dep.target.replace('mod:', ''); html += `
β†’ ${targetName} (${dep.weight} imports)
`; }); html += '
'; } // Show files in module html += `

Files (${moduleFiles.length})

`; moduleFiles.forEach(file => { const depCount = file.dependencies ? file.dependencies.length : 0; const sigCount = file.signatures ? file.signatures.length : 0; html += `
πŸ“„ ${file.name}
${file.desc || 'No description'}
${sigCount} symbols Β· ${depCount} dependencies
`; }); html += '
'; document.getElementById('details-content').innerHTML = html; // Add hover effect and click event listeners to file items document.querySelectorAll('.file-item').forEach((item, index) => { item.addEventListener('mouseenter', function() { this.style.background = '#e0f2fe'; this.style.borderColor = '#0ea5e9'; }); item.addEventListener('mouseleave', function() { this.style.background = '#f9fafb'; this.style.borderColor = '#e5e7eb'; }); item.addEventListener('click', function() { const filePath = this.getAttribute('data-file-path'); const fileData = moduleFiles.find(f => f.path === filePath); if (fileData) { showFileDetails(fileData, moduleData.name); } }); }); } // Show file details with code signatures function showFileDetails(fileData, moduleName, options) { var opts = options || {}; // Store current module in view stack for back navigation (skip when navigating back) if (!opts.skipPush) { viewStack.push({ type: 'module', name: moduleName }); } // Breadcrumb navigation let html = `
🏠 Home β€Ί ${moduleName} β€Ί ${fileData.name}

πŸ“„ ${fileData.name}

${fileData.path}

${fileData.desc || 'No description'}

Path
${fileData.path}
`; // Show file dependencies if (fileData.dependencies && fileData.dependencies.length > 0) { html += `

Dependencies (${fileData.dependencies.length})

`; fileData.dependencies.forEach(dep => { const depName = dep.split('/').pop(); html += `
${depName}
${dep}
`; }); html += '
'; } // Show code signatures if (fileData.signatures && fileData.signatures.length > 0) { // Pre-build reverse index for incoming dependency lookup const funcDepsMap = INDEX_DATA.function_dependencies || {}; const incomingTargets = new Set(); for (const deps of Object.values(funcDepsMap)) { for (const d of deps) { incomingTargets.add(d.target); } } html += `

Symbols (${fileData.signatures.length})

`; fileData.signatures.forEach((sig, sigIndex) => { const kindColor = { 'class': '#3b82f6', 'function': '#8b5cf6', 'method': '#ec4899', 'variable': '#10b981' }[sig.kind] || '#6b7280'; const isFunction = sig.kind === 'function' || sig.kind === 'method'; const funcKey = `${fileData.path}:${sig.name}`; const hasOutgoing = isFunction && funcDepsMap[funcKey]; const hasIncoming = isFunction && incomingTargets.has(funcKey); const hasDeps = hasOutgoing || hasIncoming; const clickableClass = isFunction ? 'symbol-row-clickable' : ''; const clickableAttr = isFunction ? `data-sig-index="${sigIndex}"` : ''; html += `
${sig.kind} ${sig.name} line ${sig.line} ${hasDeps ? ` ` : ''}
${sig.desc ? `
${sig.desc}
` : ''}