memscope_rs/cli/commands/html_from_json/
js.rs

1use std::fs;
2use std::sync::OnceLock;
3
4static SCRIPT_JS_CACHE: OnceLock<String> = OnceLock::new();
5
6pub fn get_embedded_script_js() -> &'static str {
7    SCRIPT_JS_CACHE.get_or_init(|| {
8        // Try to load from external file first
9        if let Ok(external_path) = std::env::var("MEMSCOPE_JS_TEMPLATE") {
10            if let Ok(content) = fs::read_to_string(&external_path) {
11                println!("๐Ÿ“ Loaded external JS template: {}", external_path);
12                return content;
13            }
14        }
15
16        // Fall back to embedded JS
17        EMBEDDED_SCRIPT_JS.to_string()
18    })
19}
20
21pub const EMBEDDED_SCRIPT_JS: &str = r#"
22// MemScope Dashboard JavaScript - Clean rendering for clean_dashboard.html
23// This file contains comprehensive functions for memory analysis dashboard
24
25// Global data store - will be populated by HTML template
26window.analysisData = window.analysisData || {};
27
28// FFI dashboard render style: 'svg' to mimic Rust SVG dashboard, 'cards' for card-based UI
29const FFI_STYLE = 'svg';
30
31// Initialize all dashboard components - Clean layout
32function initCleanTemplate() {
33    console.log('๐Ÿš€ Initializing MemScope Dashboard...');
34    console.log('๐Ÿ“Š Available data:', Object.keys(window.analysisData||{}));
35    const data = window.analysisData || {};
36
37    // KPI
38    updateKPICards(data);
39
40    // Memory by type (Chart.js)
41    const typeChartEl = document.getElementById('typeChart');
42    if (typeChartEl) {
43        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
44        const byType = {};
45        for (const a of allocs) { const t=a.type_name||'Unknown'; byType[t]=(byType[t]||0)+(a.size||0); }
46        const top = Object.entries(byType).sort((a,b)=>b[1]-a[1]).slice(0,10);
47        if (top.length>0) {
48            const ctx = typeChartEl.getContext('2d');
49            if (window.chartInstances['clean-type']) window.chartInstances['clean-type'].destroy();
50            window.chartInstances['clean-type'] = new Chart(ctx, {
51                type:'bar',
52                data:{ labels: top.map(x=>x[0]), datasets:[{ label:'Bytes', data: top.map(x=>x[1]), backgroundColor:'#3b82f6' }] },
53                options:{ responsive:true, plugins:{legend:{display:false}}, scales:{y:{beginAtZero:true}} }
54            });
55            const legend = document.getElementById('typeLegend');
56            if (legend) legend.innerHTML = top.map(([n,v])=>`<span class="pill">${n}: ${formatBytes(v)}</span>`).join(' ');
57        }
58    }
59
60    // Timeline (Chart.js)
61    const timelineEl = document.getElementById('timelineChart');
62    if (timelineEl) {
63        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
64        const rawTimeline = (data.memory_analysis && data.memory_analysis.memory_timeline) || [];
65        let points = [];
66        if (rawTimeline.length) {
67            points = rawTimeline.map((p,i)=>({ x:i, y:(p.memory_usage||0) }));
68        } else {
69            const sorted = allocs.slice().sort((a,b)=>(a.timestamp_alloc||0)-(b.timestamp_alloc||0));
70            let cum=0; const step=Math.max(1, Math.floor(sorted.length/50));
71            for(let i=0;i<sorted.length;i+=step){ cum += sorted[i].size||0; points.push({x:i, y:cum}); }
72        }
73        if (points.length>1) {
74            const ctx = timelineEl.getContext('2d');
75            if (window.chartInstances['clean-timeline']) window.chartInstances['clean-timeline'].destroy();
76            window.chartInstances['clean-timeline'] = new Chart(ctx, {
77                type:'line',
78                data:{ labels: points.map(p=>p.x), datasets:[{ label:'Cumulative', data: points.map(p=>p.y), borderColor:'#ef4444', backgroundColor:'rgba(239,68,68,0.1)', fill:true, tension:0.25 }] },
79                options:{ responsive:true, plugins:{legend:{display:false}}, scales:{y:{beginAtZero:true}} }
80            });
81        }
82    }
83
84    // Treemap
85    const treemapEl = document.getElementById('treemap');
86    if (treemapEl) {
87        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
88        treemapEl.innerHTML = createTreemapVisualization(allocs);
89    }
90
91    // Growth
92    const growthEl = document.getElementById('growth');
93    if (growthEl) {
94        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
95        const total = allocs.reduce((s,a)=>s+(a.size||0),0);
96        growthEl.innerHTML = createAdvancedGrowthTrendVisualization(allocs, Math.max(1,total));
97    }
98
99    // Lifetimes (top 10)
100    const lifetimesEl = document.getElementById('lifetimes');
101    if (lifetimesEl) {
102        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
103        const top = allocs.filter(a=>a.var_name && a.var_name!=='unknown').sort((a,b)=>(b.size||0)-(a.size||0)).slice(0,10);
104        lifetimesEl.innerHTML = top.map(a=>`<div class="flex items-center justify-between py-1 border-b">
105            <div class="text-xs font-medium">${a.var_name}</div>
106            <div class="text-xs text-gray-500">${formatBytes(a.size||0)}</div>
107        </div>`).join('');
108    }
109
110    // Update memory allocation table
111    updateAllocationsTable(data);
112    
113    // Update unsafe risk table
114    updateUnsafeTable(data);
115
116    // Initialize all charts and visualizations
117    initCharts(data);
118    
119    // Initialize lifecycle visualization
120    initLifetimeVisualization(data);
121
122    // Complex types
123    const complexSummary = document.getElementById('complexSummary');
124    if (complexSummary) {
125        const ct = data.complex_types || {};
126        const s = ct.summary || {};
127        const items = [
128            {label:'Complex Types', val: s.total_complex_types||0},
129            {label:'Smart Pointers', val: s.smart_pointers_count||0},
130            {label:'Collections', val: s.collections_count||0},
131            {label:'Generic Types', val: s.generic_types_count||s.generic_type_count||0},
132        ];
133        complexSummary.innerHTML = items.map(x=>`<div class="pill">${x.label}: ${x.val}</div>`).join('');
134        document.getElementById('complexSmart')?.replaceChildren();
135        document.getElementById('complexCollections')?.replaceChildren();
136        document.getElementById('complexGenerics')?.replaceChildren();
137    }
138
139    // Variable relationships
140    const graphEl = document.getElementById('graph');
141    if (graphEl) {
142        // reuse our D3 relationship graph init but mount into #graph
143        const container = document.createElement('div');
144        container.id = 'variable-graph-container';
145        container.style.width = '100%';
146        container.style.height = '260px';
147        graphEl.appendChild(container);
148        try { initVariableGraph(); } catch(e) { console.warn('variable graph init failed', e); }
149    }
150
151    // Security violations
152    const secEl = document.getElementById('security');
153    if (secEl) {
154        const root = data.unsafe_ffi || {};
155        const list = root.security_hotspots || root.unsafe_reports || [];
156        secEl.innerHTML = (list||[]).slice(0,12).map(h=>{
157            const score = h.risk_score || h.risk_assessment?.confidence_score || 0;
158            const level = h.risk_level || h.risk_assessment?.risk_level || 'Unknown';
159            const width = Math.min(100, Math.round((score||0)*10));
160            return `<div class="card">
161              <div class="text-sm font-semibold">${h.location||h.report_id||'Unknown'}</div>
162              <div class="text-xs text-gray-500">${h.description||h.source?.type||''}</div>
163              <div class="mt-2 bg-red-100 h-2 rounded"><div style="width:${width}%; background:#ef4444; height:100%" class="rounded"></div></div>
164              <div class="text-xs text-gray-500 mt-1">Risk: ${level} (${score})</div>
165            </div>`;
166        }).join('') || '<div class="muted">No security violations</div>';
167    }
168}
169function initializeDashboard() {
170    console.log('๐Ÿš€ Initializing MemScope dashboard...');
171    console.log('๐Ÿ“Š Available data:', Object.keys(window.analysisData || {}));
172
173    // Initialize theme system first
174    initThemeToggle();
175
176    // Initialize enhanced dashboard with comprehensive data
177    initEnhancedSummaryStats();
178    
179    // Initialize all components
180    initSummaryStats();
181    initCharts();
182    initMemoryUsageAnalysis();
183    initLifetimeVisualization();
184    initFFIVisualization();
185    initMemoryFragmentation();
186    initMemoryGrowthTrends();
187    initAllocationsTable();
188    initVariableGraph();
189}
190
191// Initialize theme toggle functionality
192function initThemeToggle() {
193    const themeToggle = document.getElementById('theme-toggle');
194    const html = document.documentElement;
195
196    // Check for saved theme preference or default to light mode
197    const savedTheme = localStorage.getItem('memscope-theme') || 'light';
198
199    console.log('๐ŸŽจ Initializing theme system, saved theme:', savedTheme);
200
201    // Apply initial theme
202    applyTheme(savedTheme === 'dark');
203
204    if (themeToggle) {
205        themeToggle.addEventListener('click', () => {
206            const isDark = html.classList.contains('dark');
207
208            if (isDark) {
209                applyTheme(false);
210                localStorage.setItem('memscope-theme', 'light');
211                console.log('๐ŸŽจ Theme switched to: light mode');
212            } else {
213                applyTheme(true);
214                localStorage.setItem('memscope-theme', 'dark');
215                console.log('๐ŸŽจ Theme switched to: dark mode');
216            }
217        });
218
219        console.log('โœ… Theme toggle initialized successfully');
220    } else {
221        console.warn('โš ๏ธ Theme toggle button not found');
222    }
223}
224
225// Apply theme to all modules
226function applyTheme(isDark) {
227    const html = document.documentElement;
228    const body = document.body;
229
230    if (isDark) {
231        html.classList.remove('light');
232        html.classList.add('dark');
233        body.classList.add('dark');
234    } else {
235        html.classList.remove('dark');
236        html.classList.add('light');
237        body.classList.remove('dark');
238    }
239
240    // Force immediate repaint
241    html.style.display = 'none';
242    html.offsetHeight; // Trigger reflow
243    html.style.display = '';
244
245    // Apply theme to all modules that need explicit dark mode support
246    applyThemeToAllModules(isDark);
247
248    // Update theme toggle button icon
249    updateThemeToggleIcon(isDark);
250
251    // Destroy existing charts before reinitializing
252    destroyAllCharts();
253
254    // Reinitialize charts to apply theme changes
255    setTimeout(() => {
256        initCharts();
257        initFFIRiskChart();
258    }, 100);
259}
260
261// Update theme toggle button icon
262function updateThemeToggleIcon(isDark) {
263    const themeToggle = document.getElementById('theme-toggle');
264    if (themeToggle) {
265        const icon = themeToggle.querySelector('i');
266        if (icon) {
267            if (isDark) {
268                icon.className = 'fa fa-sun';
269            } else {
270                icon.className = 'fa fa-moon';
271            }
272        }
273    }
274}
275
276// Global chart instances storage
277window.chartInstances = {};
278
279// Destroy all existing charts
280function destroyAllCharts() {
281    Object.keys(window.chartInstances).forEach(chartId => {
282        if (window.chartInstances[chartId]) {
283            window.chartInstances[chartId].destroy();
284            delete window.chartInstances[chartId];
285        }
286    });
287}
288
289// Apply theme to specific modules
290function applyThemeToAllModules(isDark) {
291    const modules = [
292        'memory-usage-analysis',
293        'generic-types-details',
294        'variable-relationship-graph',
295        'complex-type-analysis',
296        'memory-optimization-recommendations',
297        'unsafe-ffi-data'
298    ];
299
300    modules.forEach(moduleId => {
301        const module = document.getElementById(moduleId);
302        if (module) {
303            module.classList.toggle('dark', isDark);
304        }
305    });
306
307    // Also apply to any table elements that might need it
308    const tables = document.querySelectorAll('table');
309    tables.forEach(table => {
310        table.classList.toggle('dark', isDark);
311    });
312
313    // Apply to any chart containers
314    const chartContainers = document.querySelectorAll('canvas');
315    chartContainers.forEach(container => {
316        if (container.parentElement) {
317            container.parentElement.classList.toggle('dark', isDark);
318        }
319    });
320}
321
322// Initialize summary statistics
323function initSummaryStats() {
324    console.log('๐Ÿ“Š Initializing summary stats...');
325
326    const data = window.analysisData;
327
328    // Update complex types count
329    const complexTypesCount = data.complex_types?.summary?.total_complex_types || 0;
330    updateElement('total-complex-types', complexTypesCount);
331
332    // Update total allocations
333    const totalAllocations = data.memory_analysis?.allocations?.length || 0;
334    updateElement('total-allocations', totalAllocations);
335
336    // Update generic types count
337    const genericTypeCount = data.complex_types?.summary?.generic_type_count || 0;
338    updateElement('generic-type-count', genericTypeCount);
339
340    // Update unsafe FFI count
341    const unsafeFFICount = data.unsafe_ffi?.enhanced_ffi_data?.length || 0;
342    updateElement('unsafe-ffi-count', unsafeFFICount);
343
344    // Update category counts
345    const smartPointersCount = data.complex_types?.categorized_types?.smart_pointers?.length || 0;
346    const collectionsCount = data.complex_types?.categorized_types?.collections?.length || 0;
347    const primitivesCount = 0; // Calculate from data if available
348
349    updateElement('smart-pointers-count', smartPointersCount);
350    updateElement('collections-count', collectionsCount);
351    updateElement('primitives-count', primitivesCount);
352}
353
354// Initialize charts - simplified
355function initCharts() {
356    console.log('๐Ÿ“Š Initializing charts...');
357
358    // Initialize memory distribution chart
359    initMemoryDistributionChart();
360
361    // Initialize allocation size chart
362    initAllocationSizeChart();
363}
364
365
366
367// Initialize memory distribution chart
368function initMemoryDistributionChart() {
369    const ctx = document.getElementById('memory-distribution-chart');
370    if (!ctx) return;
371
372    const allocations = window.analysisData.memory_analysis?.allocations || [];
373    const typeDistribution = {};
374
375    allocations.forEach(alloc => {
376        const type = alloc.type_name || 'System Allocation';
377        typeDistribution[type] = (typeDistribution[type] || 0) + alloc.size;
378    });
379
380    const sortedTypes = Object.entries(typeDistribution)
381        .sort(([, a], [, b]) => b - a)
382        .slice(0, 10);
383
384    const isDark = document.documentElement.classList.contains('dark');
385
386    // Destroy existing chart if it exists
387    if (window.chartInstances['memory-distribution-chart']) {
388        window.chartInstances['memory-distribution-chart'].destroy();
389    }
390
391    window.chartInstances['memory-distribution-chart'] = new Chart(ctx, {
392        type: 'bar',
393        data: {
394            labels: sortedTypes.map(([type]) => formatTypeName(type)),
395            datasets: [{
396                label: 'Memory Usage (bytes)',
397                data: sortedTypes.map(([, size]) => size),
398                backgroundColor: '#3b82f6'
399            }]
400        },
401        options: {
402            responsive: true,
403            maintainAspectRatio: false,
404            plugins: {
405                legend: {
406                    labels: {
407                        color: isDark ? '#ffffff' : '#374151'
408                    }
409                }
410            },
411            scales: {
412                x: {
413                    ticks: {
414                        color: isDark ? '#d1d5db' : '#6b7280'
415                    },
416                    grid: {
417                        color: isDark ? '#374151' : '#e5e7eb'
418                    }
419                },
420                y: {
421                    beginAtZero: true,
422                    ticks: {
423                        color: isDark ? '#d1d5db' : '#6b7280',
424                        callback: function (value) {
425                            return formatBytes(value);
426                        }
427                    },
428                    grid: {
429                        color: isDark ? '#374151' : '#e5e7eb'
430                    }
431                }
432            }
433        }
434    });
435}
436
437// Initialize allocation size chart
438function initAllocationSizeChart() {
439    const ctx = document.getElementById('allocation-size-chart');
440    if (!ctx) return;
441
442    const allocations = window.analysisData.memory_analysis?.allocations || [];
443    const sizeDistribution = {
444        'Tiny (< 64B)': 0,
445        'Small (64B - 1KB)': 0,
446        'Medium (1KB - 64KB)': 0,
447        'Large (64KB - 1MB)': 0,
448        'Huge (> 1MB)': 0
449    };
450
451    allocations.forEach(alloc => {
452        const size = alloc.size || 0;
453        if (size < 64) sizeDistribution['Tiny (< 64B)']++;
454        else if (size < 1024) sizeDistribution['Small (64B - 1KB)']++;
455        else if (size < 65536) sizeDistribution['Medium (1KB - 64KB)']++;
456        else if (size < 1048576) sizeDistribution['Large (64KB - 1MB)']++;
457        else sizeDistribution['Huge (> 1MB)']++;
458    });
459
460    // Destroy existing chart if it exists
461    if (window.chartInstances['allocation-size-chart']) {
462        window.chartInstances['allocation-size-chart'].destroy();
463    }
464
465    window.chartInstances['allocation-size-chart'] = new Chart(ctx, {
466        type: 'pie',
467        data: {
468            labels: Object.keys(sizeDistribution),
469            datasets: [{
470                data: Object.values(sizeDistribution),
471                backgroundColor: ['#10b981', '#3b82f6', '#f59e0b', '#ef4444', '#7c2d12']
472            }]
473        },
474        options: {
475            responsive: true,
476            maintainAspectRatio: false,
477            plugins: {
478                legend: {
479                    labels: {
480                        color: document.documentElement.classList.contains('dark') ? '#ffffff' : '#374151'
481                    }
482                }
483            }
484        }
485    });
486}
487
488
489
490// Process memory analysis data with validation and fallback
491function processMemoryAnalysisData(rawData) {
492    if (!rawData || !rawData.memory_analysis) {
493        console.warn('โš ๏ธ No memory analysis data found, generating fallback data');
494        return generateFallbackMemoryData();
495    }
496
497    const memoryData = rawData.memory_analysis;
498    const processedData = {
499        stats: {
500            total_allocations: memoryData.stats?.total_allocations || 0,
501            active_allocations: memoryData.stats?.active_allocations || 0,
502            total_memory: memoryData.stats?.total_memory || 0,
503            active_memory: memoryData.stats?.active_memory || 0
504        },
505        allocations: memoryData.allocations || [],
506        trends: {
507            peak_memory: memoryData.peak_memory || 0,
508            growth_rate: memoryData.growth_rate || 0,
509            fragmentation_score: memoryData.fragmentation_score || 0
510        }
511    };
512
513    // Calculate additional metrics if not present
514    if (processedData.allocations.length > 0) {
515        const totalSize = processedData.allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
516        if (!processedData.stats.total_memory) {
517            processedData.stats.total_memory = totalSize;
518        }
519        if (!processedData.stats.total_allocations) {
520            processedData.stats.total_allocations = processedData.allocations.length;
521        }
522    }
523
524    console.log('โœ… Processed memory analysis data:', processedData);
525    return processedData;
526}
527
528// Generate fallback memory data when real data is unavailable
529function generateFallbackMemoryData() {
530    console.log('๐Ÿ”„ Generating fallback memory data');
531
532    return {
533        stats: {
534            total_allocations: 0,
535            active_allocations: 0,
536            total_memory: 0,
537            active_memory: 0
538        },
539        allocations: [],
540        trends: {
541            peak_memory: 0,
542            growth_rate: 0,
543            fragmentation_score: 0
544        },
545        isFallback: true
546    };
547}
548
549// Validate memory data structure
550function validateMemoryData(data) {
551    if (!data) return false;
552
553    const hasStats = data.stats && typeof data.stats === 'object';
554    const hasAllocations = Array.isArray(data.allocations);
555
556    return hasStats && hasAllocations;
557}
558
559// Calculate memory statistics from allocations
560function calculateMemoryStatistics(allocations) {
561    if (!Array.isArray(allocations) || allocations.length === 0) {
562        return {
563            totalSize: 0,
564            averageSize: 0,
565            largestAllocation: 0,
566            userAllocations: 0,
567            systemAllocations: 0
568        };
569    }
570
571    const totalSize = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
572    const averageSize = totalSize / allocations.length;
573    const largestAllocation = Math.max(...allocations.map(alloc => alloc.size || 0));
574
575    const userAllocations = allocations.filter(alloc =>
576        alloc.var_name && alloc.var_name !== 'unknown' &&
577        alloc.type_name && alloc.type_name !== 'unknown'
578    ).length;
579
580    const systemAllocations = allocations.length - userAllocations;
581
582    return {
583        totalSize,
584        averageSize,
585        largestAllocation,
586        userAllocations,
587        systemAllocations
588    };
589}
590
591// Initialize memory usage analysis with enhanced SVG-style visualization
592function initMemoryUsageAnalysis() {
593    const container = document.getElementById('memory-usage-analysis');
594    if (!container) return;
595
596    // Process memory data with validation
597    const memoryData = processMemoryAnalysisData(window.analysisData);
598    const allocations = memoryData.allocations;
599
600    if (allocations.length === 0 || memoryData.isFallback) {
601        container.innerHTML = createEnhancedEmptyState();
602        return;
603    }
604
605    // Calculate comprehensive statistics
606    const stats = calculateMemoryStatistics(allocations);
607    const totalMemory = stats.totalSize;
608
609    const userAllocations = allocations.filter(alloc =>
610        alloc.var_name && alloc.var_name !== 'unknown' &&
611        alloc.type_name && alloc.type_name !== 'unknown'
612    );
613    const systemAllocations = allocations.filter(alloc =>
614        !alloc.var_name || alloc.var_name === 'unknown' ||
615        !alloc.type_name || alloc.type_name === 'unknown'
616    );
617
618    const userMemory = userAllocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
619    const systemMemory = systemAllocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
620
621    // Create enhanced SVG-style visualization
622    container.innerHTML = createMemoryAnalysisSVG(stats, allocations, userMemory, systemMemory, totalMemory);
623}
624
625// Create enhanced empty state with better styling
626function createEnhancedEmptyState() {
627    return `
628        <div class="h-full flex items-center justify-center">
629            <div class="text-center p-8 bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-800 dark:to-gray-700 rounded-xl border-2 border-dashed border-blue-200 dark:border-gray-600">
630                <div class="mb-4">
631                    <svg class="w-16 h-16 mx-auto text-blue-400 dark:text-blue-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
632                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
633                    </svg>
634                </div>
635                <h4 class="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-200">Memory Analysis Ready</h4>
636                <p class="text-sm text-gray-600 dark:text-gray-400 mb-2">No memory allocation data found for analysis</p>
637                <p class="text-xs text-gray-500 dark:text-gray-500">Run your application with memory tracking enabled to see detailed analysis</p>
638            </div>
639        </div>
640    `;
641}
642
643// Create comprehensive SVG-style memory analysis visualization inspired by the memoryAnalysis.svg
644function createMemoryAnalysisSVG(stats, allocations, userMemory, systemMemory, totalMemory) {
645    const userPercentage = totalMemory > 0 ? (userMemory / totalMemory * 100) : 0;
646    const systemPercentage = totalMemory > 0 ? (systemMemory / totalMemory * 100) : 0;
647
648    // Calculate comprehensive efficiency metrics
649    const efficiency = totalMemory > 0 ? Math.min(100, (userMemory / totalMemory * 100)) : 0;
650    const reclamationRate = allocations.length > 0 ? Math.min(100, ((allocations.filter(a => a.timestamp_dealloc).length / allocations.length) * 100)) : 0;
651    const fragmentation = Math.min(100, (allocations.length / Math.max(1, totalMemory / 1024)) * 10);
652
653    // Advanced size distribution analysis
654    const sizeDistribution = {
655        tiny: allocations.filter(a => a.size < 64).length,
656        small: allocations.filter(a => a.size >= 64 && a.size < 1024).length,
657        medium: allocations.filter(a => a.size >= 1024 && a.size < 65536).length,
658        large: allocations.filter(a => a.size >= 65536).length
659    };
660
661    // Calculate median and P95 sizes
662    const sizes = allocations.map(a => a.size || 0).sort((a, b) => a - b);
663    const medianSize = sizes.length > 0 ? sizes[Math.floor(sizes.length / 2)] : 0;
664    const p95Size = sizes.length > 0 ? sizes[Math.floor(sizes.length * 0.95)] : 0;
665
666    return `
667        <div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden">
668            <!-- Header with gradient background -->
669            <div class="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6">
670                <div class="text-center">
671                    <h2 class="text-3xl font-bold mb-2">Rust Memory Usage Analysis</h2>
672                    <p class="text-blue-100 uppercase tracking-wider text-sm">Key Performance Metrics</p>
673                </div>
674            </div>
675
676            <div class="p-6">
677                <!-- Key Performance Metrics Grid -->
678                <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-8 gap-4 mb-8">
679                    ${createAdvancedMetricCard('Active Memory', formatBytes(userMemory), Math.round(userPercentage), '#3498db', 'MEDIUM')}
680                    ${createAdvancedMetricCard('Peak Memory', formatBytes(totalMemory), 100, '#e74c3c', 'HIGH')}
681                    ${createAdvancedMetricCard('Active Allocs', allocations.length, 100, '#2ecc71', 'HIGH')}
682                    ${createAdvancedMetricCard('Reclamation', reclamationRate.toFixed(1) + '%', Math.round(reclamationRate), '#f39c12', reclamationRate > 70 ? 'OPTIMAL' : 'MEDIUM')}
683                    ${createAdvancedMetricCard('Efficiency', efficiency.toFixed(1) + '%', Math.round(efficiency), '#9b59b6', efficiency > 70 ? 'OPTIMAL' : 'MEDIUM')}
684                    ${createAdvancedMetricCard('Median Size', formatBytes(medianSize), Math.min(100, medianSize / 1024), '#1abc9c', medianSize < 100 ? 'OPTIMAL' : 'MEDIUM')}
685                    ${createAdvancedMetricCard('P95 Size', formatBytes(p95Size), Math.min(100, p95Size / 1024), '#e67e22', p95Size < 1024 ? 'OPTIMAL' : 'MEDIUM')}
686                    ${createAdvancedMetricCard('Fragmentation', fragmentation.toFixed(1) + '%', Math.round(fragmentation), '#95a5a6', fragmentation < 30 ? 'OPTIMAL' : 'MEDIUM')}
687                </div>
688
689
690                <!-- Memory Usage by Type - Enhanced Treemap -->
691                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 mb-8 border border-gray-200 dark:border-gray-600">
692                    <h3 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white text-center">Memory Usage by Type - Treemap Visualization</h3>
693                    <div class="bg-gray-100 dark:bg-gray-600 rounded-lg p-4 h-64 relative overflow-hidden">
694                        ${createAdvancedTreemapVisualization(allocations, totalMemory)}
695                    </div>
696                    <div class="mt-4 grid grid-cols-3 gap-4 text-xs">
697                        <div class="flex items-center">
698                            <div class="w-3 h-3 bg-blue-500 rounded mr-2"></div>
699                            <span class="text-gray-600 dark:text-gray-300">Collections</span>
700                        </div>
701                        <div class="flex items-center">
702                            <div class="w-3 h-3 bg-green-500 rounded mr-2"></div>
703                            <span class="text-gray-600 dark:text-gray-300">Basic Types</span>
704                        </div>
705                        <div class="flex items-center">
706                            <div class="w-3 h-3 bg-gray-500 rounded mr-2"></div>
707                            <span class="text-gray-600 dark:text-gray-300">System</span>
708                        </div>
709                    </div>
710                </div>
711
712                <!-- Advanced Analysis Grid -->
713                <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
714                    <!-- Memory Fragmentation Analysis -->
715                    <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 border border-gray-200 dark:border-gray-600">
716                        <h3 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white text-center">Memory Fragmentation Analysis</h3>
717                        <div class="space-y-4">
718                            ${createAdvancedFragmentationBar('Tiny (0-64B)', sizeDistribution.tiny, allocations.length, '#27ae60')}
719                            ${createAdvancedFragmentationBar('Small (65B-1KB)', sizeDistribution.small, allocations.length, '#f39c12')}
720                            ${createAdvancedFragmentationBar('Medium (1KB-64KB)', sizeDistribution.medium, allocations.length, '#e74c3c')}
721                            ${createAdvancedFragmentationBar('Large (>64KB)', sizeDistribution.large, allocations.length, '#8e44ad')}
722                        </div>
723                    </div>
724
725                    <!-- Call Stack Analysis -->
726                    <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 border border-gray-200 dark:border-gray-600">
727                        <h3 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white text-center">Call Stack Analysis</h3>
728                        <div class="space-y-3 max-h-64 overflow-y-auto">
729                            ${createCallStackAnalysis(allocations)}
730                        </div>
731                    </div>
732                </div>
733
734                <!-- Memory Statistics Summary -->
735                <div class="mt-8 bg-gray-50 dark:bg-gray-700 rounded-lg p-6 border border-gray-200 dark:border-gray-600">
736                    <h3 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white text-center">Memory Statistics</h3>
737                    <div class="grid grid-cols-3 gap-4 text-sm text-center">
738                        <div>
739                            <span class="text-gray-600 dark:text-gray-400">Peak Memory:</span>
740                            <span class="font-semibold text-red-600 dark:text-red-400 ml-2">${formatBytes(totalMemory)}</span>
741                        </div>
742                        <div>
743                            <span class="text-gray-600 dark:text-gray-400">Fragmentation:</span>
744                            <span class="font-semibold text-orange-600 dark:text-orange-400 ml-2">${fragmentation.toFixed(1)}%</span>
745                        </div>
746                        <div>
747                            <span class="text-gray-600 dark:text-gray-400">Efficiency:</span>
748                            <span class="font-semibold text-purple-600 dark:text-purple-400 ml-2">${efficiency.toFixed(1)}%</span>
749                        </div>
750                    </div>
751                </div>
752
753                <!-- Variable Allocation Timeline -->
754                <div class="mt-8 bg-gray-50 dark:bg-gray-700 rounded-lg p-6 border border-gray-200 dark:border-gray-600">
755                    <h3 class="text-xl font-semibold mb-4 text-gray-800 dark:text-white text-center">Variable Allocation Timeline</h3>
756                    <div class="space-y-3 max-h-64 overflow-y-auto">
757                        ${createVariableAllocationTimeline(allocations)}
758                    </div>
759                </div>
760            </div>
761        </div>
762    `;
763}
764
765// Create metric card with circular progress indicator
766function createMetricCard(title, value, percentage, color, status) {
767    const circumference = 2 * Math.PI * 25;
768    const strokeDasharray = circumference;
769    const strokeDashoffset = circumference - (percentage / 100) * circumference;
770
771    const statusColors = {
772        'OPTIMAL': '#27ae60',
773        'MEDIUM': '#f39c12',
774        'HIGH': '#e74c3c'
775    };
776
777    return `
778        <div class="bg-white dark:bg-gray-700 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow">
779            <div class="flex items-center justify-between">
780                <div class="flex-1">
781                    <p class="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase">${title}</p>
782                    <p class="text-lg font-bold text-gray-900 dark:text-white">${value}</p>
783                    <div class="flex items-center mt-1">
784                        <div class="w-2 h-2 rounded-full mr-2" style="background-color: ${statusColors[status]}"></div>
785                        <span class="text-xs font-semibold" style="color: ${statusColors[status]}">${status}</span>
786                    </div>
787                </div>
788                <div class="relative w-12 h-12">
789                    <svg class="w-12 h-12 transform -rotate-90" viewBox="0 0 60 60">
790                        <circle cx='30' cy='30' r='25' stroke='#e5e7eb' stroke-width='6' fill='none' class='dark:stroke-gray-600'/>
791                        <circle cx='30' cy='30' r='25' stroke='${color}' stroke-width='6' fill='none' 
792                                stroke-dasharray='${strokeDasharray}' stroke-dashoffset='${strokeDashoffset}'
793                                stroke-linecap='round' class='transition-all duration-500'/>
794                    </svg>
795                    <div class="absolute inset-0 flex items-center justify-center">
796                        <span class="text-xs font-bold" style="color: ${color}">${Math.round(percentage)}%</span>
797                    </div>
798                </div>
799            </div>
800        </div>
801    `;
802}
803
804// Create timeline visualization
805function createTimelineVisualization(allocations) {
806    if (allocations.length === 0) return '<div class="flex items-center justify-center h-full text-gray-400">No timeline data</div>';
807
808    const sortedAllocs = allocations.sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
809    const minTime = sortedAllocs[0]?.timestamp_alloc || 0;
810    const maxTime = sortedAllocs[sortedAllocs.length - 1]?.timestamp_alloc || minTime + 1;
811    const timeRange = maxTime - minTime || 1;
812
813    return sortedAllocs.slice(0, 20).map((alloc, index) => {
814        const position = ((alloc.timestamp_alloc - minTime) / timeRange) * 100;
815        const height = Math.min(80, Math.max(4, (alloc.size / 1024) * 20));
816        const color = alloc.var_name && alloc.var_name !== 'unknown' ? '#3498db' : '#95a5a6';
817
818        return `
819            <div class="absolute bottom-0 bg-opacity-80 rounded-t transition-all hover:bg-opacity-100" 
820                 style="left: ${position}%; width: 4px; height: ${height}%; background-color: ${color};"
821                 title="${alloc.var_name || 'System'}: ${formatBytes(alloc.size)}">
822            </div>
823        `;
824    }).join('');
825}
826
827// Create treemap-style visualization
828function createTreemapVisualization(allocations) {
829    const typeGroups = {};
830    allocations.forEach(alloc => {
831        const type = alloc.type_name || 'System';
832        if (!typeGroups[type]) {
833            typeGroups[type] = { count: 0, size: 0 };
834        }
835        typeGroups[type].count++;
836        typeGroups[type].size += alloc.size || 0;
837    });
838
839    const sortedTypes = Object.entries(typeGroups)
840        .sort(([, a], [, b]) => b.size - a.size)
841        .slice(0, 8);
842
843    const totalSize = sortedTypes.reduce((sum, [, data]) => sum + data.size, 0);
844
845    let currentX = 0;
846    return sortedTypes.map(([type, data], index) => {
847        const width = totalSize > 0 ? (data.size / totalSize) * 100 : 12.5;
848        const color = getTypeColor(type, index);
849        const result = `
850            <div class="absolute h-full transition-all hover:brightness-110 cursor-pointer rounded" 
851                 style="left: ${currentX}%; width: ${width}%; background-color: ${color};"
852                 title="${type}: ${formatBytes(data.size)} (${data.count} allocs)">
853                <div class="p-2 h-full flex flex-col justify-center text-white text-xs font-semibold text-center">
854                    <div class="truncate">${type.length > 10 ? type.substring(0, 8) + '...' : type}</div>
855                    <div class="text-xs opacity-90">${formatBytes(data.size)}</div>
856                </div>
857            </div>
858        `;
859        currentX += width;
860        return result;
861    }).join('');
862}
863
864// Create fragmentation bar
865function createFragmentationBar(label, count, total, color) {
866    const percentage = total > 0 ? (count / total) * 100 : 0;
867    return `
868        <div class="flex items-center justify-between">
869            <span class="text-sm font-medium text-gray-700 dark:text-gray-300 w-24">${label}</span>
870            <div class="flex-1 mx-3">
871                <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-4">
872                    <div class="h-4 rounded-full transition-all duration-500" 
873                         style="width: ${percentage}%; background-color: ${color}"></div>
874                </div>
875            </div>
876            <span class="text-sm font-bold text-gray-900 dark:text-white w-12 text-right">${count}</span>
877        </div>
878    `;
879}
880
881// Create growth trend visualization
882function createGrowthTrendVisualization(allocations) {
883    if (allocations.length < 2) return '<div class="flex items-center justify-center h-full text-gray-400">Insufficient data</div>';
884
885    const sortedAllocs = allocations.sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
886    const points = [];
887    let cumulativeSize = 0;
888
889    sortedAllocs.forEach((alloc, index) => {
890        cumulativeSize += alloc.size || 0;
891        if (index % Math.max(1, Math.floor(sortedAllocs.length / 10)) === 0) {
892            points.push(cumulativeSize);
893        }
894    });
895
896    const maxSize = Math.max(...points);
897
898    return points.map((size, index) => {
899        const x = (index / (points.length - 1)) * 100;
900        const y = 100 - (size / maxSize) * 80;
901
902        return `
903            <div class="absolute w-2 h-2 bg-green-500 rounded-full transform -translate-x-1 -translate-y-1" 
904                 style="left: ${x}%; top: ${y}%"
905                 title="Memory: ${formatBytes(size)}">
906            </div>
907            ${index > 0 ? `
908                <div class="absolute h-0.5 bg-green-500" 
909                     style="left: ${((index - 1) / (points.length - 1)) * 100}%; 
910                            top: ${100 - (points[index - 1] / maxSize) * 80}%; 
911                            width: ${(100 / (points.length - 1))}%;
912                            transform: rotate(${Math.atan2(y - (100 - (points[index - 1] / maxSize) * 80), 100 / (points.length - 1)) * 180 / Math.PI}deg);
913                            transform-origin: left center;">
914                </div>
915            ` : ''}
916        `;
917    }).join('');
918}
919
920// Get color for type visualization
921function getTypeColor(type, index) {
922    const colors = [
923        '#3498db', '#e74c3c', '#2ecc71', '#f39c12',
924        '#9b59b6', '#1abc9c', '#e67e22', '#95a5a6'
925    ];
926
927    if (type.toLowerCase().includes('vec')) return '#3498db';
928    if (type.toLowerCase().includes('string')) return '#f39c12';
929    if (type.toLowerCase().includes('hash')) return '#e74c3c';
930    if (type.toLowerCase().includes('btree')) return '#2ecc71';
931
932    return colors[index % colors.length];
933}
934
935// Create advanced metric card with enhanced styling
936function createAdvancedMetricCard(title, value, percentage, color, status) {
937    const circumference = 2 * Math.PI * 20;
938    const strokeDasharray = circumference;
939    const strokeDashoffset = circumference - (percentage / 100) * circumference;
940
941    const statusColors = {
942        'OPTIMAL': '#27ae60',
943        'MEDIUM': '#f39c12',
944        'HIGH': '#e74c3c'
945    };
946
947    return `
948        <div class="bg-white dark:bg-gray-700 rounded-lg p-3 shadow-sm hover:shadow-md transition-all border border-gray-200 dark:border-gray-600">
949            <div class="flex flex-col items-center">
950                <div class="relative w-10 h-10 mb-2">
951                    <svg class="w-10 h-10 transform -rotate-90" viewBox="0 0 50 50">
952                        <circle cx='25' cy='25' r='20' stroke='#e5e7eb' stroke-width='4' fill='none' class='dark:stroke-gray-600'/>
953                        <circle cx='25' cy='25' r='20' stroke='${color}' stroke-width='4' fill='none' 
954                                stroke-dasharray='${strokeDasharray}' stroke-dashoffset='${strokeDashoffset}'
955                                stroke-linecap='round' class='transition-all duration-500'/>
956                    </svg>
957                    <div class="absolute inset-0 flex items-center justify-center">
958                        <span class="text-xs font-bold" style="color: ${color}">${Math.round(percentage)}%</span>
959                    </div>
960                </div>
961                <p class="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase text-center">${title}</p>
962                <p class="text-sm font-bold text-gray-900 dark:text-white text-center">${value}</p>
963                <div class="flex items-center mt-1">
964                    <div class="w-1.5 h-1.5 rounded-full mr-1" style="background-color: ${statusColors[status]}"></div>
965                    <span class="text-xs font-semibold" style="color: ${statusColors[status]}">${status}</span>
966                </div>
967            </div>
968        </div>
969    `;
970}
971
972// Create advanced timeline visualization
973function createAdvancedTimelineVisualization(allocations, totalMemory) {
974    if (allocations.length === 0) return '<div class="flex items-center justify-center h-full text-gray-400">No timeline data</div>';
975
976    const sortedAllocs = allocations.sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
977    const minTime = sortedAllocs[0]?.timestamp_alloc || 0;
978    const maxTime = sortedAllocs[sortedAllocs.length - 1]?.timestamp_alloc || minTime + 1;
979    const timeRange = maxTime - minTime || 1;
980
981    // Group allocations by scope/type for better visualization
982    const scopeGroups = {};
983    sortedAllocs.forEach(alloc => {
984        const scope = alloc.scope_name || (alloc.var_name ? 'User Variables' : 'System');
985        if (!scopeGroups[scope]) scopeGroups[scope] = [];
986        scopeGroups[scope].push(alloc);
987    });
988
989    const scopeColors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c'];
990    let scopeIndex = 0;
991
992    return Object.entries(scopeGroups).map(([scope, allocs]) => {
993        const color = scopeColors[scopeIndex % scopeColors.length];
994        scopeIndex++;
995        const yOffset = scopeIndex * 25;
996
997        return `
998            <div class="absolute" style="top: ${yOffset}px; left: 0; right: 0; height: 20px;">
999                <div class="text-xs font-medium text-gray-700 dark:text-gray-300 mb-1" style="color: ${color}">
1000                    ${scope} (${allocs.length} allocs)
1001                </div>
1002                ${allocs.slice(0, 20).map(alloc => {
1003            const position = ((alloc.timestamp_alloc - minTime) / timeRange) * 100;
1004            const width = Math.max(2, (alloc.size / totalMemory) * 100);
1005
1006            return `
1007                        <div class="absolute h-4 rounded opacity-80 hover:opacity-100 transition-opacity cursor-pointer" 
1008                             style="left: ${position}%; width: ${Math.max(4, width)}px; background-color: ${color};"
1009                             title="${alloc.var_name || 'System'}: ${formatBytes(alloc.size)}">
1010                        </div>
1011                    `;
1012        }).join('')}
1013            </div>
1014        `;
1015    }).join('');
1016}
1017
1018// Create advanced treemap visualization inspired by SVG design
1019function createAdvancedTreemapVisualization(allocations, totalMemory) {
1020    if (allocations.length === 0) return '<div class="flex items-center justify-center h-full text-gray-400">No allocation data</div>';
1021
1022    // Group allocations by type and category
1023    const typeGroups = {};
1024    const categoryGroups = {
1025        'Collections': { types: {}, totalSize: 0, color: '#3498db' },
1026        'Basic Types': { types: {}, totalSize: 0, color: '#27ae60' },
1027        'Smart Pointers': { types: {}, totalSize: 0, color: '#9b59b6' },
1028        'System': { types: {}, totalSize: 0, color: '#95a5a6' }
1029    };
1030
1031    allocations.forEach(alloc => {
1032        const type = alloc.type_name || 'System';
1033        const category = getTypeCategory(type);
1034        const categoryName = getCategoryName(category);
1035        
1036        if (!typeGroups[type]) {
1037            typeGroups[type] = { count: 0, size: 0, category: categoryName };
1038        }
1039        typeGroups[type].count++;
1040        typeGroups[type].size += alloc.size || 0;
1041        
1042        // Add to category groups
1043        if (!categoryGroups[categoryName].types[type]) {
1044            categoryGroups[categoryName].types[type] = { count: 0, size: 0 };
1045        }
1046        categoryGroups[categoryName].types[type].count++;
1047        categoryGroups[categoryName].types[type].size += alloc.size || 0;
1048        categoryGroups[categoryName].totalSize += alloc.size || 0;
1049    });
1050
1051    // Sort categories by size
1052    const sortedCategories = Object.entries(categoryGroups)
1053        .filter(([, data]) => data.totalSize > 0)
1054        .sort(([, a], [, b]) => b.totalSize - a.totalSize);
1055
1056    let html = '';
1057    let currentY = 0;
1058    const containerHeight = 240;
1059    const padding = 8;
1060
1061    sortedCategories.forEach(([categoryName, categoryData], categoryIndex) => {
1062        const categoryPercentage = (categoryData.totalSize / totalMemory) * 100;
1063        const categoryHeight = Math.max(40, (categoryPercentage / 100) * containerHeight * 0.8);
1064        
1065        // Category container with background
1066        html += `
1067            <div class="absolute w-full rounded-lg border-2 border-white shadow-sm transition-all hover:shadow-md" 
1068                 style="top: ${currentY}px; height: ${categoryHeight}px; background-color: ${categoryData.color}; opacity: 0.15;">
1069            </div>
1070        `;
1071
1072        // Category label
1073        html += `
1074            <div class="absolute left-2 font-bold text-sm z-10" 
1075                 style="top: ${currentY + 8}px; color: ${categoryData.color};">
1076                ${categoryName} (${categoryPercentage.toFixed(1)}%)
1077            </div>
1078        `;
1079
1080        // Sort types within category
1081        const sortedTypes = Object.entries(categoryData.types)
1082            .sort(([, a], [, b]) => b.size - a.size)
1083            .slice(0, 6); // Limit to top 6 types per category
1084
1085        let currentX = 20;
1086        const typeY = currentY + 25;
1087        const availableWidth = 95; // Leave some margin
1088
1089        sortedTypes.forEach(([type, typeData], typeIndex) => {
1090            const typePercentage = (typeData.size / categoryData.totalSize) * 100;
1091            const typeWidth = Math.max(60, (typePercentage / 100) * availableWidth);
1092            const typeHeight = Math.max(20, categoryHeight - 35);
1093
1094            // Type rectangle with enhanced styling
1095            html += `
1096                <div class="absolute rounded-md border border-white shadow-sm cursor-pointer transition-all hover:brightness-110 hover:scale-105 hover:z-20" 
1097                     style="left: ${currentX}px; top: ${typeY}px; width: ${typeWidth}px; height: ${typeHeight}px; 
1098                            background-color: ${categoryData.color}; opacity: 0.9;"
1099                     title="${type}: ${formatBytes(typeData.size)} (${typeData.count} allocs, ${typePercentage.toFixed(1)}% of ${categoryName})">
1100                    <div class="p-1 h-full flex flex-col justify-center text-white text-xs font-bold text-center">
1101                        <div class="truncate text-shadow" style="text-shadow: 1px 1px 2px rgba(0,0,0,0.8);">
1102                            ${type.length > 12 ? type.substring(0, 10) + '..' : type}
1103                        </div>
1104                        <div class="text-xs opacity-90 font-semibold" style="text-shadow: 1px 1px 2px rgba(0,0,0,0.6);">
1105                            ${formatBytes(typeData.size)}
1106                        </div>
1107                        <div class="text-xs opacity-75" style="text-shadow: 1px 1px 2px rgba(0,0,0,0.6);">
1108                            (${typePercentage.toFixed(1)}%)
1109                        </div>
1110                    </div>
1111                </div>
1112            `;
1113
1114            currentX += typeWidth + 4;
1115        });
1116
1117        currentY += categoryHeight + padding;
1118    });
1119
1120    return html;
1121}
1122
1123// Helper function to get category name
1124function getCategoryName(category) {
1125    const categoryMap = {
1126        'collections': 'Collections',
1127        'basic': 'Basic Types',
1128        'smart_pointers': 'Smart Pointers',
1129        'system': 'System'
1130    };
1131    return categoryMap[category] || 'System';
1132}
1133
1134// Create advanced fragmentation bar
1135function createAdvancedFragmentationBar(label, count, total, color) {
1136    const percentage = total > 0 ? (count / total) * 100 : 0;
1137    const barHeight = Math.max(8, (count / total) * 60);
1138
1139    return `
1140        <div class="flex items-center justify-between ">
1141            <div class="flex items-center w-32">
1142                <div class="w-4 rounded mr-3 border border-gray-300 dark:border-gray-500" 
1143                     style="height: ${barHeight}px; background-color: ${color}"></div>
1144                <span class="text-sm font-medium text-gray-700 dark:text-gray-300">${label}</span>
1145            </div>
1146            <div class="flex-1 mx-3">
1147                <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-3">
1148                    <div class="h-3 rounded-full transition-all duration-500" 
1149                         style="width: ${percentage}%; background-color: ${color}"></div>
1150                </div>
1151            </div>
1152            <span class="text-sm font-bold text-gray-900 dark:text-white w-12 text-right ">${count}</span>
1153        </div>
1154    `;
1155}
1156
1157// Create call stack analysis
1158function createCallStackAnalysis(allocations) {
1159    const userAllocs = allocations.filter(a => a.var_name && a.var_name !== 'unknown');
1160    const systemAllocs = allocations.filter(a => !a.var_name || a.var_name === 'unknown');
1161
1162    const topAllocations = [...userAllocs, ...systemAllocs.slice(0, 3)]
1163        .sort((a, b) => (b.size || 0) - (a.size || 0))
1164        .slice(0, 10);
1165
1166    return topAllocations.map(alloc => {
1167        const isSystem = !alloc.var_name || alloc.var_name === 'unknown';
1168        const color = isSystem ? '#e74c3c' : getTypeColor(alloc.type_name || '', 0);
1169        const radius = Math.min(8, Math.max(3, Math.sqrt((alloc.size || 0) / 100)));
1170
1171        return `
1172            <div class="flex items-center space-x-3 p-2 bg-white dark:bg-gray-600 rounded border ">
1173                <div class="w-4 h-4 rounded-full border-2 border-gray-300 dark:border-gray-500" 
1174                     style="background-color: ${color}"></div>
1175                <div class="flex-1 min-w-0">
1176                    <div class="text-sm font-medium text-gray-900 dark:text-white truncate ">
1177                        ${alloc.var_name || 'System/Runtime allocations'}
1178                    </div>
1179                    <div class="text-xs text-gray-500 dark:text-gray-400 ">
1180                        ${alloc.type_name || 'no type info'} โ€ข ${formatBytes(alloc.size || 0)}
1181                    </div>
1182                </div>
1183            </div>
1184        `;
1185    }).join('');
1186}
1187
1188// Create advanced growth trend visualization
1189function createAdvancedGrowthTrendVisualization(allocations, totalMemory) {
1190    if (allocations.length < 2) return '<div class="flex items-center justify-center h-full text-gray-400 ">Insufficient data</div>';
1191
1192    const sortedAllocs = allocations.sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
1193    const points = [];
1194    let cumulativeSize = 0;
1195
1196    sortedAllocs.forEach((alloc, index) => {
1197        cumulativeSize += alloc.size || 0;
1198        if (index % Math.max(1, Math.floor(sortedAllocs.length / 15)) === 0) {
1199            points.push({
1200                x: (index / sortedAllocs.length) * 100,
1201                y: 100 - (cumulativeSize / totalMemory) * 80,
1202                size: cumulativeSize
1203            });
1204        }
1205    });
1206
1207    return `
1208        <!-- Background Grid -->
1209        <div class="absolute inset-0">
1210            ${[20, 40, 60, 80].map(y => `
1211                <div class="absolute w-full border-t border-gray-200 dark:border-gray-500 opacity-30" 
1212                     style="top: ${y}%"></div>
1213            `).join('')}
1214        </div>
1215        
1216        <!-- Growth Line -->
1217        <svg class="absolute inset-0 w-full h-full ">
1218            <polyline
1219                fill="none"
1220                stroke='#27ae60'
1221                stroke-width="3"
1222                stroke-linecap="round"
1223                stroke-linejoin="round"
1224                points="${points.map(p => `${p.x},${p.y}`).join(' ')}"
1225                class="drop-shadow-sm "
1226            />
1227        </svg>
1228        
1229        <!-- Data Points -->
1230        ${points.map(point => `
1231            <div class="absolute w-2 h-2 bg-green-500 rounded-full border border-white dark:border-gray-600 transform -translate-x-1/2 -translate-y-1/2 hover:scale-150 transition-transform cursor-pointer " 
1232                 style="left: ${point.x}%; top: ${point.y}%"
1233                 title="Memory: ${formatBytes(point.size)}">
1234            </div>
1235        `).join('')}
1236        
1237        <!-- Peak Memory Line -->
1238        <div class="absolute w-full border-t-2 border-red-500 border-dashed opacity-60" style="top: 20%">
1239            <div class="absolute -top-1 right-0 text-xs text-red-500 bg-white dark:bg-gray-600 px-1 rounded ">
1240                Peak: ${formatBytes(totalMemory)}
1241            </div>
1242        </div>
1243    `;
1244}
1245
1246// Create variable allocation timeline
1247function createVariableAllocationTimeline(allocations) {
1248    const userAllocs = allocations.filter(a => a.var_name && a.var_name !== 'unknown')
1249        .sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0))
1250        .slice(0, 10);
1251
1252    return userAllocs.map((alloc, index) => {
1253        const color = getTypeColor(alloc.type_name || '', index);
1254
1255        return `
1256            <div class="flex items-center space-x-3 p-2 bg-white dark:bg-gray-600 rounded border ">
1257                <div class="w-3 h-3 rounded-full" style="background-color: ${color}"></div>
1258                <div class="flex-1 min-w-0">
1259                    <div class="text-sm font-medium text-gray-900 dark:text-white ">
1260                        ${alloc.var_name}
1261                    </div>
1262                    <div class="text-xs text-gray-500 dark:text-gray-400">
1263                        ${alloc.type_name || 'unknown'} โ€ข ${formatBytes(alloc.size || 0)}
1264                    </div>
1265                </div>
1266                <div class="text-xs text-gray-500 dark:text-gray-400">
1267                    ${new Date(alloc.timestamp_alloc / 1000000).toLocaleTimeString()}
1268                </div>
1269            </div>
1270        `;
1271    }).join('');
1272}
1273
1274// Helper functions for type categorization
1275function getTypeCategory(type) {
1276    if (!type || type === 'System' || type === 'unknown') return 'system';
1277    
1278    const typeLower = type.toLowerCase();
1279    
1280    // Collections
1281    if (typeLower.includes('vec') || typeLower.includes('hash') || typeLower.includes('btree') || 
1282        typeLower.includes('deque') || typeLower.includes('set') || typeLower.includes('map')) {
1283        return 'collections';
1284    }
1285    
1286    // Smart Pointers
1287    if (typeLower.includes('box') || typeLower.includes('rc') || typeLower.includes('arc') || 
1288        typeLower.includes('refcell') || typeLower.includes('cell') || typeLower.includes('weak')) {
1289        return 'smart_pointers';
1290    }
1291    
1292    // Basic types (String, primitives, etc.)
1293    return 'basic';
1294}
1295
1296function getCategoryColor(category) {
1297    const colors = {
1298        'collections': '#3498db',      // Bright blue
1299        'basic': '#27ae60',           // Bright green  
1300        'smart_pointers': '#9b59b6',  // Purple
1301        'system': '#95a5a6'           // Gray
1302    };
1303    return colors[category] || '#95a5a6';
1304}
1305
1306// Initialize allocations table with improved collapsible functionality
1307function initAllocationsTable() {
1308    console.log('๐Ÿ“Š Initializing allocations table...');
1309
1310    const tbody = document.getElementById('allocations-table');
1311    const toggleButton = document.getElementById('toggle-allocations');
1312
1313    if (!tbody) {
1314        console.warn('โš ๏ธ Allocations table body not found');
1315        return;
1316    }
1317
1318    const allocations = window.analysisData.memory_analysis?.allocations || [];
1319
1320    if (allocations.length === 0) {
1321        tbody.innerHTML = '<tr><td colspan="5" class="px-4 py-8 text-center text-gray-500 dark:text-gray-400">No allocations found</td></tr>';
1322        if (toggleButton) {
1323            toggleButton.style.display = 'none';
1324        }
1325        return;
1326    }
1327
1328    let isExpanded = false;
1329    const maxInitialRows = 5;
1330
1331    function renderTable(showAll = false) {
1332        console.log(`๐Ÿ“Š Rendering table, showAll: ${showAll}, total allocations: ${allocations.length}`);
1333
1334        const displayAllocations = showAll ? allocations : allocations.slice(0, maxInitialRows);
1335
1336        tbody.innerHTML = displayAllocations.map(alloc => `
1337            <tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors ">
1338                <td class="px-4 py-2 text-gray-900 dark:text-gray-100 font-mono ">0x${(alloc.ptr ? parseInt(alloc.ptr.toString().replace('0x', ''), 16) : 0).toString(16).padStart(8, '0')}</td>
1339                <td class="px-4 py-2 text-gray-900 dark:text-gray-100">${alloc.var_name || 'System Allocation'}</td>
1340                <td class="px-4 py-2 text-gray-900 dark:text-gray-100">${formatTypeName(alloc.type_name || 'System Allocation')}</td>
1341                <td class="px-4 py-2 text-right text-gray-900 dark:text-gray-100">${formatBytes(alloc.size || 0)}</td>
1342                <td class="px-4 py-2 text-right text-gray-900 dark:text-gray-100">
1343                    <span class="px-2 py-1 text-xs rounded-full ${alloc.is_active ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 '} ">
1344                        ${alloc.is_active ? 'Active' : 'Deallocated'}
1345                    </span>
1346                </td>
1347            </tr>
1348        `).join('');
1349
1350        if (!showAll && allocations.length > maxInitialRows) {
1351            tbody.innerHTML += `
1352                <tr class="bg-gray-50 dark:bg-gray-700">
1353                    <td colspan="5" class="px-4 py-2 text-center text-gray-500 dark:text-gray-400 text-sm">
1354                        ... and ${allocations.length - maxInitialRows} more allocations
1355                    </td>
1356                </tr>
1357            `;
1358        }
1359    }
1360
1361    // Initial render
1362    renderTable(false);
1363
1364    // Toggle functionality - Fixed event binding
1365    if (toggleButton && allocations.length > maxInitialRows) {
1366        console.log('๐Ÿ“Š Setting up toggle button for', allocations.length, 'allocations');
1367
1368        // Clear any existing event listeners and add new one
1369        toggleButton.replaceWith(toggleButton.cloneNode(true));
1370        const newToggleButton = document.getElementById('toggle-allocations');
1371
1372        newToggleButton.addEventListener('click', function (e) {
1373            e.preventDefault();
1374            e.stopPropagation();
1375            console.log('๐Ÿ“Š Toggle button clicked, current state:', isExpanded);
1376
1377            isExpanded = !isExpanded;
1378            renderTable(isExpanded);
1379
1380            const icon = newToggleButton.querySelector('i');
1381            const text = newToggleButton.querySelector('span');
1382
1383            if (isExpanded) {
1384                icon.className = 'fa fa-chevron-up mr-1';
1385                text.textContent = 'Show Less';
1386                console.log('๐Ÿ“Š Expanded table to show all allocations');
1387            } else {
1388                icon.className = 'fa fa-chevron-down mr-1';
1389                text.textContent = 'Show All';
1390                console.log('๐Ÿ“Š Collapsed table to show first', maxInitialRows, 'allocations');
1391            }
1392        });
1393
1394        console.log('โœ… Toggle button initialized successfully');
1395    } else if (toggleButton) {
1396        // Hide button if not needed
1397        toggleButton.style.display = 'none';
1398        console.log('๐Ÿ“Š Toggle button hidden (not enough data)');
1399    }
1400}
1401
1402// Initialize lifetime visualization from JSON data with collapsible functionality
1403function initLifetimeVisualization() {
1404    console.log('๐Ÿ”„ Initializing lifetime visualization...');
1405
1406    // Get lifetime data from various sources (support extended data structure)
1407    let lifetimeData = null;
1408    let lifecycleEvents = [];
1409    
1410    // Smart data source selection: merge memory_analysis and complex_types data
1411    let memoryAllocations = window.analysisData.memory_analysis?.allocations || [];
1412    let complexAllocations = window.analysisData.complex_types?.allocations || [];
1413    
1414    console.log('๐Ÿ“Š Memory analysis allocations:', memoryAllocations.length);
1415    console.log('๐Ÿ“Š Complex types allocations:', complexAllocations.length);
1416    
1417    // ๅˆๅนถๆ•ฐๆฎ๏ผšไฝฟ็”จmemory_analysis็š„lifetime_ms + complex_types็š„ๆ‰ฉๅฑ•ๅญ—ๆฎต
1418    if (memoryAllocations.length > 0 && complexAllocations.length > 0) {
1419        // Create mapping from pointer to memory analysis data
1420        const memoryMap = new Map();
1421        memoryAllocations.forEach(alloc => {
1422            if (alloc.ptr) {
1423                memoryMap.set(alloc.ptr, alloc);
1424            }
1425        });
1426        
1427        // Merge data: complex_types + lifetime_ms from memory_analysis
1428        lifecycleEvents = complexAllocations.map(complexAlloc => {
1429            const memoryAlloc = memoryMap.get(complexAlloc.ptr);
1430            return {
1431                ...complexAlloc,
1432                lifetime_ms: memoryAlloc?.lifetime_ms || null,
1433                timestamp_dealloc: memoryAlloc?.timestamp_dealloc || null
1434            };
1435        });
1436        console.log('๐Ÿ“Š Merged allocation data:', lifecycleEvents.length);
1437    } else if (memoryAllocations.length > 0) {
1438        lifecycleEvents = memoryAllocations;
1439        console.log('๐Ÿ“Š Using memory analysis data:', lifecycleEvents.length);
1440    } else if (complexAllocations.length > 0) {
1441        lifecycleEvents = complexAllocations;
1442        console.log('๐Ÿ“Š Using complex types data:', lifecycleEvents.length);
1443    } else if (window.analysisData.lifetime?.lifecycle_events) {
1444        lifecycleEvents = window.analysisData.lifetime.lifecycle_events;
1445        console.log('๐Ÿ“Š Using lifetime events data:', lifecycleEvents.length);
1446    }
1447    
1448    if (!lifecycleEvents || lifecycleEvents.length === 0) {
1449        console.warn('โš ๏ธ No lifetime data found');
1450        console.log('Available data keys:', Object.keys(window.analysisData || {}));
1451        showEmptyLifetimeState();
1452        return;
1453    }
1454
1455    console.log(`๐Ÿ“Š Total lifecycle events: ${lifecycleEvents.length}`);
1456
1457    // Check if we have Rust-preprocessed data
1458    if (lifetimeData?.visualization_ready && lifetimeData?.variable_groups) {
1459        console.log(`๐Ÿ“Š Using Rust-preprocessed data with ${lifetimeData.variable_groups.length} variable groups`);
1460        renderLifetimeVisualizationFromRustWithCollapse(lifetimeData.variable_groups);
1461        return;
1462    }
1463
1464    // Filter for user-defined variables (non-unknown var_name and type_name)
1465    const userVariables = lifecycleEvents.filter(event =>
1466        event.var_name && event.var_name !== 'unknown' &&
1467        event.type_name && event.type_name !== 'unknown'
1468    );
1469
1470    console.log(`๐Ÿ“Š Found ${userVariables.length} user-defined variables in lifetime data`);
1471
1472    // Debug: Show some examples of what we found
1473    if (userVariables.length > 0) {
1474        console.log('๐Ÿ“Š Sample user variables:', userVariables.slice(0, 3));
1475    } else {
1476        // Show some examples of unknown variables for debugging
1477        const unknownSamples = lifecycleEvents.slice(0, 3);
1478        console.log('๐Ÿ“Š Sample unknown variables:', unknownSamples);
1479    }
1480
1481    if (userVariables.length === 0) {
1482        showEmptyLifetimeState();
1483        return;
1484    }
1485
1486    // Group by variable name to get allocation/deallocation pairs
1487    const variableGroups = groupVariablesByName(userVariables);
1488
1489    // Render the lifetime visualization with collapse functionality
1490    renderLifetimeVisualizationWithCollapse(variableGroups);
1491}
1492
1493// Group variables by name to track their lifecycle (enhanced for multiple instances)
1494function groupVariablesByName(events) {
1495    const groups = {};
1496
1497    events.forEach(event => {
1498        const varName = event.var_name;
1499        const instanceKey = `${varName}_${event.ptr || event.timestamp_alloc}`; // ไธบๆฏไธชๅฎžไพ‹ๅˆ›ๅปบๅ”ฏไธ€key
1500        
1501        if (!groups[instanceKey]) {
1502            groups[instanceKey] = {
1503                var_name: `${varName}#${Object.keys(groups).filter(k => k.startsWith(varName)).length + 1}`, // ๆทปๅŠ ๅฎžไพ‹็ผ–ๅท
1504                original_var_name: varName,
1505                type_name: event.type_name,
1506                events: [],
1507                instance_info: {
1508                    ptr: event.ptr,
1509                    timestamp: event.timestamp_alloc,
1510                    thread_id: event.thread_id
1511                }
1512            };
1513        }
1514        groups[instanceKey].events.push(event);
1515    });
1516
1517    
1518    const groupValues = Object.values(groups);
1519    const varCounts = {};
1520    groupValues.forEach(group => {
1521        const originalName = group.original_var_name;
1522        varCounts[originalName] = (varCounts[originalName] || 0) + 1;
1523    });
1524    
1525    groupValues.forEach(group => {
1526        if (varCounts[group.original_var_name] === 1) {
1527            group.var_name = group.original_var_name; 
1528        }
1529    });
1530
1531    return groupValues;
1532}
1533
1534// Render lifetime visualization from Rust-preprocessed data with collapsible functionality
1535function renderLifetimeVisualizationFromRustWithCollapse(variableGroups) {
1536    console.log(`๐Ÿ“Š Rendering ${variableGroups.length} Rust-preprocessed variable groups with collapse functionality`);
1537
1538    const container = document.getElementById('lifetimeVisualization');
1539    const toggleButton = document.getElementById('toggle-lifecycle');
1540    
1541    if (!container) return;
1542
1543    // Clear loading state
1544    container.innerHTML = '';
1545
1546    if (!variableGroups || variableGroups.length === 0) {
1547        showEmptyLifetimeState();
1548        if (toggleButton) {
1549            toggleButton.style.display = 'none';
1550        }
1551        return;
1552    }
1553
1554    let isExpanded = false;
1555    const maxInitialRows = 5;
1556
1557    // Calculate timeline bounds from preprocessed data
1558    const allTimestamps = variableGroups.flatMap(group =>
1559        group.events ? group.events.map(e => e.timestamp) : [group.start_time, group.end_time].filter(t => t !== undefined)
1560    );
1561
1562    const minTime = Math.min(...allTimestamps);
1563    const maxTime = Math.max(...allTimestamps);
1564    const timeRange = maxTime - minTime || 1;
1565
1566    console.log(`๐Ÿ“Š Rust data timeline: ${minTime} to ${maxTime} (range: ${timeRange})`);
1567
1568    // Color palette for different data types and visualizations
1569    const COLOR_PALETTE = {
1570        progress: [
1571            '#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57',
1572            '#ff9ff3', '#54a0ff', '#5f27cd', '#00d2d3', '#ff9f43'
1573        ]
1574    };
1575
1576    function renderLifetimeRows(showAll = false) {
1577        console.log(`๐Ÿ“Š Rendering lifecycle rows, showAll: ${showAll}, total groups: ${variableGroups.length}`);
1578        
1579        container.innerHTML = '';
1580        
1581        const displayGroups = showAll ? variableGroups : variableGroups.slice(0, maxInitialRows);
1582
1583        // Render each variable with colorful progress bars
1584        displayGroups.forEach((group, index) => {
1585            const varDiv = document.createElement('div');
1586            varDiv.className = 'flex items-center py-4 border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors';
1587
1588            // Get color from palette (cycle through colors)
1589            const colorIndex = index % COLOR_PALETTE.progress.length;
1590            const progressColor = COLOR_PALETTE.progress[colorIndex];
1591
1592            // Use preprocessed timing data or fallback to events
1593            const startTime = group.start_time || (group.events && group.events[0] ? group.events[0].timestamp : minTime);
1594            const firstEvent = group.events && group.events[0];
1595            
1596            const startPercent = timeRange > 0 ? ((startTime - minTime) / timeRange) * 100 : 0;
1597            
1598            
1599            let widthPercent;
1600            if (firstEvent && firstEvent.lifetime_ms && firstEvent.lifetime_ms > 0) {
1601                
1602                const lifetimeNs = firstEvent.lifetime_ms * 1000000; 
1603                widthPercent = timeRange > 0 ? Math.max(1, (lifetimeNs / timeRange) * 100) : 6.8;
1604            } else {
1605                //
1606                widthPercent = 6.8;
1607            }
1608            
1609            // ๅฎ‰ๅ…จ็š„ๅ˜้‡ๅฎšไน‰๏ผŒ้˜ฒๆญขNaN
1610            const finalStartPercent = isNaN(startPercent) ? 0 : Math.max(0, Math.min(95, startPercent));
1611            const finalWidthPercent = isNaN(widthPercent) ? 40 : Math.max(2, Math.min(100 - finalStartPercent, widthPercent));
1612
1613            // Format type name for display
1614            const displayTypeName = formatTypeName(group.type_name);
1615
1616            // Create gradient background for more visual appeal
1617            const gradientStyle = `background: linear-gradient(90deg, ${progressColor}, ${progressColor}dd);`;
1618
1619            varDiv.innerHTML = `
1620                <div class="w-48 flex-shrink-0 pr-4">
1621                    <div class="text-sm font-semibold text-gray-800 dark:text-gray-200">${group.var_name}</div>
1622                    <div class="text-xs text-gray-500 dark:text-gray-400">${displayTypeName}</div>
1623                </div>
1624                <div class="flex-grow relative bg-gray-200 dark:bg-gray-600 rounded-full h-6 overflow-hidden">
1625                    <div class="absolute inset-0 rounded-full" 
1626                         style="${gradientStyle} width: ${finalWidthPercent}%; margin-left: ${finalStartPercent}%; 
1627                                box-shadow: 0 2px 4px rgba(0,0,0,0.1); 
1628                                transition: all 0.3s ease;"
1629                         title="Variable: ${group.var_name}, Type: ${displayTypeName}">
1630                        <div class="absolute inset-0 flex items-center justify-center">
1631                            <span class="text-xs font-bold text-white drop-shadow-sm">
1632                                ${Math.round(finalWidthPercent)}%
1633                            </span>
1634                        </div>
1635                    </div>
1636                    <div class="absolute -top-8 left-0 text-xs bg-gray-700 text-white px-2 py-1 rounded opacity-0 hover:opacity-100 transition-opacity whitespace-nowrap">
1637                        Duration: ${firstEvent && firstEvent.lifetime_ms ? firstEvent.lifetime_ms + 'ms' : 'Active'}
1638                    </div>
1639                </div>
1640                <div class="w-20 flex-shrink-0 pl-4 text-right">
1641                    <div class="text-xs text-gray-600 dark:text-gray-400">
1642                        ${formatBytes(group.size || (group.events && group.events[0] ? group.events[0].size : 0) || 0)}
1643                    </div>
1644                </div>
1645            `;
1646
1647            container.appendChild(varDiv);
1648        });
1649
1650        // Add "show more" indicator if collapsed
1651        if (!showAll && variableGroups.length > maxInitialRows) {
1652            const moreDiv = document.createElement('div');
1653            moreDiv.className = 'flex items-center py-4 bg-gray-50 dark:bg-gray-700 border-b border-gray-100 dark:border-gray-600';
1654            moreDiv.innerHTML = `
1655                <div class="w-full text-center text-gray-500 dark:text-gray-400 text-sm">
1656                    ... and ${variableGroups.length - maxInitialRows} more variables
1657                </div>
1658            `;
1659            container.appendChild(moreDiv);
1660        }
1661    }
1662
1663    // Initial render
1664    renderLifetimeRows(false);
1665
1666    // Toggle functionality
1667    if (toggleButton && variableGroups.length > maxInitialRows) {
1668        console.log('๐Ÿ“Š Setting up lifecycle toggle button for', variableGroups.length, 'variables');
1669
1670        // Remove any existing event listeners
1671        const newToggleButton = toggleButton.cloneNode(true);
1672        toggleButton.parentNode.replaceChild(newToggleButton, toggleButton);
1673
1674        newToggleButton.addEventListener('click', function (e) {
1675            e.preventDefault();
1676            console.log('๐Ÿ“Š Lifecycle toggle button clicked, current state:', isExpanded);
1677
1678            isExpanded = !isExpanded;
1679            renderLifetimeRows(isExpanded);
1680
1681            const icon = newToggleButton.querySelector('i');
1682            const text = newToggleButton.querySelector('span');
1683
1684            if (isExpanded) {
1685                icon.className = 'fa fa-chevron-up mr-1';
1686                text.textContent = 'Show Less';
1687                console.log('๐Ÿ“Š Expanded lifecycle to show all variables');
1688            } else {
1689                icon.className = 'fa fa-chevron-down mr-1';
1690                text.textContent = 'Show All';
1691                console.log('๐Ÿ“Š Collapsed lifecycle to show first', maxInitialRows, 'variables');
1692            }
1693        });
1694
1695        console.log('โœ… Lifecycle toggle button initialized successfully');
1696    } else if (toggleButton) {
1697        // Hide button if not needed
1698        toggleButton.style.display = 'none';
1699        console.log('๐Ÿ“Š Lifecycle toggle button hidden (not enough data)');
1700    }
1701
1702    console.log(`โœ… Rendered ${variableGroups.length} Rust-preprocessed variables in lifetime visualization with collapse functionality`);
1703}
1704
1705// Render the lifetime visualization with collapsible functionality
1706function renderLifetimeVisualizationWithCollapse(variableGroups) {
1707    const container = document.getElementById('lifetimeVisualization');
1708    const toggleButton = document.getElementById('toggle-lifecycle');
1709    
1710    if (!container) return;
1711
1712    // Clear loading state
1713    container.innerHTML = '';
1714
1715    if (!variableGroups || variableGroups.length === 0) {
1716        showEmptyLifetimeState();
1717        if (toggleButton) {
1718            toggleButton.style.display = 'none';
1719        }
1720        return;
1721    }
1722
1723    let isExpanded = false;
1724    const maxInitialRows = 5;
1725
1726    // Get color scheme for different types
1727    const typeColors = {
1728        'Vec': { bg: 'bg-blue-500', border: 'border-blue-500' },
1729        'Box': { bg: 'bg-purple-500', border: 'border-purple-500' },
1730        'Rc': { bg: 'bg-yellow-500', border: 'border-yellow-500' },
1731        'Arc': { bg: 'bg-green-500', border: 'border-green-500' },
1732        'String': { bg: 'bg-pink-500', border: 'border-pink-500' },
1733        'default': { bg: 'bg-gray-500', border: 'border-gray-500' }
1734    };
1735
1736    // Calculate timeline bounds
1737    const allTimestamps = variableGroups.flatMap(group =>
1738        group.events.map(e => e.timestamp)
1739    );
1740    const minTime = Math.min(...allTimestamps);
1741    const maxTime = Math.max(...allTimestamps);
1742    const timeRange = maxTime - minTime;
1743
1744    console.log(`๐Ÿ“Š Timeline: ${minTime} to ${maxTime} (range: ${timeRange})`);
1745
1746    function renderLifetimeRows(showAll = false) {
1747        console.log(`๐Ÿ“Š Rendering lifecycle rows, showAll: ${showAll}, total groups: ${variableGroups.length}`);
1748        
1749        container.innerHTML = '';
1750        
1751        const displayGroups = showAll ? variableGroups : variableGroups.slice(0, maxInitialRows);
1752
1753        // Render each variable
1754        displayGroups.forEach((group) => {
1755            const varDiv = document.createElement('div');
1756            varDiv.className = 'flex items-end py-3 border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors';
1757
1758            // Determine color based on type
1759            const typeKey = Object.keys(typeColors).find(key =>
1760                group.type_name.includes(key)
1761            ) || 'default';
1762            const colors = typeColors[typeKey];
1763
1764            // Calculate position and width based on timestamps
1765            const firstEvent = group.events[0];
1766            const startTime = firstEvent.timestamp;
1767            const startPositionPercent = timeRange > 0 ? ((startTime - minTime) / timeRange) * 100 : 0;
1768
1769            // real time correct time axis calculation: based on actual allocation and survival time
1770            const allocTime = firstEvent.timestamp;
1771            const deallocTime = firstEvent.timestamp_dealloc;
1772            const lifetimeMs = firstEvent.lifetime_ms || 1; // default 1ms lifetime
1773            
1774            // calculate survival time length (percentage)
1775            let durationPercent;
1776            if (deallocTime && deallocTime > allocTime) {
1777                // if there is a clear release time, use actual time span
1778                const actualDuration = deallocTime - allocTime;
1779                durationPercent = (actualDuration / timeRange) * 100;
1780            } else {
1781                // if there is no release time, use lifetime_ms calculation
1782                const lifetimeNs = lifetimeMs * 1000000; // convert to nanoseconds
1783                durationPercent = (lifetimeNs / timeRange) * 100;
1784            }
1785            
1786            // ensure value is within reasonable range
1787            const widthPercent = Math.max(0.5, Math.min(100 - startPositionPercent, durationPercent));
1788            
1789            // ๅฎ‰ๅ…จ็š„ๅ˜้‡ๅฎšไน‰๏ผŒ้˜ฒๆญขNaN
1790            const finalStartPercent = isNaN(startPositionPercent) ? 0 : Math.max(0, Math.min(95, startPositionPercent));
1791            const finalWidthPercent = isNaN(widthPercent) ? 30 : Math.max(2, Math.min(100 - finalStartPercent, widthPercent));
1792
1793            // Format type name for display
1794            const displayTypeName = formatTypeName(group.type_name);
1795
1796            varDiv.innerHTML = `
1797                <div class="w-40 flex-shrink-0 text-sm font-medium dark:text-gray-200">
1798                    ${group.var_name} (${displayTypeName})
1799                </div>
1800                <div class="flex-grow relative">
1801                    <div class="lifespan-indicator ${colors.bg}" 
1802                         style="width: ${finalWidthPercent}%; margin-left: ${finalStartPercent}%;" 
1803                         title="Variable: ${group.var_name}, Type: ${displayTypeName}">
1804                        <div class="absolute -top-6 left-0 text-xs ${colors.bg} text-white px-2 py-1 rounded whitespace-nowrap">
1805                            Allocated: ${formatTimestamp(startTime, minTime)}
1806                        </div>
1807                    </div>
1808                </div>
1809            `;
1810
1811            container.appendChild(varDiv);
1812        });
1813
1814        // Add "show more" indicator if collapsed
1815        if (!showAll && variableGroups.length > maxInitialRows) {
1816            const moreDiv = document.createElement('div');
1817            moreDiv.className = 'flex items-center py-3 bg-gray-50 dark:bg-gray-700 border-b border-gray-100 dark:border-gray-600';
1818            moreDiv.innerHTML = `
1819                <div class="w-full text-center text-gray-500 dark:text-gray-400 text-sm">
1820                    ... and ${variableGroups.length - maxInitialRows} more variables
1821                </div>
1822            `;
1823            container.appendChild(moreDiv);
1824        }
1825    }
1826
1827    // Initial render
1828    renderLifetimeRows(false);
1829
1830    // Toggle functionality
1831    if (toggleButton && variableGroups.length > maxInitialRows) {
1832        console.log('๐Ÿ“Š Setting up lifecycle toggle button for', variableGroups.length, 'variables');
1833
1834        // Remove any existing event listeners
1835        const newToggleButton = toggleButton.cloneNode(true);
1836        toggleButton.parentNode.replaceChild(newToggleButton, toggleButton);
1837
1838        newToggleButton.addEventListener('click', function (e) {
1839            e.preventDefault();
1840            console.log('๐Ÿ“Š Lifecycle toggle button clicked, current state:', isExpanded);
1841
1842            isExpanded = !isExpanded;
1843            renderLifetimeRows(isExpanded);
1844
1845            const icon = newToggleButton.querySelector('i');
1846            const text = newToggleButton.querySelector('span');
1847
1848            if (isExpanded) {
1849                icon.className = 'fa fa-chevron-up mr-1';
1850                text.textContent = 'Show Less';
1851                console.log('๐Ÿ“Š Expanded lifecycle to show all variables');
1852            } else {
1853                icon.className = 'fa fa-chevron-down mr-1';
1854                text.textContent = 'Show All';
1855                console.log('๐Ÿ“Š Collapsed lifecycle to show first', maxInitialRows, 'variables');
1856            }
1857        });
1858
1859        console.log('โœ… Lifecycle toggle button initialized successfully');
1860    } else if (toggleButton) {
1861        // Hide button if not needed
1862        toggleButton.style.display = 'none';
1863        console.log('๐Ÿ“Š Lifecycle toggle button hidden (not enough data)');
1864    }
1865
1866    console.log(`โœ… Rendered ${variableGroups.length} variables in lifetime visualization with collapse functionality`);
1867}
1868
1869// Initialize FFI visualization with enhanced support for improve.md fields
1870function initFFIVisualization() {
1871    console.log('๐Ÿ”„ Initializing FFI visualization...');
1872
1873    const container = document.getElementById('ffiVisualization');
1874    if (!container) return;
1875
1876    // Get FFI data from multiple sources with comprehensive field support
1877    let allocations = [];
1878    let unsafeReports = [];
1879    let memoryPassports = [];
1880    let ffiStatistics = {};
1881    
1882    console.log('๐Ÿ” Checking analysisData structure:', Object.keys(window.analysisData || {}));
1883    
1884    // Enhanced data extraction supporting improve.md structure
1885    if (window.analysisData) {
1886        // Debug: Show what data structure we actually have FIRST
1887        console.log('๐Ÿ” Available data keys:', Object.keys(window.analysisData));
1888        if (window.analysisData.unsafe_ffi) {
1889            console.log('๐Ÿ” unsafe_ffi keys:', Object.keys(window.analysisData.unsafe_ffi));
1890            console.log('๐Ÿ” unsafe_ffi.allocations exists:', !!window.analysisData.unsafe_ffi.allocations);
1891            
1892            // Data will be handled by initializeAnalysis function
1893            console.log('๐Ÿ” unsafe_ffi.allocations length:', window.analysisData.unsafe_ffi.allocations ? window.analysisData.unsafe_ffi.allocations.length : 'undefined');
1894        }
1895        
1896        // Try unsafe_ffi data first (improve.md structure)
1897        if (window.analysisData.unsafe_ffi) {
1898            allocations = window.analysisData.unsafe_ffi.allocations || [];
1899            unsafeReports = window.analysisData.unsafe_ffi.unsafe_reports || [];
1900            memoryPassports = window.analysisData.unsafe_ffi.memory_passports || [];
1901            ffiStatistics = window.analysisData.unsafe_ffi.ffi_statistics || {};
1902            console.log('๐Ÿ“Š Found unsafe_ffi data - allocations:', allocations.length, 'reports:', unsafeReports.length, 'passports:', memoryPassports.length);
1903        }
1904        // Try complex_types structure (for large_scale_user files)
1905        else if (window.analysisData.complex_types && window.analysisData.complex_types.allocations) {
1906            allocations = window.analysisData.complex_types.allocations;
1907            console.log('๐Ÿ“Š Found complex_types allocations:', allocations.length);
1908        }
1909        // Try direct allocations array (for files like large_scale_user_unsafe_ffi.json)
1910        else if (window.analysisData.allocations) {
1911            allocations = window.analysisData.allocations;
1912            console.log('๐Ÿ“Š Found direct allocations:', allocations.length);
1913        }
1914        // Fallback to memory_analysis
1915        else if (window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
1916            allocations = window.analysisData.memory_analysis.allocations;
1917            console.log('๐Ÿ“Š Using memory_analysis allocations:', allocations.length);
1918        }
1919        
1920        // Debug: Show what data structure we actually have
1921        console.log('๐Ÿ” Available data keys:', Object.keys(window.analysisData));
1922        if (window.analysisData.unsafe_ffi) {
1923            console.log('๐Ÿ” unsafe_ffi keys:', Object.keys(window.analysisData.unsafe_ffi));
1924        }
1925        
1926        // Extract metadata if available
1927        const metadata = window.analysisData.metadata || {};
1928        console.log('๐Ÿ“Š Metadata:', metadata);
1929    }
1930
1931    // Filter for FFI-tracked allocations with enhanced field support
1932    const ffiAllocations = allocations.filter(alloc => 
1933        alloc.ffi_tracked === true || 
1934        (alloc.safety_violations && alloc.safety_violations.length > 0) ||
1935        alloc.ownership_history_available === true ||
1936        (alloc.borrow_info && (alloc.borrow_info.immutable_borrows > 0 || alloc.borrow_info.mutable_borrows > 0)) ||
1937        (alloc.clone_info && alloc.clone_info.clone_count > 0)
1938    );
1939    console.log('๐Ÿ“Š Found FFI-tracked allocations:', ffiAllocations.length);
1940    
1941    // Debug: show first few allocations with improve.md fields
1942    if (allocations.length > 0) {
1943        console.log('๐Ÿ” Sample allocation with improve.md fields:', allocations[0]);
1944        console.log('๐Ÿ” FFI tracked allocations sample:', ffiAllocations.slice(0, 3));
1945        
1946        // Check for improve.md specific fields
1947        const sampleAlloc = allocations[0];
1948        console.log('๐Ÿ” Improve.md fields check:');
1949        console.log('  - borrow_info:', sampleAlloc.borrow_info);
1950        console.log('  - clone_info:', sampleAlloc.clone_info);
1951        console.log('  - ownership_history_available:', sampleAlloc.ownership_history_available);
1952        console.log('  - ffi_tracked:', sampleAlloc.ffi_tracked);
1953        console.log('  - safety_violations:', sampleAlloc.safety_violations);
1954    }
1955
1956    // Debug: Show what we found before filtering
1957    console.log('๐Ÿ” Before filtering - Total allocations:', allocations.length);
1958    console.log('๐Ÿ” Sample allocation fields:', allocations[0] ? Object.keys(allocations[0]) : 'No allocations');
1959    console.log('๐Ÿ” FFI tracked count:', allocations.filter(a => a.ffi_tracked === true).length);
1960    console.log('๐Ÿ” Borrow info count:', allocations.filter(a => a.borrow_info).length);
1961    console.log('๐Ÿ” Clone info count:', allocations.filter(a => a.clone_info).length);
1962    
1963    // Enhanced rendering with improve.md support - ALWAYS show if we have any allocations
1964    if (allocations.length === 0) {
1965        container.innerHTML = createFFIEmptyState();
1966        return;
1967    }
1968    
1969    // If we have allocations but no FFI-specific ones, still show the dashboard with all data
1970    const displayAllocations = ffiAllocations.length > 0 ? ffiAllocations : allocations.slice(0, 20);
1971    console.log('๐ŸŽฏ Rendering FFI dashboard with:', displayAllocations.length, 'allocations,', unsafeReports.length, 'reports,', memoryPassports.length, 'passports');
1972
1973    // Generate enhanced FFI analysis with improve.md fields
1974    try {
1975        if (FFI_STYLE === 'svg') {
1976            const boundaryEvents = window.analysisData.unsafe_ffi?.boundary_events || [];
1977            const unsafeAllocs = displayAllocations.filter(a => (a.safety_violations || []).length > 0).length;
1978            const ffiAllocs = displayAllocations.filter(a => a.ffi_tracked).length;
1979            const safetyViolations = displayAllocations.reduce((sum, a) => sum + ((a.safety_violations || []).length || 0), 0);
1980            const unsafeMemory = displayAllocations
1981                .filter(a => (a.safety_violations || []).length > 0)
1982                .reduce((sum, a) => sum + (a.size || 0), 0);
1983
1984            container.innerHTML = createFFIDashboardSVG(
1985                unsafeAllocs,
1986                ffiAllocs,
1987                boundaryEvents.length,
1988                safetyViolations,
1989                unsafeMemory,
1990                displayAllocations,
1991                boundaryEvents,
1992                unsafeReports
1993            );
1994            console.log('โœ… FFI SVG-style dashboard rendered');
1995            return;
1996        }
1997        console.log('๐Ÿ”„ Generating FFI analysis...');
1998        const ffiAnalysis = generateEnhancedFFIAnalysisWithImproveFields(displayAllocations, unsafeReports, memoryPassports, ffiStatistics);
1999        console.log('โœ… FFI analysis generated:', ffiAnalysis);
2000        
2001        console.log('๐Ÿ”„ Creating FFI dashboard...');
2002        const dashboardHTML = createEnhancedFFIDashboardWithImproveFields(ffiAnalysis, displayAllocations, unsafeReports, memoryPassports);
2003        console.log('โœ… Dashboard HTML created, length:', dashboardHTML.length);
2004        
2005        container.innerHTML = dashboardHTML;
2006        console.log('โœ… Dashboard rendered successfully!');
2007    } catch (error) {
2008        console.error('โŒ Error in FFI rendering:', error);
2009        container.innerHTML = `<div class="bg-red-100 p-4 rounded text-red-800">Error rendering FFI data: ${error.message}</div>`;
2010    }
2011}
2012
2013// Generate enhanced FFI analysis with improve.md fields support
2014function generateEnhancedFFIAnalysisWithImproveFields(ffiAllocations, unsafeReports, memoryPassports, ffiStatistics) {
2015    let totalFFI = ffiAllocations.length;
2016    let totalViolations = 0;
2017    let totalMemory = 0;
2018    let highRiskCount = 0;
2019    let mediumRiskCount = 0;
2020    let lowRiskCount = 0;
2021    let totalBorrows = 0;
2022    let totalClones = 0;
2023    let leakedAllocations = 0;
2024
2025    const analysisData = ffiAllocations.map(alloc => {
2026        const violations = alloc.safety_violations?.length || 0;
2027        const size = alloc.size || 0;
2028        
2029        // Enhanced borrow analysis from improve.md fields
2030        const borrowConflicts = alloc.borrow_info ? 
2031            (alloc.borrow_info.mutable_borrows > 0 && alloc.borrow_info.immutable_borrows > 0) : false;
2032        const totalBorrowsForAlloc = alloc.borrow_info ? 
2033            (alloc.borrow_info.immutable_borrows || 0) + (alloc.borrow_info.mutable_borrows || 0) : 0;
2034        totalBorrows += totalBorrowsForAlloc;
2035        
2036        // Enhanced clone analysis from improve.md fields
2037        const cloneCount = alloc.clone_info?.clone_count || 0;
2038        const isClone = alloc.clone_info?.is_clone || false;
2039        totalClones += cloneCount;
2040        
2041        // Enhanced ownership and lifecycle analysis
2042        const ownershipHistoryAvailable = alloc.ownership_history_available || false;
2043        const isLeaked = alloc.is_leaked || false;
2044        if (isLeaked) leakedAllocations++;
2045        
2046        // Enhanced risk calculation with improve.md fields
2047        let riskScore = 0;
2048        if (violations > 0) riskScore += 50;
2049        if (borrowConflicts) riskScore += 30;
2050        if (size > 1024) riskScore += 20;
2051        if (isLeaked) riskScore += 40;
2052        if (cloneCount > 3) riskScore += 15;
2053        if (totalBorrowsForAlloc > 5) riskScore += 10;
2054        
2055        let riskLevel = 'Low';
2056        if (riskScore >= 70) {
2057            riskLevel = 'High';
2058            highRiskCount++;
2059        } else if (riskScore >= 35) {
2060            riskLevel = 'Medium';
2061            mediumRiskCount++;
2062        } else {
2063            lowRiskCount++;
2064        }
2065
2066        totalViolations += violations;
2067        totalMemory += size;
2068
2069        return {
2070            ...alloc,
2071            riskScore,
2072            riskLevel,
2073            violations,
2074            borrowConflicts,
2075            totalBorrowsForAlloc,
2076            cloneCount,
2077            isClone,
2078            ownershipHistoryAvailable,
2079            isLeaked
2080        };
2081    });
2082
2083    // Enhanced statistics from improve.md structure
2084    const enhancedStats = {
2085        boundary_crossings: ffiStatistics.boundary_crossings || 0,
2086        memory_violations: ffiStatistics.memory_violations || 0,
2087        total_ffi_calls: ffiStatistics.total_ffi_calls || 0,
2088        unsafe_operations: ffiStatistics.unsafe_operations || 0
2089    };
2090
2091    return {
2092        totalFFI,
2093        totalViolations,
2094        totalMemory,
2095        highRiskCount,
2096        mediumRiskCount,
2097        lowRiskCount,
2098        totalBorrows,
2099        totalClones,
2100        leakedAllocations,
2101        analysisData,
2102        unsafeReports,
2103        memoryPassports,
2104        ffiStatistics: enhancedStats
2105    };
2106}
2107
2108// Legacy function for backward compatibility
2109function generateEnhancedFFIAnalysis(ffiAllocations) {
2110    return generateEnhancedFFIAnalysisWithImproveFields(ffiAllocations, [], [], {});
2111}
2112
2113// Create enhanced FFI dashboard with improve.md fields support
2114function createEnhancedFFIDashboardWithImproveFields(analysis, ffiAllocations, unsafeReports, memoryPassports) {
2115    return `
2116        <div class="space-y-6">
2117            <!-- Enhanced FFI Overview Cards with improve.md metrics -->
2118            <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
2119                <div class="bg-blue-100 dark:bg-blue-900 rounded-lg p-4 text-center">
2120                    <div class="text-2xl font-bold text-blue-600 dark:text-blue-300">${analysis.totalFFI}</div>
2121                    <div class="text-sm text-blue-700 dark:text-blue-400">FFI Allocations</div>
2122                </div>
2123                <div class="bg-red-100 dark:bg-red-900 rounded-lg p-4 text-center">
2124                    <div class="text-2xl font-bold text-red-600 dark:text-red-300">${analysis.highRiskCount}</div>
2125                    <div class="text-sm text-red-700 dark:text-red-400">High Risk</div>
2126                </div>
2127                <div class="bg-orange-100 dark:bg-orange-900 rounded-lg p-4 text-center">
2128                    <div class="text-2xl font-bold text-orange-600 dark:text-orange-300">${analysis.mediumRiskCount}</div>
2129                    <div class="text-sm text-orange-700 dark:text-orange-400">Medium Risk</div>
2130                </div>
2131                <div class="bg-green-100 dark:bg-green-900 rounded-lg p-4 text-center">
2132                    <div class="text-2xl font-bold text-green-600 dark:text-green-300">${analysis.lowRiskCount}</div>
2133                    <div class="text-sm text-green-700 dark:text-green-400">Low Risk</div>
2134                </div>
2135                <div class="bg-purple-100 dark:bg-purple-900 rounded-lg p-4 text-center">
2136                    <div class="text-2xl font-bold text-purple-600 dark:text-purple-300">${analysis.totalBorrows}</div>
2137                    <div class="text-sm text-purple-700 dark:text-purple-400">Total Borrows</div>
2138                </div>
2139                <div class="bg-indigo-100 dark:bg-indigo-900 rounded-lg p-4 text-center">
2140                    <div class="text-2xl font-bold text-indigo-600 dark:text-indigo-300">${analysis.totalClones}</div>
2141                    <div class="text-sm text-indigo-700 dark:text-indigo-400">Total Clones</div>
2142                </div>
2143            </div>
2144
2145            <!-- FFI Statistics from improve.md -->
2146            ${analysis.ffiStatistics && Object.keys(analysis.ffiStatistics).length > 0 ? `
2147                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2148                    <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">FFI Statistics</h3>
2149                    <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
2150                        <div class="text-center">
2151                            <div class="text-xl font-bold text-gray-900 dark:text-white">${analysis.ffiStatistics.boundary_crossings}</div>
2152                            <div class="text-sm text-gray-600 dark:text-gray-400">Boundary Crossings</div>
2153                        </div>
2154                        <div class="text-center">
2155                            <div class="text-xl font-bold text-gray-900 dark:text-white">${analysis.ffiStatistics.memory_violations}</div>
2156                            <div class="text-sm text-gray-600 dark:text-gray-400">Memory Violations</div>
2157                        </div>
2158                        <div class="text-center">
2159                            <div class="text-xl font-bold text-gray-900 dark:text-white">${analysis.ffiStatistics.total_ffi_calls}</div>
2160                            <div class="text-sm text-gray-600 dark:text-gray-400">Total FFI Calls</div>
2161                        </div>
2162                        <div class="text-center">
2163                            <div class="text-xl font-bold text-gray-900 dark:text-white">${analysis.ffiStatistics.unsafe_operations}</div>
2164                            <div class="text-sm text-gray-600 dark:text-gray-400">Unsafe Operations</div>
2165                        </div>
2166                    </div>
2167                </div>
2168            ` : ''}
2169
2170            <!-- Unsafe Reports from improve.md structure -->
2171            ${analysis.unsafeReports && analysis.unsafeReports.length > 0 ? `
2172                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2173                    <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Unsafe Reports</h3>
2174                    <div class="space-y-4">
2175                        ${analysis.unsafeReports.map(report => createUnsafeReportCard(report)).join('')}
2176                    </div>
2177                </div>
2178            ` : ''}
2179
2180            <!-- Memory Passports from improve.md structure -->
2181            ${analysis.memoryPassports && analysis.memoryPassports.length > 0 ? `
2182                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2183                    <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Memory Passports</h3>
2184                    <div class="space-y-3">
2185                        ${analysis.memoryPassports.map(passport => createMemoryPassportCard(passport)).join('')}
2186                    </div>
2187                </div>
2188            ` : ''}
2189
2190            <!-- Enhanced FFI Risk Analysis with improve.md fields -->
2191            <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2192                <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Enhanced FFI Risk Analysis</h3>
2193                <div class="space-y-4">
2194                    ${analysis.analysisData.map(alloc => createEnhancedFFIAllocationCard(alloc)).join('')}
2195                </div>
2196            </div>
2197
2198            <!-- Enhanced Borrow Checker Analysis with improve.md fields -->
2199            <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2200                <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Enhanced Borrow Checker Analysis</h3>
2201                <div class="space-y-3">
2202                    ${ffiAllocations.filter(alloc => alloc.borrow_info).map(alloc => createEnhancedBorrowAnalysisCard(alloc)).join('')}
2203                </div>
2204            </div>
2205
2206            <!-- Clone Analysis from improve.md fields -->
2207            ${analysis.totalClones > 0 ? `
2208                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2209                    <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Clone Analysis</h3>
2210                    <div class="space-y-3">
2211                        ${ffiAllocations.filter(alloc => alloc.clone_info && alloc.clone_info.clone_count > 0).map(alloc => createCloneAnalysisCard(alloc)).join('')}
2212                    </div>
2213                </div>
2214            ` : ''}
2215
2216            <!-- Ownership History Analysis -->
2217            ${ffiAllocations.some(alloc => alloc.ownership_history_available) ? `
2218                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">
2219                    <h3 class="text-lg font-semibold mb-4 text-gray-800 dark:text-white">Ownership History Analysis</h3>
2220                    <div class="space-y-3">
2221                        ${ffiAllocations.filter(alloc => alloc.ownership_history_available).map(alloc => createOwnershipHistoryCard(alloc)).join('')}
2222                    </div>
2223                </div>
2224            ` : ''}
2225        </div>
2226    `;
2227}
2228
2229// Legacy function for backward compatibility
2230function createEnhancedFFIDashboard(analysis, ffiAllocations) {
2231    return createEnhancedFFIDashboardWithImproveFields(analysis, ffiAllocations, [], []);
2232}
2233
2234// Create enhanced FFI allocation card with improve.md fields
2235function createEnhancedFFIAllocationCard(alloc) {
2236    const riskColor = alloc.riskLevel === 'High' ? 'red' : alloc.riskLevel === 'Medium' ? 'orange' : 'green';
2237    const hasViolations = alloc.violations > 0;
2238    const hasBorrowConflicts = alloc.borrowConflicts;
2239    const hasClones = alloc.cloneCount > 0;
2240    const isLeaked = alloc.isLeaked;
2241    const hasOwnershipHistory = alloc.ownershipHistoryAvailable;
2242    
2243    return `
2244        <div class="bg-white dark:bg-gray-600 rounded-lg p-4 border-l-4 border-${riskColor}-500">
2245            <div class="flex justify-between items-start mb-3">
2246                <div>
2247                    <h4 class="font-semibold text-gray-900 dark:text-white">${alloc.var_name || 'Unknown Variable'}</h4>
2248                    <p class="text-sm text-gray-600 dark:text-gray-300">${formatTypeName(alloc.type_name || 'Unknown Type')}</p>
2249                    ${alloc.isClone ? '<span class="inline-block px-2 py-1 text-xs bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 rounded-full mt-1">Clone</span>' : ''}
2250                </div>
2251                <div class="text-right">
2252                    <span class="px-2 py-1 text-xs font-bold rounded-full bg-${riskColor}-100 text-${riskColor}-800 dark:bg-${riskColor}-900 dark:text-${riskColor}-200">
2253                        ${alloc.riskLevel} Risk
2254                    </span>
2255                    ${isLeaked ? '<div class="mt-1"><span class="px-2 py-1 text-xs bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200 rounded-full">LEAKED</span></div>' : ''}
2256                </div>
2257            </div>
2258            
2259            <div class="grid grid-cols-2 gap-4 text-sm mb-3">
2260                <div>
2261                    <span class="text-gray-500 dark:text-gray-400">Size:</span>
2262                    <span class="ml-2 font-mono">${formatBytes(alloc.size || 0)}</span>
2263                </div>
2264                <div>
2265                    <span class="text-gray-500 dark:text-gray-400">Risk Score:</span>
2266                    <span class="ml-2 font-bold text-${riskColor}-600">${alloc.riskScore}/100</span>
2267                </div>
2268                <div>
2269                    <span class="text-gray-500 dark:text-gray-400">Pointer:</span>
2270                    <span class="ml-2 font-mono text-xs">${alloc.ptr}</span>
2271                </div>
2272                <div>
2273                    <span class="text-gray-500 dark:text-gray-400">Thread:</span>
2274                    <span class="ml-2">${alloc.thread_id || 'Unknown'}</span>
2275                </div>
2276            </div>
2277
2278            <!-- Enhanced improve.md fields -->
2279            <div class="grid grid-cols-3 gap-4 text-sm mb-3">
2280                <div>
2281                    <span class="text-gray-500 dark:text-gray-400">Total Borrows:</span>
2282                    <span class="ml-2 font-bold">${alloc.totalBorrowsForAlloc || 0}</span>
2283                </div>
2284                <div>
2285                    <span class="text-gray-500 dark:text-gray-400">Clone Count:</span>
2286                    <span class="ml-2 font-bold">${alloc.cloneCount || 0}</span>
2287                </div>
2288                <div>
2289                    <span class="text-gray-500 dark:text-gray-400">FFI Tracked:</span>
2290                    <span class="ml-2">${alloc.ffi_tracked ? 'โœ…' : 'โŒ'}</span>
2291                </div>
2292            </div>
2293            
2294            ${hasViolations || hasBorrowConflicts || hasClones || hasOwnershipHistory ? `
2295                <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-500">
2296                    <div class="text-sm space-y-1">
2297                        ${hasViolations ? `<div class="text-red-600 dark:text-red-400">โš ๏ธ ${alloc.violations} safety violations</div>` : ''}
2298                        ${hasBorrowConflicts ? `<div class="text-orange-600 dark:text-orange-400">โš ๏ธ Borrow conflicts detected</div>` : ''}
2299                        ${hasClones ? `<div class="text-blue-600 dark:text-blue-400">๐Ÿ”„ ${alloc.cloneCount} clones created</div>` : ''}
2300                        ${hasOwnershipHistory ? `<div class="text-green-600 dark:text-green-400">๐Ÿ“‹ Ownership history available</div>` : ''}
2301                    </div>
2302                </div>
2303            ` : ''}
2304        </div>
2305    `;
2306}
2307
2308// Legacy function for backward compatibility
2309function createFFIAllocationCard(alloc) {
2310    return createEnhancedFFIAllocationCard(alloc);
2311}
2312
2313// Create enhanced borrow analysis card with improve.md fields
2314function createEnhancedBorrowAnalysisCard(alloc) {
2315    const borrowInfo = alloc.borrow_info;
2316    const hasConflict = borrowInfo.mutable_borrows > 0 && borrowInfo.immutable_borrows > 0;
2317    const lastBorrowTime = borrowInfo.last_borrow_timestamp ? new Date(borrowInfo.last_borrow_timestamp / 1000000).toLocaleTimeString() : 'Unknown';
2318    
2319    return `
2320        <div class="bg-white dark:bg-gray-600 rounded-lg p-3 ${hasConflict ? 'border-l-4 border-red-500' : 'border border-gray-200 dark:border-gray-500'}">
2321            <div class="flex justify-between items-start">
2322                <div>
2323                    <h5 class="font-medium text-gray-900 dark:text-white">${alloc.var_name}</h5>
2324                    <p class="text-xs text-gray-500 dark:text-gray-400">${formatTypeName(alloc.type_name)}</p>
2325                    <p class="text-xs text-gray-500 dark:text-gray-400">Last borrow: ${lastBorrowTime}</p>
2326                </div>
2327                <div class="text-right text-sm">
2328                    <div class="text-blue-600 dark:text-blue-400">Immutable: ${borrowInfo.immutable_borrows}</div>
2329                    <div class="text-red-600 dark:text-red-400">Mutable: ${borrowInfo.mutable_borrows}</div>
2330                    <div class="text-purple-600 dark:text-purple-400">Max Concurrent: ${borrowInfo.max_concurrent_borrows}</div>
2331                    <div class="text-xs text-gray-500 dark:text-gray-400">Total: ${(borrowInfo.immutable_borrows || 0) + (borrowInfo.mutable_borrows || 0)}</div>
2332                </div>
2333            </div>
2334            ${hasConflict ? `
2335                <div class="mt-2 text-xs text-red-600 dark:text-red-400 font-bold">
2336                    โš ๏ธ BORROW CONFLICT: Simultaneous mutable and immutable borrows detected
2337                </div>
2338            ` : ''}
2339        </div>
2340    `;
2341}
2342
2343// Legacy function for backward compatibility
2344function createBorrowAnalysisCard(alloc) {
2345    return createEnhancedBorrowAnalysisCard(alloc);
2346}
2347
2348// Create clone analysis card for improve.md clone_info fields
2349function createCloneAnalysisCard(alloc) {
2350    const cloneInfo = alloc.clone_info;
2351    const isClone = cloneInfo.is_clone;
2352    const cloneCount = cloneInfo.clone_count;
2353    const originalPtr = cloneInfo.original_ptr;
2354    
2355    return `
2356        <div class="bg-white dark:bg-gray-600 rounded-lg p-3 border-l-4 border-blue-500">
2357            <div class="flex justify-between items-start">
2358                <div>
2359                    <h5 class="font-medium text-gray-900 dark:text-white">${alloc.var_name}</h5>
2360                    <p class="text-xs text-gray-500 dark:text-gray-400">${formatTypeName(alloc.type_name)}</p>
2361                    ${isClone ? `<p class="text-xs text-blue-600 dark:text-blue-400">Clone of: ${originalPtr}</p>` : ''}
2362                </div>
2363                <div class="text-right">
2364                    <div class="text-blue-600 dark:text-blue-400 font-bold text-lg">${cloneCount}</div>
2365                    <div class="text-xs text-gray-500 dark:text-gray-400">Clones Created</div>
2366                    ${isClone ? '<div class="text-xs bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded mt-1">IS CLONE</div>' : ''}
2367                </div>
2368            </div>
2369            <div class="mt-2 text-sm text-gray-600 dark:text-gray-300">
2370                ${cloneCount > 0 ? `๐Ÿ”„ This allocation has been cloned ${cloneCount} times` : ''}
2371                ${isClone ? `<br>๐Ÿ“‹ This is a clone of allocation at ${originalPtr}` : ''}
2372            </div>
2373        </div>
2374    `;
2375}
2376
2377// Create ownership history card for improve.md ownership_history_available field
2378function createOwnershipHistoryCard(alloc) {
2379    return `
2380        <div class="bg-white dark:bg-gray-600 rounded-lg p-3 border-l-4 border-green-500">
2381            <div class="flex justify-between items-center">
2382                <div>
2383                    <h5 class="font-medium text-gray-900 dark:text-white">${alloc.var_name}</h5>
2384                    <p class="text-xs text-gray-500 dark:text-gray-400">${formatTypeName(alloc.type_name)}</p>
2385                </div>
2386                <div class="text-right">
2387                    <div class="text-green-600 dark:text-green-400">๐Ÿ“‹ History Available</div>
2388                    <div class="text-xs text-gray-500 dark:text-gray-400">Detailed tracking enabled</div>
2389                </div>
2390            </div>
2391            <div class="mt-2 text-sm text-gray-600 dark:text-gray-300">
2392                โœ… Ownership history is available for this allocation in lifetime.json
2393            </div>
2394        </div>
2395    `;
2396}
2397
2398// Create unsafe report card for improve.md UnsafeReport structure
2399function createUnsafeReportCard(report) {
2400    const riskLevel = report.risk_assessment?.risk_level || 'Unknown';
2401    const riskColor = riskLevel === 'High' ? 'red' : riskLevel === 'Medium' ? 'orange' : 'green';
2402    const confidenceScore = report.risk_assessment?.confidence_score || 0;
2403    const riskFactors = report.risk_assessment?.risk_factors || [];
2404    const dynamicViolations = report.dynamic_violations || [];
2405    
2406    return `
2407        <div class="bg-white dark:bg-gray-600 rounded-lg p-4 border-l-4 border-${riskColor}-500">
2408            <div class="flex justify-between items-start mb-3">
2409                <div>
2410                    <h4 class="font-semibold text-gray-900 dark:text-white">Unsafe Report: ${report.report_id || 'Unknown'}</h4>
2411                    <p class="text-sm text-gray-600 dark:text-gray-300">${report.source?.type || 'Unknown'} at ${report.source?.location || 'Unknown location'}</p>
2412                </div>
2413                <div class="text-right">
2414                    <span class="px-2 py-1 text-xs font-bold rounded-full bg-${riskColor}-100 text-${riskColor}-800 dark:bg-${riskColor}-900 dark:text-${riskColor}-200">
2415                        ${riskLevel} Risk
2416                    </span>
2417                    <div class="text-xs text-gray-500 dark:text-gray-400 mt-1">Confidence: ${(confidenceScore * 100).toFixed(1)}%</div>
2418                </div>
2419            </div>
2420            
2421            ${riskFactors.length > 0 ? `
2422                <div class="mb-3">
2423                    <h5 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Risk Factors:</h5>
2424                    <div class="space-y-1">
2425                        ${riskFactors.map(factor => `
2426                            <div class="text-sm">
2427                                <span class="font-medium text-${riskColor}-600 dark:text-${riskColor}-400">${factor.factor_type}</span>
2428                                <span class="text-gray-600 dark:text-gray-400"> (Severity: ${factor.severity}/10)</span>
2429                                <div class="text-xs text-gray-500 dark:text-gray-400">${factor.description}</div>
2430                            </div>
2431                        `).join('')}
2432                    </div>
2433                </div>
2434            ` : ''}
2435            
2436            ${dynamicViolations.length > 0 ? `
2437                <div class="mt-3 pt-3 border-t border-gray-200 dark:border-gray-500">
2438                    <h5 class="text-sm font-medium text-red-700 dark:text-red-300 mb-2">Dynamic Violations:</h5>
2439                    <div class="space-y-1">
2440                        ${dynamicViolations.map(violation => `
2441                            <div class="text-sm text-red-600 dark:text-red-400">
2442                                โš ๏ธ ${violation.violation_type}: ${violation.description}
2443                            </div>
2444                        `).join('')}
2445                    </div>
2446                </div>
2447            ` : ''}
2448        </div>
2449    `;
2450}
2451
2452// Create memory passport card for improve.md MemoryPassport structure
2453function createMemoryPassportCard(passport) {
2454    const status = passport.status_at_shutdown || 'Unknown';
2455    const statusColor = status === 'Reclaimed' ? 'green' : status === 'InForeignCustody' ? 'red' : 'orange';
2456    const lifecycleEvents = passport.lifecycle_events || [];
2457    
2458    return `
2459        <div class="bg-white dark:bg-gray-600 rounded-lg p-3 border-l-4 border-${statusColor}-500">
2460            <div class="flex justify-between items-start">
2461                <div>
2462                    <h5 class="font-medium text-gray-900 dark:text-white">Passport: ${passport.passport_id || 'Unknown'}</h5>
2463                    <p class="text-xs text-gray-500 dark:text-gray-400">Allocation: ${passport.allocation_ptr} (${formatBytes(passport.size_bytes || 0)})</p>
2464                </div>
2465                <div class="text-right">
2466                    <span class="px-2 py-1 text-xs font-bold rounded-full bg-${statusColor}-100 text-${statusColor}-800 dark:bg-${statusColor}-900 dark:text-${statusColor}-200">
2467                        ${status}
2468                    </span>
2469                </div>
2470            </div>
2471            
2472            ${lifecycleEvents.length > 0 ? `
2473                <div class="mt-2">
2474                    <h6 class="text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">Lifecycle Events:</h6>
2475                    <div class="space-y-1">
2476                        ${lifecycleEvents.slice(0, 3).map(event => `
2477                            <div class="text-xs text-gray-600 dark:text-gray-400">
2478                                ๐Ÿ“… ${event.event_type} ${event.how ? `(${event.how})` : ''}
2479                            </div>
2480                        `).join('')}
2481                        ${lifecycleEvents.length > 3 ? `<div class="text-xs text-gray-500 dark:text-gray-400">... and ${lifecycleEvents.length - 3} more events</div>` : ''}
2482                    </div>
2483                </div>
2484            ` : ''}
2485        </div>
2486    `;
2487}
2488
2489// Create FFI empty state
2490function createFFIEmptyState() {
2491    return `
2492        <div class="text-center py-8">
2493            <div class="mb-4">
2494                <svg class="w-16 h-16 mx-auto text-green-400 dark:text-green-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2495                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
2496                </svg>
2497            </div>
2498            <h4 class="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-200">Memory Safety Verified</h4>
2499            <p class="text-sm text-gray-600 dark:text-gray-400">No unsafe FFI operations detected in this analysis</p>
2500            <p class="text-xs mt-2 text-gray-500 dark:text-gray-500">Your code appears to be using safe Rust patterns</p>
2501        </div>
2502    `;
2503}
2504
2505// Create comprehensive FFI dashboard with SVG-style visualization
2506function createFFIDashboardSVG(unsafeAllocs, ffiAllocs, boundaryCrossings, safetyViolations, unsafeMemory, enhancedData, boundaryEvents, violations) {
2507    return `
2508        <div class="bg-gradient-to-br from-gray-800 to-gray-900 rounded-xl p-6 text-white shadow-2xl">
2509            <!-- Header -->
2510            <div class="text-center mb-6">
2511                <h2 class="text-2xl font-bold mb-2 flex items-center justify-center">
2512                    <i class="fa fa-shield mr-3 text-red-400"></i>
2513                    Unsafe Rust & FFI Memory Analysis Dashboard
2514                </h2>
2515            </div>
2516
2517            <!-- Key Metrics Row -->
2518            <div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
2519                ${createFFIMetricCard('Unsafe Allocations', unsafeAllocs, '#e74c3c', 'fa-exclamation-triangle')}
2520                ${createFFIMetricCard('FFI Allocations', ffiAllocs, '#3498db', 'fa-exchange')}
2521                ${createFFIMetricCard('Boundary Crossings', boundaryCrossings, '#f39c12', 'fa-arrows-h')}
2522                ${createFFIMetricCard('Safety Violations', safetyViolations, '#e67e22', 'fa-warning')}
2523                ${createFFIMetricCard('Unsafe Memory', formatBytes(unsafeMemory), '#9b59b6', 'fa-memory')}
2524            </div>
2525
2526            <!-- Main Dashboard Content -->
2527            <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
2528                <!-- Memory Allocation Sources -->
2529                <div class="bg-gray-700/50 rounded-lg p-4 backdrop-blur-sm">
2530                    <h3 class="text-lg font-semibold mb-4 text-white">Memory Allocation Sources</h3>
2531                    <div class="space-y-4">
2532                        ${createAllocationSourceBar('Unsafe Rust', unsafeAllocs, Math.max(unsafeAllocs, ffiAllocs), '#e74c3c')}
2533                        ${createAllocationSourceBar('FFI', ffiAllocs, Math.max(unsafeAllocs, ffiAllocs), '#3498db')}
2534                    </div>
2535                </div>
2536
2537                <!-- Memory Safety Status -->
2538                <div class="bg-gray-700/50 rounded-lg p-4 backdrop-blur-sm">
2539                    <h3 class="text-lg font-semibold mb-4 text-white">Memory Safety Status</h3>
2540                    ${safetyViolations > 0 ? `
2541                        <div class="bg-red-900/30 border border-red-500/50 rounded-lg p-4">
2542                            <h4 class="text-red-300 font-semibold mb-2 flex items-center">
2543                                <i class="fa fa-exclamation-triangle mr-2"></i>
2544                                ${safetyViolations} Safety Violations Detected
2545                            </h4>
2546                            ${enhancedData.filter(item => (item.safety_violations || 0) > 0).slice(0, 2).map(item => `
2547                                <div class="text-red-400 text-sm flex items-center mb-1">
2548                                    <i class="fa fa-dot-circle-o mr-2 text-xs"></i>
2549                                    Pointer ${item.ptr}: ${item.safety_violations} violations
2550                                </div>
2551                            `).join('')}
2552                        </div>
2553                    ` : `
2554                        <div class="bg-green-900/30 border border-green-500/50 rounded-lg p-4">
2555                            <h4 class="text-green-300 font-semibold flex items-center mb-2">
2556                                <i class="fa fa-check-circle mr-2"></i>
2557                                No Safety Violations Detected
2558                            </h4>
2559                            <p class="text-green-400 text-sm">All unsafe operations appear to be handled correctly</p>
2560                        </div>
2561                    `}
2562                </div>
2563            </div>
2564
2565            <!-- Cross-Language Memory Flow -->
2566            <div class="bg-gray-700/50 rounded-lg p-6 mb-6 backdrop-blur-sm">
2567                <h3 class="text-lg font-semibold mb-6 text-white text-center">Cross-Language Memory Flow</h3>
2568                <div class="flex items-center justify-center space-x-8">
2569                    <!-- Rust Side -->
2570                    <div class="bg-green-800/30 border-2 border-green-400/50 rounded-lg p-6 text-center backdrop-blur-sm">
2571                        <div class="text-green-300 font-bold text-xl mb-2">RUST</div>
2572                        <div class="text-green-400 text-sm">${unsafeAllocs} allocations</div>
2573                        <div class="w-16 h-16 mx-auto mt-3 bg-green-500/20 rounded-full flex items-center justify-center">
2574                            <i class="fa fa-rust text-green-400 text-2xl"></i>
2575                        </div>
2576                    </div>
2577                    
2578                    <!-- Flow Arrows -->
2579                    <div class="flex flex-col items-center space-y-4">
2580                        <div class="flex items-center space-x-2">
2581                            <div class="flex items-center space-x-1">
2582                                <div class="w-8 h-0.5 bg-red-400"></div>
2583                                <div class="w-0 h-0 border-l-4 border-l-red-400 border-t-2 border-t-transparent border-b-2 border-b-transparent"></div>
2584                            </div>
2585                            <span class="text-red-400 text-sm font-bold bg-red-900/30 px-2 py-1 rounded">
2586                                ${boundaryEvents.filter(e => e.event_type === 'RustToFfi').length}
2587                            </span>
2588                        </div>
2589                        <div class="flex items-center space-x-2">
2590                            <span class="text-orange-400 text-sm font-bold bg-orange-900/30 px-2 py-1 rounded">
2591                                ${boundaryEvents.filter(e => e.event_type === 'FfiToRust').length}
2592                            </span>
2593                            <div class="flex items-center space-x-1">
2594                                <div class="w-0 h-0 border-r-4 border-r-orange-400 border-t-2 border-t-transparent border-b-2 border-b-transparent"></div>
2595                                <div class="w-8 h-0.5 bg-orange-400"></div>
2596                            </div>
2597                        </div>
2598                    </div>
2599                    
2600                    <!-- FFI/C Side -->
2601                    <div class="bg-blue-800/30 border-2 border-blue-400/50 rounded-lg p-6 text-center backdrop-blur-sm">
2602                        <div class="text-blue-300 font-bold text-xl mb-2">FFI / C</div>
2603                        <div class="text-blue-400 text-sm">${ffiAllocs} allocations</div>
2604                        <div class="w-16 h-16 mx-auto mt-3 bg-blue-500/20 rounded-full flex items-center justify-center">
2605                            <i class="fa fa-code text-blue-400 text-2xl"></i>
2606                        </div>
2607                    </div>
2608                </div>
2609            </div>
2610
2611            <!-- Unsafe Memory Hotspots -->
2612            <div class="bg-gray-700/50 rounded-lg p-4 backdrop-blur-sm">
2613                <h3 class="text-lg font-semibold mb-4 text-white">Unsafe Memory Hotspots</h3>
2614                <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
2615                    ${enhancedData.slice(0, 12).map(item => createMemoryHotspot(item)).join('')}
2616                </div>
2617                ${enhancedData.length === 0 ? `
2618                    <div class="text-center py-8 text-gray-400">
2619                        <i class="fa fa-shield-alt text-4xl mb-2"></i>
2620                        <p>No unsafe memory hotspots detected</p>
2621                    </div>
2622                ` : ''}
2623            </div>
2624        </div>
2625    `;
2626}
2627
2628// Create FFI metric card
2629function createFFIMetricCard(title, value, color, icon) {
2630    return `
2631        <div class="bg-gray-700/30 border border-gray-600/50 rounded-lg p-4 text-center backdrop-blur-sm hover:bg-gray-600/30 transition-all">
2632            <div class="flex items-center justify-center mb-2">
2633                <i class="fa ${icon} text-2xl" style="color: ${color}"></i>
2634            </div>
2635            <div class="text-2xl font-bold mb-1" style="color: ${color}">${value}</div>
2636            <div class="text-xs text-gray-300 uppercase tracking-wide">${title}</div>
2637        </div>
2638    `;
2639}
2640
2641// Create allocation source bar
2642function createAllocationSourceBar(label, count, maxCount, color) {
2643    const percentage = maxCount > 0 ? (count / maxCount) * 100 : 0;
2644    const barHeight = Math.max(20, (count / maxCount) * 80);
2645
2646    return `
2647        <div class="flex items-end space-x-4">
2648            <div class="flex-1">
2649                <div class="flex justify-between items-center mb-2">
2650                    <span class="text-sm font-medium text-gray-300">${label}</span>
2651                    <span class="text-lg font-bold text-white">${count}</span>
2652                </div>
2653                <div class="w-full bg-gray-600 rounded-full h-6 overflow-hidden">
2654                    <div class="h-full rounded-full transition-all duration-500 flex items-center justify-center text-white text-xs font-bold" 
2655                         style="width: ${percentage}%; background-color: ${color};">
2656                        ${count > 0 ? count : ''}
2657                    </div>
2658                </div>
2659            </div>
2660        </div>
2661    `;
2662}
2663
2664// Create memory hotspot visualization
2665function createMemoryHotspot(item) {
2666    const size = item.size || 0;
2667    const isUnsafe = !item.ffi_tracked;
2668    const radius = Math.min(30, Math.max(12, Math.sqrt(size / 50)));
2669    const color = isUnsafe ? '#e74c3c' : '#3498db';
2670    const bgColor = isUnsafe ? 'bg-red-900/20' : 'bg-blue-900/20';
2671    const borderColor = isUnsafe ? 'border-red-500/50' : 'border-blue-500/50';
2672
2673    // Interactive hotspot with data attributes for detail panel
2674    return `
2675        <div class="flex flex-col items-center p-3 ${bgColor} border ${borderColor} rounded-lg backdrop-blur-sm hover:scale-105 transition-transform cursor-pointer"
2676             data-ptr="${item.ptr || ''}"
2677             data-var="${(item.var_name || 'Unknown').toString().replace(/\"/g, '&quot;')}"
2678             data-type="${(item.type_name || 'Unknown').toString().replace(/\"/g, '&quot;')}"
2679             data-size="${size}"
2680             data-violations="${(item.safety_violations || 0)}"
2681             onclick="window.showFFIDetailFromDataset && window.showFFIDetailFromDataset(this)">
2682            <div class="relative mb-2">
2683                <div class="rounded-full border-2 flex items-center justify-center text-white text-xs font-bold shadow-lg"
2684                     style="width: ${radius * 2}px; height: ${radius * 2}px; background-color: ${color}; border-color: ${color};">
2685                    ${size > 1024 ? Math.round(size / 1024) + 'K' : size + 'B'}
2686                </div>
2687                ${(item.safety_violations || 0) > 0 ? `
2688                    <div class="absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full flex items-center justify-center">
2689                        <i class="fa fa-exclamation text-white text-xs"></i>
2690                    </div>
2691                ` : ''}
2692            </div>
2693            <div class="text-xs text-center">
2694                <div class="font-semibold" style="color: ${color}">
2695                    ${isUnsafe ? 'UNSAFE' : 'FFI'}
2696                </div>
2697                <div class="text-gray-400 text-xs">
2698                    ${formatBytes(size)}
2699                </div>
2700            </div>
2701        </div>
2702    `;
2703}
2704
2705// Simple detail panel for FFI hotspot items
2706window.showFFIDetailFromDataset = function(el) {
2707    try {
2708        const container = document.getElementById('ffiVisualization');
2709        if (!container) return;
2710
2711        // Remove existing panel
2712        const existing = container.querySelector('#ffi-detail-panel');
2713        if (existing) existing.remove();
2714
2715        // Build panel
2716        const panel = document.createElement('div');
2717        panel.id = 'ffi-detail-panel';
2718        panel.style.position = 'absolute';
2719        panel.style.right = '16px';
2720        panel.style.top = '16px';
2721        panel.style.zIndex = '1000';
2722        panel.style.minWidth = '280px';
2723        panel.style.maxWidth = '360px';
2724        panel.style.background = 'var(--bg-primary)';
2725        panel.style.border = '1px solid var(--border-light)';
2726        panel.style.borderRadius = '10px';
2727        panel.style.boxShadow = '0 10px 25px rgba(0,0,0,0.2)';
2728        panel.style.padding = '12px';
2729
2730        const name = el.getAttribute('data-var');
2731        const type = el.getAttribute('data-type');
2732        const size = parseInt(el.getAttribute('data-size') || '0', 10);
2733        const ptr = el.getAttribute('data-ptr');
2734        const violations = parseInt(el.getAttribute('data-violations') || '0', 10);
2735
2736        panel.innerHTML = `
2737            <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
2738                <div style="font-weight:700; font-size:14px; color: var(--text-primary);">FFI Allocation Detail</div>
2739                <button onclick="this.parentNode.parentNode.remove()" style="border:none; background:transparent; color: var(--text-secondary); font-size:18px; cursor:pointer">ร—</button>
2740            </div>
2741            <div style="font-size:12px; color: var(--text-primary);">
2742                <div style="margin-bottom:6px;"><strong>Name:</strong> ${name}</div>
2743                <div style="margin-bottom:6px;"><strong>Type:</strong> ${type}</div>
2744                <div style="margin-bottom:6px;"><strong>Size:</strong> ${formatBytes(size)}</div>
2745                ${ptr ? `<div style=\"margin-bottom:6px;\"><strong>Pointer:</strong> <code>${ptr}</code></div>` : ''}
2746                <div style="margin-bottom:6px;"><strong>Safety Violations:</strong> ${violations}</div>
2747            </div>
2748        `;
2749
2750        container.appendChild(panel);
2751    } catch(e) {
2752        console.warn('Failed to show FFI detail panel', e);
2753    }
2754};
2755
2756// Initialize memory fragmentation analysis with enhanced SVG-style visualization
2757function initMemoryFragmentation() {
2758    const container = document.getElementById('memoryFragmentation');
2759    if (!container) return;
2760
2761    const allocations = window.analysisData.memory_analysis?.allocations || [];
2762
2763    if (allocations.length === 0) {
2764        container.innerHTML = createFragmentationEmptyState();
2765        return;
2766    }
2767
2768    // Fixed memory fragmentation analysis: based on allocation size distribution rather than address gaps
2769    const sortedAllocs = allocations
2770        .filter(alloc => alloc.size && alloc.size > 0)
2771        .map(alloc => ({
2772            size: alloc.size,
2773            type: alloc.type_name || 'System Allocation',
2774            var_name: alloc.var_name || 'unknown'
2775        }))
2776        .sort((a, b) => a.size - b.size);
2777
2778    const totalMemory = sortedAllocs.reduce((sum, alloc) => sum + alloc.size, 0);
2779    
2780    // Calculate fragmentation based on allocation size distribution
2781    const sizeVariance = calculateSizeVariance(sortedAllocs);
2782    const smallAllocRatio = sortedAllocs.filter(a => a.size < 1024).length / sortedAllocs.length;
2783    
2784    // Fragmentation score: based on size distribution unevenness
2785    const fragmentationRatio = Math.min(100, (sizeVariance / 1000 + smallAllocRatio * 50));
2786    
2787    // Simplified gap analysis: only count quantity, not fake address gaps
2788    const gaps = Math.max(0, sortedAllocs.length - 1);
2789    const maxGap = 0; // No longer calculate address gaps
2790    let totalGapSize = 0; // Reset to 0 to avoid huge fake values
2791
2792    // Size distribution analysis (inspired by SVG)
2793    const sizeDistribution = {
2794        tiny: sortedAllocs.filter(a => a.size < 64).length,
2795        small: sortedAllocs.filter(a => a.size >= 64 && a.size < 1024).length,
2796        medium: sortedAllocs.filter(a => a.size >= 1024 && a.size < 65536).length,
2797        large: sortedAllocs.filter(a => a.size >= 65536).length
2798    };
2799
2800    container.innerHTML = createFragmentationAnalysisSVG(
2801        fragmentationRatio, gaps, maxGap, sortedAllocs.length,
2802        totalMemory, sizeDistribution, sortedAllocs
2803    );
2804}
2805
2806// Create fragmentation empty state
2807function createFragmentationEmptyState() {
2808    return `
2809        <div class="bg-white dark:bg-gray-800 rounded-xl p-6 card-shadow transition-colors">
2810            <h2 class="text-xl font-semibold mb-4 flex items-center text-heading">
2811                <i class="fa fa-puzzle-piece text-orange-500 mr-2"></i>Memory Fragmentation Analysis
2812            </h2>
2813            <div class="text-center py-8">
2814                <div class="mb-4">
2815                    <svg class="w-16 h-16 mx-auto text-gray-400 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2816                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
2817                    </svg>
2818                </div>
2819                <h4 class="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-200">No Memory Data for Analysis</h4>
2820                <p class="text-sm text-gray-600 dark:text-gray-400">Memory fragmentation analysis requires allocation data</p>
2821            </div>
2822        </div>
2823    `;
2824}
2825
2826// Create comprehensive fragmentation analysis with SVG-style visualization
2827function createFragmentationAnalysisSVG(fragmentationRatio, gaps, maxGap, blockCount, totalMemory, sizeDistribution, sortedAllocs) {
2828    return `
2829        <div class="bg-white dark:bg-gray-800 rounded-xl p-6 card-shadow transition-colors">
2830            <h2 class="text-xl font-semibold mb-6 flex items-center text-heading">
2831                <i class="fa fa-puzzle-piece text-orange-500 mr-2"></i>Memory Fragmentation Analysis
2832            </h2>
2833            
2834            <!-- Key Metrics Grid -->
2835            <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
2836                ${createFragmentationMetricCard('Fragmentation', fragmentationRatio.toFixed(1) + '%', fragmentationRatio, '#f39c12')}
2837                ${createFragmentationMetricCard('Memory Gaps', gaps, 100, '#3498db')}
2838                ${createFragmentationMetricCard('Largest Gap', formatBytes(maxGap), 100, '#27ae60')}
2839                ${createFragmentationMetricCard('Memory Blocks', blockCount, 100, '#9b59b6')}
2840            </div>
2841
2842            <!-- Main Analysis Content -->
2843            <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
2844                <!-- Fragmentation Assessment -->
2845                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
2846                    <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Fragmentation Assessment</h4>
2847                    <div class="space-y-4">
2848                        <div>
2849                            <div class="flex justify-between items-center mb-2">
2850                                <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Overall Health</span>
2851                                <span class="text-sm font-bold ${getFragmentationColor(fragmentationRatio)}">${fragmentationRatio.toFixed(1)}%</span>
2852                            </div>
2853                            <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-4">
2854                                <div class="h-4 rounded-full transition-all duration-500 ${getFragmentationBgColor(fragmentationRatio)}" 
2855                                     style="width: ${Math.min(fragmentationRatio, 100)}%"></div>
2856                            </div>
2857                        </div>
2858                        <div class="text-sm text-gray-600 dark:text-gray-300">
2859                            ${getFragmentationAssessment(fragmentationRatio)}
2860                        </div>
2861                    </div>
2862                </div>
2863
2864                <!-- Size Distribution (inspired by SVG bar chart) -->
2865                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
2866                    <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Size Distribution</h4>
2867                    <div class="space-y-3">
2868                        ${createSizeDistributionBar('Tiny (0-64B)', sizeDistribution.tiny, blockCount, '#27ae60')}
2869                        ${createSizeDistributionBar('Small (64B-1KB)', sizeDistribution.small, blockCount, '#f39c12')}
2870                        ${createSizeDistributionBar('Medium (1KB-64KB)', sizeDistribution.medium, blockCount, '#e74c3c')}
2871                        ${createSizeDistributionBar('Large (>64KB)', sizeDistribution.large, blockCount, '#8e44ad')}
2872                    </div>
2873                </div>
2874            </div>
2875
2876            <!-- Memory Layout Visualization -->
2877            <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
2878                <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Memory Layout Visualization</h4>
2879                <div class="relative">
2880                    <!-- Memory blocks visualization -->
2881                    <div class="h-16 bg-gray-200 dark:bg-gray-600 rounded relative overflow-hidden mb-4">
2882                        ${createMemoryLayoutVisualization(sortedAllocs, totalMemory)}
2883                    </div>
2884                    
2885                    <!-- Memory address timeline -->
2886                    <div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mb-2">
2887                        <span>Low Address</span>
2888                        <span>Memory Layout</span>
2889                        <span>High Address</span>
2890                    </div>
2891                    
2892                    <!-- Legend -->
2893                    <div class="flex flex-wrap gap-4 text-xs">
2894                        <div class="flex items-center">
2895                            <div class="w-3 h-3 bg-blue-500 rounded mr-2"></div>
2896                            <span class="text-gray-600 dark:text-gray-300">User Allocations</span>
2897                        </div>
2898                        <div class="flex items-center">
2899                            <div class="w-3 h-3 bg-gray-400 rounded mr-2"></div>
2900                            <span class="text-gray-600 dark:text-gray-300">System Allocations</span>
2901                        </div>
2902                        <div class="flex items-center">
2903                            <div class="w-3 h-3 bg-red-300 rounded mr-2"></div>
2904                            <span class="text-gray-600 dark:text-gray-300">Memory Gaps</span>
2905                        </div>
2906                    </div>
2907                </div>
2908            </div>
2909        </div>
2910    `;
2911}
2912
2913// Create fragmentation metric card with circular progress
2914function createFragmentationMetricCard(title, value, percentage, color) {
2915    const normalizedPercentage = Math.min(100, Math.max(0, percentage));
2916    const circumference = 2 * Math.PI * 20;
2917    const strokeDashoffset = circumference - (normalizedPercentage / 100) * circumference;
2918
2919    return `
2920        <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 text-center hover:shadow-md transition-shadow">
2921            <div class="flex items-center justify-between">
2922                <div class="flex-1">
2923                    <p class="text-xs font-medium text-gray-600 dark:text-gray-400 uppercase">${title}</p>
2924                    <p class="text-lg font-bold text-gray-900 dark:text-white">${value}</p>
2925                </div>
2926                <div class="relative w-10 h-10">
2927                    <svg class="w-10 h-10 transform -rotate-90" viewBox="0 0 50 50">
2928                        <circle cx="25" cy="25" r="20" stroke='#e5e7eb' stroke-width='4' fill='none' class='dark:stroke-gray-600'/>
2929                        <circle cx='25' cy='25' r='20' stroke='${color}' stroke-width='4' fill='none' 
2930                                stroke-dasharray='${circumference}' stroke-dashoffset='${strokeDashoffset}'
2931                                stroke-linecap='round' class='transition-all duration-500'/>
2932                    </svg>
2933                </div>
2934            </div>
2935        </div>
2936    `;
2937}
2938
2939// Create size distribution bar
2940function createSizeDistributionBar(label, count, total, color) {
2941    const percentage = total > 0 ? (count / total) * 100 : 0;
2942    return `
2943        <div class="flex items-center justify-between">
2944            <span class="text-sm font-medium text-gray-700 dark:text-gray-300 w-28">${label}</span>
2945            <div class="flex-1 mx-3">
2946                <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-4">
2947                    <div class="h-4 rounded-full transition-all duration-500" 
2948                         style="width: ${percentage}%; background-color: ${color}"></div>
2949                </div>
2950            </div>
2951            <span class="text-sm font-bold text-gray-900 dark:text-white w-8 text-right">${count}</span>
2952        </div>
2953    `;
2954}
2955
2956// Create memory layout visualization
2957function createMemoryLayoutVisualization(sortedAllocs, totalMemory) {
2958    if (sortedAllocs.length === 0) return '<div class="flex items-center justify-center h-full text-gray-400">No memory layout data</div>';
2959
2960    return sortedAllocs.slice(0, 30).map((alloc, index) => {
2961        const width = Math.max(1, (alloc.size / totalMemory) * 100);
2962        const left = (index / 30) * 100;
2963        const isUserAlloc = alloc.type !== 'System Allocation';
2964        const color = isUserAlloc ? '#3498db' : '#95a5a6';
2965
2966        return `
2967            <div class="absolute h-full transition-all hover:brightness-110 cursor-pointer" 
2968                 style="left: ${left}%; width: ${width}%; background-color: ${color}; opacity: 0.8;"
2969                 title="${alloc.type}: ${formatBytes(alloc.size)} at ${(alloc.address || 0).toString(16)}">
2970            </div>
2971        `;
2972    }).join('');
2973}
2974
2975// Calculate variance of allocation sizes to assess fragmentation level
2976function calculateSizeVariance(allocations) {
2977    if (allocations.length === 0) return 0;
2978    
2979    const sizes = allocations.map(a => a.size);
2980    const mean = sizes.reduce((sum, size) => sum + size, 0) / sizes.length;
2981    const variance = sizes.reduce((sum, size) => sum + Math.pow(size - mean, 2), 0) / sizes.length;
2982    
2983    return Math.sqrt(variance); // ่ฟ”ๅ›žๆ ‡ๅ‡†ๅทฎ
2984}
2985
2986// Helper functions for fragmentation analysis
2987function getFragmentationColor(ratio) {
2988    if (ratio < 10) return 'text-green-600 dark:text-green-400';
2989    if (ratio < 25) return 'text-yellow-600 dark:text-yellow-400';
2990    if (ratio < 50) return 'text-orange-600 dark:text-orange-400';
2991    return 'text-red-600 dark:text-red-400';
2992}
2993
2994function getFragmentationBgColor(ratio) {
2995    if (ratio < 10) return 'bg-green-500';
2996    if (ratio < 25) return 'bg-yellow-500';
2997    if (ratio < 50) return 'bg-orange-500';
2998    return 'bg-red-500';
2999}
3000
3001function getFragmentationAssessment(ratio) {
3002    if (ratio < 10) return 'Excellent memory layout with minimal fragmentation. Memory is well-organized.';
3003    if (ratio < 25) return 'Good memory layout with low fragmentation. No immediate concerns.';
3004    if (ratio < 50) return 'Moderate fragmentation detected. Consider memory pool allocation strategies.';
3005    return 'High fragmentation detected. Memory layout optimization strongly recommended.';
3006}
3007
3008// Initialize memory growth trends with enhanced SVG-style visualization
3009function initMemoryGrowthTrends() {
3010    const container = document.getElementById('memoryGrowthTrends');
3011    if (!container) return;
3012
3013    const allocations = window.analysisData.memory_analysis?.allocations || [];
3014
3015    // Sort allocations by timestamp
3016    const sortedAllocs = allocations
3017        .filter(alloc => alloc.timestamp_alloc)
3018        .sort((a, b) => a.timestamp_alloc - b.timestamp_alloc);
3019
3020    if (sortedAllocs.length === 0) {
3021        container.innerHTML = createGrowthTrendsEmptyState();
3022        return;
3023    }
3024
3025    // Calculate cumulative memory usage over time
3026    let cumulativeMemory = 0;
3027    let peakMemory = 0;
3028    const timePoints = [];
3029
3030    sortedAllocs.forEach((alloc, index) => {
3031        cumulativeMemory += alloc.size || 0;
3032        peakMemory = Math.max(peakMemory, cumulativeMemory);
3033
3034        if (index % Math.max(1, Math.floor(sortedAllocs.length / 20)) === 0) {
3035            timePoints.push({
3036                timestamp: alloc.timestamp_alloc,
3037                memory: cumulativeMemory,
3038                index: index,
3039                allocCount: index + 1
3040            });
3041        }
3042    });
3043
3044    const startMemory = timePoints[0]?.memory || 0;
3045    const endMemory = timePoints[timePoints.length - 1]?.memory || 0;
3046    const growthRate = startMemory > 0 ? ((endMemory - startMemory) / startMemory * 100) : 0;
3047    const averageMemory = timePoints.reduce((sum, point) => sum + point.memory, 0) / timePoints.length;
3048
3049    container.innerHTML = createMemoryGrowthTrendsSVG(
3050        peakMemory, averageMemory, growthRate, timePoints, sortedAllocs.length
3051    );
3052}
3053
3054// Create growth trends empty state
3055function createGrowthTrendsEmptyState() {
3056    return `
3057        <div class="bg-white dark:bg-gray-800 rounded-xl p-6 card-shadow transition-colors">
3058            <h2 class="text-xl font-semibold mb-4 flex items-center text-heading">
3059                <i class="fa fa-line-chart text-green-500 mr-2"></i>Memory Growth Trends
3060            </h2>
3061            <div class="text-center py-8">
3062                <div class="mb-4">
3063                    <svg class="w-16 h-16 mx-auto text-gray-400 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
3064                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
3065                    </svg>
3066                </div>
3067                <h4 class="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-200">No Timeline Data Available</h4>
3068                <p class="text-sm text-gray-600 dark:text-gray-400">Memory growth analysis requires timestamp data</p>
3069            </div>
3070        </div>
3071    `;
3072}
3073
3074// Create comprehensive memory growth trends visualization
3075function createMemoryGrowthTrendsSVG(peakMemory, averageMemory, growthRate, timePoints, totalAllocs) {
3076    return `
3077        <div class="bg-white dark:bg-gray-800 rounded-xl p-6 card-shadow transition-colors">
3078            <h2 class="text-xl font-semibold mb-6 flex items-center text-heading">
3079                <i class="fa fa-line-chart text-green-500 mr-2"></i>Memory Growth Trends
3080            </h2>
3081            
3082            <!-- Key Metrics Grid -->
3083            <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
3084                ${createGrowthMetricCard('Peak Memory', formatBytes(peakMemory), 100, '#e74c3c')}
3085                ${createGrowthMetricCard('Average Memory', formatBytes(averageMemory), Math.round((averageMemory / peakMemory) * 100), '#3498db')}
3086                ${createGrowthMetricCard('Growth Rate', (growthRate > 0 ? '+' : '') + growthRate.toFixed(1) + '%', Math.abs(growthRate), getGrowthRateColor(growthRate))}
3087                ${createGrowthMetricCard('Total Allocations', totalAllocs, 100, '#9b59b6')}
3088            </div>
3089
3090            <!-- Main Growth Chart -->
3091            <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 mb-6">
3092                <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Memory Usage Over Time</h4>
3093                <div class="relative">
3094                    <!-- Chart Container -->
3095                    <div class="h-48 relative bg-white dark:bg-gray-600 rounded border dark:border-gray-500 overflow-hidden">
3096                        ${createMemoryGrowthChart(timePoints, peakMemory)}
3097                    </div>
3098                    
3099                    <!-- Chart Labels -->
3100                    <div class="flex justify-between text-xs text-gray-500 dark:text-gray-400 mt-2">
3101                        <span>Start</span>
3102                        <span>Memory Usage Timeline</span>
3103                        <span>End</span>
3104                    </div>
3105                    
3106                    <!-- Peak Memory Line -->
3107                    <div class="absolute top-2 right-2 text-xs text-red-500 dark:text-red-400 bg-white dark:bg-gray-800 px-2 py-1 rounded shadow">
3108                        Peak: ${formatBytes(peakMemory)}
3109                    </div>
3110                </div>
3111            </div>
3112
3113            <!-- Growth Analysis -->
3114            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
3115                <!-- Growth Assessment -->
3116                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
3117                    <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Growth Assessment</h4>
3118                    <div class="space-y-3">
3119                        <div class="flex items-center justify-between">
3120                            <span class="text-sm text-gray-600 dark:text-gray-300">Memory Efficiency</span>
3121                            <span class="text-sm font-bold ${getEfficiencyColor(averageMemory, peakMemory)}">${((averageMemory / peakMemory) * 100).toFixed(1)}%</span>
3122                        </div>
3123                        <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
3124                            <div class="h-2 rounded-full transition-all duration-500 ${getEfficiencyBgColor(averageMemory, peakMemory)}" 
3125                                 style="width: ${(averageMemory / peakMemory) * 100}%"></div>
3126                        </div>
3127                        <div class="text-sm text-gray-600 dark:text-gray-300">
3128                            ${getGrowthAssessment(growthRate)}
3129                        </div>
3130                    </div>
3131                </div>
3132
3133                <!-- Memory Allocation Timeline -->
3134                <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4">
3135                    <h4 class="font-semibold mb-4 text-gray-800 dark:text-white">Recent Allocations</h4>
3136                    <div class="space-y-2 max-h-32 overflow-y-auto">
3137                        ${timePoints.slice(-6).map((point, index) => `
3138                            <div class="flex justify-between items-center text-sm">
3139                                <span class="text-gray-600 dark:text-gray-300">Alloc #${point.allocCount}</span>
3140                                <span class="font-mono text-xs font-bold text-gray-900 dark:text-white">${formatBytes(point.memory)}</span>
3141                            </div>
3142                        `).join('')}
3143                    </div>
3144                    <div class="text-xs text-gray-500 dark:text-gray-400 mt-2">
3145                        Showing latest allocation points
3146                    </div>
3147                </div>
3148            </div>
3149        </div>
3150    `;
3151}
3152
3153// Create growth metric card
3154function createGrowthMetricCard(title, value, percentage, color) {
3155    const normalizedPercentage = Math.min(100, Math.max(0, percentage));
3156
3157    return `
3158        <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-4 text-center hover:shadow-md transition-shadow">
3159            <div class="mb-2">
3160                <div class="text-2xl font-bold" style="color: ${color}">${value}</div>
3161                <div class="text-xs text-gray-600 dark:text-gray-400 uppercase tracking-wide">${title}</div>
3162            </div>
3163            <div class="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
3164                <div class="h-2 rounded-full transition-all duration-500" 
3165                     style="width: ${normalizedPercentage}%; background-color: ${color}"></div>
3166            </div>
3167        </div>
3168    `;
3169}
3170
3171// Create memory growth chart
3172function createMemoryGrowthChart(timePoints, peakMemory) {
3173    if (timePoints.length < 2) return '<div class="flex items-center justify-center h-full text-gray-400">Insufficient data points</div>';
3174
3175    const chartHeight = 180;
3176    const chartWidth = 100; // percentage
3177
3178    // Create SVG path for the growth line
3179    const pathPoints = timePoints.map((point, index) => {
3180        const x = (index / (timePoints.length - 1)) * chartWidth;
3181        const y = chartHeight - ((point.memory / peakMemory) * (chartHeight - 20));
3182        return `${x},${y}`;
3183    });
3184
3185    return `
3186        <!-- Background Grid -->
3187        <div class="absolute inset-0">
3188            ${[0, 25, 50, 75, 100].map(y => `
3189                <div class="absolute w-full border-t border-gray-200 dark:border-gray-500 opacity-30" 
3190                     style="top: ${y}%"></div>
3191            `).join('')}
3192        </div>
3193        
3194        <!-- Growth Line -->
3195        <svg class="absolute inset-0 w-full h-full" preserveAspectRatio="none">
3196            <polyline
3197                fill="none"
3198                stroke='#27ae60'
3199                stroke-width="3"
3200                stroke-linecap='round'
3201                stroke-linejoin='round'
3202                points='${timePoints.map((point, index) => {
3203        const x = (index / (timePoints.length - 1)) * 100;
3204        const y = 100 - ((point.memory / peakMemory) * 90);
3205        return `${x},${y}`;
3206    }).join(' ')}'
3207                class='drop-shadow-sm'
3208            />
3209        </svg>
3210        
3211        <!-- Data Points -->
3212        ${timePoints.map((point, index) => {
3213        const x = (index / (timePoints.length - 1)) * 100;
3214        const y = 100 - ((point.memory / peakMemory) * 90);
3215        return `
3216                <div class="absolute w-3 h-3 bg-green-500 rounded-full border-2 border-white dark:border-gray-600 shadow-sm transform -translate-x-1/2 -translate-y-1/2 hover:scale-125 transition-transform cursor-pointer" 
3217                     style="left: ${x}%; top: ${y}%"
3218                     title="Memory: ${formatBytes(point.memory)} at allocation #${point.allocCount}">
3219                </div>
3220            `;
3221    }).join('')}
3222        
3223        <!-- Peak Memory Indicator -->
3224        <div class="absolute w-full border-t-2 border-red-500 border-dashed opacity-60" style="top: 10%">
3225            <div class="absolute -top-1 right-0 text-xs text-red-500 bg-white dark:bg-gray-600 px-1 rounded">
3226                Peak
3227            </div>
3228        </div>
3229    `;
3230}
3231
3232// Helper functions for growth analysis
3233function getGrowthRateColor(rate) {
3234    if (rate < -10) return '#27ae60'; // Green for decreasing
3235    if (rate < 10) return '#3498db';  // Blue for stable
3236    if (rate < 50) return '#f39c12'; // Orange for moderate growth
3237    return '#e74c3c'; // Red for high growth
3238}
3239
3240function getEfficiencyColor(avg, peak) {
3241    const efficiency = (avg / peak) * 100;
3242    if (efficiency > 80) return 'text-red-600 dark:text-red-400';
3243    if (efficiency > 60) return 'text-orange-600 dark:text-orange-400';
3244    if (efficiency > 40) return 'text-yellow-600 dark:text-yellow-400';
3245    return 'text-green-600 dark:text-green-400';
3246}
3247
3248function getEfficiencyBgColor(avg, peak) {
3249    const efficiency = (avg / peak) * 100;
3250    if (efficiency > 80) return 'bg-red-500';
3251    if (efficiency > 60) return 'bg-orange-500';
3252    if (efficiency > 40) return 'bg-yellow-500';
3253    return 'bg-green-500';
3254}
3255
3256function getGrowthAssessment(rate) {
3257    if (rate < -10) return 'Excellent: Memory usage is decreasing, indicating good cleanup.';
3258    if (rate < 10) return 'Good: Stable memory usage with minimal growth.';
3259    if (rate < 50) return 'Moderate: Some memory growth detected, monitor for trends.';
3260    return 'Concerning: High memory growth detected, investigate for potential leaks.';
3261}
3262
3263// Node Detail Panel for Variable Relationship Graph
3264class NodeDetailPanel {
3265    constructor(containerId) {
3266        this.container = document.getElementById(containerId);
3267        this.panel = null;
3268        this.currentNode = null;
3269    }
3270
3271    show(nodeData, position) {
3272        console.log('Showing panel for:', nodeData.id);
3273        this.hide(); // Close existing panel
3274        this.panel = this.createPanel(nodeData);
3275        console.log('Panel created:', this.panel);
3276        this.positionPanel(position);
3277        this.container.appendChild(this.panel);
3278        console.log('Panel added to container');
3279        this.currentNode = nodeData;
3280    }
3281
3282    hide() {
3283        if (this.panel) {
3284            this.panel.remove();
3285            this.panel = null;
3286            this.currentNode = null;
3287        }
3288    }
3289
3290    createPanel(nodeData) {
3291        const panel = document.createElement('div');
3292        panel.className = 'node-detail-panel';
3293
3294        // Find related allocation data
3295        const allocations = window.analysisData.memory_analysis?.allocations || [];
3296        const allocation = allocations.find(alloc => alloc.var_name === nodeData.id);
3297
3298        // Calculate relationships
3299        const sameTypeCount = allocations.filter(alloc =>
3300            alloc.type_name === nodeData.type_name && alloc.var_name !== nodeData.id
3301        ).length;
3302
3303        const sameCategoryCount = allocations.filter(alloc =>
3304            getTypeCategory(alloc.type_name || '') === (nodeData.category || 'primitive') && alloc.var_name !== nodeData.id
3305        ).length;
3306
3307        panel.innerHTML = `
3308            <div class="flex justify-between items-center mb-3">
3309                <h3>Variable Details</h3>
3310                <button class="close-button text-xl leading-none">&times;</button>
3311            </div>
3312            
3313            <div class="space-y-3">
3314                <div>
3315                    <label>Variable Name</label>
3316                    <p class="font-mono">${nodeData.id}</p>
3317                </div>
3318                
3319                <div>
3320                    <label>Type</label>
3321                    <p class="font-mono">${nodeData.type_name || 'Unknown'}</p>
3322                    <div class="flex items-center mt-1">
3323                        <div class="w-3 h-3 rounded-full mr-2" style="background-color: ${getEnhancedTypeColor(nodeData.type_name || 'unknown', nodeData.category || 'primitive')}"></div>
3324                        <span class="text-xs capitalize">${(nodeData.category || 'primitive').replace('_', ' ')}</span>
3325                    </div>
3326                </div>
3327                
3328                <div>
3329                    <label>Memory Size</label>
3330                    <p>${formatBytes(nodeData.size)}</p>
3331                </div>
3332                
3333                <div>
3334                    <label>Complexity Score</label>
3335                    <div class="flex items-center mb-2">
3336                        <div class="w-5 h-5 rounded-full mr-2 flex items-center justify-center text-white font-bold text-xs" style="background-color: ${getComplexityColor(nodeData.complexity || 2)}">${nodeData.complexity || 2}</div>
3337                        <span class="font-semibold">${nodeData.complexity || 2}/10 - ${getComplexityLevel(nodeData.complexity || 2)}</span>
3338                    </div>
3339                    <div class="text-xs text-gray-600 dark:text-gray-400">
3340                        ${getComplexityExplanation(nodeData.complexity || 2)}
3341                    </div>
3342                </div>
3343                
3344                ${allocation ? `
3345                    <div>
3346                        <label>Memory Address</label>
3347                        <p class="font-mono text-xs">${allocation.ptr}</p>
3348                    </div>
3349                    
3350                    <div>
3351                        <label>Allocated At</label>
3352                        <p class="text-sm">${new Date(allocation.timestamp_alloc / 1000000).toLocaleString()}</p>
3353                    </div>
3354                ` : ''}
3355                
3356                <div>
3357                    <label>Relationships</label>
3358                    <div class="text-sm space-y-1">
3359                        <div class="flex justify-between">
3360                            <span>Same type:</span>
3361                            <span class="font-semibold">${sameTypeCount}</span>
3362                        </div>
3363                        <div class="flex justify-between">
3364                            <span>Same category:</span>
3365                            <span class="font-semibold">${sameCategoryCount}</span>
3366                        </div>
3367                    </div>
3368                </div>
3369                
3370                <div>
3371                    <label>Type Analysis</label>
3372                    <div class="text-xs space-y-1">
3373                        ${getTypeAnalysis(nodeData.type_name || 'unknown', nodeData.size)}
3374                    </div>
3375                </div>
3376            </div>
3377        `;
3378
3379        // Add close button functionality
3380        const closeButton = panel.querySelector('.close-button');
3381        closeButton.addEventListener('click', () => {
3382            this.hide();
3383        });
3384
3385        return panel;
3386    }
3387
3388    positionPanel(position) {
3389        if (!this.panel) return;
3390
3391        // Simple positioning - place panel at a fixed position relative to container
3392        this.panel.style.position = 'absolute';
3393        this.panel.style.left = '20px';
3394        this.panel.style.top = '20px';
3395        this.panel.style.zIndex = '1000';
3396
3397        console.log('Panel positioned at:', this.panel.style.left, this.panel.style.top);
3398    }
3399}
3400
3401// Initialize variable relationship graph with enhanced D3.js force simulation
3402function initVariableGraph() {
3403    const container = document.getElementById('variable-graph-container');
3404    if (!container) return;
3405
3406    const allocations = window.analysisData.memory_analysis?.allocations || [];
3407    const userAllocations = allocations.filter(alloc =>
3408        alloc.var_name && alloc.var_name !== 'unknown' &&
3409        alloc.type_name && alloc.type_name !== 'unknown'
3410    );
3411
3412    if (userAllocations.length === 0) {
3413        container.innerHTML = `
3414            <div class="flex items-center justify-center h-full text-gray-500 dark:text-gray-400">
3415                <div class="text-center">
3416                    <i class="fa fa-sitemap text-4xl mb-4"></i>
3417                    <p class="text-lg font-semibold mb-2">No User Variables Found</p>
3418                    <p class="text-sm">Use track_var! macro to track variable relationships</p>
3419                </div>
3420            </div>
3421        `;
3422        return;
3423    }
3424
3425    // Clear container
3426    container.innerHTML = '';
3427
3428    // Set up dimensions
3429    const width = container.clientWidth;
3430    const height = container.clientHeight;
3431
3432    // Create SVG
3433    const svg = d3.select(container)
3434        .append('svg')
3435        .attr('width', width)
3436        .attr('height', height)
3437        .style('background', 'transparent');
3438
3439    // Create zoom behavior
3440    const zoom = d3.zoom()
3441        .scaleExtent([0.1, 4])
3442        .on('zoom', (event) => {
3443            g.attr('transform', event.transform);
3444        });
3445
3446    svg.call(zoom);
3447
3448    // Create main group for zooming/panning
3449    const g = svg.append('g');
3450
3451    // Prepare nodes data
3452    const nodes = userAllocations.map((alloc, index) => ({
3453        id: alloc.var_name,
3454        type: alloc.type_name,
3455        size: alloc.size || 0,
3456        complexity: getComplexityFromType(alloc.type_name),
3457        category: getTypeCategory(alloc.type_name),
3458        allocation: alloc
3459    }));
3460
3461    // Create more sophisticated relationships
3462    const links = [];
3463
3464    // Type similarity relationships
3465    for (let i = 0; i < nodes.length; i++) {
3466        for (let j = i + 1; j < nodes.length; j++) {
3467            const node1 = nodes[i];
3468            const node2 = nodes[j];
3469
3470            // Same type relationship
3471            if (node1.type === node2.type) {
3472                links.push({
3473                    source: node1.id,
3474                    target: node2.id,
3475                    type: 'same_type',
3476                    strength: 1.0
3477                });
3478            }
3479            // Similar category relationship
3480            else if (node1.category === node2.category && node1.category !== 'primitive') {
3481                links.push({
3482                    source: node1.id,
3483                    target: node2.id,
3484                    type: 'similar_category',
3485                    strength: 0.6
3486                });
3487            }
3488            // Generic type relationship (Vec<T>, Box<T>, etc.)
3489            else if (getGenericBase(node1.type) === getGenericBase(node2.type)) {
3490                links.push({
3491                    source: node1.id,
3492                    target: node2.id,
3493                    type: 'generic_family',
3494                    strength: 0.8
3495                });
3496            }
3497        }
3498    }
3499
3500    // Create force simulation
3501    const simulation = d3.forceSimulation(nodes)
3502        .force('link', d3.forceLink(links)
3503            .id(d => d.id)
3504            .distance(d => 80 + (1 - d.strength) * 40)
3505            .strength(d => d.strength * 0.7)
3506        )
3507        .force('charge', d3.forceManyBody()
3508            .strength(d => -200 - (d.size / 100))
3509        )
3510        .force('center', d3.forceCenter(width / 2, height / 2))
3511        .force('collision', d3.forceCollide()
3512            .radius(d => {
3513                const minRadius = 15;
3514                const maxRadius = 50;
3515                const maxSize = Math.max(...nodes.map(n => n.size));
3516                const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3517                const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3518                return nodeRadius + 5;
3519            })
3520        );
3521
3522    // Create link elements
3523    const link = g.append('g')
3524        .attr('class', 'links')
3525        .selectAll('line')
3526        .data(links)
3527        .enter().append('line')
3528        .attr('stroke', d => getLinkColor(d.type))
3529        .attr('stroke-opacity', d => 0.3 + d.strength * 0.4)
3530        .attr('stroke-width', d => 1 + d.strength * 2)
3531        .attr('stroke-dasharray', d => d.type === 'similar_category' ? '5,5' : null);
3532
3533    // Create node groups
3534    const node = g.append('g')
3535        .attr('class', 'nodes')
3536        .selectAll('g')
3537        .data(nodes)
3538        .enter().append('g')
3539        .attr('class', 'graph-node')
3540        .style('cursor', 'pointer')
3541        .call(d3.drag()
3542            .on('start', dragstarted)
3543            .on('drag', dragged)
3544            .on('end', dragended)
3545        );
3546
3547    // Add circles to nodes - size based on memory usage
3548    node.append('circle')
3549        .attr('r', d => {
3550            // Scale node size based on memory usage (larger memory = larger node)
3551            const minRadius = 15;
3552            const maxRadius = 50;
3553            const maxSize = Math.max(...nodes.map(n => n.size));
3554            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3555            return minRadius + (sizeRatio * (maxRadius - minRadius));
3556        })
3557        .attr('fill', d => getEnhancedTypeColor(d.type, d.category))
3558        .attr('stroke', '#fff')
3559        .attr('stroke-width', 2)
3560        .style('filter', 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))')
3561        .on('mouseover', function (event, d) {
3562            const currentRadius = d3.select(this).attr('r');
3563            d3.select(this)
3564                .transition()
3565                .duration(200)
3566                .attr('r', parseFloat(currentRadius) * 1.2)
3567                .style('filter', 'drop-shadow(0px 4px 8px rgba(0,0,0,0.3))');
3568
3569            // Highlight connected links
3570            link.style('stroke-opacity', l =>
3571                (l.source.id === d.id || l.target.id === d.id) ? 0.8 : 0.1
3572            );
3573        })
3574        .on('mouseout', function (event, d) {
3575            const minRadius = 15;
3576            const maxRadius = 50;
3577            const maxSize = Math.max(...nodes.map(n => n.size));
3578            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3579            const originalRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3580            
3581            d3.select(this)
3582                .transition()
3583                .duration(200)
3584                .attr('r', originalRadius)
3585                .style('filter', 'drop-shadow(0px 2px 4px rgba(0,0,0,0.2))');
3586
3587            // Reset link opacity
3588            link.style('stroke-opacity', l => 0.3 + l.strength * 0.4);
3589        });
3590
3591    // Add complexity indicators (small circles with numbers)
3592    const complexityGroup = node.append('g')
3593        .attr('class', 'complexity-indicator');
3594
3595    complexityGroup.append('circle')
3596        .attr('r', 8)
3597        .attr('cx', d => {
3598            const minRadius = 15;
3599            const maxRadius = 50;
3600            const maxSize = Math.max(...nodes.map(n => n.size));
3601            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3602            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3603            return nodeRadius + 8;
3604        })
3605        .attr('cy', d => {
3606            const minRadius = 15;
3607            const maxRadius = 50;
3608            const maxSize = Math.max(...nodes.map(n => n.size));
3609            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3610            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3611            return -nodeRadius - 8;
3612        })
3613        .attr('fill', d => getComplexityColor(d.complexity))
3614        .attr('stroke', '#fff')
3615        .attr('stroke-width', 2);
3616
3617    // Add complexity score text
3618    complexityGroup.append('text')
3619        .text(d => d.complexity || 2)
3620        .attr('x', d => {
3621            const minRadius = 15;
3622            const maxRadius = 50;
3623            const maxSize = Math.max(...nodes.map(n => n.size));
3624            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3625            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3626            return nodeRadius + 8;
3627        })
3628        .attr('y', d => {
3629            const minRadius = 15;
3630            const maxRadius = 50;
3631            const maxSize = Math.max(...nodes.map(n => n.size));
3632            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3633            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3634            return -nodeRadius - 8 + 3;
3635        })
3636        .attr('text-anchor', 'middle')
3637        .style('font-size', '10px')
3638        .style('font-weight', 'bold')
3639        .style('fill', '#fff')
3640        .style('pointer-events', 'none');
3641
3642    // Add variable names
3643    node.append('text')
3644        .text(d => d.id)
3645        .attr('text-anchor', 'middle')
3646        .attr('dy', d => {
3647            const minRadius = 15;
3648            const maxRadius = 50;
3649            const maxSize = Math.max(...nodes.map(n => n.size));
3650            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3651            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3652            return nodeRadius + 15;
3653        })
3654        .style('font-size', '11px')
3655        .style('font-weight', 'bold')
3656        .style('fill', 'var(--text-primary)')
3657        .style('pointer-events', 'none');
3658
3659    // Add type labels
3660    node.append('text')
3661        .text(d => formatTypeName(d.type))
3662        .attr('text-anchor', 'middle')
3663        .attr('dy', d => {
3664            const minRadius = 15;
3665            const maxRadius = 50;
3666            const maxSize = Math.max(...nodes.map(n => n.size));
3667            const sizeRatio = maxSize > 0 ? d.size / maxSize : 0;
3668            const nodeRadius = minRadius + (sizeRatio * (maxRadius - minRadius));
3669            return nodeRadius + 28;
3670        })
3671        .style('font-size', '9px')
3672        .style('fill', 'var(--text-secondary)')
3673        .style('pointer-events', 'none');
3674
3675    // Add click interaction
3676    const detailPanel = new NodeDetailPanel('variable-graph-container');
3677
3678    node.on('click', function (event, d) {
3679        event.stopPropagation();
3680        console.log('Node clicked:', d.id, d);
3681        const position = {
3682            x: event.pageX,
3683            y: event.pageY
3684        };
3685        detailPanel.show(d, position);
3686    });
3687
3688    // Click on empty space to hide panel
3689    svg.on('click', function (event) {
3690        if (event.target === this) {
3691            detailPanel.hide();
3692        }
3693    });
3694
3695    // Update positions on simulation tick
3696    simulation.on('tick', () => {
3697        link
3698            .attr('x1', d => d.source.x)
3699            .attr('y1', d => d.source.y)
3700            .attr('x2', d => d.target.x)
3701            .attr('y2', d => d.target.y);
3702
3703        node
3704            .attr('transform', d => `translate(${d.x},${d.y})`);
3705    });
3706
3707    // Add control buttons
3708    const controls = d3.select(container)
3709        .append('div')
3710        .attr('class', 'absolute top-2 right-2 flex space-x-2');
3711
3712    controls.append('button')
3713        .attr('class', 'px-3 py-1 bg-blue-500 hover:bg-blue-600 text-white text-xs rounded transition-colors')
3714        .text('Reset View')
3715        .on('click', () => {
3716            svg.transition().duration(750).call(
3717                zoom.transform,
3718                d3.zoomIdentity
3719            );
3720        });
3721
3722    controls.append('button')
3723        .attr('class', 'px-3 py-1 bg-green-500 hover:bg-green-600 text-white text-xs rounded transition-colors')
3724        .text('Reheat')
3725        .on('click', () => {
3726            simulation.alpha(0.3).restart();
3727        });
3728
3729    // Drag functions
3730    function dragstarted(event, d) {
3731        if (!event.active) simulation.alphaTarget(0.3).restart();
3732        d.fx = d.x;
3733        d.fy = d.y;
3734    }
3735
3736    function dragged(event, d) {
3737        d.fx = event.x;
3738        d.fy = event.y;
3739    }
3740
3741    function dragended(event, d) {
3742        if (!event.active) simulation.alphaTarget(0);
3743        d.fx = null;
3744        d.fy = null;
3745    }
3746}
3747
3748// Get color for variable type
3749function getTypeColor(typeName) {
3750    if (typeName.includes('Vec')) return '#3b82f6';
3751    if (typeName.includes('Box')) return '#8b5cf6';
3752    if (typeName.includes('Rc') || typeName.includes('Arc')) return '#10b981';
3753    if (typeName.includes('String')) return '#f59e0b';
3754    return '#6b7280';
3755}
3756
3757// Enhanced type color with comprehensive type mapping
3758function getEnhancedTypeColor(typeName, category) {
3759    // Comprehensive color mapping for specific types
3760    const typeColorMap = {
3761        // Smart Pointers - Purple/Violet family
3762        'Box': '#8b5cf6',           // Purple
3763        'Rc': '#a855f7',            // Purple-500
3764        'Arc': '#9333ea',           // Violet-600
3765        'RefCell': '#7c3aed',       // Violet-700
3766        'Cell': '#6d28d9',          // Violet-800
3767        'Weak': '#5b21b6',          // Violet-900
3768
3769        // Collections - Blue family
3770        'Vec': '#3b82f6',           // Blue-500
3771        'VecDeque': '#2563eb',      // Blue-600
3772        'LinkedList': '#1d4ed8',    // Blue-700
3773        'HashMap': '#1e40af',       // Blue-800
3774        'BTreeMap': '#1e3a8a',      // Blue-900
3775        'HashSet': '#60a5fa',       // Blue-400
3776        'BTreeSet': '#93c5fd',      // Blue-300
3777
3778        // String types - Orange/Amber family
3779        'String': '#f59e0b',        // Amber-500
3780        'str': '#d97706',           // Amber-600
3781        'OsString': '#b45309',      // Amber-700
3782        'OsStr': '#92400e',         // Amber-800
3783        'CString': '#78350f',       // Amber-900
3784        'CStr': '#fbbf24',          // Amber-400
3785
3786        // Numeric types - Green family
3787        'i8': '#10b981',            // Emerald-500
3788        'i16': '#059669',           // Emerald-600
3789        'i32': '#047857',           // Emerald-700
3790        'i64': '#065f46',           // Emerald-800
3791        'i128': '#064e3b',          // Emerald-900
3792        'u8': '#34d399',            // Emerald-400
3793        'u16': '#6ee7b7',           // Emerald-300
3794        'u32': '#a7f3d0',           // Emerald-200
3795        'u64': '#d1fae5',           // Emerald-100
3796        'u128': '#ecfdf5',          // Emerald-50
3797        'f32': '#14b8a6',           // Teal-500
3798        'f64': '#0d9488',           // Teal-600
3799        'usize': '#0f766e',         // Teal-700
3800        'isize': '#115e59',         // Teal-800
3801
3802        // Boolean and char - Pink family
3803        'bool': '#ec4899',          // Pink-500
3804        'char': '#db2777',          // Pink-600
3805
3806        // Option and Result - Indigo family
3807        'Option': '#6366f1',        // Indigo-500
3808        'Result': '#4f46e5',        // Indigo-600
3809        'Some': '#4338ca',          // Indigo-700
3810        'None': '#3730a3',          // Indigo-800
3811        'Ok': '#312e81',            // Indigo-900
3812        'Err': '#6366f1',           // Indigo-500
3813
3814        // Synchronization types - Red family
3815        'Mutex': '#ef4444',         // Red-500
3816        'RwLock': '#dc2626',        // Red-600
3817        'Condvar': '#b91c1c',       // Red-700
3818        'Barrier': '#991b1b',       // Red-800
3819        'Once': '#7f1d1d',          // Red-900
3820
3821        // Channel types - Cyan family
3822        'Sender': '#06b6d4',        // Cyan-500
3823        'Receiver': '#0891b2',      // Cyan-600
3824        'mpsc': '#0e7490',          // Cyan-700
3825
3826        // Path types - Lime family
3827        'Path': '#84cc16',          // Lime-500
3828        'PathBuf': '#65a30d',       // Lime-600
3829
3830        // Time types - Yellow family
3831        'Duration': '#eab308',      // Yellow-500
3832        'Instant': '#ca8a04',       // Yellow-600
3833        'SystemTime': '#a16207',    // Yellow-700
3834
3835        // IO types - Stone family
3836        'File': '#78716c',          // Stone-500
3837        'BufReader': '#57534e',     // Stone-600
3838        'BufWriter': '#44403c',     // Stone-700
3839
3840        // Thread types - Rose family
3841        'Thread': '#f43f5e',        // Rose-500
3842        'JoinHandle': '#e11d48',    // Rose-600
3843
3844        // Custom/Unknown types - Gray family
3845        'unknown': '#6b7280',       // Gray-500
3846        'custom': '#4b5563',        // Gray-600
3847    };
3848
3849    // First, try exact type name match
3850    if (typeColorMap[typeName]) {
3851        return typeColorMap[typeName];
3852    }
3853
3854    // Then try to match by type name contains
3855    for (const [type, color] of Object.entries(typeColorMap)) {
3856        if (typeName.includes(type)) {
3857            return color;
3858        }
3859    }
3860
3861    // Extract generic base type and try to match
3862    const genericBase = getGenericBase(typeName);
3863    if (typeColorMap[genericBase]) {
3864        return typeColorMap[genericBase];
3865    }
3866
3867    // Fall back to category-based colors
3868    switch (category) {
3869        case 'smart_pointer': return '#8b5cf6';  // Purple
3870        case 'collection': return '#3b82f6';     // Blue
3871        case 'string': return '#f59e0b';         // Amber
3872        case 'numeric': return '#10b981';        // Emerald
3873        case 'sync': return '#ef4444';           // Red
3874        case 'channel': return '#06b6d4';        // Cyan
3875        case 'path': return '#84cc16';           // Lime
3876        case 'time': return '#eab308';           // Yellow
3877        case 'io': return '#78716c';             // Stone
3878        case 'thread': return '#f43f5e';         // Rose
3879        default: return '#6b7280';               // Gray
3880    }
3881}
3882
3883// Get type category for grouping with comprehensive type recognition
3884function getTypeCategory(typeName) {
3885    // Smart pointers
3886    if (typeName.includes('Box') || typeName.includes('Rc') || typeName.includes('Arc') ||
3887        typeName.includes('RefCell') || typeName.includes('Cell') || typeName.includes('Weak')) {
3888        return 'smart_pointer';
3889    }
3890
3891    // Collections
3892    if (typeName.includes('Vec') || typeName.includes('HashMap') || typeName.includes('BTreeMap') ||
3893        typeName.includes('HashSet') || typeName.includes('BTreeSet') || typeName.includes('VecDeque') ||
3894        typeName.includes('LinkedList')) {
3895        return 'collection';
3896    }
3897
3898    // String types
3899    if (typeName.includes('String') || typeName.includes('str') || typeName.includes('OsString') ||
3900        typeName.includes('OsStr') || typeName.includes('CString') || typeName.includes('CStr')) {
3901        return 'string';
3902    }
3903
3904    // Numeric types
3905    if (typeName.match(/^[iuf]\d+$/) || typeName === 'usize' || typeName === 'isize' ||
3906        typeName === 'bool' || typeName === 'char') {
3907        return 'numeric';
3908    }
3909
3910    // Synchronization types
3911    if (typeName.includes('Mutex') || typeName.includes('RwLock') || typeName.includes('Condvar') ||
3912        typeName.includes('Barrier') || typeName.includes('Once')) {
3913        return 'sync';
3914    }
3915
3916    // Channel types
3917    if (typeName.includes('Sender') || typeName.includes('Receiver') || typeName.includes('mpsc')) {
3918        return 'channel';
3919    }
3920
3921    // Path types
3922    if (typeName.includes('Path') || typeName.includes('PathBuf')) {
3923        return 'path';
3924    }
3925
3926    // Time types
3927    if (typeName.includes('Duration') || typeName.includes('Instant') || typeName.includes('SystemTime')) {
3928        return 'time';
3929    }
3930
3931    // IO types
3932    if (typeName.includes('File') || typeName.includes('BufReader') || typeName.includes('BufWriter')) {
3933        return 'io';
3934    }
3935
3936    // Thread types
3937    if (typeName.includes('Thread') || typeName.includes('JoinHandle')) {
3938        return 'thread';
3939    }
3940
3941    // Option and Result
3942    if (typeName.includes('Option') || typeName.includes('Result')) {
3943        return 'option_result';
3944    }
3945
3946    return 'primitive';
3947}
3948
3949// Get generic base type (Vec<T> -> Vec, Box<T> -> Box)
3950function getGenericBase(typeName) {
3951    const match = typeName.match(/^([^<]+)/);
3952    return match ? match[1] : typeName;
3953}
3954
3955// Get complexity score from type with comprehensive scoring
3956function getComplexityFromType(typeName) {
3957    // Very high complexity (9-10)
3958    if (typeName.includes('HashMap') || typeName.includes('BTreeMap') ||
3959        typeName.includes('BTreeSet') || typeName.includes('LinkedList')) return 9;
3960
3961    // High complexity (7-8)
3962    if (typeName.includes('Arc') || typeName.includes('Mutex') || typeName.includes('RwLock') ||
3963        typeName.includes('Condvar') || typeName.includes('Barrier')) return 8;
3964    if (typeName.includes('Rc') || typeName.includes('RefCell') || typeName.includes('HashSet') ||
3965        typeName.includes('VecDeque')) return 7;
3966
3967    // Medium complexity (5-6)
3968    if (typeName.includes('Vec') || typeName.includes('Box') || typeName.includes('Option') ||
3969        typeName.includes('Result')) return 6;
3970    if (typeName.includes('String') || typeName.includes('PathBuf') || typeName.includes('OsString') ||
3971        typeName.includes('CString')) return 5;
3972
3973    // Low complexity (3-4)
3974    if (typeName.includes('str') || typeName.includes('Path') || typeName.includes('OsStr') ||
3975        typeName.includes('CStr') || typeName.includes('Duration') || typeName.includes('Instant')) return 4;
3976    if (typeName.includes('Sender') || typeName.includes('Receiver') || typeName.includes('File') ||
3977        typeName.includes('Thread') || typeName.includes('JoinHandle')) return 3;
3978
3979    // Very low complexity (1-2)
3980    if (typeName.match(/^[iuf]\d+$/) || typeName === 'usize' || typeName === 'isize' ||
3981        typeName === 'bool' || typeName === 'char') return 1;
3982
3983    // Default for unknown types
3984    return 2;
3985}
3986
3987// Get link color based on relationship type
3988function getLinkColor(linkType) {
3989    switch (linkType) {
3990        case 'same_type': return '#ef4444';
3991        case 'similar_category': return '#3b82f6';
3992        case 'generic_family': return '#10b981';
3993        default: return '#6b7280';
3994    }
3995}
3996
3997// Get complexity level description
3998function getComplexityLevel(score) {
3999    if (score <= 2) return 'Simple';
4000    if (score <= 5) return 'Medium';
4001    if (score <= 8) return 'Complex';
4002    return 'Very Complex';
4003}
4004
4005// Get complexity explanation
4006function getComplexityExplanation(score) {
4007    if (score <= 2) return 'Basic types with minimal performance overhead and simple memory usage';
4008    if (score <= 5) return 'Medium complexity with some memory management overhead';
4009    if (score <= 8) return 'Complex types involving heap allocation and smart pointers, performance considerations needed';
4010    return 'Very complex types with significant performance overhead, optimization recommended';
4011}
4012
4013// Get type analysis information
4014function getTypeAnalysis(typeName, size) {
4015    const analysis = [];
4016
4017    if (typeName.includes('Vec')) {
4018        analysis.push('โ€ข Dynamic array with heap allocation');
4019        analysis.push('โ€ข Grows automatically as needed');
4020        if (size > 1000) analysis.push('โ€ข Large allocation - consider capacity optimization');
4021    } else if (typeName.includes('Box')) {
4022        analysis.push('โ€ข Single heap allocation');
4023        analysis.push('โ€ข Unique ownership semantics');
4024    } else if (typeName.includes('Rc')) {
4025        analysis.push('โ€ข Reference counted smart pointer');
4026        analysis.push('โ€ข Shared ownership with runtime checks');
4027    } else if (typeName.includes('Arc')) {
4028        analysis.push('โ€ข Atomic reference counted pointer');
4029        analysis.push('โ€ข Thread-safe shared ownership');
4030    } else if (typeName.includes('String')) {
4031        analysis.push('โ€ข Growable UTF-8 string');
4032        analysis.push('โ€ข Heap allocated with capacity buffer');
4033    } else {
4034        analysis.push('โ€ข Basic type allocation');
4035    }
4036
4037    if (size === 0) {
4038        analysis.push('โ€ข Zero-sized type (ZST)');
4039    } else if (size < 64) {
4040        analysis.push('โ€ข Small allocation - good for performance');
4041    } else if (size > 1024) {
4042        analysis.push('โ€ข Large allocation - monitor memory usage');
4043    }
4044
4045    return analysis.join('<br>');
4046}
4047
4048// Initialize generic types table
4049function initGenericTypesTable() {
4050    const tbody = document.getElementById('generic-types-table-body');
4051    if (!tbody) return;
4052
4053    const genericTypes = window.analysisData.complex_types?.categorized_types?.generic_types || [];
4054
4055    if (genericTypes.length === 0) {
4056        tbody.innerHTML = '<tr><td colspan="6" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">No generic types found</td></tr>';
4057        return;
4058    }
4059
4060    tbody.innerHTML = genericTypes.map(type => `
4061        <tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
4062            <td class="px-6 py-4 text-gray-900 dark:text-gray-100">${type.var_name || 'System Allocation'}</td>
4063            <td class="px-6 py-4 text-gray-900 dark:text-gray-100">${formatTypeName(type.type_name || 'System Allocation')}</td>
4064            <td class="px-6 py-4 font-mono text-xs text-gray-900 dark:text-gray-100">${type.ptr}</td>
4065            <td class="px-6 py-4 text-gray-900 dark:text-gray-100">${formatBytes(type.size || 0)}</td>
4066            <td class="px-6 py-4 text-gray-900 dark:text-gray-100">N/A</td>
4067            <td class="px-6 py-4">
4068                <span class="px-2 py-1 rounded text-xs ${getComplexityColor(type.complexity_score)} text-white">
4069                    ${type.complexity_score || 0}
4070                </span>
4071            </td>
4072        </tr>
4073    `).join('');
4074}
4075
4076// Initialize complex type analysis
4077function initComplexTypeAnalysis() {
4078    const tbody = document.getElementById('complex-type-analysis-table');
4079    if (!tbody) return;
4080
4081    const complexTypeAnalysis = window.analysisData.complex_types?.complex_type_analysis || [];
4082
4083    if (complexTypeAnalysis.length === 0) {
4084        tbody.innerHTML = '<tr><td colspan="6" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">No complex type analysis available</td></tr>';
4085        return;
4086    }
4087
4088    tbody.innerHTML = complexTypeAnalysis.map(analysis => `
4089        <tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
4090            <td class="px-6 py-4 text-gray-900 dark:text-gray-100">${formatTypeName(analysis.type_name)}</td>
4091            <td class="px-6 py-4 text-center">
4092                <span class="px-2 py-1 rounded text-xs ${getComplexityColor(analysis.complexity_score)} text-white">
4093                    ${analysis.complexity_score}
4094                </span>
4095            </td>
4096            <td class="px-6 py-4 text-center">
4097                <span class="px-2 py-1 rounded text-xs ${getEfficiencyColor(analysis.memory_efficiency)} text-white">
4098                    ${analysis.memory_efficiency}%
4099                </span>
4100            </td>
4101            <td class="px-6 py-4 text-center text-gray-900 dark:text-gray-100">${analysis.allocation_count || 0}</td>
4102            <td class="px-6 py-4 text-center text-gray-900 dark:text-gray-100">${formatBytes(analysis.total_size || 0)}</td>
4103            <td class="px-6 py-4 text-gray-700 dark:text-gray-300">
4104                ${Array.isArray(analysis.optimization_suggestions) && analysis.optimization_suggestions.length > 0 
4105                    ? analysis.optimization_suggestions.join(', ') 
4106                    : '<span class="text-gray-400 italic">No optimization suggestions available</span>'}
4107            </td>
4108        </tr>
4109    `).join('');
4110}
4111
4112// Initialize memory optimization recommendations
4113function initMemoryOptimizationRecommendations() {
4114    const container = document.getElementById('memory-optimization-recommendations');
4115    if (!container) return;
4116
4117    const recommendations = window.analysisData.complex_types?.optimization_recommendations || [];
4118
4119    if (recommendations.length === 0) {
4120        container.innerHTML = '<li class="text-gray-500 dark:text-gray-400">No specific recommendations available</li>';
4121        return;
4122    }
4123
4124    container.innerHTML = recommendations.map(rec => `
4125        <li class="flex items-start">
4126            <i class="fa fa-lightbulb-o text-yellow-500 mr-2 mt-1"></i>
4127            <span class="dark:text-gray-200">${rec}</span>
4128        </li>
4129    `).join('');
4130}
4131
4132// Initialize FFI risk chart
4133function initFFIRiskChart() {
4134    // Guard: ensure canvas isn't holding an existing chart instance
4135    try {
4136        if (window.chartInstances && window.chartInstances['ffi-risk-chart']) {
4137            window.chartInstances['ffi-risk-chart'].destroy();
4138            delete window.chartInstances['ffi-risk-chart'];
4139        }
4140    } catch (_) {}
4141
4142    const ctx = document.getElementById('ffi-risk-chart');
4143    if (!ctx) return;
4144
4145    const ffiData = window.analysisData.unsafe_ffi?.enhanced_ffi_data || [];
4146
4147    const riskLevels = {
4148        'Low Risk': ffiData.filter(item => (item.safety_violations || 0) === 0).length,
4149        'Medium Risk': ffiData.filter(item => (item.safety_violations || 0) > 0 && (item.safety_violations || 0) <= 2).length,
4150        'High Risk': ffiData.filter(item => (item.safety_violations || 0) > 2).length
4151    };
4152
4153    const isDark = document.documentElement.classList.contains('dark');
4154
4155    // Destroy existing chart if it exists
4156    if (window.chartInstances['ffi-risk-chart']) {
4157        window.chartInstances['ffi-risk-chart'].destroy();
4158    }
4159
4160    window.chartInstances['ffi-risk-chart'] = new Chart(ctx, {
4161        type: 'doughnut',
4162        data: {
4163            labels: Object.keys(riskLevels),
4164            datasets: [{
4165                data: Object.values(riskLevels),
4166                backgroundColor: ['#10b981', '#f59e0b', '#ef4444'],
4167                borderColor: isDark ? '#374151' : '#ffffff',
4168                borderWidth: 2
4169            }]
4170        },
4171        options: {
4172            responsive: true,
4173            maintainAspectRatio: false,
4174            plugins: {
4175                legend: {
4176                    labels: {
4177                        color: isDark ? '#f9fafb' : '#374151',
4178                        font: {
4179                            size: 12
4180                        }
4181                    }
4182                },
4183                tooltip: {
4184                    backgroundColor: isDark ? '#1f2937' : '#ffffff',
4185                    titleColor: isDark ? '#f9fafb' : '#374151',
4186                    bodyColor: isDark ? '#f9fafb' : '#374151',
4187                    borderColor: isDark ? '#374151' : '#e5e7eb',
4188                    borderWidth: 1
4189                }
4190            }
4191        }
4192    });
4193}
4194
4195// Initialize complex type analysis chart
4196function initComplexTypeAnalysisChart() {
4197    const ctx = document.getElementById('complex-type-analysis-chart');
4198    if (!ctx) return;
4199
4200    const complexTypeAnalysis = window.analysisData.complex_types?.complex_type_analysis || [];
4201
4202    if (complexTypeAnalysis.length === 0) {
4203        // Show empty state
4204        const container = ctx.parentElement;
4205        container.innerHTML = `
4206            <div class="h-64 flex items-center justify-center text-gray-500 dark:text-gray-400">
4207                <div class="text-center">
4208                    <i class="fa fa-chart-bar text-4xl mb-4"></i>
4209                    <p class="text-lg font-semibold mb-2">No Complex Type Data</p>
4210                    <p class="text-sm">No complex type analysis data available</p>
4211                </div>
4212            </div>
4213        `;
4214        return;
4215    }
4216
4217    const isDark = document.documentElement.classList.contains('dark');
4218
4219    // Destroy existing chart if it exists
4220    if (window.chartInstances['complex-type-analysis-chart']) {
4221        window.chartInstances['complex-type-analysis-chart'].destroy();
4222    }
4223
4224    window.chartInstances['complex-type-analysis-chart'] = new Chart(ctx, {
4225        type: 'scatter',
4226        data: {
4227            datasets: [{
4228                label: 'Type Complexity vs Memory Efficiency',
4229                data: complexTypeAnalysis.map(analysis => ({
4230                    x: analysis.complexity_score || 0,
4231                    y: analysis.memory_efficiency || 0,
4232                    typeName: analysis.type_name
4233                })),
4234                backgroundColor: 'rgba(59, 130, 246, 0.6)',
4235                borderColor: 'rgba(59, 130, 246, 1)',
4236                borderWidth: 2,
4237                pointRadius: 6,
4238                pointHoverRadius: 8
4239            }]
4240        },
4241        options: {
4242            responsive: true,
4243            maintainAspectRatio: false,
4244            scales: {
4245                x: {
4246                    title: {
4247                        display: true,
4248                        text: 'Complexity Score',
4249                        color: isDark ? '#f9fafb' : '#374151'
4250                    },
4251                    ticks: {
4252                        color: isDark ? '#d1d5db' : '#6b7280'
4253                    },
4254                    grid: {
4255                        color: isDark ? '#374151' : '#e5e7eb'
4256                    }
4257                },
4258                y: {
4259                    title: {
4260                        display: true,
4261                        text: 'Memory Efficiency (%)',
4262                        color: isDark ? '#f9fafb' : '#374151'
4263                    },
4264                    ticks: {
4265                        color: isDark ? '#d1d5db' : '#6b7280'
4266                    },
4267                    grid: {
4268                        color: isDark ? '#374151' : '#e5e7eb'
4269                    }
4270                }
4271            },
4272            plugins: {
4273                legend: {
4274                    labels: {
4275                        color: isDark ? '#f9fafb' : '#374151'
4276                    }
4277                },
4278                tooltip: {
4279                    backgroundColor: isDark ? '#1f2937' : '#ffffff',
4280                    titleColor: isDark ? '#f9fafb' : '#374151',
4281                    bodyColor: isDark ? '#f9fafb' : '#374151',
4282                    borderColor: isDark ? '#374151' : '#e5e7eb',
4283                    borderWidth: 1,
4284                    callbacks: {
4285                        title: function (context) {
4286                            return context[0].raw.typeName || 'Unknown Type';
4287                        },
4288                        label: function (context) {
4289                            return [
4290                                `Complexity: ${context.parsed.x}`,
4291                                `Efficiency: ${context.parsed.y}%`
4292                            ];
4293                        }
4294                    }
4295                }
4296            }
4297        }
4298    });
4299}
4300
4301// Format type name for better display
4302function formatTypeName(typeName) {
4303    if (!typeName || typeName === 'unknown') return 'System Allocation';
4304    // Simplify complex type names
4305    return typeName
4306        .replace(/alloc::/g, '')
4307        .replace(/std::/g, '')
4308        .replace(/::Vec/g, 'Vec')
4309        .replace(/::Box/g, 'Box')
4310        .replace(/::Rc/g, 'Rc')
4311        .replace(/::Arc/g, 'Arc')
4312        .replace(/::String/g, 'String');
4313}
4314
4315// Format timestamp relative to start time
4316function formatTimestamp(timestamp, minTime) {
4317    const relativeMs = Math.round((timestamp - minTime) / 1000000); // Convert nanoseconds to milliseconds
4318    return `${relativeMs}ms`;
4319}
4320
4321// Enhanced summary statistics with comprehensive data analysis
4322function initEnhancedSummaryStats() {
4323    console.log('๐Ÿ“Š Initializing enhanced summary statistics...');
4324    
4325    try {
4326        // Get merged data from all sources
4327        const memoryAllocations = window.analysisData.memory_analysis?.allocations || [];
4328        const complexAllocations = window.analysisData.complex_types?.allocations || [];
4329        const unsafeAllocations = window.analysisData.unsafe_ffi?.allocations || [];
4330        
4331        // Merge all data sources for comprehensive analysis
4332        const allData = mergeAllDataSources(memoryAllocations, complexAllocations, unsafeAllocations);
4333        
4334        // Calculate comprehensive statistics
4335        const stats = calculateComprehensiveStats(allData);
4336        
4337        // Update enhanced dashboard
4338        updateElement('total-allocations', stats.totalAllocations);
4339        updateElement('allocation-rate', `${stats.allocationRate.toFixed(1)}/ms`);
4340        updateElement('active-variables', stats.activeVariables);
4341        updateElement('variable-types', `${stats.uniqueTypes} types`);
4342        updateElement('borrow-operations', stats.totalBorrows);
4343        updateElement('max-concurrent', `Max: ${stats.maxConcurrent}`);
4344        updateElement('safety-score', `${stats.safetyScore}%`);
4345        updateElement('ffi-tracked', `${stats.ffiTracked} FFI`);
4346        
4347        console.log('โœ… Enhanced dashboard updated successfully');
4348    } catch (error) {
4349        console.error('โŒ Error initializing enhanced stats:', error);
4350    }
4351}
4352
4353// Merge data from all sources with comprehensive field mapping
4354function mergeAllDataSources(memory, complex, unsafe) {
4355    const dataMap = new Map();
4356    
4357    // Add memory analysis data (has lifetime_ms)
4358    memory.forEach(alloc => {
4359        if (alloc.ptr) {
4360            dataMap.set(alloc.ptr, { ...alloc, source: 'memory' });
4361        }
4362    });
4363    
4364    // Merge complex types data (has extended fields)
4365    complex.forEach(alloc => {
4366        if (alloc.ptr) {
4367            const existing = dataMap.get(alloc.ptr) || {};
4368            dataMap.set(alloc.ptr, { 
4369                ...existing, 
4370                ...alloc, 
4371                source: existing.source ? `${existing.source}+complex` : 'complex'
4372            });
4373        }
4374    });
4375    
4376    // Merge unsafe FFI data (has safety info)
4377    unsafe.forEach(alloc => {
4378        if (alloc.ptr) {
4379            const existing = dataMap.get(alloc.ptr) || {};
4380            dataMap.set(alloc.ptr, { 
4381                ...existing, 
4382                ...alloc, 
4383                source: existing.source ? `${existing.source}+unsafe` : 'unsafe'
4384            });
4385        }
4386    });
4387    
4388    return Array.from(dataMap.values());
4389}
4390
4391// Calculate comprehensive statistics from merged data
4392function calculateComprehensiveStats(allData) {
4393    const validData = allData.filter(d => d.var_name && d.var_name !== 'unknown');
4394    
4395    // Basic counts
4396    const totalAllocations = validData.length;
4397    const uniqueVars = new Set(validData.map(d => d.var_name)).size;
4398    const uniqueTypes = new Set(validData.map(d => d.type_name)).size;
4399    
4400    // Time-based calculations
4401    const timestamps = validData.map(d => d.timestamp_alloc).filter(t => t);
4402    const timeRange = timestamps.length > 0 ? (Math.max(...timestamps) - Math.min(...timestamps)) / 1000000 : 1;
4403    const allocationRate = totalAllocations / Math.max(timeRange, 1);
4404    
4405    // Borrow analysis
4406    let totalBorrows = 0;
4407    let maxConcurrent = 0;
4408    validData.forEach(d => {
4409        if (d.borrow_info) {
4410            totalBorrows += (d.borrow_info.immutable_borrows || 0) + (d.borrow_info.mutable_borrows || 0);
4411            maxConcurrent = Math.max(maxConcurrent, d.borrow_info.max_concurrent_borrows || 0);
4412        }
4413    });
4414    
4415    // Safety analysis
4416    const ffiTracked = validData.filter(d => d.ffi_tracked).length;
4417    const leaked = validData.filter(d => d.is_leaked).length;
4418    const withSafetyViolations = validData.filter(d => d.safety_violations && d.safety_violations.length > 0).length;
4419    const safetyScore = Math.max(0, 100 - (leaked * 20) - (withSafetyViolations * 10));
4420    
4421    return {
4422        totalAllocations,
4423        activeVariables: uniqueVars,
4424        uniqueTypes,
4425        allocationRate,
4426        totalBorrows,
4427        maxConcurrent,
4428        ffiTracked,
4429        safetyScore: Math.round(safetyScore)
4430    };
4431}
4432
4433// Helper function to safely update DOM elements
4434function updateElement(id, value) {
4435    const element = document.getElementById(id);
4436    if (element) {
4437        element.textContent = value;
4438    }
4439}
4440
4441// Utility function to format bytes
4442function formatBytes(bytes) {
4443    if (bytes === 0) return '0 B';
4444    const k = 1024;
4445    const sizes = ['B', 'KB', 'MB', 'GB'];
4446    const i = Math.floor(Math.log(bytes) / Math.log(k));
4447    return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
4448}
4449
4450// Show empty state when no user variables found
4451function showEmptyLifetimeState() {
4452    const container = document.getElementById('lifetimeVisualization');
4453    if (!container) return;
4454
4455    container.innerHTML = `
4456        <div class="text-center py-8 text-gray-500 dark:text-gray-400">
4457            <i class="fa fa-info-circle text-2xl mb-2"></i>
4458            <p>No user-defined variables found in lifetime data</p>
4459            <p class="text-sm mt-1">Use track_var! macro to track variable lifetimes</p>
4460        </div>
4461    `;
4462}
4463
4464// Utility functions
4465function updateElement(id, value) {
4466    const element = document.getElementById(id);
4467    if (element) {
4468        element.textContent = value;
4469    }
4470}
4471
4472function getComplexityColor(score) {
4473    if (score <= 2) return '#10b981';  // Green - Low complexity
4474    if (score <= 5) return '#eab308';  // Yellow - Medium complexity  
4475    if (score <= 8) return '#f97316';  // Orange - High complexity
4476    return '#ef4444';                  // Red - Very high complexity
4477}
4478
4479function getEfficiencyColor(efficiency) {
4480    if (efficiency >= 80) return 'bg-green-500';
4481    if (efficiency >= 60) return 'bg-yellow-500';
4482    if (efficiency >= 40) return 'bg-orange-500';
4483    return 'bg-red-500';
4484}
4485
4486// Update KPI Cards
4487function updateKPICards(data) {
4488    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
4489    const total = allocs.reduce((s,a)=>s+(a.size||0),0);
4490    const active = allocs.filter(a=>!a.timestamp_dealloc).length;
4491    const safetyScore = calculateSafetyScore(allocs);
4492    
4493    updateElement('total-allocations', allocs.length.toLocaleString());
4494    updateElement('active-variables', active.toLocaleString());
4495    updateElement('total-memory', formatBytes(total));
4496    updateElement('safety-score', safetyScore + '%');
4497}
4498
4499// Calculate Safety Score
4500function calculateSafetyScore(allocs) {
4501    if (!allocs.length) return 100;
4502    const leaked = allocs.filter(a => a.is_leaked).length;
4503    const violations = allocs.filter(a => a.safety_violations && a.safety_violations.length > 0).length;
4504    return Math.max(0, 100 - (leaked * 20) - (violations * 10));
4505}
4506
4507// Theme Toggle Functionality
4508function initThemeToggle() {
4509    const toggleBtn = document.getElementById('theme-toggle');
4510    if (!toggleBtn) return;
4511    
4512    // Check local storage for theme
4513    const savedTheme = localStorage.getItem('memscope-theme') || 'light';
4514    applyTheme(savedTheme === 'dark');
4515    
4516    toggleBtn.addEventListener('click', () => {
4517        const isDark = document.documentElement.classList.contains('dark');
4518        const newTheme = isDark ? 'light' : 'dark';
4519        
4520        applyTheme(newTheme === 'dark');
4521        localStorage.setItem('memscope-theme', newTheme);
4522        
4523        // Update button text
4524        const icon = toggleBtn.querySelector('i');
4525        const text = toggleBtn.querySelector('span');
4526        if (newTheme === 'dark') {
4527            icon.className = 'fa fa-sun';
4528            text.textContent = 'Light Mode';
4529        } else {
4530            icon.className = 'fa fa-moon';
4531            text.textContent = 'Dark Mode';
4532        }
4533        
4534        console.log('๐ŸŽจ Theme switched to:', newTheme);
4535    });
4536}
4537
4538// ๅบ”็”จไธป้ข˜
4539function applyTheme(isDark) {
4540    const html = document.documentElement;
4541    if (isDark) {
4542        html.classList.add('dark');
4543    } else {
4544        html.classList.remove('dark');
4545    }
4546}
4547
4548// Update Memory Allocation Table
4549function updateAllocationsTable(data) {
4550    const allocTable = document.getElementById('allocTable');
4551    if (!allocTable) return;
4552    
4553    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
4554    const top = allocs.slice().sort((a,b)=>(b.size||0)-(a.size||0)).slice(0,50);
4555    
4556    allocTable.innerHTML = top.map(a => {
4557        const status = a.is_leaked ? 'Leaked' : (a.timestamp_dealloc ? 'Freed' : 'Active');
4558        const statusClass = a.is_leaked ? 'status-leaked' : (a.timestamp_dealloc ? 'status-freed' : 'status-active');
4559        
4560        return `<tr>
4561            <td>${a.var_name || 'Unknown'}</td>
4562            <td>${formatTypeName(a.type_name || 'Unknown')}</td>
4563            <td>${formatBytes(a.size || 0)}</td>
4564            <td><span class="status-badge ${statusClass}">${status}</span></td>
4565        </tr>`;
4566    }).join('');
4567}
4568
4569// Update Unsafe Risk Table
4570function updateUnsafeTable(data) {
4571    const unsafeTable = document.getElementById('unsafeTable');
4572    if (!unsafeTable) return;
4573    
4574    const root = data.unsafe_ffi || {};
4575    const ops = root.enhanced_ffi_data || root.unsafe_operations || root.allocations || [];
4576    
4577    unsafeTable.innerHTML = (ops || []).slice(0, 50).map(op => {
4578        const riskLevel = op.risk_level || ((op.safety_violations||[]).length > 2 ? 'High' : 
4579                         ((op.safety_violations||[]).length > 0 ? 'Medium' : 'Low'));
4580        
4581        const riskText = riskLevel === 'High' ? 'High Risk' : (riskLevel === 'Medium' ? 'Medium Risk' : 'Low Risk');
4582        const riskClass = riskLevel === 'High' ? 'risk-high' : (riskLevel === 'Medium' ? 'risk-medium' : 'risk-low');
4583        
4584        return `<tr>
4585            <td>${op.location || op.var_name || 'Unknown'}</td>
4586            <td>${op.operation_type || op.type_name || 'Unknown'}</td>
4587            <td><span class="status-badge ${riskClass}">${riskText}</span></td>
4588        </tr>`;
4589    }).join('');
4590}
4591
4592// Initialize Charts
4593function initCharts(data) {
4594    console.log('๐Ÿ“Š Initializing charts...');
4595    
4596    // Memory type distribution chart
4597    initTypeChart(data);
4598    
4599    // Memory timeline chart
4600    initTimelineChart(data);
4601    
4602    // Type treemap chart
4603    initTreemapChart(data);
4604    
4605    // FFI risk chart
4606    initFFIRiskChart(data);
4607    
4608    // Memory growth trends
4609    initGrowthTrends(data);
4610    
4611    // Memory fragmentation
4612    initMemoryFragmentation(data);
4613    
4614    // Variable relationship graph
4615    initVariableGraph(data);
4616}
4617
4618// Memory Type Distribution Chart
4619function initTypeChart(data) {
4620    const ctx = document.getElementById('typeChart');
4621    if (!ctx) return;
4622    
4623    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
4624    const byType = {};
4625    
4626    allocs.forEach(a => {
4627        const type = a.type_name || 'Unknown';
4628        byType[type] = (byType[type] || 0) + (a.size || 0);
4629    });
4630    
4631    const top = Object.entries(byType).sort((a,b) => b[1] - a[1]).slice(0, 8);
4632    
4633    if (top.length > 0 && window.Chart) {
4634        const chart = new Chart(ctx, {
4635            type: 'bar',
4636            data: {
4637                labels: top.map(x => {
4638                    const formatted = formatTypeName(x[0]);
4639                    return formatted.length > 15 ? formatted.substring(0, 12) + '...' : formatted;
4640                }),
4641                datasets: [{
4642                    label: 'Memory Usage',
4643                    data: top.map(x => x[1]),
4644                    backgroundColor: '#2563eb',
4645                    borderRadius: 6
4646                }]
4647            },
4648            options: {
4649                responsive: true,
4650                maintainAspectRatio: false,
4651                plugins: {
4652                    legend: { display: false }
4653                },
4654                scales: {
4655                    x: {
4656                        ticks: {
4657                            maxRotation: 45,
4658                            minRotation: 0,
4659                            font: {
4660                                size: 10
4661                            }
4662                        }
4663                    },
4664                    y: { 
4665                        beginAtZero: true,
4666                        ticks: {
4667                            callback: function(value) {
4668                                return formatBytes(value);
4669                            }
4670                        }
4671                    }
4672                }
4673            }
4674        });
4675    }
4676}
4677
4678// Memory Timeline Chart with optional Growth Rate (dual y-axes)
4679function initTimelineChart(data) {
4680    const ctx = document.getElementById('timelineChart');
4681    if (!ctx || !window.Chart) return;
4682
4683    // Comprehensive cleanup for timeline chart
4684    try {
4685        if (ctx.chart) {
4686            ctx.chart.destroy();
4687            delete ctx.chart;
4688        }
4689        
4690        if (window.Chart.instances) {
4691            Object.values(window.Chart.instances).forEach(instance => {
4692                if (instance.canvas === ctx) {
4693                    instance.destroy();
4694                }
4695            });
4696        }
4697        
4698        if (window.chartInstances && window.chartInstances['timelineChart']) {
4699            window.chartInstances['timelineChart'].destroy();
4700            delete window.chartInstances['timelineChart'];
4701        }
4702        
4703        const context = ctx.getContext('2d');
4704        context.clearRect(0, 0, ctx.width, ctx.height);
4705        
4706    } catch(e) {
4707        console.warn('Timeline chart cleanup warning:', e);
4708    }
4709    
4710    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
4711    const sorted = allocs.slice().sort((a,b) => (a.timestamp_alloc||0) - (b.timestamp_alloc||0));
4712    
4713    // Bucketize by timestamp to ~200 buckets; compute bytes/sec
4714    if (sorted.length === 0) return;
4715    const minTs = sorted[0].timestamp_alloc || 0;
4716    const maxTs = sorted[sorted.length-1].timestamp_alloc || minTs;
4717    const rangeNs = Math.max(1, maxTs - minTs);
4718    const bucketCount = Math.min(200, Math.max(1, Math.floor(sorted.length / 2)));
4719    const bucketNs = Math.max(1, Math.floor(rangeNs / bucketCount));
4720
4721    const cumSeries = [];
4722    const rateSeries = [];
4723
4724    let cumulative = 0;
4725    let windowQueue = [];
4726    const windowBuckets = 3; // simple smoothing window
4727
4728    for (let b = 0; b <= bucketCount; b++) {
4729        const start = minTs + b * bucketNs;
4730        const end = Math.min(maxTs, start + bucketNs);
4731        const slice = sorted.filter(a => (a.timestamp_alloc||0) >= start && (a.timestamp_alloc||0) < end);
4732        const sum = slice.reduce((s,a)=>s+(a.size||0),0);
4733        cumulative += sum;
4734        cumSeries.push({ x: start, y: cumulative });
4735        const dtSec = Math.max(1e-9, (end - start) / 1e9);
4736        const rate = sum / dtSec; // bytes/sec in this bucket
4737        windowQueue.push(rate);
4738        if (windowQueue.length > windowBuckets) windowQueue.shift();
4739        const smoothed = windowQueue.reduce((s,v)=>s+v,0) / windowQueue.length;
4740        rateSeries.push({ x: start, y: smoothed });
4741    }
4742
4743    if (cumSeries.length > 1 && window.Chart) {
4744        // destroy previous chart if exists
4745        if (window.chartInstances && window.chartInstances['timelineChart']) {
4746            try { window.chartInstances['timelineChart'].destroy(); } catch(_) {}
4747            delete window.chartInstances['timelineChart'];
4748        }
4749
4750        const labels = cumSeries.map(p=> new Date(p.x/1e6).toLocaleTimeString());
4751        const showGrowthCheckbox = document.getElementById('toggleGrowthRate');
4752        const datasets = [
4753            {
4754                type: 'line',
4755                label: 'Cumulative Memory',
4756                data: cumSeries.map(p=>p.y),
4757                borderColor: '#059669',
4758                backgroundColor: 'rgba(5, 150, 105, 0.1)',
4759                fill: true,
4760                tension: 0.3,
4761                yAxisID: 'y'
4762            }
4763        ];
4764
4765        // add growth rate dataset (hidden by default; user toggles it)
4766        datasets.push({
4767            type: 'line',
4768            label: 'Growth Rate (bytes/sec)',
4769            data: rateSeries.map(p=>p.y),
4770            borderColor: '#eab308',
4771            backgroundColor: 'rgba(234, 179, 8, 0.15)',
4772            fill: true,
4773            tension: 0.2,
4774            hidden: showGrowthCheckbox ? !showGrowthCheckbox.checked : true,
4775            yAxisID: 'y1'
4776        });
4777
4778        const chart = new Chart(ctx, {
4779            data: { labels, datasets },
4780            options: {
4781                responsive: true,
4782                maintainAspectRatio: false,
4783                plugins: {
4784                    legend: { position: 'bottom' }
4785                },
4786                scales: {
4787                    y: { 
4788                        beginAtZero: true,
4789                        title: { display: true, text: 'Cumulative Memory' },
4790                        ticks: { callback: v => formatBytes(v) }
4791                    },
4792                    y1: {
4793                        beginAtZero: true,
4794                        position: 'right',
4795                        grid: { drawOnChartArea: false },
4796                        title: { display: true, text: 'Growth Rate (bytes/step)' }
4797                    },
4798                    x: { title: { display: false } }
4799                }
4800            }
4801        });
4802
4803        window.chartInstances = window.chartInstances || {};
4804        window.chartInstances['timelineChart'] = chart;
4805
4806        if (showGrowthCheckbox) {
4807            showGrowthCheckbox.onchange = () => {
4808                const ds = chart.data.datasets.find(d => d.yAxisID === 'y1');
4809                if (!ds) return;
4810                ds.hidden = !showGrowthCheckbox.checked;
4811                chart.update();
4812            };
4813        }
4814    }
4815}
4816
4817// Enhanced type chart with better label handling for complex Rust types
4818function initEnhancedTypeChart(data) {
4819    const ctx = document.getElementById('typeChart');
4820    if (!ctx || !window.Chart) return;
4821
4822    // Comprehensive cleanup for this specific chart
4823    try {
4824        // Check if there's already a chart attached to this canvas
4825        if (ctx.chart) {
4826            ctx.chart.destroy();
4827            delete ctx.chart;
4828        }
4829        
4830        // Clear any Chart.js instance for this canvas
4831        if (window.Chart.instances) {
4832            Object.values(window.Chart.instances).forEach(instance => {
4833                if (instance.canvas === ctx) {
4834                    instance.destroy();
4835                }
4836            });
4837        }
4838        
4839        // Clear our tracked instance
4840        if (window.chartInstances && window.chartInstances['typeChart']) {
4841            window.chartInstances['typeChart'].destroy();
4842            delete window.chartInstances['typeChart'];
4843        }
4844        
4845        // Clear canvas context
4846        const context = ctx.getContext('2d');
4847        context.clearRect(0, 0, ctx.width, ctx.height);
4848        
4849    } catch(e) {
4850        console.warn('Chart cleanup warning:', e);
4851    }
4852
4853    const typeData = {};
4854    const allocs = data.memory_analysis?.allocations || data.allocations || [];
4855    
4856    console.log('Type chart data extraction:', { totalAllocs: allocs.length });
4857    
4858    allocs.forEach(alloc => {
4859        let type = alloc.type_name || 'Unknown';
4860        const originalType = type;
4861        
4862        // Simplify complex Rust type names for better readability
4863        type = type.replace(/alloc::sync::Arc/g, 'Arc');
4864        type = type.replace(/alloc::rc::Rc/g, 'Rc');
4865        type = type.replace(/alloc::string::String/g, 'String');
4866        type = type.replace(/alloc::vec::Vec/g, 'Vec');
4867        type = type.replace(/std::collections::hash::map::HashMap/g, 'HashMap');
4868        type = type.replace(/std::collections::btree::map::BTreeMap/g, 'BTreeMap');
4869        type = type.replace(/alloc::collections::\w+::\w+::/g, '');
4870        
4871        // Remove generic parameters for cleaner display
4872        type = type.replace(/<[^>]+>/g, '<T>');
4873        
4874        // Truncate very long type names
4875        if (type.length > 25) {
4876            type = type.substring(0, 22) + '...';
4877        }
4878        
4879        const size = alloc.size || 0;
4880        typeData[type] = (typeData[type] || 0) + size;
4881        
4882        if (size > 0) {
4883            console.log(`Adding ${originalType} -> ${type}: ${size} bytes`);
4884        }
4885    });
4886    
4887    console.log('Type data aggregated:', typeData);
4888
4889    const sortedEntries = Object.entries(typeData).sort((a, b) => b[1] - a[1]);
4890    const labels = sortedEntries.map(([k, v]) => k);
4891    const values = sortedEntries.map(([k, v]) => v);
4892    
4893    if (labels.length > 0 && window.Chart) {
4894        const chart = new Chart(ctx, {
4895            type: 'bar',
4896            data: {
4897                labels: labels,
4898                datasets: [{
4899                    label: 'Memory Usage',
4900                    data: values,
4901                    backgroundColor: labels.map((_, i) => {
4902                        const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#84cc16'];
4903                        return colors[i % colors.length] + '80';
4904                    }),
4905                    borderColor: labels.map((_, i) => {
4906                        const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#84cc16'];
4907                        return colors[i % colors.length];
4908                    }),
4909                    borderWidth: 2
4910                }]
4911            },
4912            options: {
4913                responsive: true,
4914                maintainAspectRatio: false,
4915                plugins: {
4916                    legend: { display: false },
4917                    tooltip: {
4918                        callbacks: {
4919                            label: (context) => {
4920                                const originalType = allocs.find(a => {
4921                                    let simplified = a.type_name || 'Unknown';
4922                                    simplified = simplified.replace(/alloc::(sync::Arc|rc::Rc|collections::\w+::\w+::|string::String|vec::Vec)/g, (match, p1) => {
4923                                        switch(p1) {
4924                                            case 'sync::Arc': return 'Arc';
4925                                            case 'rc::Rc': return 'Rc';
4926                                            case 'string::String': return 'String';
4927                                            case 'vec::Vec': return 'Vec';
4928                                            default: return p1.split('::').pop();
4929                                        }
4930                                    });
4931                                    simplified = simplified.replace(/std::collections::hash::map::HashMap/g, 'HashMap');
4932                                    simplified = simplified.replace(/std::collections::btree::map::BTreeMap/g, 'BTreeMap');
4933                                    if (simplified.length > 30) simplified = simplified.substring(0, 27) + '...';
4934                                    return simplified === context.label;
4935                                })?.type_name || context.label;
4936                                return [`Type: ${originalType}`, `Memory: ${formatBytes(context.parsed.y)}`];
4937                            }
4938                        }
4939                    }
4940                },
4941                scales: {
4942                    x: { 
4943                        ticks: {
4944                            maxRotation: 45,
4945                            minRotation: 0,
4946                            font: { 
4947                                size: 11, 
4948                                weight: '500',
4949                                family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
4950                            },
4951                            color: function(context) {
4952                                return document.documentElement.classList.contains('dark-theme') ? '#e2e8f0' : '#475569';
4953                            },
4954                            padding: 8,
4955                            callback: function(value, index) {
4956                                const label = this.getLabelForValue(value);
4957                                // Ensure readability by adding spacing
4958                                return label.length > 15 ? label.substring(0, 13) + '...' : label;
4959                            }
4960                        },
4961                        grid: {
4962                            display: false
4963                        }
4964                    },
4965                    y: { 
4966                        beginAtZero: true,
4967                        ticks: {
4968                            callback: (value) => formatBytes(value),
4969                            color: function(context) {
4970                                return document.documentElement.classList.contains('dark-theme') ? '#cbd5e1' : '#64748b';
4971                            },
4972                            font: { size: 10, weight: '400' }
4973                        },
4974                        grid: {
4975                            color: function(context) {
4976                                return document.documentElement.classList.contains('dark-theme') ? '#374151' : '#e2e8f0';
4977                            },
4978                            lineWidth: 1
4979                        }
4980                    }
4981                }
4982            }
4983        });
4984        
4985        window.chartInstances = window.chartInstances || {};
4986        window.chartInstances['typeChart'] = chart;
4987    }
4988}
4989
4990// Type Treemap Chart
4991function initTreemapChart(data) {
4992    const container = document.getElementById('treemap');
4993    if (!container) return;
4994    
4995    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
4996    const byType = {};
4997    
4998    allocs.forEach(a => {
4999        const type = a.type_name || 'Unknown';
5000        byType[type] = (byType[type] || 0) + (a.size || 0);
5001    });
5002    
5003    const top = Object.entries(byType).sort((a,b) => b[1] - a[1]).slice(0, 12);
5004    const totalSize = top.reduce((sum, [, size]) => sum + size, 0);
5005    
5006    if (totalSize > 0) {
5007        let html = '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; height: 100%; padding: 16px;">';
5008        
5009        top.forEach(([type, size], index) => {
5010            const percentage = (size / totalSize) * 100;
5011            const color = `hsl(${index * 30}, 70%, 55%)`;
5012            
5013            html += `
5014                <div style="
5015                    background: ${color};
5016                    color: white;
5017                    padding: 12px;
5018                    border-radius: 8px;
5019                    font-size: 11px;
5020                    font-weight: 600;
5021                    display: flex;
5022                    flex-direction: column;
5023                    justify-content: center;
5024                    text-align: center;
5025                    min-height: 80px;
5026                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
5027                    transition: transform 0.2s ease;
5028                " title="${type}: ${formatBytes(size)}" onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
5029                    <div style="margin-bottom: 4px;">${formatTypeName(type)}</div>
5030                    <div style="font-size: 10px; opacity: 0.9;">${formatBytes(size)}</div>
5031                    <div style="font-size: 9px; opacity: 0.7;">${percentage.toFixed(1)}%</div>
5032                </div>
5033            `;
5034        });
5035        
5036        html += '</div>';
5037        container.innerHTML = html;
5038    } else {
5039        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No data available</div>';
5040    }
5041}
5042
5043// FFI Risk Chart
5044function initFFIRiskChart(data) {
5045    // Guard destroy if exists
5046    try {
5047        if (window.chartInstances && window.chartInstances['ffi-risk-chart']) {
5048            window.chartInstances['ffi-risk-chart'].destroy();
5049            delete window.chartInstances['ffi-risk-chart'];
5050        }
5051    } catch (_) {}
5052
5053    const ctx = document.getElementById('ffi-risk-chart');
5054    if (!ctx) return;
5055    
5056    const ffiData = data.unsafe_ffi?.enhanced_ffi_data || [];
5057    
5058    const riskLevels = {
5059        'Low Risk': ffiData.filter(item => (item.safety_violations || []).length === 0).length,
5060        'Medium Risk': ffiData.filter(item => (item.safety_violations || []).length > 0 && (item.safety_violations || []).length <= 2).length,
5061        'High Risk': ffiData.filter(item => (item.safety_violations || []).length > 2).length
5062    };
5063    
5064    if (window.Chart) {
5065        const chart = new Chart(ctx, {
5066            type: 'doughnut',
5067            data: {
5068                labels: Object.keys(riskLevels),
5069                datasets: [{
5070                    data: Object.values(riskLevels),
5071                    backgroundColor: ['#059669', '#ea580c', '#dc2626'],
5072                    borderWidth: 2,
5073                    borderColor: 'var(--bg-primary)'
5074                }]
5075            },
5076            options: {
5077                responsive: true,
5078                maintainAspectRatio: false,
5079                plugins: {
5080                    legend: {
5081                        position: 'bottom',
5082                        labels: {
5083                            padding: 20,
5084                            usePointStyle: true
5085                        }
5086                    }
5087                }
5088            }
5089        });
5090    }
5091}
5092
5093// Add missing chart and graph functions
5094function initGrowthTrends(data) {
5095    const container = document.getElementById('growth');
5096    if (!container) return;
5097    
5098    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5099    if (allocs.length === 0) {
5100        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No growth data available</div>';
5101        return;
5102    }
5103    
5104    // Simple growth visualization
5105    const sorted = allocs.slice().sort((a,b) => (a.timestamp_alloc||0) - (b.timestamp_alloc||0));
5106    let cumulative = 0;
5107    const points = [];
5108    
5109    for (let i = 0; i < Math.min(sorted.length, 20); i++) {
5110        cumulative += sorted[i].size || 0;
5111        points.push(cumulative);
5112    }
5113    
5114    const maxValue = Math.max(...points);
5115    let html = '<div style="display: flex; align-items: end; height: 200px; gap: 4px; padding: 20px;">';
5116    
5117    points.forEach((value, i) => {
5118        const height = (value / maxValue) * 160;
5119        html += `
5120            <div style="
5121                width: 12px;
5122                height: ${height}px;
5123                background: linear-gradient(to top, #2563eb, #3b82f6);
5124                border-radius: 2px;
5125                margin: 0 1px;
5126            " title="Step ${i + 1}: ${formatBytes(value)}"></div>
5127        `;
5128    });
5129    
5130    html += '</div>';
5131    container.innerHTML = html;
5132}
5133
5134function initMemoryFragmentation(data) {
5135    const container = document.getElementById('memoryFragmentation');
5136    if (!container) return;
5137    
5138    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5139    const totalMemory = allocs.reduce((sum, a) => sum + (a.size || 0), 0);
5140    const activeMemory = allocs.filter(a => !a.timestamp_dealloc).reduce((sum, a) => sum + (a.size || 0), 0);
5141    const fragmentationRate = totalMemory > 0 ? ((totalMemory - activeMemory) / totalMemory * 100) : 0;
5142    
5143    container.innerHTML = `
5144        <div style="padding: 20px;">
5145            <div style="display: flex; justify-content: space-between; margin-bottom: 16px;">
5146                <div>
5147                    <div style="color: var(--text-secondary); font-size: 0.9rem;">Fragmentation Rate</div>
5148                    <div style="font-size: 2rem; font-weight: 700; color: ${fragmentationRate > 30 ? '#dc2626' : fragmentationRate > 15 ? '#ea580c' : '#059669'};">
5149                        ${fragmentationRate.toFixed(1)}%
5150                    </div>
5151                </div>
5152                <div>
5153                    <div style="color: var(--text-secondary); font-size: 0.9rem;">Active Memory</div>
5154                    <div style="font-size: 1.2rem; font-weight: 600;">${formatBytes(activeMemory)}</div>
5155                </div>
5156            </div>
5157            <div style="background: var(--bg-secondary); height: 8px; border-radius: 4px; overflow: hidden;">
5158                <div style="
5159                    background: linear-gradient(to right, #059669, #ea580c);
5160                    width: ${Math.min(100, fragmentationRate)}%;
5161                    height: 100%;
5162                    border-radius: 4px;
5163                    transition: width 0.8s ease;
5164                "></div>
5165            </div>
5166            <div style="margin-top: 12px; font-size: 0.8rem; color: var(--text-secondary);">
5167                ${fragmentationRate > 30 ? 'High fragmentation detected' : fragmentationRate > 15 ? 'Moderate fragmentation' : 'Low fragmentation'}
5168            </div>
5169        </div>
5170    `;
5171}
5172
5173function initVariableGraph(data) {
5174    const container = document.getElementById('graph');
5175    if (!container) return;
5176    
5177    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5178    if (allocs.length === 0) {
5179        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No relationship data available</div>';
5180        return;
5181    }
5182    
5183    // Create a simple node-link visualization
5184    const nodes = allocs.slice(0, 20).map((a, i) => ({
5185        id: i,
5186        name: a.var_name || `var_${i}`,
5187        type: a.type_name || 'unknown',
5188        size: a.size || 0,
5189        x: 50 + (i % 4) * 80,
5190        y: 50 + Math.floor(i / 4) * 60
5191    }));
5192    
5193    let svg = `
5194        <svg width="100%" height="100%" style="background: transparent;">
5195            <defs>
5196                <filter id="glow">
5197                    <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
5198                    <feMerge>
5199                        <feMergeNode in="coloredBlur"/>
5200                        <feMergeNode in="SourceGraphic"/>
5201                    </feMerge>
5202                </filter>
5203            </defs>
5204    `;
5205    
5206    // Add links between nearby nodes
5207    for (let i = 0; i < nodes.length - 1; i++) {
5208        if (i % 4 !== 3) { // Connect horizontally
5209            svg += `<line x1="${nodes[i].x}" y1="${nodes[i].y}" x2="${nodes[i+1].x}" y2="${nodes[i+1].y}" stroke="var(--border-light)" stroke-width="1" opacity="0.3"/>`;
5210        }
5211        if (i < nodes.length - 4) { // Connect vertically
5212            svg += `<line x1="${nodes[i].x}" y1="${nodes[i].y}" x2="${nodes[i+4].x}" y2="${nodes[i+4].y}" stroke="var(--border-light)" stroke-width="1" opacity="0.3"/>`;
5213        }
5214    }
5215    
5216    // Add nodes
5217    nodes.forEach(node => {
5218        const radius = Math.max(8, Math.min(20, Math.log(node.size + 1) * 2));
5219        const color = node.type.includes('String') ? '#fbbf24' : 
5220                     node.type.includes('Vec') ? '#3b82f6' : 
5221                     node.type.includes('Box') || node.type.includes('Rc') ? '#8b5cf6' : '#6b7280';
5222        
5223        svg += `
5224            <circle 
5225                cx="${node.x}" 
5226                cy="${node.y}" 
5227                r="${radius}" 
5228                fill="${color}" 
5229                stroke="white" 
5230                stroke-width="2" 
5231                filter="url(#glow)"
5232                style="cursor: pointer;"
5233                onmouseover="this.r.baseVal.value = ${radius + 3}"
5234                onmouseout="this.r.baseVal.value = ${radius}"
5235            >
5236                <title>${node.name} (${node.type})</title>
5237            </circle>
5238            <text 
5239                x="${node.x}" 
5240                y="${node.y + radius + 12}" 
5241                text-anchor="middle" 
5242                font-size="10" 
5243                fill="var(--text-primary)"
5244                style="font-weight: 500;"
5245            >${node.name.length > 8 ? node.name.substring(0, 8) + '...' : node.name}</text>
5246        `;
5247    });
5248    
5249    svg += '</svg>';
5250    container.innerHTML = svg;
5251}
5252
5253// Initialize lifetime visualization
5254function initLifetimeVisualization(data) {
5255    const container = document.getElementById('lifetimes');
5256    if (!container) return;
5257    
5258    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5259    if (allocs.length === 0) {
5260        container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No lifetime data available</div>';
5261        return;
5262    }
5263    
5264    // Show top allocations by lifetime
5265    const withLifetime = allocs.filter(a => a.lifetime_ms || (a.timestamp_alloc && a.timestamp_dealloc));
5266    const sorted = withLifetime.sort((a, b) => {
5267        const aLifetime = a.lifetime_ms || (a.timestamp_dealloc - a.timestamp_alloc);
5268        const bLifetime = b.lifetime_ms || (b.timestamp_dealloc - b.timestamp_alloc);
5269        return bLifetime - aLifetime;
5270    }).slice(0, 10);
5271    
5272    if (sorted.length === 0) {
5273        container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No lifetime data available</div>';
5274        return;
5275    }
5276    
5277    let html = '<div style="padding: 16px;">';
5278    
5279    sorted.forEach((alloc, index) => {
5280        const lifetime = alloc.lifetime_ms || (alloc.timestamp_dealloc - alloc.timestamp_alloc);
5281        const isActive = !alloc.timestamp_dealloc;
5282        const varName = alloc.var_name || `allocation_${index}`;
5283        const size = formatBytes(alloc.size || 0);
5284        
5285        html += `
5286            <div style="
5287                margin-bottom: 12px; 
5288                padding: 12px; 
5289                background: var(--bg-secondary); 
5290                border-radius: 8px;
5291                border-left: 4px solid ${isActive ? '#059669' : '#2563eb'};
5292            ">
5293                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
5294                    <span style="font-weight: 600; color: var(--text-primary);">${varName}</span>
5295                    <span style="font-size: 0.9rem; color: var(--text-secondary);">${size}</span>
5296                </div>
5297                <div style="display: flex; justify-content: space-between; align-items: center;">
5298                    <span style="font-size: 0.8rem; color: var(--text-secondary);">
5299                        ${formatTypeName(alloc.type_name || 'Unknown')}
5300                    </span>
5301                    <span style="
5302                        font-size: 0.8rem; 
5303                        font-weight: 600; 
5304                        color: ${isActive ? '#059669' : '#2563eb'};
5305                    ">
5306                        ${isActive ? 'Active' : `${lifetime}ms`}
5307                    </span>
5308                </div>
5309            </div>
5310        `;
5311    });
5312    
5313    html += '</div>';
5314    container.innerHTML = html;
5315}
5316
5317// Helper function to update elements
5318function updateElement(id, value) {
5319    const element = document.getElementById(id);
5320    if (element) {
5321        element.textContent = value;
5322    }
5323}
5324
5325// Original dashboard functions from dashboard.html
5326function renderKpis() {
5327    const data = window.analysisData || {};
5328    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5329    const total = allocs.reduce((s,a)=>s+(a.size||0),0);
5330    const active = allocs.filter(a=>!a.timestamp_dealloc).length;
5331    const leaks = allocs.filter(a=>a.is_leaked).length;
5332    const safety = Math.max(0, 100 - (leaks * 20));
5333    
5334    updateElement('total-allocations', allocs.length.toLocaleString());
5335    updateElement('active-variables', active.toLocaleString());
5336    updateElement('total-memory', formatBytes(total));
5337    updateElement('safety-score', safety + '%');
5338}
5339
5340function populateAllocationsTable() {
5341    const data = window.analysisData || {};
5342    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5343    const allocTable = document.getElementById('allocTable');
5344    if (!allocTable) return;
5345    
5346    const top = allocs.slice().sort((a,b)=>(b.size||0)-(a.size||0)).slice(0,50);
5347    allocTable.innerHTML = top.map(a => {
5348        const status = a.is_leaked ? 'Leaked' : (a.timestamp_dealloc ? 'Freed' : 'Active');
5349        const statusClass = a.is_leaked ? 'status-leaked' : (a.timestamp_dealloc ? 'status-freed' : 'status-active');
5350        
5351        return `<tr>
5352            <td>${a.var_name || 'Unknown'}</td>
5353            <td>${formatTypeName(a.type_name || 'Unknown')}</td>
5354            <td>${formatBytes(a.size || 0)}</td>
5355            <td><span class="status-badge ${statusClass}">${status}</span></td>
5356        </tr>`;
5357    }).join('');
5358}
5359
5360function renderTypeChart() {
5361    const ctx = document.getElementById('typeChart');
5362    if (!ctx || !window.Chart) return;
5363    
5364    const data = window.analysisData || {};
5365    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5366    const byType = {};
5367    
5368    allocs.forEach(a => {
5369        const type = a.type_name || 'Unknown';
5370        byType[type] = (byType[type] || 0) + (a.size || 0);
5371    });
5372    
5373    const top = Object.entries(byType).sort((a,b) => b[1] - a[1]).slice(0, 8);
5374    
5375    if (top.length > 0) {
5376        new Chart(ctx, {
5377            type: 'bar',
5378            data: {
5379                labels: top.map(x => formatTypeName(x[0])),
5380                datasets: [{
5381                    label: 'Memory Usage',
5382                    data: top.map(x => x[1]),
5383                    backgroundColor: '#2563eb',
5384                    borderRadius: 6
5385                }]
5386            },
5387            options: {
5388                responsive: true,
5389                maintainAspectRatio: false,
5390                plugins: { legend: { display: false } },
5391                scales: {
5392                    y: { 
5393                        beginAtZero: true,
5394                        ticks: { callback: function(value) { return formatBytes(value); } }
5395                    }
5396                }
5397            }
5398        });
5399    }
5400}
5401
5402function renderTimelineChart() {
5403    const ctx = document.getElementById('timelineChart');
5404    if (!ctx || !window.Chart) return;
5405    
5406    const data = window.analysisData || {};
5407    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5408    const sorted = allocs.slice().sort((a,b) => (a.timestamp_alloc||0) - (b.timestamp_alloc||0));
5409    
5410    let cumulative = 0;
5411    const points = [];
5412    const step = Math.max(1, Math.floor(sorted.length / 30));
5413    
5414    for (let i = 0; i < sorted.length; i += step) {
5415        cumulative += sorted[i].size || 0;
5416        points.push({ x: i, y: cumulative });
5417    }
5418    
5419    if (points.length > 1) {
5420        new Chart(ctx, {
5421            type: 'line',
5422            data: {
5423                labels: points.map(p => p.x),
5424                datasets: [{
5425                    label: 'Cumulative Memory',
5426                    data: points.map(p => p.y),
5427                    borderColor: '#059669',
5428                    backgroundColor: 'rgba(5, 150, 105, 0.1)',
5429                    fill: true,
5430                    tension: 0.4
5431                }]
5432            },
5433            options: {
5434                responsive: true,
5435                maintainAspectRatio: false,
5436                plugins: { legend: { display: false } },
5437                scales: {
5438                    y: { 
5439                        beginAtZero: true,
5440                        ticks: { callback: function(value) { return formatBytes(value); } }
5441                    }
5442                }
5443            }
5444        });
5445    }
5446}
5447
5448function renderTreemap() {
5449    const container = document.getElementById('treemap');
5450    if (!container) return;
5451    
5452    const data = window.analysisData || {};
5453    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5454    const byType = {};
5455    
5456    allocs.forEach(a => {
5457        const type = a.type_name || 'Unknown';
5458        byType[type] = (byType[type] || 0) + (a.size || 0);
5459    });
5460    
5461    const top = Object.entries(byType).sort((a,b) => b[1] - a[1]).slice(0, 12);
5462    const totalSize = top.reduce((sum, [, size]) => sum + size, 0);
5463    
5464    if (totalSize > 0) {
5465        let html = '<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; height: 100%; padding: 16px;">';
5466        
5467        top.forEach(([type, size], index) => {
5468            const percentage = (size / totalSize) * 100;
5469            const color = `hsl(${index * 30}, 70%, 55%)`;
5470            
5471            html += `
5472                <div style="
5473                    background: ${color};
5474                    color: white;
5475                    padding: 12px;
5476                    border-radius: 8px;
5477                    font-size: 11px;
5478                    font-weight: 600;
5479                    display: flex;
5480                    flex-direction: column;
5481                    justify-content: center;
5482                    text-align: center;
5483                    min-height: 80px;
5484                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
5485                    transition: transform 0.2s ease;
5486                " title="${type}: ${formatBytes(size)}" onmouseover="this.style.transform='scale(1.05)'" onmouseout="this.style.transform='scale(1)'">
5487                    <div style="margin-bottom: 4px;">${formatTypeName(type)}</div>
5488                    <div style="font-size: 10px; opacity: 0.9;">${formatBytes(size)}</div>
5489                    <div style="font-size: 9px; opacity: 0.7;">${percentage.toFixed(1)}%</div>
5490                </div>
5491            `;
5492        });
5493        
5494        html += '</div>';
5495        container.innerHTML = html;
5496    } else {
5497        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No data available</div>';
5498    }
5499}
5500
5501function renderLifetimes() {
5502    const container = document.getElementById('lifetimes');
5503    if (!container) return;
5504    
5505    const data = window.analysisData || {};
5506    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
5507    const withLifetime = allocs.filter(a => a.lifetime_ms || (a.timestamp_alloc && a.timestamp_dealloc));
5508    const sorted = withLifetime.sort((a, b) => {
5509        const aLifetime = a.lifetime_ms || (a.timestamp_dealloc - a.timestamp_alloc);
5510        const bLifetime = b.lifetime_ms || (b.timestamp_dealloc - b.timestamp_alloc);
5511        return bLifetime - aLifetime;
5512    }).slice(0, 10);
5513    
5514    if (sorted.length === 0) {
5515        container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No lifetime data available</div>';
5516        return;
5517    }
5518    
5519    let html = '<div style="padding: 16px;">';
5520    
5521    sorted.forEach((alloc, index) => {
5522        const lifetime = alloc.lifetime_ms || (alloc.timestamp_dealloc - alloc.timestamp_alloc);
5523        const isActive = !alloc.timestamp_dealloc;
5524        const varName = alloc.var_name || `allocation_${index}`;
5525        const size = formatBytes(alloc.size || 0);
5526        
5527        html += `
5528            <div style="
5529                margin-bottom: 12px; 
5530                padding: 12px; 
5531                background: var(--bg-secondary); 
5532                border-radius: 8px;
5533                border-left: 4px solid ${isActive ? '#059669' : '#2563eb'};
5534            ">
5535                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
5536                    <span style="font-weight: 600; color: var(--text-primary);">${varName}</span>
5537                    <span style="font-size: 0.9rem; color: var(--text-secondary);">${size}</span>
5538                </div>
5539                <div style="display: flex; justify-content: space-between; align-items: center;">
5540                    <span style="font-size: 0.8rem; color: var(--text-secondary);">
5541                        ${formatTypeName(alloc.type_name || 'Unknown')}
5542                    </span>
5543                    <span style="
5544                        font-size: 0.8rem; 
5545                        font-weight: 600; 
5546                        color: ${isActive ? '#059669' : '#2563eb'};
5547                    ">
5548                        ${isActive ? 'Active' : `${lifetime}ms`}
5549                    </span>
5550                </div>
5551            </div>
5552        `;
5553    });
5554    
5555    html += '</div>';
5556    container.innerHTML = html;
5557}
5558
5559function renderFFI() {
5560    // First try the chart container for simple chart
5561    const chartContainer = document.getElementById('ffi-risk-chart');
5562    if (chartContainer && window.Chart) {
5563        const data = window.analysisData || {};
5564        const ffiData = data.unsafe_ffi || data.unsafeFFI || {};
5565        const operations = ffiData.enhanced_ffi_data || ffiData.allocations || ffiData.unsafe_operations || [];
5566        
5567        if (operations.length > 0) {
5568            // Guard: destroy existing chart instance if any
5569            try {
5570                if (window.chartInstances && window.chartInstances['ffi-risk-chart']) {
5571                    window.chartInstances['ffi-risk-chart'].destroy();
5572                    delete window.chartInstances['ffi-risk-chart'];
5573                }
5574            } catch (_) {}
5575
5576            const highRisk = operations.filter(op => (op.safety_violations || []).length > 2).length;
5577            const mediumRisk = operations.filter(op => (op.safety_violations || []).length > 0 && (op.safety_violations || []).length <= 2).length;
5578            const lowRisk = operations.filter(op => (op.safety_violations || []).length === 0).length;
5579            
5580            window.chartInstances = window.chartInstances || {};
5581            window.chartInstances['ffi-risk-chart'] = new Chart(chartContainer, {
5582                type: 'doughnut',
5583                data: {
5584                    labels: ['Low Risk', 'Medium Risk', 'High Risk'],
5585                    datasets: [{
5586                        data: [lowRisk, mediumRisk, highRisk],
5587                        backgroundColor: ['#059669', '#ea580c', '#dc2626'],
5588                        borderWidth: 2,
5589                        borderColor: 'var(--bg-primary)'
5590                    }]
5591                },
5592                options: {
5593                    responsive: true,
5594                    maintainAspectRatio: false,
5595                    plugins: {
5596                        legend: {
5597                            position: 'bottom',
5598                            labels: {
5599                                padding: 20,
5600                                usePointStyle: true,
5601                                generateLabels: function(chart) {
5602                                    const data = chart.data;
5603                                    return data.labels.map((label, i) => ({
5604                                        text: `${label}: ${data.datasets[0].data[i]}`,
5605                                        fillStyle: data.datasets[0].backgroundColor[i],
5606                                        strokeStyle: data.datasets[0].backgroundColor[i],
5607                                        pointStyle: 'circle'
5608                                    }));
5609                                }
5610                            }
5611                        }
5612                    }
5613                }
5614            });
5615            return;
5616        }
5617    }
5618    
5619    // Main comprehensive FFI visualization based on project's actual SVG code
5620    const container = document.getElementById('ffiVisualization');
5621    if (!container) return;
5622    
5623    const data = window.analysisData || {};
5624    const ffiData = data.unsafe_ffi || data.unsafeFFI || {};
5625    const allocations = ffiData.allocations || ffiData.enhanced_ffi_data || [];
5626    const violations = ffiData.violations || [];
5627    
5628    if (allocations.length === 0) {
5629        container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No FFI data available</div>';
5630        return;
5631    }
5632    
5633    // Create the ACTUAL project-based unsafe/FFI dashboard SVG
5634    createProjectBasedUnsafeFFIDashboard(container, allocations, violations);
5635}
5636
5637// PROJECT-BASED Unsafe/FFI Dashboard - Direct implementation from visualization.rs
5638function createProjectBasedUnsafeFFIDashboard(container, allocations, violations) {
5639    const width = 1400;
5640    const height = 1000;
5641    
5642    // Calculate key metrics exactly like the Rust code
5643    const unsafeCount = allocations.filter(a => 
5644        (a.source && (a.source.UnsafeRust || a.source === 'UnsafeRust')) ||
5645        (a.allocation_source === 'UnsafeRust')
5646    ).length;
5647    
5648    const ffiCount = allocations.filter(a => 
5649        (a.source && (a.source.FfiC || a.source === 'FfiC')) ||
5650        (a.allocation_source === 'FfiC')
5651    ).length;
5652    
5653    const crossBoundaryEvents = allocations.reduce((sum, a) => 
5654        sum + ((a.cross_boundary_events && a.cross_boundary_events.length) || 0), 0
5655    );
5656    
5657    const totalUnsafeMemory = allocations
5658        .filter(a => a.source !== 'RustSafe' && a.allocation_source !== 'RustSafe')
5659        .reduce((sum, a) => sum + ((a.base && a.base.size) || a.size || 0), 0);
5660    
5661    // Create the full SVG dashboard exactly like the Rust implementation
5662    let html = `
5663        <div style="width: 100%; height: ${height}px; background: linear-gradient(135deg, #2c3e50 0%, #34495e 50%, #2c3e50 100%); border-radius: 12px; overflow: hidden; position: relative;">
5664            <svg width="100%" height="100%" viewBox="0 0 ${width} ${height}" style="font-family: 'Segoe UI', Arial, sans-serif;">
5665                <!-- SVG Definitions -->
5666                <defs>
5667                    <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
5668                        <polygon points="0 0, 10 3.5, 0 7" fill='#e74c3c'/>
5669                    </marker>
5670                    <filter id="glow">
5671                        <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
5672                        <feMerge>
5673                            <feMergeNode in="coloredBlur"/>
5674                            <feMergeNode in="SourceGraphic"/>
5675                        </feMerge>
5676                    </filter>
5677                </defs>
5678                
5679                <!-- Main Title -->
5680                <text x="${width/2}" y="40" text-anchor="middle" font-size="24" font-weight="bold" fill='#ecf0f1'>
5681                    Unsafe Rust &amp; FFI Memory Analysis Dashboard
5682                </text>
5683                
5684                <!-- Key Metrics Cards -->
5685                <g id="metrics-cards">
5686                    ${createMetricsCards(unsafeCount, ffiCount, crossBoundaryEvents, violations.length, totalUnsafeMemory)}
5687                </g>
5688                
5689                <!-- Allocation Source Breakdown -->
5690                <g id="allocation-breakdown" transform="translate(50, 150)">
5691                    ${createAllocationSourceBreakdown(allocations)}
5692                </g>
5693                
5694                <!-- Memory Safety Status -->
5695                <g id="safety-status" transform="translate(750, 150)">
5696                    ${createMemorySafetyStatus(violations)}
5697                </g>
5698                
5699                <!-- Cross-Language Memory Flow -->
5700                <g id="boundary-flow" transform="translate(50, 500)">
5701                    ${createBoundaryFlow(allocations)}
5702                </g>
5703                
5704                <!-- Unsafe Memory Hotspots -->
5705                <g id="unsafe-hotspots" transform="translate(750, 500)">
5706                    ${createUnsafeHotspots(allocations)}
5707                </g>
5708            </svg>
5709        </div>
5710    `;
5711    
5712    container.innerHTML = html;
5713    
5714    // Add interactivity
5715    setTimeout(() => {
5716        addFFIInteractivity();
5717    }, 100);
5718}
5719
5720// Create metrics cards exactly like Rust implementation
5721function createMetricsCards(unsafeCount, ffiCount, crossBoundaryEvents, violationCount, totalUnsafeMemory) {
5722    const metrics = [
5723        { label: 'Unsafe Allocations', value: unsafeCount, color: '#e74c3c', x: 100 },
5724        { label: 'FFI Allocations', value: ffiCount, color: '#3498db', x: 350 },
5725        { label: 'Boundary Crossings', value: crossBoundaryEvents, color: '#f39c12', x: 600 },
5726        { label: 'Safety Violations', value: violationCount, color: '#e67e22', x: 850 },
5727        { label: 'Unsafe Memory', value: formatBytes(totalUnsafeMemory), color: '#9b59b6', x: 1100 }
5728    ];
5729    
5730    return metrics.map(metric => `
5731        <!-- Card background -->
5732        <rect x="${metric.x - 60}" y="55" width="120" height="50" 
5733              fill="${metric.color}" fill-opacity="0.2" 
5734              stroke="${metric.color}" stroke-width="2" rx="8"/>
5735        
5736        <!-- Value -->
5737        <text x="${metric.x}" y="70" text-anchor="middle" font-size="16" font-weight="bold" fill="${metric.color}">
5738            ${metric.value}
5739        </text>
5740        
5741        <!-- Label -->
5742        <text x="${metric.x}" y="95" text-anchor="middle" font-size="10" fill='#bdc3c7'>
5743            ${metric.label}
5744        </text>
5745    `).join('');
5746}
5747
5748// Create allocation source breakdown
5749function createAllocationSourceBreakdown(allocations) {
5750    let safeCount = 0, unsafeCount = 0, ffiCount = 0, crossBoundaryCount = 0;
5751    
5752    allocations.forEach(allocation => {
5753        const source = allocation.source || allocation.allocation_source || 'Unknown';
5754        if (source === 'RustSafe' || source.RustSafe) safeCount++;
5755        else if (source === 'UnsafeRust' || source.UnsafeRust) unsafeCount++;
5756        else if (source === 'FfiC' || source.FfiC) ffiCount++;
5757        else if (source === 'CrossBoundary' || source.CrossBoundary) crossBoundaryCount++;
5758    });
5759    
5760    const total = safeCount + unsafeCount + ffiCount + crossBoundaryCount;
5761    if (total === 0) {
5762        return `<text x="300" y="150" text-anchor="middle" font-size="14" fill='#95a5a6'>No allocation data available</text>`;
5763    }
5764    
5765    const sources = [
5766        { label: 'Safe Rust', count: safeCount, color: '#2ecc71', x: 50 },
5767        { label: 'Unsafe Rust', count: unsafeCount, color: '#e74c3c', x: 170 },
5768        { label: 'FFI', count: ffiCount, color: '#3498db', x: 290 },
5769        { label: 'Cross-boundary', count: crossBoundaryCount, color: '#9b59b6', x: 410 }
5770    ];
5771    
5772    let svg = `
5773        <!-- Section background -->
5774        <rect x="0" y="0" width="600" height="300" fill="rgba(52, 73, 94, 0.3)" 
5775              stroke='#34495e' stroke-width="2" rx="10"/>
5776        
5777        <!-- Section title -->
5778        <text x="300" y="-10" text-anchor="middle" font-size="18" font-weight="bold" fill='#ecf0f1'>
5779            Memory Allocation Sources
5780        </text>
5781    `;
5782    
5783    sources.forEach(source => {
5784        if (source.count > 0) {
5785            const barHeight = (source.count / total * 100);
5786            svg += `
5787                <!-- Bar -->
5788                <rect x="${source.x}" y="${200 - barHeight}" width="40" height="${barHeight}" fill="${source.color}"/>
5789                
5790                <!-- Count label -->
5791                <text x="${source.x + 20}" y="${200 - barHeight - 5}" text-anchor="middle" 
5792                      font-size="12" font-weight="bold" fill="${source.color}">
5793                    ${source.count}
5794                </text>
5795                
5796                <!-- Label -->
5797                <text x="${source.x + 20}" y="220" text-anchor="middle" font-size="10" fill='#ecf0f1'>
5798                    ${source.label}
5799                </text>
5800            `;
5801        }
5802    });
5803    
5804    return svg;
5805}
5806
5807// Create memory safety status
5808function createMemorySafetyStatus(violations) {
5809    const bgColor = violations.length === 0 ? '#27ae60' : '#e74c3c';
5810    
5811    let svg = `
5812        <!-- Section background -->
5813        <rect x="0" y="0" width="600" height="300" fill="${bgColor}20" 
5814              stroke="${bgColor}" stroke-width="2" rx="10"/>
5815        
5816        <!-- Section title -->
5817        <text x="300" y="-10" text-anchor="middle" font-size="18" font-weight="bold" fill='#ecf0f1'>
5818            Memory Safety Status
5819        </text>
5820    `;
5821    
5822    if (violations.length === 0) {
5823        svg += `
5824            <text x="300" y="150" text-anchor="middle" font-size="16" font-weight="bold" fill='#27ae60'>
5825                No Safety Violations Detected
5826            </text>
5827            <text x="300" y="180" text-anchor="middle" font-size="12" fill='#2ecc71'>
5828                All unsafe operations and FFI calls appear to be memory-safe
5829            </text>
5830        `;
5831    } else {
5832        svg += `
5833            <text x="300" y="120" text-anchor="middle" font-size="16" font-weight="bold" fill='#e74c3c'>
5834                ${violations.length} Safety Violations Detected
5835            </text>
5836        `;
5837        
5838        violations.slice(0, 5).forEach((violation, i) => {
5839            const y = 160 + i * 20;
5840            const description = getViolationDescription(violation);
5841            svg += `
5842                <text x="30" y="${y}" font-size="12" fill='#e74c3c'>โ€ข ${description}</text>
5843            `;
5844        });
5845    }
5846    
5847    return svg;
5848}
5849
5850// Create boundary flow diagram
5851function createBoundaryFlow(allocations) {
5852    let rustToFfi = 0, ffiToRust = 0;
5853    
5854    allocations.forEach(allocation => {
5855        if (allocation.cross_boundary_events) {
5856            allocation.cross_boundary_events.forEach(event => {
5857                const eventType = event.event_type || event.type;
5858                if (eventType === 'RustToFfi' || eventType === 'OwnershipTransfer') rustToFfi++;
5859                else if (eventType === 'FfiToRust') ffiToRust++;
5860                else if (eventType === 'SharedAccess') {
5861                    rustToFfi++;
5862                    ffiToRust++;
5863                }
5864            });
5865        }
5866    });
5867    
5868    return `
5869        <!-- Section background -->
5870        <rect x="0" y="0" width="600" height="200" fill="rgba(52, 73, 94, 0.3)" 
5871              stroke='#34495e' stroke-width="2" rx="10"/>
5872        
5873        <!-- Section title -->
5874        <text x="300" y="-10" text-anchor="middle" font-size="18" font-weight="bold" fill='#ecf0f1'>
5875            Cross-Language Memory Flow
5876        </text>
5877        
5878        <!-- Rust territory -->
5879        <rect x="50" y="50" width="200" height="100" fill='#2ecc71' fill-opacity="0.2" 
5880              stroke='#2ecc71' stroke-width="2" rx="8"/>
5881        <text x="150" y="110" text-anchor="middle" font-size="14" font-weight="bold" fill='#2ecc71'>
5882            RUST
5883        </text>
5884        
5885        <!-- FFI territory -->
5886        <rect x="350" y="50" width="200" height="100" fill='#3498db' fill-opacity="0.2" 
5887              stroke='#3498db' stroke-width="2" rx="8"/>
5888        <text x="450" y="110" text-anchor="middle" font-size="14" font-weight="bold" fill='#3498db'>
5889            FFI / C
5890        </text>
5891        
5892        ${rustToFfi > 0 ? `
5893            <!-- Rust to FFI arrow -->
5894            <line x1="250" y1="80" x2="350" y2="80" stroke='#e74c3c' stroke-width="3" marker-end="url(#arrowhead)"/>
5895            <text x="300" y="75" text-anchor="middle" font-size="12" font-weight="bold" fill='#e74c3c'>
5896                ${rustToFfi}
5897            </text>
5898        ` : ''}
5899        
5900        ${ffiToRust > 0 ? `
5901            <!-- FFI to Rust indicator -->
5902            <text x="300" y="135" text-anchor="middle" font-size="12" font-weight="bold" fill='#f39c12'>
5903                โ† ${ffiToRust}
5904            </text>
5905        ` : ''}
5906    `;
5907}
5908
5909// Create unsafe hotspots
5910function createUnsafeHotspots(allocations) {
5911    const unsafeAllocations = allocations.filter(a => 
5912        a.source !== 'RustSafe' && a.allocation_source !== 'RustSafe'
5913    );
5914    
5915    if (unsafeAllocations.length === 0) {
5916        return `
5917            <rect x="0" y="0" width="600" height="200" fill="rgba(52, 73, 94, 0.3)" 
5918                  stroke='#34495e' stroke-width="2" rx="10"/>
5919            <text x="300" y="-10" text-anchor="middle" font-size="18" font-weight="bold" fill='#ecf0f1'>
5920                Unsafe Memory Hotspots
5921            </text>
5922            <text x="300" y="100" text-anchor="middle" font-size="14" fill='#2ecc71'>
5923                No unsafe memory allocations detected
5924            </text>
5925        `;
5926    }
5927    
5928    let svg = `
5929        <rect x="0" y="0" width="600" height="200" fill="rgba(52, 73, 94, 0.3)" 
5930              stroke='#34495e' stroke-width="2" rx="10"/>
5931        <text x="300" y="-10" text-anchor="middle" font-size="18" font-weight="bold" fill='#ecf0f1'>
5932            Unsafe Memory Hotspots
5933        </text>
5934    `;
5935    
5936    unsafeAllocations.slice(0, 6).forEach((allocation, i) => {
5937        const x = 80 + (i % 3) * 180;
5938        const y = 80 + Math.floor(i / 3) * 70;
5939        const size = (allocation.base && allocation.base.size) || allocation.size || 0;
5940        const sizeFactor = Math.max(5, Math.min(20, Math.log(size + 1) * 2));
5941        
5942        const source = allocation.source || allocation.allocation_source || 'Unknown';
5943        let color = '#95a5a6';
5944        let label = 'OTHER';
5945        
5946        if (source === 'UnsafeRust' || source.UnsafeRust) {
5947            color = '#e74c3c';
5948            label = 'UNSAFE';
5949        } else if (source === 'FfiC' || source.FfiC) {
5950            color = '#3498db';
5951            label = 'FFI';
5952        } else if (source === 'CrossBoundary' || source.CrossBoundary) {
5953            color = '#9b59b6';
5954            label = 'CROSS';
5955        }
5956        
5957        svg += `
5958            <!-- Hotspot circle -->
5959            <circle cx="${x}" cy="${y}" r="${sizeFactor}" fill="${color}" fill-opacity="0.7" 
5960                    stroke="${color}" stroke-width="2" filter="url(#glow)"/>
5961            
5962            <!-- Size label -->
5963            <text x="${x}" y="${y + 4}" text-anchor="middle" font-size="8" font-weight="bold" fill='#ffffff'>
5964                ${formatBytes(size)}
5965            </text>
5966            
5967            <!-- Type label -->
5968            <text x="${x}" y="${y + 35}" text-anchor="middle" font-size="10" fill="${color}">
5969                ${label}
5970            </text>
5971        `;
5972    });
5973    
5974    return svg;
5975}
5976
5977// Helper functions
5978function getViolationDescription(violation) {
5979    if (violation.DoubleFree || violation.type === 'DoubleFree') return 'Double Free';
5980    if (violation.InvalidFree || violation.type === 'InvalidFree') return 'Invalid Free';
5981    if (violation.PotentialLeak || violation.type === 'PotentialLeak') return 'Memory Leak';
5982    if (violation.CrossBoundaryRisk || violation.type === 'CrossBoundaryRisk') return 'Cross-Boundary Risk';
5983    return 'Unknown Violation';
5984}
5985
5986function addFFIInteractivity() {
5987    // Add hover effects to hotspots
5988    const hotspots = document.querySelectorAll('#unsafe-hotspots circle');
5989    hotspots.forEach(hotspot => {
5990        hotspot.addEventListener('mouseover', function() {
5991            this.setAttribute('r', parseInt(this.getAttribute('r')) * 1.2);
5992        });
5993        hotspot.addEventListener('mouseout', function() {
5994            this.setAttribute('r', parseInt(this.getAttribute('r')) / 1.2);
5995        });
5996    });
5997}
5998
5999function renderAllocationSourceChart(allocations) {
6000    const container = document.getElementById('allocation-source-chart');
6001    if (!container) return;
6002    
6003    // Count allocations by source
6004    let safeCount = 0, unsafeCount = 0, ffiCount = 0, crossBoundaryCount = 0;
6005    
6006    allocations.forEach(allocation => {
6007        if (allocation.source) {
6008            if (allocation.source.includes && allocation.source.includes('Safe')) safeCount++;
6009            else if (allocation.source.includes && allocation.source.includes('Unsafe')) unsafeCount++;
6010            else if (allocation.source.includes && allocation.source.includes('Ffi')) ffiCount++;
6011            else if (allocation.source.includes && allocation.source.includes('Cross')) crossBoundaryCount++;
6012        }
6013    });
6014    
6015    const total = safeCount + unsafeCount + ffiCount + crossBoundaryCount;
6016    if (total === 0) {
6017        container.innerHTML = '<div style="text-align: center; color: #95a5a6;">No allocation data available</div>';
6018        return;
6019    }
6020    
6021    const sources = [
6022        { label: 'Safe Rust', count: safeCount, color: '#2ecc71' },
6023        { label: 'Unsafe Rust', count: unsafeCount, color: '#e74c3c' },
6024        { label: 'FFI', count: ffiCount, color: '#3498db' },
6025        { label: 'Cross-boundary', count: crossBoundaryCount, color: '#9b59b6' }
6026    ];
6027    
6028    let html = '<div style="display: flex; justify-content: space-around; align-items: end; height: 100px;">';
6029    
6030    sources.forEach(source => {
6031        if (source.count > 0) {
6032            const barHeight = (source.count / total * 80);
6033            html += `
6034                <div style="text-align: center;">
6035                    <div style="font-size: 12px; font-weight: bold; color: ${source.color}; margin-bottom: 5px;">${source.count}</div>
6036                    <div style="width: 30px; height: ${barHeight}px; background: ${source.color}; margin: 0 auto 5px;"></div>
6037                    <div style="font-size: 10px; color: #ecf0f1; writing-mode: vertical-rl; text-orientation: mixed;">${source.label}</div>
6038                </div>
6039            `;
6040        }
6041    });
6042    
6043    html += '</div>';
6044    container.innerHTML = html;
6045}
6046
6047function renderSafetyStatusPanel(violations) {
6048    const container = document.getElementById('safety-status-panel');
6049    if (!container) return;
6050    
6051    if (violations.length === 0) {
6052        container.innerHTML = `
6053            <div style="text-align: center; color: #27ae60;">
6054                <div style="font-size: 16px; font-weight: bold; margin-bottom: 10px;">No Safety Violations Detected</div>
6055                <div style="font-size: 12px; color: #2ecc71;">All unsafe operations and FFI calls appear to be memory-safe</div>
6056            </div>
6057        `;
6058    } else {
6059        let html = `
6060            <div style="text-align: center; color: #e74c3c; margin-bottom: 15px;">
6061                <div style="font-size: 16px; font-weight: bold;">${violations.length} Safety Violations Detected</div>
6062            </div>
6063            <div style="max-height: 80px; overflow-y: auto;">
6064        `;
6065        
6066        violations.slice(0, 5).forEach(violation => {
6067            const description = violation.type || 'Unknown Violation';
6068            html += `<div style="font-size: 12px; color: #e74c3c; margin-bottom: 5px;">โ€ข ${description}</div>`;
6069        });
6070        
6071        html += '</div>';
6072        container.innerHTML = html;
6073    }
6074}
6075
6076function renderBoundaryFlowDiagram(allocations) {
6077    const container = document.getElementById('boundary-flow-diagram');
6078    if (!container) return;
6079    
6080    // Count boundary events
6081    let rustToFfi = 0, ffiToRust = 0;
6082    
6083    allocations.forEach(allocation => {
6084        if (allocation.cross_boundary_events) {
6085            allocation.cross_boundary_events.forEach(event => {
6086                if (event.event_type === 'RustToFfi') rustToFfi++;
6087                else if (event.event_type === 'FfiToRust') ffiToRust++;
6088                else if (event.event_type === 'OwnershipTransfer') rustToFfi++;
6089                else if (event.event_type === 'SharedAccess') {
6090                    rustToFfi++;
6091                    ffiToRust++;
6092                }
6093            });
6094        }
6095    });
6096    
6097    container.innerHTML = `
6098        <div style="display: flex; justify-content: space-around; align-items: center; height: 80px;">
6099            <!-- Rust territory -->
6100            <div style="width: 150px; height: 60px; background: rgba(46, 204, 113, 0.2); border: 2px solid #2ecc71; border-radius: 8px; display: flex; align-items: center; justify-content: center;">
6101                <div style="text-align: center;">
6102                    <div style="font-size: 14px; font-weight: bold; color: #2ecc71;">RUST</div>
6103                </div>
6104            </div>
6105            
6106            <!-- Flow arrows -->
6107            <div style="text-align: center;">
6108                ${rustToFfi > 0 ? `
6109                    <div style="margin-bottom: 5px;">
6110                        <span style="color: #e74c3c; font-weight: bold;">${rustToFfi}</span>
6111                        <span style="color: #e74c3c;"> โ†’</span>
6112                    </div>
6113                ` : ''}
6114                ${ffiToRust > 0 ? `
6115                    <div>
6116                        <span style="color: #f39c12;"> โ†</span>
6117                        <span style="color: #f39c12; font-weight: bold;">${ffiToRust}</span>
6118                    </div>
6119                ` : ''}
6120            </div>
6121            
6122            <!-- FFI territory -->
6123            <div style="width: 150px; height: 60px; background: rgba(52, 152, 219, 0.2); border: 2px solid #3498db; border-radius: 8px; display: flex; align-items: center; justify-content: center;">
6124                <div style="text-align: center;">
6125                    <div style="font-size: 14px; font-weight: bold; color: #3498db;">FFI / C</div>
6126                </div>
6127            </div>
6128        </div>
6129    `;
6130}
6131
6132function renderMemoryUsageAnalysis() {
6133    // Will be implemented if container exists
6134}
6135
6136function renderMemoryFragmentation() {
6137    const container = document.getElementById('memoryFragmentation');
6138    if (!container) return;
6139    
6140    const data = window.analysisData || {};
6141    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
6142    
6143    if (allocs.length === 0) {
6144        container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); padding: 40px;">No allocation data available</div>';
6145        return;
6146    }
6147    
6148    // Calculate fragmentation metrics
6149    const sizes = allocs.map(a => a.size || 0).filter(s => s > 0);
6150    const totalMemory = sizes.reduce((sum, size) => sum + size, 0);
6151    const avgSize = totalMemory / sizes.length;
6152    const variance = sizes.reduce((sum, size) => sum + Math.pow(size - avgSize, 2), 0) / sizes.length;
6153    const stdDev = Math.sqrt(variance);
6154    const fragmentation = Math.min(100, (stdDev / avgSize) * 100);
6155    
6156    // Sort allocations by size for visualization
6157    const sortedAllocs = allocs.slice().sort((a, b) => (a.size || 0) - (b.size || 0));
6158    
6159    // Create memory fragmentation visualization
6160    container.innerHTML = `
6161        <div style="height: 100%; display: flex; flex-direction: column; gap: 12px; padding: 12px;">
6162            <!-- Fragmentation Score -->
6163            <div style="display: flex; justify-content: space-between; align-items: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6164                <div>
6165                    <div style="font-size: 14px; font-weight: 600; color: var(--text-primary);">Fragmentation Level</div>
6166                    <div style="font-size: 11px; color: var(--text-secondary);">Memory size variance indicator</div>
6167                </div>
6168                <div style="text-align: right;">
6169                    <div style="font-size: 24px; font-weight: 700; color: ${fragmentation > 50 ? 'var(--primary-red)' : fragmentation > 25 ? 'var(--primary-orange)' : 'var(--primary-green)'};">
6170                        ${fragmentation.toFixed(1)}%
6171                    </div>
6172                    <div style="font-size: 10px; color: var(--text-secondary);">
6173                        ${fragmentation > 50 ? 'High' : fragmentation > 25 ? 'Medium' : 'Low'}
6174                    </div>
6175                </div>
6176            </div>
6177            
6178            <!-- Memory Layout Visualization -->
6179            <div style="flex: 1; background: var(--bg-secondary); border-radius: 8px; padding: 12px;">
6180                <div style="font-size: 12px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px;">Memory Layout (${allocs.length} allocations)</div>
6181                <div style="height: 80px; background: var(--bg-primary); border-radius: 6px; padding: 4px; position: relative; overflow: hidden;">
6182                    <!-- Memory blocks representing allocations -->
6183                    <div style="display: flex; height: 100%; align-items: end; gap: 1px;">
6184                        ${sortedAllocs.slice(0, 40).map((alloc, i) => {
6185                            const size = alloc.size || 0;
6186                            const maxSize = Math.max(...sizes);
6187                            const height = Math.max(8, (size / maxSize) * 70);
6188                            const width = Math.max(2, Math.min(8, 100 / Math.min(40, allocs.length)));
6189                            
6190                            let color = '#10b981'; // Green for small
6191                            if (size > 10240) color = '#ef4444'; // Red for large
6192                            else if (size > 1024) color = '#f59e0b'; // Orange for medium
6193                            else if (size > 100) color = '#3b82f6'; // Blue for small-medium
6194                            
6195                            return `
6196                                <div style="width: ${width}px; height: ${height}px; background: ${color}; 
6197                                           border-radius: 1px; cursor: pointer; transition: all 0.2s; opacity: 0.8;"
6198                                     title="${alloc.var_name}: ${formatBytes(size)}"
6199                                     onmouseover="this.style.transform='scaleY(1.2)'; this.style.opacity='1'"
6200                                     onmouseout="this.style.transform='scaleY(1)'; this.style.opacity='0.8'"
6201                                     onclick="showAllocationDetail('${alloc.ptr}')"></div>
6202                            `;
6203                        }).join('')}
6204                    </div>
6205                    
6206                    <!-- Size legend -->
6207                    <div style="position: absolute; bottom: 4px; right: 4px; display: flex; gap: 4px; font-size: 8px;">
6208                        <div style="display: flex; align-items: center; gap: 2px;">
6209                            <div style="width: 8px; height: 4px; background: #10b981;"></div>
6210                            <span style="color: var(--text-secondary);">Tiny</span>
6211                        </div>
6212                        <div style="display: flex; align-items: center; gap: 2px;">
6213                            <div style="width: 8px; height: 4px; background: #3b82f6;"></div>
6214                            <span style="color: var(--text-secondary);">Small</span>
6215                        </div>
6216                        <div style="display: flex; align-items: center; gap: 2px;">
6217                            <div style="width: 8px; height: 4px; background: #f59e0b;"></div>
6218                            <span style="color: var(--text-secondary);">Medium</span>
6219                        </div>
6220                        <div style="display: flex; align-items: center; gap: 2px;">
6221                            <div style="width: 8px; height: 4px; background: #ef4444;"></div>
6222                            <span style="color: var(--text-secondary);">Large</span>
6223                        </div>
6224                    </div>
6225                </div>
6226                
6227                <!-- Fragmentation Analysis -->
6228                <div style="margin-top: 8px; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; font-size: 11px;">
6229                    <div style="text-align: center; padding: 6px; background: var(--bg-primary); border-radius: 4px;">
6230                        <div style="font-weight: 600; color: var(--text-primary);">${formatBytes(avgSize)}</div>
6231                        <div style="color: var(--text-secondary);">Avg Size</div>
6232                    </div>
6233                    <div style="text-align: center; padding: 6px; background: var(--bg-primary); border-radius: 4px;">
6234                        <div style="font-weight: 600; color: var(--text-primary);">${formatBytes(Math.max(...sizes))}</div>
6235                        <div style="color: var(--text-secondary);">Max Size</div>
6236                    </div>
6237                    <div style="text-align: center; padding: 6px; background: var(--bg-primary); border-radius: 4px;">
6238                        <div style="font-weight: 600; color: var(--text-primary);">${formatBytes(Math.min(...sizes))}</div>
6239                        <div style="color: var(--text-secondary);">Min Size</div>
6240                    </div>
6241                </div>
6242            </div>
6243        </div>
6244    `;
6245}
6246
6247function renderMemoryGrowthTrends() {
6248    const container = document.getElementById('growth');
6249    if (!container) return;
6250    
6251    const data = window.analysisData || {};
6252    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
6253    if (allocs.length === 0) {
6254        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No growth data available</div>';
6255        return;
6256    }
6257    
6258    // Create a proper growth chart using Chart.js
6259    const canvas = document.createElement('canvas');
6260    canvas.style.width = '100%';
6261    canvas.style.height = '100%';
6262    container.innerHTML = '';
6263    container.appendChild(canvas);
6264    
6265    const sorted = allocs.slice().sort((a,b) => (a.timestamp_alloc||0) - (b.timestamp_alloc||0));
6266    let cumulative = 0;
6267    const points = [];
6268    
6269    for (let i = 0; i < Math.min(sorted.length, 30); i++) {
6270        cumulative += sorted[i].size || 0;
6271        points.push({ x: i, y: cumulative });
6272    }
6273    
6274    if (points.length > 1 && window.Chart) {
6275        new Chart(canvas, {
6276            type: 'line',
6277            data: {
6278                labels: points.map((_, i) => `T${i}`),
6279                datasets: [{
6280                    label: 'Memory Growth',
6281                    data: points.map(p => p.y),
6282                    borderColor: '#059669',
6283                    backgroundColor: 'rgba(5, 150, 105, 0.1)',
6284                    fill: true,
6285                    tension: 0.3,
6286                    pointRadius: 3,
6287                    pointHoverRadius: 5
6288                }]
6289            },
6290            options: {
6291                responsive: true,
6292                maintainAspectRatio: false,
6293                plugins: {
6294                    legend: { display: false }
6295                },
6296                scales: {
6297                    x: {
6298                        title: {
6299                            display: true,
6300                            text: 'Time Steps'
6301                        }
6302                    },
6303                    y: { 
6304                        beginAtZero: true,
6305                        title: {
6306                            display: true,
6307                            text: 'Cumulative Memory'
6308                        },
6309                        ticks: {
6310                            callback: function(value) {
6311                                return formatBytes(value);
6312                            }
6313                        }
6314                    }
6315                }
6316            }
6317        });
6318    }
6319}
6320
6321function setupLifecycle() {
6322    // Lifecycle setup functionality
6323}
6324
6325function populateUnsafeTable() {
6326    const data = window.analysisData || {};
6327    const root = data.unsafe_ffi || {};
6328    const ops = root.enhanced_ffi_data || root.unsafe_operations || root.allocations || [];
6329    const unsafeTable = document.getElementById('unsafeTable');
6330    if (!unsafeTable) return;
6331    
6332    unsafeTable.innerHTML = (ops || []).slice(0, 50).map(op => {
6333        const riskLevel = op.risk_level || ((op.safety_violations||[]).length > 2 ? 'High' : 
6334                         ((op.safety_violations||[]).length > 0 ? 'Medium' : 'Low'));
6335        
6336        const riskText = riskLevel === 'High' ? 'High Risk' : (riskLevel === 'Medium' ? 'Medium Risk' : 'Low Risk');
6337        const riskClass = riskLevel === 'High' ? 'risk-high' : (riskLevel === 'Medium' ? 'risk-medium' : 'risk-low');
6338        
6339        return `<tr>
6340            <td>${op.location || op.var_name || 'Unknown'}</td>
6341            <td>${op.operation_type || op.type_name || 'Unknown'}</td>
6342            <td><span class="status-badge ${riskClass}">${riskText}</span></td>
6343        </tr>`;
6344    }).join('');
6345}
6346
6347function renderVariableGraph() {
6348    const container = document.getElementById('graph');
6349    if (!container) return;
6350    
6351    const data = window.analysisData || {};
6352    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
6353    if (allocs.length === 0) {
6354        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No relationship data available</div>';
6355        return;
6356    }
6357    
6358    // Enhanced variable relationship graph with pan/zoom and drag functionality
6359    const nodes = allocs.slice(0, 30).map((a, i) => ({
6360        id: i,
6361        name: a.var_name || `var_${i}`,
6362        type: a.type_name || 'unknown',
6363        size: a.size || 0,
6364        status: a.is_leaked ? 'leaked' : (a.timestamp_dealloc ? 'freed' : 'active'),
6365        ptr: a.ptr || 'unknown',
6366        timestamp_alloc: a.timestamp_alloc || 0,
6367        timestamp_dealloc: a.timestamp_dealloc || null,
6368        x: 200 + (i % 6) * 120 + Math.random() * 40,
6369        y: 200 + Math.floor(i / 6) * 120 + Math.random() * 40,
6370        isDragging: false
6371    }));
6372    
6373    // Create enhanced links with copy/clone/move relationships
6374    const links = [];
6375    for (let i = 0; i < nodes.length; i++) {
6376        for (let j = i + 1; j < nodes.length; j++) {
6377            const nodeA = nodes[i];
6378            const nodeB = nodes[j];
6379            const allocA = allocs[i];
6380            const allocB = allocs[j];
6381            
6382            // Clone relationship (based on clone_info)
6383            if (allocA.clone_info?.clone_count > 0 && allocB.clone_info?.is_clone) {
6384                links.push({ 
6385                    source: i, target: j, type: 'clone', 
6386                    color: '#f59e0b', strokeWidth: 3, dashArray: '5,5'
6387                });
6388            }
6389            // Copy relationship (same type, similar size)
6390            else if (nodeA.type === nodeB.type && 
6391                     Math.abs(nodeA.size - nodeB.size) < Math.max(nodeA.size, nodeB.size) * 0.1) {
6392                links.push({ 
6393                    source: i, target: j, type: 'copy', 
6394                    color: '#06b6d4', strokeWidth: 2, dashArray: 'none'
6395                });
6396            }
6397            // Move relationship (same type, different timestamps)
6398            else if (nodeA.type === nodeB.type && 
6399                     Math.abs(nodeA.timestamp_alloc - nodeB.timestamp_alloc) > 1000000) {
6400                links.push({ 
6401                    source: i, target: j, type: 'move', 
6402                    color: '#8b5cf6', strokeWidth: 2, dashArray: '10,5'
6403                });
6404            }
6405            // General relationship (same type prefix)
6406            else if (nodeA.type === nodeB.type || 
6407                     nodeA.name.startsWith(nodeB.name.substring(0, 3))) {
6408                links.push({ 
6409                    source: i, target: j, type: 'related', 
6410                    color: 'var(--border-light)', strokeWidth: 1, dashArray: 'none'
6411                });
6412            }
6413        }
6414    }
6415    
6416    const width = 1200;  // Larger virtual canvas
6417    const height = 800;
6418    const viewWidth = container.offsetWidth || 500;
6419    const viewHeight = 400;
6420    
6421    // Create SVG with pan/zoom capabilities and legend
6422    let html = `
6423        <div style="position: relative; width: 100%; height: ${viewHeight}px; overflow: hidden; border: 1px solid var(--border-light); border-radius: 8px;">
6424            <!-- Relationship Legend -->
6425            <div style="position: absolute; top: 10px; right: 10px; background: var(--bg-primary); padding: 12px; border-radius: 8px; font-size: 11px; z-index: 10; border: 1px solid var(--border-light); box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
6426                <div style="font-weight: bold; margin-bottom: 8px; color: var(--text-primary); font-size: 12px;">Variable Relationships</div>
6427                <div style="display: flex; align-items: center; margin-bottom: 4px;">
6428                    <svg width="20" height="3" style="margin-right: 8px;">
6429                        <line x1="0" y1="1.5" x2="20" y2="1.5" stroke='#06b6d4' stroke-width="2"/>
6430                    </svg>
6431                    <span style="color: var(--text-secondary);">Copy</span>
6432                </div>
6433                <div style="display: flex; align-items: center; margin-bottom: 4px;">
6434                    <svg width="20" height="3" style="margin-right: 8px;">
6435                        <line x1="0" y1="1.5" x2="20" y2="1.5" stroke='#f59e0b' stroke-width="3" stroke-dasharray="5,5"/>
6436                    </svg>
6437                    <span style="color: var(--text-secondary);">Clone</span>
6438                </div>
6439                <div style="display: flex; align-items: center; margin-bottom: 4px;">
6440                    <svg width="20" height="3" style="margin-right: 8px;">
6441                        <line x1="0" y1="1.5" x2="20" y2="1.5" stroke='#8b5cf6' stroke-width="2" stroke-dasharray="10,5"/>
6442                    </svg>
6443                    <span style="color: var(--text-secondary);">Move</span>
6444                </div>
6445                <div style="display: flex; align-items: center;">
6446                    <svg width="20" height="3" style="margin-right: 8px;">
6447                        <line x1="0" y1="1.5" x2="20" y2="1.5" stroke='#64748b' stroke-width="1"/>
6448                    </svg>
6449                    <span style="color: var(--text-secondary);">Related</span>
6450                </div>
6451            </div>
6452            
6453            <svg id="graph-svg" width="100%" height="100%" viewBox="0 0 ${viewWidth} ${viewHeight}" style="background: var(--bg-secondary); cursor: grab;">
6454                <defs>
6455                    <filter id="node-glow">
6456                        <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
6457                        <feMerge>
6458                            <feMergeNode in="coloredBlur"/>
6459                            <feMergeNode in="SourceGraphic"/>
6460                        </feMerge>
6461                    </filter>
6462                    <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="3"
6463                            markerWidth="6" markerHeight="6" orient="auto">
6464                        <path d="M0,0 L0,6 L9,3 z" fill="var(--border-light)" opacity="0.6"/>
6465                    </marker>
6466                </defs>
6467                <g id="graph-container" transform="translate(0,0) scale(1)">
6468                    <g id="links-group">`;
6469    
6470    // Draw enhanced links with relationship types
6471    links.forEach((link, linkIndex) => {
6472        const source = nodes[link.source];
6473        const target = nodes[link.target];
6474        const strokeDashArray = link.dashArray !== 'none' ? link.dashArray : '';
6475        
6476        html += `
6477            <line id="link-${linkIndex}" x1="${source.x}" y1="${source.y}" x2="${target.x}" y2="${target.y}" 
6478                  stroke="${link.color}" stroke-width="${link.strokeWidth}" opacity="0.8"
6479                  stroke-dasharray="${strokeDashArray}">
6480                <animate attributeName="opacity" values="0.8;1;0.8" dur="3s" repeatCount="indefinite"/>
6481            </line>`;
6482        
6483        // Add relationship label for special types
6484        const midX = (source.x + target.x) / 2;
6485        const midY = (source.y + target.y) / 2;
6486        if (link.type !== 'related') {
6487            html += `
6488            <text x="${midX}" y="${midY - 5}" text-anchor="middle" font-size="8" 
6489                  fill="${link.color}" font-weight="bold" opacity="0.9">
6490                ${link.type.toUpperCase()}
6491            </text>`;
6492        }
6493    });
6494    
6495    html += '</g><g id="nodes-group">';
6496    
6497    // Draw nodes
6498    nodes.forEach((node, nodeIndex) => {
6499        const radius = Math.max(12, Math.min(30, Math.log(node.size + 1) * 4));
6500        let color = '#6b7280'; // default
6501        
6502        if (node.type.includes('String')) color = '#fbbf24';
6503        else if (node.type.includes('Vec')) color = '#3b82f6';
6504        else if (node.type.includes('Box') || node.type.includes('Rc')) color = '#8b5cf6';
6505        else if (node.type.includes('HashMap')) color = '#10b981';
6506        else if (node.type.includes('Arc')) color = '#f59e0b';
6507        
6508        if (node.status === 'leaked') color = '#dc2626';
6509        else if (node.status === 'freed') color = '#9ca3af';
6510        
6511        html += `
6512            <circle 
6513                id="node-${nodeIndex}"
6514                cx="${node.x}" 
6515                cy="${node.y}" 
6516                r="${radius}" 
6517                fill="${color}" 
6518                stroke="white" 
6519                stroke-width="3" 
6520                filter="url(#node-glow)"
6521                style="cursor: grab;"
6522                class="graph-node"
6523                data-index="${nodeIndex}"
6524                data-name="${node.name}"
6525                data-type="${node.type}"
6526                data-size="${node.size}"
6527                data-status="${node.status}"
6528                data-ptr="${node.ptr}"
6529                data-alloc="${node.timestamp_alloc}"
6530                data-dealloc="${node.timestamp_dealloc || 'null'}"
6531            />
6532            <text 
6533                id="text-${nodeIndex}"
6534                x="${node.x}" 
6535                y="${node.y + radius + 20}" 
6536                text-anchor="middle" 
6537                font-size="12" 
6538                font-weight="600"
6539                fill="var(--text-primary)"
6540                style="pointer-events: none;"
6541            >${node.name.length > 12 ? node.name.substring(0, 10) + '...' : node.name}</text>
6542        `;
6543    });
6544    
6545    html += `
6546                </g>
6547            </g>
6548        </svg>
6549        
6550        <!-- Controls -->
6551        <div style="position: absolute; top: 10px; right: 10px; display: flex; gap: 8px;">
6552            <button id="zoom-in" style="background: var(--primary-blue); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 14px;">
6553                <i class="fa fa-plus"></i>
6554            </button>
6555            <button id="zoom-out" style="background: var(--primary-blue); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 14px;">
6556                <i class="fa fa-minus"></i>
6557            </button>
6558            <button id="reset-view" style="background: var(--primary-green); color: white; border: none; padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 14px;">
6559                <i class="fa fa-home"></i>
6560            </button>
6561        </div>
6562        
6563        <!-- Node detail panel -->
6564        <div id="node-detail-panel" style="
6565            position: absolute;
6566            background: var(--bg-primary);
6567            border: 1px solid var(--border-light);
6568            border-radius: 8px;
6569            padding: 12px;
6570            width: 280px;
6571            box-shadow: 0 10px 25px rgba(0,0,0,0.1);
6572            z-index: 1000;
6573            font-size: 0.875rem;
6574            display: none;
6575            backdrop-filter: blur(10px);
6576        ">
6577            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
6578                <h4 id="detail-title" style="margin: 0; font-size: 1rem; font-weight: 600;"></h4>
6579                <button onclick="hideNodeDetails()" style="background: none; border: none; font-size: 16px; cursor: pointer; color: var(--text-secondary);">ร—</button>
6580            </div>
6581            <div id="detail-content"></div>
6582        </div>
6583        
6584        <!-- Legend -->
6585        <div style="display: flex; gap: 12px; margin-top: 12px; font-size: 0.75rem; flex-wrap: wrap;">
6586            <div style="display: flex; align-items: center; gap: 4px;">
6587                <div style="width: 10px; height: 10px; background: #fbbf24; border-radius: 50%;"></div>
6588                <span>String</span>
6589            </div>
6590            <div style="display: flex; align-items: center; gap: 4px;">
6591                <div style="width: 10px; height: 10px; background: #3b82f6; border-radius: 50%;"></div>
6592                <span>Vec</span>
6593            </div>
6594            <div style="display: flex; align-items: center; gap: 4px;">
6595                <div style="width: 10px; height: 10px; background: #8b5cf6; border-radius: 50%;"></div>
6596                <span>Smart Ptr</span>
6597            </div>
6598            <div style="display: flex; align-items: center; gap: 4px;">
6599                <div style="width: 10px; height: 10px; background: #dc2626; border-radius: 50%;"></div>
6600                <span>Leaked</span>
6601            </div>
6602            <div style="display: flex; align-items: center; gap: 4px;">
6603                <div style="width: 10px; height: 10px; background: #9ca3af; border-radius: 50%;"></div>
6604                <span>Freed</span>
6605            </div>
6606        </div>
6607    </div>
6608    `;
6609    
6610    container.innerHTML = html;
6611    
6612    // Store nodes and links data for interaction
6613    window.graphNodes = nodes;
6614    window.graphLinks = links;
6615    window.graphTransform = { x: 0, y: 0, scale: 1 };
6616    
6617    // Add pan/zoom and drag functionality
6618    setTimeout(() => {
6619        setupGraphInteractions();
6620        setupPanZoom();
6621    }, 100);
6622}
6623
6624// Graph interaction functions
6625function setupGraphInteractions() {
6626    const svg = document.getElementById('graph-svg');
6627    const nodeElements = document.querySelectorAll('.graph-node');
6628    
6629    let draggedNode = null;
6630    let isDragging = false;
6631    let startX, startY;
6632    
6633    nodeElements.forEach(node => {
6634        // Mouse events for drag
6635        node.addEventListener('mousedown', function(e) {
6636            e.preventDefault();
6637            draggedNode = this;
6638            isDragging = false;
6639            startX = e.clientX;
6640            startY = e.clientY;
6641            this.style.cursor = 'grabbing';
6642            svg.style.cursor = 'grabbing';
6643        });
6644        
6645        // Click event for details
6646        node.addEventListener('click', function(e) {
6647            if (!isDragging) {
6648                showNodeDetails(this);
6649            }
6650        });
6651        
6652        // Hover effects
6653        node.addEventListener('mouseover', function() {
6654            if (!draggedNode) {
6655                const currentRadius = parseInt(this.getAttribute('r'));
6656                this.setAttribute('r', Math.round(currentRadius * 1.2));
6657            }
6658        });
6659        
6660        node.addEventListener('mouseout', function() {
6661            if (!draggedNode) {
6662                const currentRadius = parseInt(this.getAttribute('r'));
6663                this.setAttribute('r', Math.round(currentRadius / 1.2));
6664            }
6665        });
6666    });
6667    
6668    // Global mouse events for dragging
6669    document.addEventListener('mousemove', function(e) {
6670        if (draggedNode) {
6671            e.preventDefault();
6672            const deltaX = e.clientX - startX;
6673            const deltaY = e.clientY - startY;
6674            
6675            if (Math.abs(deltaX) > 3 || Math.abs(deltaY) > 3) {
6676                isDragging = true;
6677            }
6678            
6679            if (isDragging) {
6680                const rect = svg.getBoundingClientRect();
6681                const svgX = Math.max(20, Math.min(rect.width - 20, e.clientX - rect.left));
6682                const svgY = Math.max(20, Math.min(rect.height - 20, e.clientY - rect.top));
6683                
6684                // Update node position
6685                draggedNode.setAttribute('cx', svgX);
6686                draggedNode.setAttribute('cy', svgY);
6687                
6688                // Update text position
6689                const nodeIndex = draggedNode.getAttribute('data-index');
6690                const textElement = document.getElementById(`text-${nodeIndex}`);
6691                if (textElement) {
6692                    textElement.setAttribute('x', svgX);
6693                    textElement.setAttribute('y', svgY + parseInt(draggedNode.getAttribute('r')) + 15);
6694                }
6695                
6696                // Update connected links
6697                updateConnectedLinks(parseInt(nodeIndex), svgX, svgY);
6698                
6699                // Update stored node position
6700                if (window.graphNodes && window.graphNodes[nodeIndex]) {
6701                    window.graphNodes[nodeIndex].x = svgX;
6702                    window.graphNodes[nodeIndex].y = svgY;
6703                }
6704            }
6705        }
6706    });
6707    
6708    document.addEventListener('mouseup', function(e) {
6709        if (draggedNode) {
6710            draggedNode.style.cursor = 'grab';
6711            svg.style.cursor = 'grab';
6712            
6713            // Reset hover effect
6714            const originalRadius = parseInt(draggedNode.getAttribute('r'));
6715            draggedNode.setAttribute('r', originalRadius);
6716            
6717            draggedNode = null;
6718            setTimeout(() => { isDragging = false; }, 100);
6719        }
6720    });
6721}
6722
6723function updateConnectedLinks(nodeIndex, newX, newY) {
6724    if (!window.graphLinks) return;
6725    
6726    window.graphLinks.forEach((link, linkIndex) => {
6727        const linkElement = document.getElementById(`link-${linkIndex}`);
6728        if (!linkElement) return;
6729        
6730        if (link.source === nodeIndex) {
6731            linkElement.setAttribute('x1', newX);
6732            linkElement.setAttribute('y1', newY);
6733        }
6734        if (link.target === nodeIndex) {
6735            linkElement.setAttribute('x2', newX);
6736            linkElement.setAttribute('y2', newY);
6737        }
6738    });
6739}
6740
6741function showNodeDetails(nodeElement) {
6742    const panel = document.getElementById('node-detail-panel');
6743    const title = document.getElementById('detail-title');
6744    const content = document.getElementById('detail-content');
6745    
6746    if (!panel || !title || !content) return;
6747    
6748    const name = nodeElement.getAttribute('data-name');
6749    const type = nodeElement.getAttribute('data-type');
6750    const size = parseInt(nodeElement.getAttribute('data-size'));
6751    const status = nodeElement.getAttribute('data-status');
6752    const ptr = nodeElement.getAttribute('data-ptr');
6753    const alloc = nodeElement.getAttribute('data-alloc');
6754    const dealloc = nodeElement.getAttribute('data-dealloc');
6755    
6756    title.textContent = name;
6757    
6758    const lifetime = dealloc !== 'null' ? parseInt(dealloc) - parseInt(alloc) : 'Active';
6759    
6760    content.innerHTML = `
6761        <div style="margin-bottom: 8px;">
6762            <strong>Type:</strong> ${formatTypeName(type)}
6763        </div>
6764        <div style="margin-bottom: 8px;">
6765            <strong>Size:</strong> ${formatBytes(size)}
6766        </div>
6767        <div style="margin-bottom: 8px;">
6768            <strong>Status:</strong> <span style="color: ${status === 'leaked' ? '#dc2626' : status === 'freed' ? '#6b7280' : '#059669'};">${status.charAt(0).toUpperCase() + status.slice(1)}</span>
6769        </div>
6770        <div style="margin-bottom: 8px;">
6771            <strong>Pointer:</strong> <code style="font-size: 0.8rem; background: var(--bg-secondary); padding: 2px 4px; border-radius: 3px;">${ptr}</code>
6772        </div>
6773        <div style="margin-bottom: 8px;">
6774            <strong>Allocated:</strong> ${alloc}ms
6775        </div>
6776        <div style="margin-bottom: 8px;">
6777            <strong>Lifetime:</strong> ${typeof lifetime === 'number' ? lifetime + 'ms' : lifetime}
6778        </div>
6779    `;
6780    
6781    // Position panel near the node
6782    const rect = nodeElement.getBoundingClientRect();
6783    const containerRect = nodeElement.closest('#graph').getBoundingClientRect();
6784    
6785    panel.style.left = Math.min(rect.left - containerRect.left + 30, containerRect.width - 300) + 'px';
6786    panel.style.top = Math.max(rect.top - containerRect.top - 50, 10) + 'px';
6787    panel.style.display = 'block';
6788}
6789
6790function hideNodeDetails() {
6791    const panel = document.getElementById('node-detail-panel');
6792    if (panel) {
6793        panel.style.display = 'none';
6794    }
6795}
6796
6797// Pan and zoom functionality for the graph
6798function setupPanZoom() {
6799    const svg = document.getElementById('graph-svg');
6800    const container = document.getElementById('graph-container');
6801    const zoomInBtn = document.getElementById('zoom-in');
6802    const zoomOutBtn = document.getElementById('zoom-out');
6803    const resetBtn = document.getElementById('reset-view');
6804    
6805    if (!svg || !container) return;
6806    
6807    let isPanning = false;
6808    let startX, startY;
6809    let transform = window.graphTransform;
6810    
6811    // Zoom functions
6812    function updateTransform() {
6813        container.setAttribute('transform', `translate(${transform.x},${transform.y}) scale(${transform.scale})`);
6814    }
6815    
6816    function zoom(factor, centerX = 0, centerY = 0) {
6817        const newScale = Math.max(0.1, Math.min(3, transform.scale * factor));
6818        
6819        // Zoom towards center point
6820        const dx = centerX - transform.x;
6821        const dy = centerY - transform.y;
6822        
6823        transform.x = centerX - dx * (newScale / transform.scale);
6824        transform.y = centerY - dy * (newScale / transform.scale);
6825        transform.scale = newScale;
6826        
6827        updateTransform();
6828    }
6829    
6830    // Button controls
6831    if (zoomInBtn) {
6832        zoomInBtn.addEventListener('click', () => {
6833            const rect = svg.getBoundingClientRect();
6834            zoom(1.2, rect.width / 2, rect.height / 2);
6835        });
6836    }
6837    
6838    if (zoomOutBtn) {
6839        zoomOutBtn.addEventListener('click', () => {
6840            const rect = svg.getBoundingClientRect();
6841            zoom(0.8, rect.width / 2, rect.height / 2);
6842        });
6843    }
6844    
6845    if (resetBtn) {
6846        resetBtn.addEventListener('click', () => {
6847            transform.x = 0;
6848            transform.y = 0;
6849            transform.scale = 1;
6850            updateTransform();
6851        });
6852    }
6853    
6854    // Mouse wheel zoom
6855    svg.addEventListener('wheel', function(e) {
6856        e.preventDefault();
6857        const rect = svg.getBoundingClientRect();
6858        const mouseX = e.clientX - rect.left;
6859        const mouseY = e.clientY - rect.top;
6860        
6861        const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
6862        zoom(zoomFactor, mouseX, mouseY);
6863    });
6864    
6865    // Pan functionality
6866    svg.addEventListener('mousedown', function(e) {
6867        if (e.target === svg || e.target === container) {
6868            isPanning = true;
6869            startX = e.clientX - transform.x;
6870            startY = e.clientY - transform.y;
6871            svg.style.cursor = 'grabbing';
6872        }
6873    });
6874    
6875    document.addEventListener('mousemove', function(e) {
6876        if (isPanning) {
6877            e.preventDefault();
6878            transform.x = e.clientX - startX;
6879            transform.y = e.clientY - startY;
6880            updateTransform();
6881        }
6882    });
6883    
6884    document.addEventListener('mouseup', function() {
6885        if (isPanning) {
6886            isPanning = false;
6887            svg.style.cursor = 'grab';
6888        }
6889    });
6890}
6891
6892// Lifecycle toggle functionality
6893function setupLifecycleToggle() {
6894    // Hard reset any previous click bindings by cloning the button
6895    const oldBtn = document.getElementById('toggle-lifecycle');
6896    if (oldBtn) {
6897        const cloned = oldBtn.cloneNode(true);
6898        oldBtn.parentNode.replaceChild(cloned, oldBtn);
6899    }
6900
6901    const toggleBtn = document.getElementById('toggle-lifecycle');
6902    if (!toggleBtn) return;
6903    
6904    const lifeContainer = document.getElementById('lifetimeVisualization');
6905    
6906    let isExpanded = false;
6907    
6908    toggleBtn.addEventListener('click', function() {
6909        const container = document.getElementById('lifetimeVisualization');
6910        if (!container) return;
6911        
6912        const data = window.analysisData || {};
6913        const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
6914        
6915        if (allocs.length === 0) {
6916            container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No lifecycle data available</div>';
6917            return;
6918        }
6919        
6920        const icon = toggleBtn.querySelector('i');
6921        const text = toggleBtn.querySelector('span');
6922        
6923        if (!isExpanded) {
6924            renderFullLifecycleTimeline(allocs);
6925            icon.className = 'fa fa-chevron-up';
6926            text.textContent = 'Show Less';
6927            isExpanded = true;
6928        } else {
6929            renderLimitedLifecycleTimeline(allocs);
6930            icon.className = 'fa fa-chevron-down';
6931            text.textContent = 'Show All';
6932            isExpanded = false;
6933        }
6934        // Ensure the container scrolls to top after toggle for visual confirmation
6935        if (lifeContainer) { lifeContainer.scrollTop = 0; }
6936    });
6937}
6938
6939function renderLimitedLifecycleTimeline(allocs) {
6940    const container = document.getElementById('lifetimeVisualization');
6941    if (!container) return;
6942    
6943    // Create timeline visualization (limited to 20)
6944    const maxTime = Math.max(...allocs.map(a => a.timestamp_dealloc || a.timestamp_alloc || 0));
6945    const minTime = Math.min(...allocs.map(a => a.timestamp_alloc || 0));
6946    const timeRange = maxTime - minTime || 1;
6947    
6948    let html = '<div style="padding: 16px; max-height: 300px; overflow-y: auto;">';
6949    
6950    allocs.slice(0, 20).forEach((alloc, index) => {
6951        const startTime = alloc.timestamp_alloc || 0;
6952        const endTime = alloc.timestamp_dealloc || maxTime;
6953        const startPercent = ((startTime - minTime) / timeRange) * 100;
6954        const widthPercent = ((endTime - startTime) / timeRange) * 100;
6955        const isActive = !alloc.timestamp_dealloc;
6956        
6957        html += `
6958            <div style="margin-bottom: 8px;">
6959                <div style="display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 0.8rem;">
6960                    <span style="font-weight: 600;">${alloc.var_name || `var_${index}`}</span>
6961                    <span style="color: var(--text-secondary);">${formatBytes(alloc.size || 0)}</span>
6962                </div>
6963                <div style="position: relative; background: var(--bg-secondary); height: 8px; border-radius: 4px;">
6964                    <div style="
6965                        position: absolute;
6966                        left: ${startPercent}%;
6967                        width: ${widthPercent}%;
6968                        height: 100%;
6969                        background: ${isActive ? 'linear-gradient(to right, #059669, #34d399)' : 'linear-gradient(to right, #2563eb, #60a5fa)'};
6970                        border-radius: 4px;
6971                        ${isActive ? 'animation: pulse 2s infinite;' : ''}
6972                    " title="Lifetime: ${endTime - startTime}ms"></div>
6973                </div>
6974            </div>
6975        `;
6976    });
6977    
6978    html += '</div>';
6979    
6980    // Add CSS for pulse animation
6981    html += `
6982        <style>
6983            @keyframes pulse {
6984                0%, 100% { opacity: 1; }
6985                50% { opacity: 0.7; }
6986            }
6987        </style>
6988    `;
6989    
6990    container.innerHTML = html;
6991}
6992
6993function renderFullLifecycleTimeline(allocs) {
6994    const container = document.getElementById('lifetimeVisualization');
6995    if (!container) return;
6996    
6997    // Create full timeline visualization
6998    const maxTime = Math.max(...allocs.map(a => a.timestamp_dealloc || a.timestamp_alloc || 0));
6999    const minTime = Math.min(...allocs.map(a => a.timestamp_alloc || 0));
7000    const timeRange = maxTime - minTime || 1;
7001    
7002    let html = '<div style="padding: 16px; max-height: 600px; overflow-y: auto;">';
7003    
7004    // Add timeline header
7005    html += `
7006        <div style="margin-bottom: 16px; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
7007            <div style="font-weight: 600; margin-bottom: 8px;">Full Lifecycle Timeline</div>
7008            <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; font-size: 0.8rem;">
7009                <div>
7010                    <div style="color: var(--text-secondary);">Total Variables</div>
7011                    <div style="font-weight: 600;">${allocs.length}</div>
7012                </div>
7013                <div>
7014                    <div style="color: var(--text-secondary);">Active</div>
7015                    <div style="font-weight: 600; color: #059669;">${allocs.filter(a => !a.timestamp_dealloc).length}</div>
7016                </div>
7017                <div>
7018                    <div style="color: var(--text-secondary);">Freed</div>
7019                    <div style="font-weight: 600; color: #2563eb;">${allocs.filter(a => a.timestamp_dealloc && !a.is_leaked).length}</div>
7020                </div>
7021                <div>
7022                    <div style="color: var(--text-secondary);">Leaked</div>
7023                    <div style="font-weight: 600; color: #dc2626;">${allocs.filter(a => a.is_leaked).length}</div>
7024                </div>
7025            </div>
7026        </div>
7027    `;
7028    
7029    allocs.forEach((alloc, index) => {
7030        const startTime = alloc.timestamp_alloc || 0;
7031        const endTime = alloc.timestamp_dealloc || maxTime;
7032        const startPercent = ((startTime - minTime) / timeRange) * 100;
7033        const widthPercent = ((endTime - startTime) / timeRange) * 100;
7034        const isActive = !alloc.timestamp_dealloc;
7035        const isLeaked = alloc.is_leaked;
7036        
7037        let barColor = 'linear-gradient(to right, #2563eb, #60a5fa)'; // freed
7038        if (isActive) barColor = 'linear-gradient(to right, #059669, #34d399)'; // active
7039        if (isLeaked) barColor = 'linear-gradient(to right, #dc2626, #f87171)'; // leaked
7040        
7041        html += `
7042            <div style="margin-bottom: 6px;">
7043                <div style="display: flex; justify-content: space-between; margin-bottom: 3px; font-size: 0.75rem;">
7044                    <span style="font-weight: 600;">${alloc.var_name || `var_${index}`}</span>
7045                    <div style="display: flex; gap: 8px;">
7046                        <span style="color: var(--text-secondary);">${formatTypeName(alloc.type_name || 'Unknown')}</span>
7047                        <span style="color: var(--text-secondary);">${formatBytes(alloc.size || 0)}</span>
7048                    </div>
7049                </div>
7050                <div style="position: relative; background: var(--bg-secondary); height: 6px; border-radius: 3px;">
7051                    <div style="
7052                        position: absolute;
7053                        left: ${startPercent}%;
7054                        width: ${widthPercent}%;
7055                        height: 100%;
7056                        background: ${barColor};
7057                        border-radius: 3px;
7058                        ${isActive ? 'animation: pulse 2s infinite;' : ''}
7059                    " title="Lifetime: ${endTime - startTime}ms | Status: ${isLeaked ? 'Leaked' : isActive ? 'Active' : 'Freed'}"></div>
7060                </div>
7061            </div>
7062        `;
7063    });
7064    
7065    html += '</div>';
7066    
7067    // Add CSS for pulse animation
7068    html += `
7069        <style>
7070            @keyframes pulse {
7071                0%, 100% { opacity: 1; }
7072                50% { opacity: 0.7; }
7073            }
7074        </style>
7075    `;
7076    
7077    container.innerHTML = html;
7078}
7079
7080function setupLifecycleVisualization() {
7081    const container = document.getElementById('lifetimeVisualization');
7082    if (!container) return;
7083    
7084    const data = window.analysisData || {};
7085    const allocs = (data.memory_analysis && data.memory_analysis.allocations) || [];
7086    
7087    if (allocs.length === 0) {
7088        container.innerHTML = '<div style="padding: 20px; text-align: center; color: var(--text-secondary);">No lifecycle data available</div>';
7089        return;
7090    }
7091    
7092    // Create timeline visualization
7093    const maxTime = Math.max(...allocs.map(a => a.timestamp_dealloc || a.timestamp_alloc || 0));
7094    const minTime = Math.min(...allocs.map(a => a.timestamp_alloc || 0));
7095    const timeRange = maxTime - minTime || 1;
7096    
7097    let html = '<div style="padding: 16px; max-height: 300px; overflow-y: auto;">';
7098    
7099    allocs.slice(0, 20).forEach((alloc, index) => {
7100        const startTime = alloc.timestamp_alloc || 0;
7101        const endTime = alloc.timestamp_dealloc || maxTime;
7102        const startPercent = ((startTime - minTime) / timeRange) * 100;
7103        const widthPercent = ((endTime - startTime) / timeRange) * 100;
7104        const isActive = !alloc.timestamp_dealloc;
7105        
7106        html += `
7107            <div style="margin-bottom: 8px;">
7108                <div style="display: flex; justify-content: space-between; margin-bottom: 4px; font-size: 0.8rem;">
7109                    <span style="font-weight: 600;">${alloc.var_name || `var_${index}`}</span>
7110                    <span style="color: var(--text-secondary);">${formatBytes(alloc.size || 0)}</span>
7111                </div>
7112                <div style="position: relative; background: var(--bg-secondary); height: 8px; border-radius: 4px;">
7113                    <div style="
7114                        position: absolute;
7115                        left: ${startPercent}%;
7116                        width: ${widthPercent}%;
7117                        height: 100%;
7118                        background: ${isActive ? 'linear-gradient(to right, #059669, #34d399)' : 'linear-gradient(to right, #2563eb, #60a5fa)'};
7119                        border-radius: 4px;
7120                        ${isActive ? 'animation: pulse 2s infinite;' : ''}
7121                    " title="Lifetime: ${endTime - startTime}ms"></div>
7122                </div>
7123            </div>
7124        `;
7125    });
7126    
7127    html += '</div>';
7128    
7129    // Add CSS for pulse animation
7130    html += `
7131        <style>
7132            @keyframes pulse {
7133                0%, 100% { opacity: 1; }
7134                50% { opacity: 0.7; }
7135            }
7136        </style>
7137    `;
7138    
7139    container.innerHTML = html;
7140}
7141
7142function initFFIVisualization() {
7143    // Additional FFI initialization if needed
7144    renderFFI();
7145}
7146
7147// Complete JSON Data Explorer
7148function initCompleteJSONExplorer() {
7149    const container = document.getElementById('jsonDataExplorer');
7150    const expandBtn = document.getElementById('expand-all-json');
7151    const collapseBtn = document.getElementById('collapse-all-json');
7152    
7153    if (!container) return;
7154    
7155    const data = window.analysisData || {};
7156    
7157    if (Object.keys(data).length === 0) {
7158        container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); padding: 40px;">No JSON data available</div>';
7159        return;
7160    }
7161    
7162    // Generate comprehensive JSON explorer
7163    let html = '<div class="json-explorer">';
7164    
7165    // Add data summary
7166    html += `
7167        <div style="background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: 8px; padding: 16px; margin-bottom: 16px;">
7168            <h3 style="margin: 0 0 12px 0; color: var(--text-primary);">Data Summary</h3>
7169            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
7170                ${Object.keys(data).map(key => {
7171                    const value = data[key];
7172                    let itemCount = 'N/A';
7173                    let dataType = typeof value;
7174                    
7175                    if (Array.isArray(value)) {
7176                        itemCount = value.length + ' items';
7177                        dataType = 'Array';
7178                    } else if (value && typeof value === 'object') {
7179                        itemCount = Object.keys(value).length + ' properties';
7180                        dataType = 'Object';
7181                    }
7182                    
7183                    return `
7184                        <div style="background: var(--bg-secondary); padding: 12px; border-radius: 6px;">
7185                            <div style="font-weight: 600; color: var(--text-primary);">${key}</div>
7186                            <div style="font-size: 0.8rem; color: var(--text-secondary);">${dataType}</div>
7187                            <div style="font-size: 0.8rem; color: var(--text-secondary);">${itemCount}</div>
7188                        </div>
7189                    `;
7190                }).join('')}
7191            </div>
7192        </div>
7193    `;
7194    
7195    // Generate expandable JSON tree for each top-level key
7196    Object.keys(data).forEach((key, index) => {
7197        const value = data[key];
7198        html += createJSONSection(key, value, index);
7199    });
7200    
7201    html += '</div>';
7202    container.innerHTML = html;
7203    
7204    // Setup expand/collapse functionality
7205    if (expandBtn) {
7206        expandBtn.addEventListener('click', () => {
7207            const details = container.querySelectorAll('details');
7208            details.forEach(detail => detail.open = true);
7209        });
7210    }
7211    
7212    if (collapseBtn) {
7213        collapseBtn.addEventListener('click', () => {
7214            const details = container.querySelectorAll('details');
7215            details.forEach(detail => detail.open = false);
7216        });
7217    }
7218}
7219
7220function createJSONSection(key, value, index) {
7221    const isOpen = index < 3; // Open first 3 sections by default
7222    
7223    let html = `
7224        <details class="json-section" ${isOpen ? 'open' : ''} style="
7225            border: 1px solid var(--border-light); 
7226            border-radius: 8px; 
7227            margin-bottom: 12px; 
7228            background: var(--bg-primary);
7229        ">
7230            <summary style="
7231                cursor: pointer; 
7232                padding: 12px 16px; 
7233                font-weight: 600; 
7234                color: var(--text-primary); 
7235                background: var(--bg-secondary);
7236                border-radius: 8px 8px 0 0;
7237                user-select: none;
7238            ">
7239                <i class="fa fa-chevron-right" style="margin-right: 8px; transition: transform 0.2s;"></i>
7240                ${key}
7241                <span style="font-weight: normal; color: var(--text-secondary); margin-left: 8px;">
7242                    ${getDataTypeInfo(value)}
7243                </span>
7244            </summary>
7245            <div style="padding: 16px;">
7246    `;
7247    
7248    if (Array.isArray(value)) {
7249        html += createArrayView(value, key);
7250    } else if (value && typeof value === 'object') {
7251        html += createObjectView(value, key);
7252    } else {
7253        html += `<pre style="margin: 0; color: var(--text-primary); font-size: 0.9rem;">${JSON.stringify(value, null, 2)}</pre>`;
7254    }
7255    
7256    html += '</div></details>';
7257    return html;
7258}
7259
7260function createArrayView(array, parentKey) {
7261    if (array.length === 0) {
7262        return '<div style="color: var(--text-secondary); font-style: italic;">Empty array</div>';
7263    }
7264    
7265    let html = `
7266        <div style="margin-bottom: 12px;">
7267            <strong>Array with ${array.length} items</strong>
7268            ${array.length > 10 ? `<span style="color: var(--text-secondary);"> (showing first 10)</span>` : ''}
7269        </div>
7270    `;
7271    
7272    // Show first 10 items
7273    const itemsToShow = array.slice(0, 10);
7274    
7275    itemsToShow.forEach((item, index) => {
7276        html += `
7277            <details style="margin-bottom: 8px; border: 1px solid var(--border-light); border-radius: 6px;">
7278                <summary style="cursor: pointer; padding: 8px 12px; background: var(--bg-secondary); font-size: 0.9rem;">
7279                    [${index}] ${getDataTypeInfo(item)}
7280                </summary>
7281                <div style="padding: 12px;">
7282                    <pre style="margin: 0; font-size: 0.8rem; color: var(--text-primary); max-height: 300px; overflow: auto;">${JSON.stringify(item, null, 2)}</pre>
7283                </div>
7284            </details>
7285        `;
7286    });
7287    
7288    if (array.length > 10) {
7289        html += `<div style="color: var(--text-secondary); font-style: italic; margin-top: 12px;">... and ${array.length - 10} more items</div>`;
7290    }
7291    
7292    return html;
7293}
7294
7295function createObjectView(obj, parentKey) {
7296    const keys = Object.keys(obj);
7297    
7298    if (keys.length === 0) {
7299        return '<div style="color: var(--text-secondary); font-style: italic;">Empty object</div>';
7300    }
7301    
7302    let html = `
7303        <div style="margin-bottom: 12px;">
7304            <strong>Object with ${keys.length} properties</strong>
7305        </div>
7306    `;
7307    
7308    keys.forEach(key => {
7309        const value = obj[key];
7310        html += `
7311            <details style="margin-bottom: 8px; border: 1px solid var(--border-light); border-radius: 6px;">
7312                <summary style="cursor: pointer; padding: 8px 12px; background: var(--bg-secondary); font-size: 0.9rem;">
7313                    <code style="background: var(--bg-primary); padding: 2px 6px; border-radius: 3px;">${key}</code>
7314                    <span style="margin-left: 8px; color: var(--text-secondary);">${getDataTypeInfo(value)}</span>
7315                </summary>
7316                <div style="padding: 12px;">
7317                    <pre style="margin: 0; font-size: 0.8rem; color: var(--text-primary); max-height: 300px; overflow: auto;">${JSON.stringify(value, null, 2)}</pre>
7318                </div>
7319            </details>
7320        `;
7321    });
7322    
7323    return html;
7324}
7325
7326function getDataTypeInfo(value) {
7327    if (value === null) return 'null';
7328    if (value === undefined) return 'undefined';
7329    if (Array.isArray(value)) return `Array[${value.length}]`;
7330    if (typeof value === 'object') return `Object{${Object.keys(value).length}}`;
7331    if (typeof value === 'string') return `String(${value.length})`;
7332    if (typeof value === 'number') return `Number(${value})`;
7333    if (typeof value === 'boolean') return `Boolean(${value})`;
7334    return typeof value;
7335}
7336
7337// Enhanced FFI Visualization with rich lifecycle data
7338function initEnhancedFFIVisualization() {
7339    const container = document.getElementById('ffiVisualization');
7340    if (!container) return;
7341
7342    const data = window.analysisData || {};
7343    const allocs = data.unsafe_ffi?.allocations || data.memory_analysis?.allocations || data.allocations || [];
7344    
7345    if (allocs.length === 0) {
7346        container.innerHTML = `<div style="background: var(--bg-secondary); border-radius:8px; padding:16px; text-align:center; color: var(--text-secondary);"><i class="fa fa-exclamation-triangle" style="font-size:24px; margin-bottom:8px; color: var(--primary-red);"></i><div>Critical: No FFI allocation data found!</div></div>`;
7347        return;
7348    }
7349
7350    // Rich data analysis
7351    const ffiTracked = allocs.filter(a => a.ffi_tracked).length;
7352    const withViolations = allocs.filter(a => a.safety_violations && a.safety_violations.length > 0).length;
7353    const withClones = allocs.filter(a => a.clone_info?.clone_count > 0).length;
7354    const leaked = allocs.filter(a => a.is_leaked).length;
7355    const totalBorrows = allocs.reduce((sum, a) => sum + (a.borrow_info?.immutable_borrows || 0) + (a.borrow_info?.mutable_borrows || 0), 0);
7356    const totalMemory = allocs.reduce((sum, a) => sum + (a.size || 0), 0);
7357    const avgLifetime = allocs.filter(a => a.lifetime_ms).reduce((sum, a) => sum + a.lifetime_ms, 0) / allocs.filter(a => a.lifetime_ms).length || 0;
7358
7359    // Get time range for lifecycle visualization
7360    const timestamps = allocs.map(a => a.timestamp_alloc).filter(t => t).sort((a, b) => a - b);
7361    const minTime = timestamps[0] || 0;
7362    const maxTime = timestamps[timestamps.length - 1] || minTime;
7363    const timeRange = maxTime - minTime || 1;
7364
7365    // Content that will be contained within the section's background
7366    container.innerHTML = `
7367        <!-- KPI Cards -->
7368        <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 20px;">
7369            <div style="background: var(--bg-primary); padding: 14px; border-radius: 8px; text-align: center; border-left: 4px solid var(--primary-blue);">
7370                <div style="font-size: 1.6rem; font-weight: 700; color: var(--primary-blue);">${allocs.length}</div>
7371                <div style="font-size: 0.75rem; color: var(--text-secondary);">FFI Allocations</div>
7372            </div>
7373            <div style="background: var(--bg-primary); padding: 14px; border-radius: 8px; text-align: center; border-left: 4px solid var(--primary-green);">
7374                <div style="font-size: 1.6rem; font-weight: 700; color: var(--primary-green);">${totalBorrows}</div>
7375                <div style="font-size: 0.75rem; color: var(--text-secondary);">Total Borrows</div>
7376            </div>
7377            <div style="background: var(--bg-primary); padding: 14px; border-radius: 8px; text-align: center; border-left: 4px solid var(--primary-orange);">
7378                <div style="font-size: 1.6rem; font-weight: 700; color: var(--primary-orange);">${withClones}</div>
7379                <div style="font-size: 0.75rem; color: var(--text-secondary);">With Clones</div>
7380            </div>
7381            <div style="background: var(--bg-primary); padding: 14px; border-radius: 8px; text-align: center; border-left: 4px solid ${leaked > 0 ? 'var(--primary-red)' : 'var(--primary-green)'};">
7382                <div style="font-size: 1.6rem; font-weight: 700; color: ${leaked > 0 ? 'var(--primary-red)' : 'var(--primary-green)'};">${leaked}</div>
7383                <div style="font-size: 0.75rem; color: var(--text-secondary);">Memory Leaks</div>
7384            </div>
7385            <div style="background: var(--bg-primary); padding: 14px; border-radius: 8px; text-align: center; border-left: 4px solid var(--primary-red);">
7386                <div style="font-size: 1.6rem; font-weight: 700; color: ${withViolations > 0 ? 'var(--primary-red)' : 'var(--primary-green)'};">${withViolations}</div>
7387                <div style="font-size: 0.75rem; color: var(--text-secondary);">Safety Violations</div>
7388            </div>
7389        </div>
7390        
7391        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;">
7392            <div style="background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
7393                <h3 style="margin: 0 0 12px 0; color: var(--text-primary); display: flex; align-items: center;"><i class="fa fa-clock-o" style="margin-right: 8px;"></i>Lifecycle Metrics</h3>
7394                <div style="margin-bottom: 10px;">
7395                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Avg Lifetime:</span>
7396                    <span style="color: var(--text-primary); font-weight: 600; margin-left: 8px;">${avgLifetime.toFixed(2)}ms</span>
7397                </div>
7398                <div style="margin-bottom: 10px;">
7399                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Total Memory:</span>
7400                    <span style="color: var(--text-primary); font-weight: 600; margin-left: 8px;">${formatBytes(totalMemory)}</span>
7401                </div>
7402                <div style="margin-bottom: 10px;">
7403                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Time Span:</span>
7404                    <span style="color: var(--text-primary); font-weight: 600; margin-left: 8px;">${(timeRange / 1e6).toFixed(2)}ms</span>
7405                </div>
7406            </div>
7407            
7408            <div style="background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
7409                <h3 style="margin: 0 0 12px 0; color: var(--text-primary); display: flex; align-items: center;"><i class="fa fa-share-alt" style="margin-right: 8px;"></i>Borrow & Clone Activity</h3>
7410                <div style="margin-bottom: 8px;">
7411                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Immutable Borrows:</span>
7412                    <span style="color: var(--primary-blue); font-weight: 600; margin-left: 8px;">${allocs.reduce((s, a) => s + (a.borrow_info?.immutable_borrows || 0), 0)}</span>
7413                </div>
7414                <div style="margin-bottom: 8px;">
7415                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Mutable Borrows:</span>
7416                    <span style="color: var(--primary-orange); font-weight: 600; margin-left: 8px;">${allocs.reduce((s, a) => s + (a.borrow_info?.mutable_borrows || 0), 0)}</span>
7417                </div>
7418                <div>
7419                    <span style="color: var(--text-secondary); font-size: 0.9rem;">Clone Operations:</span>
7420                    <span style="color: var(--primary-green); font-weight: 600; margin-left: 8px;">${allocs.reduce((s, a) => s + (a.clone_info?.clone_count || 0), 0)}</span>
7421                </div>
7422            </div>
7423        </div>
7424
7425        <!-- FFI Data Flow Visualization (data stream) -->
7426        <div style="margin-top: 20px; background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
7427            <h3 style="margin: 0 0 12px 0; color: var(--text-primary); display: flex; align-items: center;">
7428                <i class="fa fa-exchange" style="margin-right: 8px;"></i>FFI Data Flow
7429                <button id="ffi-flow-toggle" style="margin-left: auto; background: var(--primary-green); color: white; border: none; padding: 4px 8px; border-radius: 4px; font-size: 11px; cursor: pointer;">
7430                    <i class="fa fa-play"></i> Animate
7431                </button>
7432            </h3>
7433            <div id="ffi-flow-container" style="height: 200px; position: relative; border: 1px solid var(--border-light); border-radius: 6px; overflow: hidden; background: linear-gradient(135deg, #1e293b 0%, #0f172a 50%, #1e293b 100%);">
7434                ${createFFIDataFlow(allocs)}
7435            </div>
7436            <div style="margin-top: 8px; font-size: 11px; color: var(--text-secondary); text-align: center;">
7437                ๐Ÿฆ€ Rust โ†” C Data Flow โ€ข ${ffiTracked} FFI-tracked allocations โ€ข Click nodes for details
7438            </div>
7439        </div>
7440
7441        <!-- Interactive Allocation Analysis (Two Column Layout) -->
7442        <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 20px;">
7443            <!-- Timeline Column -->
7444            <div style="background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
7445                <h3 style="margin: 0 0 12px 0; color: var(--text-primary); display: flex; align-items: center;">
7446                    <i class="fa fa-clock-o" style="margin-right: 8px;"></i>Allocation Timeline
7447                    <button id="ffi-timeline-toggle" style="margin-left: auto; background: var(--primary-blue); color: white; border: none; padding: 4px 8px; border-radius: 4px; font-size: 11px; cursor: pointer;">
7448                        <i class="fa fa-expand"></i> Details
7449                    </button>
7450                </h3>
7451                <div id="ffi-timeline-container" style="height: 160px; position: relative; border: 1px solid var(--border-light); border-radius: 6px; overflow: hidden; background: linear-gradient(45deg, var(--bg-primary) 0%, var(--bg-secondary) 100%);">
7452                    ${createAllocationTimeline(allocs, minTime, timeRange)}
7453                </div>
7454                <div style="margin-top: 8px; font-size: 11px; color: var(--text-secondary); text-align: center;">
7455                    Timeline spans ${(timeRange / 1e6).toFixed(1)}ms โ€ข Click dots for details
7456                </div>
7457            </div>
7458            
7459            <!-- Details Column -->
7460            <div style="background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
7461                <h3 style="margin: 0 0 12px 0; color: var(--text-primary); display: flex; align-items: center;">
7462                    <i class="fa fa-list" style="margin-right: 8px;"></i>Allocation Details
7463                    <select id="ffi-filter" style="margin-left: auto; background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: 4px; padding: 4px 8px; font-size: 11px; color: var(--text-primary);">
7464                        <option value="all">All (${allocs.length})</option>
7465                        <option value="leaked">Leaked (${allocs.filter(a => a.is_leaked).length})</option>
7466                        <option value="cloned">With Clones (${withClones})</option>
7467                        <option value="borrowed">With Borrows (${allocs.filter(a => ((a.borrow_info?.immutable_borrows || 0) + (a.borrow_info?.mutable_borrows || 0)) > 0).length})</option>
7468                    </select>
7469                </h3>
7470                <div id="ffi-allocation-table" style="max-height: 160px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 6px;">
7471                    ${createAllocationTable(allocs)}
7472                </div>
7473                <div style="margin-top: 8px; font-size: 11px; color: var(--text-secondary); text-align: center;">
7474                    Click rows for detailed view โ€ข Use filter to narrow results
7475                </div>
7476            </div>
7477        </div>
7478    `;
7479
7480    // Add interactivity
7481    setupFFIInteractivity(allocs, minTime, timeRange);
7482    setupFFIFlowInteractivity(allocs);
7483}
7484
7485// Create super cool FFI data flow visualization
7486function createFFIDataFlow(allocs) {
7487    const ffiAllocs = allocs.filter(a => a.ffi_tracked);
7488    const rustAllocs = allocs.filter(a => !a.ffi_tracked);
7489    
7490    // Create dynamic SVG-based flow visualization
7491    let html = `
7492        <svg width="100%" height="100%" viewBox="0 0 800 200" style="position: absolute; top: 0; left: 0;">
7493            <!-- Background grid pattern -->
7494            <defs>
7495                <pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
7496                    <path d="M 20 0 L 0 0 0 20" fill="none" stroke='#334155' stroke-width="0.5" opacity="0.3"/>
7497                </pattern>
7498                <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
7499                    <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
7500                    <feMerge>
7501                        <feMergeNode in="coloredBlur"/>
7502                        <feMergeNode in="SourceGraphic"/>
7503                    </feMerge>
7504                </filter>
7505                <linearGradient id="rustGradient" x1="0%" y1="0%" x2="100%" y2="0%">
7506                    <stop offset="0%" style="stop-color:#f97316;stop-opacity:1" />
7507                    <stop offset="100%" style="stop-color:#ea580c;stop-opacity:1" />
7508                </linearGradient>
7509                <linearGradient id="cGradient" x1="0%" y1="0%" x2="100%" y2="0%">
7510                    <stop offset="0%" style="stop-color:#3b82f6;stop-opacity:1" />
7511                    <stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
7512                </linearGradient>
7513            </defs>
7514            
7515            <rect width="100%" height="100%" fill="url(#grid)"/>
7516            
7517            <!-- Rust Side (Left) -->
7518            <g id="rust-side">
7519                <rect x="50" y="40" width="200" height="120" rx="15" fill="url(#rustGradient)" opacity="0.2" stroke='#f97316' stroke-width="2"/>
7520                <text x="150" y="30" text-anchor="middle" font-size="16" font-weight="bold" fill='#f97316' filter="url(#glow)">
7521                    ๐Ÿฆ€ RUST
7522                </text>
7523                <text x="150" y="190" text-anchor="middle" font-size="12" fill='#f97316'>
7524                    ${rustAllocs.length} allocations
7525                </text>
7526                
7527                <!-- Rust memory nodes -->
7528                ${rustAllocs.slice(0, 8).map((alloc, i) => {
7529                    const x = 80 + (i % 4) * 35;
7530                    const y = 60 + Math.floor(i / 4) * 35;
7531                    const size = Math.max(8, Math.min(16, Math.sqrt((alloc.size || 0) / 1000)));
7532                    return `
7533                        <circle cx="${x}" cy="${y}" r="${size}" fill='#f97316' opacity="0.8" 
7534                                stroke='#fff' stroke-width="2" class='rust-node '
7535                                data-ptr="${alloc.ptr}" data-size="${alloc.size || 0}"
7536                                style="cursor: pointer; transition: all 0.3s;">
7537                            <animate attributeName="opacity" values="0.8;1;0.8" dur="2s" repeatCount="indefinite"/>
7538                        </circle>
7539                    `;
7540                }).join('')}
7541            </g>
7542            
7543            <!-- C/FFI Side (Right) -->
7544            <g id="c-side">
7545                <rect x="550" y="40" width="200" height="120" rx="15" fill="url(#cGradient)" opacity="0.2" stroke='#3b82f6' stroke-width="2"/>
7546                <text x="650" y="30" text-anchor="middle" font-size="16" font-weight="bold" fill='#3b82f6' filter="url(#glow)">
7547                    โš™๏ธ C/FFI
7548                </text>
7549                <text x="650" y="190" text-anchor="middle" font-size="12" fill='#3b82f6'>
7550                    ${ffiAllocs.length} FFI allocations
7551                </text>
7552                
7553                <!-- FFI memory nodes -->
7554                ${ffiAllocs.slice(0, 8).map((alloc, i) => {
7555                    const x = 580 + (i % 4) * 35;
7556                    const y = 60 + Math.floor(i / 4) * 35;
7557                    const size = Math.max(8, Math.min(16, Math.sqrt((alloc.size || 0) / 1000)));
7558                    return `
7559                        <circle cx="${x}" cy="${y}" r="${size}" fill='#3b82f6' opacity="0.9" 
7560                                stroke='#fff' stroke-width="2" class='ffi-node '
7561                                data-ptr="${alloc.ptr}" data-size="${alloc.size || 0}"
7562                                style="cursor: pointer;">
7563                        </circle>
7564                    `;
7565                }).join('')}
7566            </g>
7567            
7568            <!-- Data Flow Arrows -->
7569            <g id="data-flows">
7570                <!-- Rust to C flow -->
7571                <path d="M 250 80 Q 400 60 550 80" stroke='#10b981' stroke-width="3" fill="none" opacity="0.7">
7572                    <animate attributeName="stroke-dasharray " values="0,1000;1000,0" dur="3s" repeatCount="indefinite "/>
7573                </path>
7574                <text x="400" y="55" text-anchor="middle" font-size="10" fill='#10b981' font-weight="bold">
7575                    Rust โ†’ C
7576                </text>
7577                
7578                <!-- C to Rust flow -->
7579                <path d="M 550 120 Q 400 140 250 120" stroke='#ec4899' stroke-width="3" fill="none" opacity="0.7">
7580                    <animate attributeName="stroke-dasharray" values="0,1000;1000,0" dur="3.5s" repeatCount="indefinite"/>
7581                </path>
7582                <text x="400" y="155" text-anchor="middle" font-size="10" fill='#ec4899' font-weight="bold">
7583                    C โ†’ Rust
7584                </text>
7585                
7586                <!-- Central processing hub -->
7587                <circle cx="400" cy="100" r="20" fill='#8b5cf6' opacity="0.3" stroke='#8b5cf6' stroke-width="2">
7588                    <animate attributeName="r" values="20;25;20" dur="2s" repeatCount="indefinite"/>
7589                </circle>
7590                <text x="400" y="105" text-anchor="middle" font-size="10" fill='#8b5cf6' font-weight="bold">FFI</text>
7591            </g>
7592            
7593            <!-- Memory flow particles -->
7594            <g id="flow-particles">
7595                ${Array.from({length: 6}, (_, i) => `
7596                    <circle r="3" fill='#fbbf24' opacity="0.8">
7597                        <animateMotion dur="${3 + i * 0.5}s" repeatCount="indefinite">
7598                            <path d="M 250 80 Q 400 60 550 80"/>
7599                        </animateMotion>
7600                        <animate attributeName="opacity" values="0;1;0" dur="1s" repeatCount="indefinite"/>
7601                    </circle>
7602                `).join('')}
7603                
7604                ${Array.from({length: 4}, (_, i) => `
7605                    <circle r="3" fill='#06d6a0' opacity="0.8">
7606                        <animateMotion dur="${3.5 + i * 0.7}s" repeatCount="indefinite">
7607                            <path d="M 550 120 Q 400 140 250 120"/>
7608                        </animateMotion>
7609                        <animate attributeName="opacity" values="0;1;0" dur="1s" repeatCount="indefinite"/>
7610                    </circle>
7611                `).join('')}
7612            </g>
7613        </svg>
7614    `;
7615    
7616    return html;
7617}
7618
7619// Create allocation timeline visualization
7620function createAllocationTimeline(allocs, minTime, timeRange) {
7621    const sorted = allocs.slice().sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
7622    let html = '<div style="position: relative; height: 100%; background: linear-gradient(90deg, rgba(59,130,246,0.1) 0%, rgba(16,185,129,0.1) 50%, rgba(239,68,68,0.1) 100%);">';
7623    
7624    // Add time axis with better spacing
7625    html += '<div style="position: absolute; bottom: 25px; left: 0; right: 0; height: 1px; background: var(--border-light);"></div>';
7626    html += '<div style="position: absolute; bottom: 18px; left: 12px; font-size: 10px; color: var(--text-secondary); background: var(--bg-primary); padding: 2px 4px; border-radius: 3px;">0ms</div>';
7627    html += '<div style="position: absolute; bottom: 18px; right: 12px; font-size: 10px; color: var(--text-secondary); background: var(--bg-primary); padding: 2px 4px; border-radius: 3px;">' + (timeRange / 1e6).toFixed(1) + 'ms</div>';
7628    
7629    // Add middle time markers for better readability
7630    const midTime = (timeRange / 1e6) / 2;
7631    html += '<div style="position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); background: var(--bg-primary); padding: 2px 4px; border-radius: 3px;">' + midTime.toFixed(1) + 'ms</div>';
7632    
7633    // Group nearby allocations to prevent overlap
7634    const groups = [];
7635    const threshold = timeRange * 0.05; // 5% of time range
7636    
7637    sorted.forEach(alloc => {
7638        const found = groups.find(g => Math.abs(g.avgTime - alloc.timestamp_alloc) < threshold);
7639        if (found) {
7640            found.allocs.push(alloc);
7641            found.avgTime = found.allocs.reduce((sum, a) => sum + a.timestamp_alloc, 0) / found.allocs.length;
7642        } else {
7643            groups.push({ allocs: [alloc], avgTime: alloc.timestamp_alloc });
7644        }
7645    });
7646    
7647    groups.forEach((group, groupIndex) => {
7648        const relativeTime = (group.avgTime - minTime) / timeRange;
7649        const left = Math.max(2, Math.min(93, relativeTime * 90 + 5));
7650        
7651        if (group.allocs.length === 1) {
7652            const alloc = group.allocs[0];
7653            const size = Math.max(10, Math.min(20, Math.sqrt((alloc.size || 0) / 50)));
7654            const isLeaked = alloc.is_leaked;
7655            const hasClones = alloc.clone_info?.clone_count > 0;
7656            const color = isLeaked ? '#dc2626' : hasClones ? '#ea580c' : '#2563eb';
7657            
7658            html += `<div style="position: absolute; left: ${left}%; top: 60%; transform: translateY(-50%); 
7659                     width: ${size}px; height: ${size}px; background: ${color}; border-radius: 50%; 
7660                     border: 2px solid white; cursor: pointer; z-index: 100; 
7661                     box-shadow: 0 2px 4px rgba(0,0,0,0.2); transition: transform 0.2s;"
7662                     onmouseover="this.style.transform='translateY(-50%) scale(1.2)'" 
7663                     onmouseout="this.style.transform='translateY(-50%) scale(1)'"
7664                     title="${alloc.var_name || 'unnamed'} | ${formatBytes(alloc.size || 0)} | ${new Date(alloc.timestamp_alloc / 1e6).toLocaleTimeString()}"
7665                     onclick="showAllocationDetail('${alloc.ptr}')"></div>`;
7666        } else {
7667            // Multiple allocations - create a cluster
7668            const totalSize = group.allocs.reduce((sum, a) => sum + (a.size || 0), 0);
7669            const hasLeaks = group.allocs.some(a => a.is_leaked);
7670            const hasClones = group.allocs.some(a => a.clone_info?.clone_count > 0);
7671            const clusterSize = Math.max(16, Math.min(28, Math.sqrt(totalSize / 100)));
7672            const color = hasLeaks ? '#dc2626' : hasClones ? '#ea580c' : '#2563eb';
7673            
7674            html += `<div style="position: absolute; left: ${left}%; top: 60%; transform: translateY(-50%); 
7675                     width: ${clusterSize}px; height: ${clusterSize}px; background: ${color}; border-radius: 50%; 
7676                     border: 3px solid white; cursor: pointer; z-index: 100;
7677                     box-shadow: 0 2px 8px rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center;
7678                     color: white; font-size: 9px; font-weight: bold; transition: transform 0.2s;"
7679                     onmouseover="this.style.transform='translateY(-50%) scale(1.2)'" 
7680                     onmouseout="this.style.transform='translateY(-50%) scale(1)'"
7681                     title="${group.allocs.length} allocations | Total: ${formatBytes(totalSize)} | Avg time: ${new Date(group.avgTime / 1e6).toLocaleTimeString()}"
7682                     onclick="showClusterDetail(${JSON.stringify(group.allocs.map(a => a.ptr)).replace(/"/g, '&quot;')})">${group.allocs.length}</div>`;
7683        }
7684    });
7685    
7686    html += '</div>';
7687    return html;
7688}
7689
7690// Create allocation details table
7691function createAllocationTable(allocs) {
7692    const sorted = allocs.slice().sort((a, b) => (b.timestamp_alloc || 0) - (a.timestamp_alloc || 0));
7693    
7694    let html = `
7695        <div style="border: 1px solid var(--border-light); border-radius: 6px; overflow: hidden;">
7696            <table style="width: 100%; border-collapse: collapse; font-size: 12px;">
7697                <thead style="background: var(--bg-primary); border-bottom: 1px solid var(--border-light);">
7698                    <tr>
7699                        <th style="padding: 8px; text-align: left; color: var(--text-primary);">Variable</th>
7700                        <th style="padding: 8px; text-align: left; color: var(--text-primary);">Type</th>
7701                        <th style="padding: 8px; text-align: right; color: var(--text-primary);">Size</th>
7702                        <th style="padding: 8px; text-align: center; color: var(--text-primary);">Borrows</th>
7703                        <th style="padding: 8px; text-align: center; color: var(--text-primary);">Clones</th>
7704                        <th style="padding: 8px; text-align: center; color: var(--text-primary);">Status</th>
7705                        <th style="padding: 8px; text-align: right; color: var(--text-primary);">Lifetime</th>
7706                    </tr>
7707                </thead>
7708                <tbody>`;
7709    
7710    sorted.forEach((alloc, i) => {
7711        const typeName = (alloc.type_name || 'Unknown').replace(/alloc::|std::/g, '').replace(/collections::\w+::/g, '');
7712        const shortType = typeName.length > 20 ? typeName.substring(0, 17) + '...' : typeName;
7713        const totalBorrows = (alloc.borrow_info?.immutable_borrows || 0) + (alloc.borrow_info?.mutable_borrows || 0);
7714        const cloneCount = alloc.clone_info?.clone_count || 0;
7715        const isLeaked = alloc.is_leaked;
7716        const lifetime = alloc.lifetime_ms || 'Active';
7717        
7718        const statusColor = isLeaked ? 'var(--primary-red)' : 'var(--primary-green)';
7719        const statusText = isLeaked ? 'LEAKED' : 'OK';
7720        
7721        html += `
7722            <tr style="border-bottom: 1px solid var(--border-light); cursor: pointer;" 
7723                onclick="showAllocationDetail('${alloc.ptr}')" 
7724                onmouseover="this.style.background='var(--bg-secondary)'" 
7725                onmouseout="this.style.background='transparent'">
7726                <td style="padding: 8px; color: var(--text-primary); font-weight: 500;">${alloc.var_name || 'unnamed'}</td>
7727                <td style="padding: 8px; color: var(--text-secondary);" title="${alloc.type_name}">${shortType}</td>
7728                <td style="padding: 8px; text-align: right; color: var(--text-primary); font-weight: 600;">${formatBytes(alloc.size || 0)}</td>
7729                <td style="padding: 8px; text-align: center; color: var(--primary-blue);">${totalBorrows}</td>
7730                <td style="padding: 8px; text-align: center; color: var(--primary-orange);">${cloneCount}</td>
7731                <td style="padding: 8px; text-align: center;">
7732                    <span style="color: ${statusColor}; font-weight: 600; font-size: 11px;">${statusText}</span>
7733                </td>
7734                <td style="padding: 8px; text-align: right; color: var(--text-secondary); font-size: 11px;">
7735                    ${typeof lifetime === 'number' ? lifetime.toFixed(2) + 'ms' : lifetime}
7736                </td>
7737            </tr>`;
7738    });
7739    
7740    html += '</tbody></table></div>';
7741    return html;
7742}
7743
7744// Setup FFI interactivity
7745function setupFFIInteractivity(allocs, minTime, timeRange) {
7746    // Timeline toggle
7747    const toggleBtn = document.getElementById('ffi-timeline-toggle');
7748    if (toggleBtn) {
7749        let expanded = false;
7750        toggleBtn.onclick = () => {
7751            const container = document.getElementById('ffi-timeline-container');
7752            if (!container) return;
7753            
7754            expanded = !expanded;
7755            container.style.height = expanded ? '200px' : '120px';
7756            toggleBtn.textContent = expanded ? 'Hide Details' : 'Show Details';
7757            
7758            if (expanded) {
7759                // Add detailed timeline with labels
7760                container.innerHTML = createAllocationTimeline(allocs, minTime, timeRange) + 
7761                    '<div style="position: absolute; bottom: 4px; left: 8px; font-size: 10px; color: var(--text-secondary);">Start</div>' +
7762                    '<div style="position: absolute; bottom: 4px; right: 8px; font-size: 10px; color: var(--text-secondary);">End</div>';
7763            } else {
7764                container.innerHTML = createAllocationTimeline(allocs, minTime, timeRange);
7765            }
7766        };
7767    }
7768    
7769    // Table filter
7770    const filterSelect = document.getElementById('ffi-filter');
7771    if (filterSelect) {
7772        filterSelect.onchange = () => {
7773            const filterValue = filterSelect.value;
7774            let filteredAllocs = allocs;
7775            
7776            switch(filterValue) {
7777                case 'leaked':
7778                    filteredAllocs = allocs.filter(a => a.is_leaked);
7779                    break;
7780                case 'cloned':
7781                    filteredAllocs = allocs.filter(a => a.clone_info?.clone_count > 0);
7782                    break;
7783                case 'borrowed':
7784                    filteredAllocs = allocs.filter(a => {
7785                        const borrows = (a.borrow_info?.immutable_borrows || 0) + (a.borrow_info?.mutable_borrows || 0);
7786                        return borrows > 0;
7787                    });
7788                    break;
7789                default:
7790                    filteredAllocs = allocs;
7791            }
7792            
7793            const tableContainer = document.getElementById('ffi-allocation-table');
7794            if (tableContainer) {
7795                tableContainer.innerHTML = createAllocationTable(filteredAllocs);
7796            }
7797        };
7798    }
7799}
7800
7801// Show allocation detail modal
7802window.showAllocationDetail = function(ptr) {
7803    const data = window.analysisData || {};
7804    const allocs = data.unsafe_ffi?.allocations || data.memory_analysis?.allocations || data.allocations || [];
7805    const alloc = allocs.find(a => a.ptr === ptr);
7806    
7807    if (!alloc) return;
7808    
7809    const modal = document.createElement('div');
7810    modal.style.cssText = `
7811        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
7812        background: rgba(0,0,0,0.5); z-index: 1000; 
7813        display: flex; align-items: center; justify-content: center;
7814    `;
7815    
7816    modal.innerHTML = `
7817        <div style="background: var(--bg-primary); border-radius: 12px; padding: 24px; min-width: 400px; max-width: 600px; border: 1px solid var(--border-light);">
7818            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
7819                <h3 style="margin: 0; color: var(--text-primary);">Allocation Details</h3>
7820                <button onclick="this.closest('div').parentNode.remove()" style="background: none; border: none; font-size: 20px; color: var(--text-secondary); cursor: pointer;">ร—</button>
7821            </div>
7822            <div style="color: var(--text-primary); line-height: 1.6;">
7823                <div style="margin-bottom: 12px;"><strong>Variable:</strong> ${alloc.var_name || 'unnamed'}</div>
7824                <div style="margin-bottom: 12px;"><strong>Type:</strong> ${alloc.type_name || 'Unknown'}</div>
7825                <div style="margin-bottom: 12px;"><strong>Size:</strong> ${formatBytes(alloc.size || 0)}</div>
7826                <div style="margin-bottom: 12px;"><strong>Pointer:</strong> <code>${alloc.ptr}</code></div>
7827                <div style="margin-bottom: 12px;"><strong>Thread:</strong> ${alloc.thread_id}</div>
7828                <div style="margin-bottom: 12px;"><strong>Allocated:</strong> ${new Date(alloc.timestamp_alloc / 1e6).toLocaleString()}</div>
7829                <div style="margin-bottom: 12px;"><strong>Lifetime:</strong> ${alloc.lifetime_ms ? alloc.lifetime_ms.toFixed(2) + 'ms' : 'Active'}</div>
7830                <div style="margin-bottom: 12px;"><strong>Immutable Borrows:</strong> ${alloc.borrow_info?.immutable_borrows || 0}</div>
7831                <div style="margin-bottom: 12px;"><strong>Mutable Borrows:</strong> ${alloc.borrow_info?.mutable_borrows || 0}</div>
7832                <div style="margin-bottom: 12px;"><strong>Clone Count:</strong> ${alloc.clone_info?.clone_count || 0}</div>
7833                <div style="margin-bottom: 12px;"><strong>FFI Tracked:</strong> ${alloc.ffi_tracked ? 'Yes' : 'No'}</div>
7834                <div style="margin-bottom: 12px;"><strong>Status:</strong> 
7835                    <span style="color: ${alloc.is_leaked ? 'var(--primary-red)' : 'var(--primary-green)'}; font-weight: 600;">
7836                        ${alloc.is_leaked ? 'LEAKED' : 'OK'}
7837                    </span>
7838                </div>
7839                ${alloc.safety_violations && alloc.safety_violations.length > 0 ? 
7840                    `<div style="margin-bottom: 12px; color: var(--primary-red);"><strong>Safety Violations:</strong> ${alloc.safety_violations.join(', ')}</div>` 
7841                    : ''}
7842            </div>
7843        </div>
7844    `;
7845    
7846    document.body.appendChild(modal);
7847    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
7848};
7849
7850// Show cluster detail for grouped allocations
7851window.showClusterDetail = function(ptrs) {
7852    const data = window.analysisData || {};
7853    const allocs = data.unsafe_ffi?.allocations || data.memory_analysis?.allocations || data.allocations || [];
7854    const clusterAllocs = allocs.filter(a => ptrs.includes(a.ptr));
7855    
7856    if (clusterAllocs.length === 0) return;
7857    
7858    const totalSize = clusterAllocs.reduce((sum, a) => sum + (a.size || 0), 0);
7859    const totalBorrows = clusterAllocs.reduce((sum, a) => sum + (a.borrow_info?.immutable_borrows || 0) + (a.borrow_info?.mutable_borrows || 0), 0);
7860    const totalClones = clusterAllocs.reduce((sum, a) => sum + (a.clone_info?.clone_count || 0), 0);
7861    const leakCount = clusterAllocs.filter(a => a.is_leaked).length;
7862    
7863    const modal = document.createElement('div');
7864    modal.style.cssText = `
7865        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
7866        background: rgba(0,0,0,0.5); z-index: 1000; 
7867        display: flex; align-items: center; justify-content: center;
7868    `;
7869    
7870    modal.innerHTML = `
7871        <div style="background: var(--bg-primary); border-radius: 12px; padding: 24px; min-width: 500px; max-width: 700px; max-height: 80vh; overflow-y: auto; border: 1px solid var(--border-light);">
7872            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
7873                <h3 style="margin: 0; color: var(--text-primary);">Allocation Cluster (${clusterAllocs.length} items)</h3>
7874                <button onclick="this.closest('div').parentNode.remove()" style="background: none; border: none; font-size: 20px; color: var(--text-secondary); cursor: pointer;">ร—</button>
7875            </div>
7876            <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px;">
7877                <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 6px;">
7878                    <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-blue);">${formatBytes(totalSize)}</div>
7879                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Total Size</div>
7880                </div>
7881                <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 6px;">
7882                    <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-green);">${totalBorrows}</div>
7883                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Total Borrows</div>
7884                </div>
7885                <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 6px;">
7886                    <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-orange);">${totalClones}</div>
7887                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Total Clones</div>
7888                </div>
7889                <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 6px;">
7890                    <div style="font-size: 1.2rem; font-weight: 600; color: ${leakCount > 0 ? 'var(--primary-red)' : 'var(--primary-green)'};">${leakCount}</div>
7891                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Leaks</div>
7892                </div>
7893            </div>
7894            <div style="color: var(--text-primary);">
7895                <h4 style="margin: 0 0 12px 0; color: var(--text-primary);">Individual Allocations:</h4>
7896                <div style="max-height: 300px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 6px;">
7897                    <table style="width: 100%; border-collapse: collapse; font-size: 12px;">
7898                        <thead style="background: var(--bg-secondary); position: sticky; top: 0;">
7899                            <tr>
7900                                <th style="padding: 8px; text-align: left;">Variable</th>
7901                                <th style="padding: 8px; text-align: right;">Size</th>
7902                                <th style="padding: 8px; text-align: center;">Borrows</th>
7903                                <th style="padding: 8px; text-align: center;">Clones</th>
7904                                <th style="padding: 8px; text-align: center;">Status</th>
7905                            </tr>
7906                        </thead>
7907                        <tbody>
7908                            ${clusterAllocs.map(alloc => {
7909                                const totalBorrows = (alloc.borrow_info?.immutable_borrows || 0) + (alloc.borrow_info?.mutable_borrows || 0);
7910                                const cloneCount = alloc.clone_info?.clone_count || 0;
7911                                const isLeaked = alloc.is_leaked;
7912                                const statusColor = isLeaked ? 'var(--primary-red)' : 'var(--primary-green)';
7913                                const statusText = isLeaked ? 'LEAKED' : 'OK';
7914                                
7915                                return `
7916                                    <tr style="border-bottom: 1px solid var(--border-light); cursor: pointer;" onclick="showAllocationDetail('${alloc.ptr}')">
7917                                        <td style="padding: 8px; font-weight: 500;">${alloc.var_name || 'unnamed'}</td>
7918                                        <td style="padding: 8px; text-align: right; font-weight: 600;">${formatBytes(alloc.size || 0)}</td>
7919                                        <td style="padding: 8px; text-align: center; color: var(--primary-blue);">${totalBorrows}</td>
7920                                        <td style="padding: 8px; text-align: center; color: var(--primary-orange);">${cloneCount}</td>
7921                                        <td style="padding: 8px; text-align: center;"><span style="color: ${statusColor}; font-weight: 600; font-size: 11px;">${statusText}</span></td>
7922                                    </tr>
7923                                `;
7924                            }).join('')}
7925                        </tbody>
7926                    </table>
7927                </div>
7928            </div>
7929        </div>
7930    `;
7931    
7932    document.body.appendChild(modal);
7933    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
7934};
7935
7936// Render enhanced data insights with beautiful visualizations
7937function renderEnhancedDataInsights() {
7938    const data = window.analysisData || {};
7939    const allocs = data.memory_analysis?.allocations || data.allocations || [];
7940    
7941    if (allocs.length === 0) return;
7942    
7943    // Calculate timeline insights
7944    const timestamps = allocs.map(a => a.timestamp_alloc).filter(t => t).sort((a, b) => a - b);
7945    const timeSpanMs = timestamps.length > 1 ? (timestamps[timestamps.length - 1] - timestamps[0]) / 1e6 : 0;
7946    const allocationBurst = (allocs.length / Math.max(1, timeSpanMs / 1000)).toFixed(1);
7947    
7948    // Calculate borrow patterns
7949    const borrowPatterns = {};
7950    let totalBorrows = 0;
7951    let totalMutable = 0;
7952    let totalImmutable = 0;
7953    
7954    allocs.forEach(alloc => {
7955        const bi = alloc.borrow_info || {};
7956        const immut = bi.immutable_borrows || 0;
7957        const mut = bi.mutable_borrows || 0;
7958        const pattern = `${immut}i+${mut}m`;
7959        borrowPatterns[pattern] = (borrowPatterns[pattern] || 0) + 1;
7960        totalBorrows += immut + mut;
7961        totalImmutable += immut;
7962        totalMutable += mut;
7963    });
7964    
7965    // Calculate clone operations
7966    const totalClones = allocs.reduce((sum, a) => sum + (a.clone_info?.clone_count || 0), 0);
7967    
7968    // Update Timeline Insights
7969    document.getElementById('time-span').textContent = timeSpanMs.toFixed(2) + 'ms';
7970    document.getElementById('allocation-burst').textContent = allocationBurst + '/sec';
7971    document.getElementById('peak-concurrency').textContent = Math.max(...allocs.map(a => (a.borrow_info?.max_concurrent_borrows || 0)));
7972    document.getElementById('thread-activity').textContent = 'Single Thread';
7973    
7974    // Update Memory Operations
7975    document.getElementById('borrow-ops').textContent = totalBorrows;
7976    document.getElementById('clone-ops').textContent = totalClones;
7977    document.getElementById('mut-ratio').textContent = totalImmutable > 0 ? (totalMutable / totalImmutable).toFixed(1) : '0';
7978    document.getElementById('avg-borrows').textContent = (totalBorrows / allocs.length).toFixed(1);
7979    
7980    // Render charts with forced data refresh
7981    renderBorrowPatternChart(borrowPatterns);
7982    
7983    // Force Type Memory Distribution to render with debug info
7984    console.log('๐Ÿ” Forcing Type Memory Distribution render with data:', allocs.length, 'allocations');
7985    setTimeout(() => {
7986        renderMemoryDistributionChart(allocs);
7987    }, 100);
7988    
7989    console.log('โœ… Enhanced data insights rendered:', {
7990        timeSpan: timeSpanMs.toFixed(2) + 'ms',
7991        totalBorrows,
7992        totalClones,
7993        borrowPatterns,
7994        allocCount: allocs.length
7995    });
7996}
7997
7998// Render borrow activity heatmap (ๆ–ฐๅฅ‡็›ด่ง‚็š„ๅฏ่ง†ๅŒ–)
7999function renderBorrowPatternChart(patterns) {
8000    const container = document.getElementById('borrowPatternChart');
8001    if (!container) return;
8002    
8003    const data = window.analysisData || {};
8004    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8005    
8006    // Create interactive borrow activity heatmap
8007    container.innerHTML = '';
8008    container.style.cssText = 'height: 200px; overflow-y: auto; padding: 8px; background: var(--bg-primary); border-radius: 8px; border: 1px solid var(--border-light);';
8009    
8010    // Group variables by borrow intensity
8011    const borrowGroups = {
8012        'High Activity (4i+2m)': [],
8013        'Normal Activity (2i+1m)': [],
8014        'Low Activity (0-1 borrows)': []
8015    };
8016    
8017    allocs.forEach(alloc => {
8018        const bi = alloc.borrow_info || {};
8019        const immut = bi.immutable_borrows || 0;
8020        const mut = bi.mutable_borrows || 0;
8021        const total = immut + mut;
8022        
8023        if (total >= 5) {
8024            borrowGroups['High Activity (4i+2m)'].push(alloc);
8025        } else if (total >= 2) {
8026            borrowGroups['Normal Activity (2i+1m)'].push(alloc);
8027        } else {
8028            borrowGroups['Low Activity (0-1 borrows)'].push(alloc);
8029        }
8030    });
8031    
8032    // Create visual representation
8033    Object.entries(borrowGroups).forEach(([groupName, groupAllocs], groupIndex) => {
8034        if (groupAllocs.length === 0) return;
8035        
8036        const groupDiv = document.createElement('div');
8037        groupDiv.style.cssText = 'margin-bottom: 12px;';
8038        
8039        const groupHeader = document.createElement('div');
8040        groupHeader.style.cssText = `
8041            font-size: 11px; font-weight: 600; margin-bottom: 6px; 
8042            color: var(--text-primary); display: flex; align-items: center; gap: 8px;
8043        `;
8044        
8045        const colors = ['#ef4444', '#f59e0b', '#10b981'];
8046        const icons = ['๐Ÿ”ฅ', 'โšก', '๐Ÿ’ง'];
8047        
8048        groupHeader.innerHTML = `
8049            <span style="font-size: 14px;">${icons[groupIndex]}</span>
8050            <span>${groupName}</span>
8051            <span style="background: ${colors[groupIndex]}; color: white; padding: 2px 6px; border-radius: 10px; font-size: 9px;">
8052                ${groupAllocs.length}
8053            </span>
8054        `;
8055        
8056        const bubbleContainer = document.createElement('div');
8057        bubbleContainer.style.cssText = `
8058            display: flex; flex-wrap: wrap; gap: 4px; padding: 12px; 
8059            background: var(--bg-secondary); border-radius: 6px; min-height: 60px;
8060            align-items: center; justify-content: flex-start;
8061        `;
8062        
8063        // Create borrow activity bubbles
8064        groupAllocs.forEach((alloc, index) => {
8065            const bi = alloc.borrow_info || {};
8066            const immut = bi.immutable_borrows || 0;
8067            const mut = bi.mutable_borrows || 0;
8068            const maxConcurrent = bi.max_concurrent_borrows || 0;
8069            
8070            const bubble = document.createElement('div');
8071            const size = Math.max(16, Math.min(32, 12 + (immut + mut) * 2));
8072            
8073            bubble.style.cssText = `
8074                width: ${size}px; height: ${size}px; border-radius: 50%; 
8075                background: linear-gradient(45deg, ${colors[groupIndex]}80, ${colors[groupIndex]});
8076                border: 2px solid ${colors[groupIndex]}; cursor: pointer;
8077                display: flex; align-items: center; justify-content: center;
8078                font-size: 8px; font-weight: bold; color: white;
8079                transition: transform 0.2s, box-shadow 0.2s;
8080                position: relative;
8081            `;
8082            
8083            bubble.textContent = immut + mut;
8084            bubble.title = `${alloc.var_name}: ${immut}i + ${mut}m (max: ${maxConcurrent})`;
8085            
8086            // Add interactive effects
8087            bubble.onmouseover = () => {
8088                bubble.style.transform = 'scale(1.2)';
8089                bubble.style.boxShadow = `0 4px 12px ${colors[groupIndex]}40`;
8090            };
8091            bubble.onmouseout = () => {
8092                bubble.style.transform = 'scale(1)';
8093                bubble.style.boxShadow = 'none';
8094            };
8095            
8096            // Add click to show details
8097            bubble.onclick = () => {
8098                showBorrowDetail(alloc);
8099            };
8100            
8101            bubbleContainer.appendChild(bubble);
8102        });
8103        
8104        groupDiv.appendChild(groupHeader);
8105        groupDiv.appendChild(bubbleContainer);
8106        container.appendChild(groupDiv);
8107    });
8108    
8109    // Add summary stats at bottom
8110    const summaryDiv = document.createElement('div');
8111    summaryDiv.style.cssText = `
8112        margin-top: 8px; padding: 8px; background: var(--bg-secondary); 
8113        border-radius: 6px; font-size: 10px; color: var(--text-secondary);
8114        display: flex; justify-content: space-between;
8115    `;
8116    
8117    const totalBorrows = allocs.reduce((sum, a) => sum + (a.borrow_info?.immutable_borrows || 0) + (a.borrow_info?.mutable_borrows || 0), 0);
8118    const avgBorrows = (totalBorrows / allocs.length).toFixed(1);
8119    const maxConcurrent = Math.max(...allocs.map(a => a.borrow_info?.max_concurrent_borrows || 0));
8120    
8121    summaryDiv.innerHTML = `
8122        <span>Total Borrows: <strong>${totalBorrows}</strong></span>
8123        <span>Avg/Variable: <strong>${avgBorrows}</strong></span>
8124        <span>Peak Concurrent: <strong>${maxConcurrent}</strong></span>
8125    `;
8126    
8127    container.appendChild(summaryDiv);
8128}
8129
8130// Show borrow detail modal
8131function showBorrowDetail(alloc) {
8132    const modal = document.createElement('div');
8133    modal.style.cssText = `
8134        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
8135        background: rgba(0,0,0,0.5); z-index: 1000; 
8136        display: flex; align-items: center; justify-content: center;
8137    `;
8138    
8139    const bi = alloc.borrow_info || {};
8140    
8141    modal.innerHTML = `
8142        <div style="background: var(--bg-primary); border-radius: 12px; padding: 20px; min-width: 350px; border: 1px solid var(--border-light);">
8143            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
8144                <h3 style="margin: 0; color: var(--text-primary);">๐Ÿ” Borrow Analysis</h3>
8145                <button onclick="this.closest('div').parentNode.remove()" style="background: none; border: none; font-size: 18px; color: var(--text-secondary); cursor: pointer;">ร—</button>
8146            </div>
8147            <div style="color: var(--text-primary); line-height: 1.6;">
8148                <div style="margin-bottom: 12px;"><strong>Variable:</strong> ${alloc.var_name}</div>
8149                <div style="margin-bottom: 12px;"><strong>Type:</strong> ${alloc.type_name}</div>
8150                <div style="margin-bottom: 12px;"><strong>Size:</strong> ${formatBytes(alloc.size || 0)}</div>
8151                <hr style="border: none; border-top: 1px solid var(--border-light); margin: 16px 0;">
8152                <div style="margin-bottom: 8px;"><strong>๐Ÿ“– Immutable Borrows:</strong> <span style="color: var(--primary-blue); font-weight: 600;">${bi.immutable_borrows || 0}</span></div>
8153                <div style="margin-bottom: 8px;"><strong>โœ๏ธ Mutable Borrows:</strong> <span style="color: var(--primary-orange); font-weight: 600;">${bi.mutable_borrows || 0}</span></div>
8154                <div style="margin-bottom: 8px;"><strong>๐Ÿ”ฅ Max Concurrent:</strong> <span style="color: var(--primary-red); font-weight: 600;">${bi.max_concurrent_borrows || 0}</span></div>
8155                <div style="margin-bottom: 8px;"><strong>โšก Total Activity:</strong> <span style="color: var(--primary-green); font-weight: 600;">${(bi.immutable_borrows || 0) + (bi.mutable_borrows || 0)}</span></div>
8156            </div>
8157        </div>
8158    `;
8159    
8160    document.body.appendChild(modal);
8161    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
8162}
8163
8164// Render type memory distribution as interactive memory blocks
8165function renderMemoryDistributionChart(allocs) {
8166    const container = document.getElementById('memoryDistributionChart');
8167    if (!container) return;
8168    
8169    // Create unique memory blocks visualization (not pie chart)
8170    container.innerHTML = '';
8171    
8172    // Group by type and sum memory
8173    const typeMemory = {};
8174    console.log('๐Ÿ” Processing allocations for memory blocks:', allocs.length);
8175    
8176    allocs.forEach((alloc, index) => {
8177        let typeName = alloc.type_name || 'Unknown';
8178        const originalType = typeName;
8179        const size = alloc.size || 0;
8180        
8181        // Simplify type names
8182        if (typeName.includes('HashMap')) {
8183            typeName = 'HashMap';
8184        } else if (typeName.includes('BTreeMap')) {
8185            typeName = 'BTreeMap';
8186        } else if (typeName.includes('Arc')) {
8187            typeName = 'Arc';
8188        } else if (typeName.includes('Rc')) {
8189            typeName = 'Rc';
8190        } else if (typeName.includes('String')) {
8191            typeName = 'String';
8192        } else if (typeName.includes('Vec')) {
8193            typeName = 'Vec';
8194        } else {
8195            typeName = originalType.split('::').pop() || 'Unknown';
8196        }
8197        
8198        if (!typeMemory[typeName]) {
8199            typeMemory[typeName] = { size: 0, count: 0, allocations: [] };
8200        }
8201        typeMemory[typeName].size += size;
8202        typeMemory[typeName].count += 1;
8203        typeMemory[typeName].allocations.push(alloc);
8204        
8205        console.log(`[${index}] ${originalType} -> ${typeName}: ${size} bytes`);
8206    });
8207    
8208    // Sort by memory size
8209    const sortedTypes = Object.entries(typeMemory)
8210        .filter(([type, data]) => data.size > 0)
8211        .sort((a, b) => b[1].size - a[1].size);
8212    
8213    console.log('Memory blocks data:', sortedTypes);
8214    
8215    if (sortedTypes.length === 0) {
8216        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No type data available</div>';
8217        return;
8218    }
8219    
8220    const totalMemory = sortedTypes.reduce((sum, [_, data]) => sum + data.size, 0);
8221    const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#84cc16', '#f97316'];
8222    
8223    // Create memory blocks visualization
8224    container.innerHTML = `
8225        <div style="height: 100%; display: flex; flex-direction: column; gap: 8px; padding: 8px;">
8226            ${sortedTypes.map(([typeName, data], index) => {
8227                const percentage = ((data.size / totalMemory) * 100);
8228                const color = colors[index % colors.length];
8229                const blockHeight = Math.max(20, Math.min(60, percentage * 2));
8230                
8231                return `
8232                    <div style="display: flex; align-items: center; gap: 12px; cursor: pointer; padding: 6px; border-radius: 6px; transition: all 0.2s;"
8233                         onmouseover="this.style.background='var(--bg-primary)'; this.style.transform='scale(1.02)'"
8234                         onmouseout="this.style.background='transparent'; this.style.transform='scale(1)'"
8235                         onclick="showTypeDetail('${typeName}', ${JSON.stringify(data).replace(/"/g, '&quot;')})">
8236                        
8237                        <!-- Memory Block -->
8238                        <div style="width: 40px; height: ${blockHeight}px; background: linear-gradient(135deg, ${color}, ${color}80); 
8239                                    border-radius: 4px; position: relative; border: 2px solid ${color};">
8240                            <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); 
8241                                        color: white; font-size: 8px; font-weight: bold;">
8242                                ${data.count}
8243                            </div>
8244                        </div>
8245                        
8246                        <!-- Type Info -->
8247                        <div style="flex: 1; min-width: 0;">
8248                            <div style="font-size: 13px; font-weight: 600; color: var(--text-primary); margin-bottom: 2px;">
8249                                ${typeName}
8250                            </div>
8251                            <div style="font-size: 11px; color: var(--text-secondary);">
8252                                ${formatBytes(data.size)} โ€ข ${data.count} allocation${data.count > 1 ? 's' : ''}
8253                            </div>
8254                        </div>
8255                        
8256                        <!-- Percentage Bar -->
8257                        <div style="width: 60px; text-align: right;">
8258                            <div style="font-size: 12px; font-weight: 600; color: ${color}; margin-bottom: 2px;">
8259                                ${percentage.toFixed(1)}%
8260                            </div>
8261                            <div style="width: 100%; height: 4px; background: var(--border-light); border-radius: 2px; overflow: hidden;">
8262                                <div style="width: ${percentage}%; height: 100%; background: ${color}; border-radius: 2px;"></div>
8263                            </div>
8264                        </div>
8265                    </div>
8266                `;
8267            }).join('')}
8268        </div>
8269    `;
8270}
8271
8272// Show type detail modal
8273window.showTypeDetail = function(typeName, data) {
8274    const modal = document.createElement('div');
8275    modal.style.cssText = `
8276        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
8277        background: rgba(0,0,0,0.6); z-index: 1000; 
8278        display: flex; align-items: center; justify-content: center;
8279    `;
8280    
8281    const typeColors = {
8282        'HashMap': '#3b82f6', 'BTreeMap': '#10b981', 'Arc': '#f59e0b', 
8283        'Rc': '#ef4444', 'String': '#8b5cf6', 'Vec': '#06b6d4'
8284    };
8285    const color = typeColors[typeName] || '#64748b';
8286    
8287    modal.innerHTML = `
8288        <div style="background: var(--bg-primary); border-radius: 16px; padding: 24px; min-width: 500px; max-width: 700px; max-height: 80vh; overflow-y: auto; border: 1px solid var(--border-light);">
8289            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
8290                <h3 style="margin: 0; color: var(--text-primary); display: flex; align-items: center; gap: 12px;">
8291                    <div style="width: 24px; height: 24px; background: ${color}; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: white; font-size: 12px; font-weight: bold;">
8292                        ${data.count}
8293                    </div>
8294                    ${typeName} Memory Analysis
8295                </h3>
8296                <button onclick="this.closest('div').parentNode.remove()" style="background: none; border: none; font-size: 20px; color: var(--text-secondary); cursor: pointer;">ร—</button>
8297            </div>
8298            
8299            <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 20px;">
8300                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8301                    <div style="font-size: 1.8rem; font-weight: 700; color: ${color};">${formatBytes(data.size)}</div>
8302                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Total Memory</div>
8303                </div>
8304                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8305                    <div style="font-size: 1.8rem; font-weight: 700; color: var(--text-primary);">${data.count}</div>
8306                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Allocations</div>
8307                </div>
8308                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8309                    <div style="font-size: 1.8rem; font-weight: 700; color: var(--text-primary);">${formatBytes(data.size / data.count)}</div>
8310                    <div style="font-size: 0.8rem; color: var(--text-secondary);">Avg Size</div>
8311                </div>
8312            </div>
8313            
8314            <h4 style="margin: 0 0 12px 0; color: var(--text-primary);">Individual Allocations:</h4>
8315            <div style="max-height: 300px; overflow-y: auto; border: 1px solid var(--border-light); border-radius: 8px;">
8316                <table style="width: 100%; border-collapse: collapse; font-size: 12px;">
8317                    <thead style="background: var(--bg-secondary); position: sticky; top: 0;">
8318                        <tr>
8319                            <th style="padding: 8px; text-align: left; color: var(--text-primary);">Variable</th>
8320                            <th style="padding: 8px; text-align: right; color: var(--text-primary);">Size</th>
8321                            <th style="padding: 8px; text-align: center; color: var(--text-primary);">Status</th>
8322                            <th style="padding: 8px; text-align: right; color: var(--text-primary);">Lifetime</th>
8323                        </tr>
8324                    </thead>
8325                    <tbody>
8326                        ${data.allocations.map(alloc => {
8327                            const isLeaked = alloc.is_leaked;
8328                            const statusColor = isLeaked ? 'var(--primary-red)' : 'var(--primary-green)';
8329                            const statusText = isLeaked ? 'LEAKED' : 'OK';
8330                            const lifetime = alloc.lifetime_ms || 'Active';
8331                            
8332                            return `
8333                                <tr style="border-bottom: 1px solid var(--border-light); cursor: pointer;" onclick="showAllocationDetail('${alloc.ptr}')">
8334                                    <td style="padding: 8px; color: var(--text-primary); font-weight: 500;">${alloc.var_name || 'unnamed'}</td>
8335                                    <td style="padding: 8px; text-align: right; color: var(--text-primary); font-weight: 600;">${formatBytes(alloc.size || 0)}</td>
8336                                    <td style="padding: 8px; text-align: center;">
8337                                        <span style="color: ${statusColor}; font-weight: 600; font-size: 11px;">${statusText}</span>
8338                                    </td>
8339                                    <td style="padding: 8px; text-align: right; color: var(--text-secondary); font-size: 11px;">
8340                                        ${typeof lifetime === 'number' ? lifetime.toFixed(2) + 'ms' : lifetime}
8341                                    </td>
8342                                </tr>
8343                            `;
8344                        }).join('')}
8345                    </tbody>
8346                </table>
8347            </div>
8348        </div>
8349    `;
8350    
8351    document.body.appendChild(modal);
8352    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
8353};
8354
8355// Render detailed allocation timeline with heap/stack and timing info
8356function renderAllocationTimelineDetail() {
8357    const container = document.getElementById('allocationTimelineDetail');
8358    if (!container) return;
8359    
8360    const data = window.analysisData || {};
8361    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8362    
8363    if (allocs.length === 0) {
8364        container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); margin-top: 80px;">No allocation data available</div>';
8365        return;
8366    }
8367    
8368    // Sort by allocation time
8369    const sortedAllocs = allocs.slice().sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
8370    const minTime = sortedAllocs[0].timestamp_alloc || 0;
8371    
8372    // Classify allocations as heap/stack
8373    const classifyAllocation = (typeName) => {
8374        const heapIndicators = ['Arc', 'Rc', 'Box', 'Vec', 'HashMap', 'BTreeMap', 'String'];
8375        const stackIndicators = ['&', 'i32', 'u32', 'i64', 'u64', 'f32', 'f64', 'bool', 'char'];
8376        
8377        if (heapIndicators.some(indicator => typeName.includes(indicator))) {
8378            return { type: 'heap', color: '#ef4444', icon: '๐Ÿ—๏ธ' };
8379        } else if (stackIndicators.some(indicator => typeName.includes(indicator))) {
8380            return { type: 'stack', color: '#10b981', icon: '๐Ÿ“š' };
8381        } else {
8382            return { type: 'unknown', color: '#64748b', icon: 'โ“' };
8383        }
8384    };
8385    
8386    container.innerHTML = `
8387        <div style="display: flex; flex-direction: column; gap: 4px; height: 100%;">
8388            ${sortedAllocs.slice(0, 15).map((alloc, index) => {
8389                const allocTime = alloc.timestamp_alloc || 0;
8390                const lifetime = alloc.lifetime_ms || 0;
8391                const dropTime = allocTime + (lifetime * 1_000_000); // Convert ms to ns
8392                const relativeTime = ((allocTime - minTime) / 1_000_000).toFixed(2); // Convert to ms
8393                
8394                const classification = classifyAllocation(alloc.type_name || '');
8395                const typeName = (alloc.type_name || 'Unknown').split('::').pop().split('<')[0];
8396                
8397                return `
8398                    <div style="display: flex; align-items: center; gap: 8px; padding: 6px; border-radius: 4px; cursor: pointer; transition: all 0.2s;"
8399                         onmouseover="this.style.background='var(--bg-primary)'"
8400                         onmouseout="this.style.background='transparent'"
8401                         onclick="showAllocationTimeDetail('${alloc.ptr}', ${allocTime}, ${dropTime}, '${classification.type}')">
8402                        
8403                        <!-- Allocation Type Icon -->
8404                        <div style="width: 24px; height: 24px; background: ${classification.color}20; border: 1px solid ${classification.color}; border-radius: 4px; display: flex; align-items: center; justify-content: center; font-size: 12px;">
8405                            ${classification.icon}
8406                        </div>
8407                        
8408                        <!-- Variable Info -->
8409                        <div style="flex: 1; min-width: 0;">
8410                            <div style="font-size: 11px; font-weight: 600; color: var(--text-primary); margin-bottom: 1px;">
8411                                ${alloc.var_name || 'unnamed'} (${typeName})
8412                            </div>
8413                            <div style="font-size: 9px; color: var(--text-secondary);">
8414                                ${formatBytes(alloc.size || 0)} โ€ข ${classification.type.toUpperCase()}
8415                            </div>
8416                        </div>
8417                        
8418                        <!-- Timing Info -->
8419                        <div style="text-align: right; font-size: 9px;">
8420                            <div style="color: var(--primary-blue); font-weight: 600;">+${relativeTime}ms</div>
8421                            <div style="color: var(--text-secondary);">โ†’ ${lifetime}ms</div>
8422                        </div>
8423                    </div>
8424                `;
8425            }).join('')}
8426            
8427            ${sortedAllocs.length > 15 ? `
8428                <div style="text-align: center; padding: 8px; color: var(--text-secondary); font-size: 10px; border-top: 1px solid var(--border-light);">
8429                    ... and ${sortedAllocs.length - 15} more allocations
8430                </div>
8431            ` : ''}
8432        </div>
8433    `;
8434}
8435
8436// Show detailed allocation timing modal
8437window.showAllocationTimeDetail = function(ptr, allocTime, dropTime, allocationType) {
8438    const data = window.analysisData || {};
8439    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8440    const alloc = allocs.find(a => a.ptr === ptr);
8441    
8442    if (!alloc) return;
8443    
8444    const modal = document.createElement('div');
8445    modal.style.cssText = `
8446        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
8447        background: rgba(0,0,0,0.6); z-index: 1000; 
8448        display: flex; align-items: center; justify-content: center;
8449    `;
8450    
8451    const allocDate = new Date(allocTime / 1_000_000);
8452    const dropDate = new Date(dropTime / 1_000_000);
8453    const typeColor = allocationType === 'heap' ? '#ef4444' : allocationType === 'stack' ? '#10b981' : '#64748b';
8454    const typeIcon = allocationType === 'heap' ? '๐Ÿ—๏ธ' : allocationType === 'stack' ? '๐Ÿ“š' : 'โ“';
8455    
8456    modal.innerHTML = `
8457        <div style="background: var(--bg-primary); border-radius: 16px; padding: 24px; min-width: 500px; color: var(--text-primary); border: 1px solid var(--border-light);">
8458            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
8459                <h3 style="margin: 0; display: flex; align-items: center; gap: 12px;">
8460                    <span style="font-size: 24px;">${typeIcon}</span>
8461                    ${allocationType.toUpperCase()} Allocation Timeline
8462                </h3>
8463                <button onclick="this.closest('div').parentNode.remove()" style="background: none; border: none; font-size: 20px; color: var(--text-secondary); cursor: pointer;">ร—</button>
8464            </div>
8465            
8466            <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px;">
8467                <div style="padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8468                    <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">Variable</div>
8469                    <div style="font-size: 16px; font-weight: 600;">${alloc.var_name || 'unnamed'}</div>
8470                </div>
8471                <div style="padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8472                    <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">Type</div>
8473                    <div style="font-size: 14px; font-weight: 600; word-break: break-all;">${alloc.type_name || 'Unknown'}</div>
8474                </div>
8475            </div>
8476            
8477            <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 20px;">
8478                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8479                    <div style="font-size: 18px; font-weight: 700; color: ${typeColor};">${formatBytes(alloc.size || 0)}</div>
8480                    <div style="font-size: 12px; color: var(--text-secondary);">Size</div>
8481                </div>
8482                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8483                    <div style="font-size: 18px; font-weight: 700; color: var(--primary-blue);">${alloc.lifetime_ms || 0}ms</div>
8484                    <div style="font-size: 12px; color: var(--text-secondary);">Lifetime</div>
8485                </div>
8486                <div style="text-align: center; padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
8487                    <div style="font-size: 18px; font-weight: 700; color: ${typeColor};">${allocationType.toUpperCase()}</div>
8488                    <div style="font-size: 12px; color: var(--text-secondary);">Location</div>
8489                </div>
8490            </div>
8491            
8492            <div style="background: var(--bg-secondary); padding: 16px; border-radius: 8px;">
8493                <h4 style="margin: 0 0 12px 0; color: var(--text-primary);">Timeline Details</h4>
8494                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
8495                    <div>
8496                        <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">๐ŸŸข Allocated At</div>
8497                        <div style="font-size: 14px; font-weight: 600; color: var(--primary-green);">${allocDate.toLocaleString()}</div>
8498                        <div style="font-size: 11px; color: var(--text-secondary); margin-top: 2px;">Timestamp: ${allocTime}</div>
8499                    </div>
8500                    <div>
8501                        <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 4px;">๐Ÿ”ด Dropped At</div>
8502                        <div style="font-size: 14px; font-weight: 600; color: var(--primary-red);">${dropDate.toLocaleString()}</div>
8503                        <div style="font-size: 11px; color: var(--text-secondary); margin-top: 2px;">Timestamp: ${dropTime}</div>
8504                    </div>
8505                </div>
8506            </div>
8507        </div>
8508    `;
8509    
8510    document.body.appendChild(modal);
8511    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
8512};
8513
8514// Update lifecycle statistics and render distribution chart
8515function updateLifecycleStatistics() {
8516    const data = window.analysisData || {};
8517    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8518    
8519    if (allocs.length === 0) return;
8520    
8521    // Calculate lifecycle statistics
8522    const activeVars = allocs.filter(a => !a.is_leaked && a.lifetime_ms === undefined).length;
8523    const freedVars = allocs.filter(a => !a.is_leaked && a.lifetime_ms !== undefined).length;
8524    const leakedVars = allocs.filter(a => a.is_leaked).length;
8525    const avgLifetime = allocs.filter(a => a.lifetime_ms).reduce((sum, a) => sum + a.lifetime_ms, 0) / Math.max(1, allocs.filter(a => a.lifetime_ms).length);
8526    
8527    // Update statistics display
8528    document.getElementById('active-vars').textContent = activeVars;
8529    document.getElementById('freed-vars').textContent = freedVars;
8530    document.getElementById('leaked-vars').textContent = leakedVars;
8531    document.getElementById('avg-lifetime-stat').textContent = avgLifetime.toFixed(2) + 'ms';
8532    
8533    // Render lifecycle distribution chart
8534    renderLifecycleDistributionChart(allocs);
8535}
8536
8537// Render lifecycle distribution chart
8538function renderLifecycleDistributionChart(allocs) {
8539    const ctx = document.getElementById('lifecycleDistributionChart');
8540    if (!ctx || !window.Chart) return;
8541    
8542    // Cleanup existing chart
8543    if (window.chartInstances && window.chartInstances['lifecycleDistributionChart']) {
8544        try { window.chartInstances['lifecycleDistributionChart'].destroy(); } catch(_) {}
8545        delete window.chartInstances['lifecycleDistributionChart'];
8546    }
8547    
8548    // Group allocations by lifetime ranges
8549    const lifetimeRanges = {
8550        'Instant (0ms)': 0,
8551        'Quick (0-1ms)': 0,
8552        'Short (1-10ms)': 0,
8553        'Long (10ms+)': 0,
8554        'Active': 0
8555    };
8556    
8557    allocs.forEach(alloc => {
8558        const lifetime = alloc.lifetime_ms;
8559        if (lifetime === undefined) {
8560            lifetimeRanges['Active']++;
8561        } else if (lifetime === 0) {
8562            lifetimeRanges['Instant (0ms)']++;
8563        } else if (lifetime <= 1) {
8564            lifetimeRanges['Quick (0-1ms)']++;
8565        } else if (lifetime <= 10) {
8566            lifetimeRanges['Short (1-10ms)']++;
8567        } else {
8568            lifetimeRanges['Long (10ms+)']++;
8569        }
8570    });
8571    
8572    const labels = Object.keys(lifetimeRanges);
8573    const values = Object.values(lifetimeRanges);
8574    const colors = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6'];
8575    
8576    if (values.some(v => v > 0)) {
8577        const chart = new Chart(ctx, {
8578            type: 'bar',
8579            data: {
8580                labels: labels,
8581                datasets: [{
8582                    data: values,
8583                    backgroundColor: colors,
8584                    borderWidth: 1,
8585                    borderColor: colors.map(c => c + '80')
8586                }]
8587            },
8588            options: {
8589                responsive: true,
8590                maintainAspectRatio: false,
8591                plugins: {
8592                    legend: { display: false },
8593                    tooltip: {
8594                        callbacks: {
8595                            label: (context) => {
8596                                const range = context.label;
8597                                const count = context.parsed.y;
8598                                const total = context.dataset.data.reduce((a, b) => a + b, 0);
8599                                const percentage = ((count / total) * 100).toFixed(1);
8600                                return `${range}: ${count} vars (${percentage}%)`;
8601                            }
8602                        }
8603                    }
8604                },
8605                scales: {
8606                    x: {
8607                        ticks: {
8608                            font: { size: 9 },
8609                            color: function(context) {
8610                                return document.documentElement.classList.contains('dark-theme') ? '#cbd5e1' : '#64748b';
8611                            },
8612                            maxRotation: 45
8613                        },
8614                        grid: { display: false }
8615                    },
8616                    y: {
8617                        beginAtZero: true,
8618                        ticks: {
8619                            font: { size: 9 },
8620                            color: function(context) {
8621                                return document.documentElement.classList.contains('dark-theme') ? '#cbd5e1' : '#64748b';
8622                            }
8623                        },
8624                        grid: {
8625                            color: function(context) {
8626                                return document.documentElement.classList.contains('dark-theme') ? '#374151' : '#e2e8f0';
8627                            }
8628                        }
8629                    }
8630                }
8631            }
8632        });
8633        
8634        window.chartInstances = window.chartInstances || {};
8635        window.chartInstances['lifecycleDistributionChart'] = chart;
8636    }
8637}
8638
8639// Render memory hotspots visualization
8640function renderMemoryHotspots() {
8641    const container = document.getElementById('memoryHotspots');
8642    if (!container) return;
8643    
8644    const data = window.analysisData || {};
8645    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8646    
8647    if (allocs.length === 0) {
8648        container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); margin-top: 100px;">No allocation data available</div>';
8649        return;
8650    }
8651    
8652    // Sort allocations by size to identify hotspots
8653    const sortedAllocs = allocs.slice().sort((a, b) => (b.size || 0) - (a.size || 0));
8654    const topHotspots = sortedAllocs.slice(0, 10); // Top 10 largest allocations
8655    
8656    container.innerHTML = '';
8657    
8658    // Create hotspot visualization
8659    topHotspots.forEach((alloc, index) => {
8660        const size = alloc.size || 0;
8661        const maxSize = sortedAllocs[0].size || 1;
8662        const intensity = (size / maxSize) * 100;
8663        
8664        const hotspotDiv = document.createElement('div');
8665        hotspotDiv.style.cssText = `
8666            display: flex; align-items: center; padding: 8px; margin-bottom: 6px;
8667            background: linear-gradient(90deg, var(--bg-primary) 0%, var(--primary-red)${Math.floor(intensity/4)} ${intensity}%, var(--bg-primary) 100%);
8668            border-radius: 6px; border: 1px solid var(--border-light);
8669            cursor: pointer; transition: transform 0.2s;
8670        `;
8671        
8672        const heatColor = intensity > 80 ? '#ef4444' : intensity > 60 ? '#f59e0b' : intensity > 40 ? '#10b981' : '#3b82f6';
8673        
8674        hotspotDiv.innerHTML = `
8675            <div style="width: 24px; height: 24px; border-radius: 50%; background: ${heatColor}; 
8676                        display: flex; align-items: center; justify-content: center; margin-right: 12px;
8677                        font-size: 10px; font-weight: bold; color: white;">
8678                ${index + 1}
8679            </div>
8680            <div style="flex: 1;">
8681                <div style="font-size: 12px; font-weight: 600; color: var(--text-primary);">
8682                    ${alloc.var_name || 'unnamed'}
8683                </div>
8684                <div style="font-size: 10px; color: var(--text-secondary);">
8685                    ${(alloc.type_name || 'Unknown').replace(/std::|alloc::/g, '').substring(0, 30)}...
8686                </div>
8687            </div>
8688            <div style="text-align: right;">
8689                <div style="font-size: 12px; font-weight: 700; color: ${heatColor};">
8690                    ${formatBytes(size)}
8691                </div>
8692                <div style="font-size: 9px; color: var(--text-secondary);">
8693                    ${intensity.toFixed(1)}% of max
8694                </div>
8695            </div>
8696        `;
8697        
8698        hotspotDiv.onmouseover = () => {
8699            hotspotDiv.style.transform = 'scale(1.02)';
8700            hotspotDiv.style.boxShadow = `0 4px 12px ${heatColor}40`;
8701        };
8702        hotspotDiv.onmouseout = () => {
8703            hotspotDiv.style.transform = 'scale(1)';
8704            hotspotDiv.style.boxShadow = 'none';
8705        };
8706        
8707        hotspotDiv.onclick = () => {
8708            showAllocationDetail(alloc.ptr);
8709        };
8710        
8711        container.appendChild(hotspotDiv);
8712    });
8713    
8714    // Add summary at bottom
8715    const summaryDiv = document.createElement('div');
8716    summaryDiv.style.cssText = `
8717        margin-top: 12px; padding: 8px; background: var(--bg-primary); 
8718        border-radius: 6px; font-size: 10px; color: var(--text-secondary);
8719        text-align: center; border: 1px solid var(--border-light);
8720    `;
8721    
8722    const totalHotspotMemory = topHotspots.reduce((sum, a) => sum + (a.size || 0), 0);
8723    const totalMemory = allocs.reduce((sum, a) => sum + (a.size || 0), 0);
8724    const hotspotPercentage = ((totalHotspotMemory / totalMemory) * 100).toFixed(1);
8725    
8726    summaryDiv.innerHTML = `
8727        Top ${topHotspots.length} hotspots: <strong>${formatBytes(totalHotspotMemory)}</strong> 
8728        (${hotspotPercentage}% of total memory)
8729    `;
8730    
8731    container.appendChild(summaryDiv);
8732}
8733
8734// Render thread analysis
8735function renderThreadAnalysis() {
8736    const data = window.analysisData || {};
8737    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8738    
8739    // Update thread timeline
8740    renderThreadTimeline(allocs);
8741    
8742    // Update contention analysis
8743    updateContentionAnalysis(allocs);
8744}
8745
8746// Render thread timeline
8747function renderThreadTimeline(allocs) {
8748    const container = document.getElementById('threadTimeline');
8749    if (!container) return;
8750    
8751    container.innerHTML = '';
8752    
8753    // Get unique threads
8754    const threads = [...new Set(allocs.map(a => a.thread_id).filter(t => t))];
8755    
8756    if (threads.length <= 1) {
8757        // Single thread visualization
8758        container.innerHTML = `
8759            <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">
8760                <div style="text-align: center;">
8761                    <div style="font-size: 14px; margin-bottom: 4px;">๐Ÿงต</div>
8762                    <div style="font-size: 10px;">Single Thread</div>
8763                    <div style="font-size: 9px;">ThreadId(${threads[0] || 1})</div>
8764                </div>
8765            </div>
8766        `;
8767        return;
8768    }
8769    
8770    // Multi-thread visualization (if applicable)
8771    threads.forEach((threadId, index) => {
8772        const threadAllocs = allocs.filter(a => a.thread_id === threadId);
8773        const threadDiv = document.createElement('div');
8774        threadDiv.style.cssText = `
8775            height: ${100/threads.length}%; display: flex; align-items: center; 
8776            padding: 0 8px; border-bottom: 1px solid var(--border-light);
8777        `;
8778        
8779        threadDiv.innerHTML = `
8780            <div style="width: 60px; font-size: 9px; color: var(--text-secondary);">
8781                Thread ${threadId}
8782            </div>
8783            <div style="flex: 1; height: 4px; background: var(--bg-secondary); border-radius: 2px; position: relative;">
8784                <div style="height: 100%; background: var(--primary-blue); border-radius: 2px; width: ${(threadAllocs.length / allocs.length) * 100}%;"></div>
8785            </div>
8786            <div style="width: 40px; text-align: right; font-size: 9px; color: var(--text-primary);">
8787                ${threadAllocs.length}
8788            </div>
8789        `;
8790        
8791        container.appendChild(threadDiv);
8792    });
8793}
8794
8795// Update contention analysis
8796function updateContentionAnalysis(allocs) {
8797    const levelEl = document.getElementById('contention-level');
8798    const detailsEl = document.getElementById('contention-details');
8799    
8800    if (!levelEl || !detailsEl) return;
8801    
8802    // Calculate contention metrics
8803    const maxConcurrentBorrows = Math.max(...allocs.map(a => a.borrow_info?.max_concurrent_borrows || 0));
8804    const avgConcurrentBorrows = allocs.reduce((sum, a) => sum + (a.borrow_info?.max_concurrent_borrows || 0), 0) / allocs.length;
8805    
8806    let level = 'LOW';
8807    let color = 'var(--primary-green)';
8808    let details = 'Single-threaded';
8809    
8810    if (maxConcurrentBorrows > 5) {
8811        level = 'HIGH';
8812        color = 'var(--primary-red)';
8813        details = `Max ${maxConcurrentBorrows} concurrent`;
8814    } else if (maxConcurrentBorrows > 2) {
8815        level = 'MEDIUM';
8816        color = 'var(--primary-orange)';
8817        details = `Avg ${avgConcurrentBorrows.toFixed(1)} concurrent`;
8818    }
8819    
8820    levelEl.textContent = level;
8821    levelEl.style.color = color;
8822    detailsEl.textContent = details;
8823}
8824
8825// Update Performance Metrics and Thread Safety Analysis
8826function updateEnhancedMetrics() {
8827    const data = window.analysisData || {};
8828    const allocs = data.memory_analysis?.allocations || data.allocations || [];
8829    
8830    if (allocs.length === 0) return;
8831    
8832    // Calculate Performance Metrics
8833    const totalMemory = allocs.reduce((sum, a) => sum + (a.size || 0), 0);
8834    const peakMemory = Math.max(...allocs.map(a => a.size || 0));
8835    const timestamps = allocs.map(a => a.timestamp_alloc).filter(t => t).sort((a, b) => a - b);
8836    const timeRangeNs = timestamps.length > 1 ? timestamps[timestamps.length - 1] - timestamps[0] : 1e9;
8837    const allocationRate = allocs.length / (timeRangeNs / 1e9); // allocations per second
8838    const avgLifetime = allocs.filter(a => a.lifetime_ms).reduce((sum, a) => sum + a.lifetime_ms, 0) / Math.max(1, allocs.filter(a => a.lifetime_ms).length);
8839    
8840    // Simple fragmentation calculation: variance in allocation sizes
8841    const avgSize = totalMemory / allocs.length;
8842    const variance = allocs.reduce((sum, a) => sum + Math.pow((a.size || 0) - avgSize, 2), 0) / allocs.length;
8843    const fragmentation = Math.min(100, (Math.sqrt(variance) / avgSize) * 100);
8844
8845    // Update Performance Metrics
8846    const peakEl = document.getElementById('peak-memory');
8847    const rateEl = document.getElementById('allocation-rate');
8848    const lifetimeEl = document.getElementById('avg-lifetime');
8849    const fragEl = document.getElementById('fragmentation');
8850    
8851    if (peakEl) peakEl.textContent = formatBytes(peakMemory);
8852    if (rateEl) rateEl.textContent = allocationRate.toFixed(1) + '/sec';
8853    if (lifetimeEl) lifetimeEl.textContent = avgLifetime.toFixed(2) + 'ms';
8854    if (fragEl) fragEl.textContent = fragmentation.toFixed(1) + '%';
8855
8856    // Calculate Thread Safety Analysis
8857    const arcCount = allocs.filter(a => (a.type_name || '').includes('Arc')).length;
8858    const rcCount = allocs.filter(a => (a.type_name || '').includes('Rc')).length;
8859    const collectionsCount = allocs.filter(a => {
8860        const type = a.type_name || '';
8861        return type.includes('HashMap') || type.includes('BTreeMap') || type.includes('Vec') || type.includes('HashSet');
8862    }).length;
8863
8864    // Update Thread Safety Analysis
8865    const arcEl = document.getElementById('arc-count');
8866    const rcEl = document.getElementById('rc-count');
8867    const collEl = document.getElementById('collections-count');
8868    
8869    if (arcEl) arcEl.textContent = arcCount;
8870    if (rcEl) rcEl.textContent = rcCount;
8871    if (collEl) collEl.textContent = collectionsCount;
8872    
8873    console.log('โœ… Enhanced metrics updated:', {
8874        peakMemory: formatBytes(peakMemory),
8875        allocationRate: allocationRate.toFixed(1) + '/sec',
8876        avgLifetime: avgLifetime.toFixed(2) + 'ms',
8877        fragmentation: fragmentation.toFixed(1) + '%',
8878        arcCount, rcCount, collectionsCount
8879    });
8880}
8881
8882// Enhanced chart rendering with comprehensive cleanup
8883function renderEnhancedCharts() {
8884    const data = window.analysisData || {};
8885    
8886    // Step 1: Destroy all Chart.js instances globally
8887    if (window.Chart && window.Chart.instances) {
8888        Object.keys(window.Chart.instances).forEach(id => {
8889            try {
8890                window.Chart.instances[id].destroy();
8891            } catch(e) {
8892                console.warn('Failed to destroy Chart.js instance:', id, e);
8893            }
8894        });
8895    }
8896    
8897    // Step 2: Destroy our tracked instances
8898    if (window.chartInstances) {
8899        Object.keys(window.chartInstances).forEach(chartId => {
8900            try {
8901                window.chartInstances[chartId].destroy();
8902                delete window.chartInstances[chartId];
8903            } catch(e) {
8904                console.warn('Failed to destroy tracked chart:', chartId, e);
8905            }
8906        });
8907    }
8908    window.chartInstances = {};
8909    
8910    // Step 3: Clear canvas contexts manually
8911    ['typeChart', 'timelineChart', 'ffi-risk-chart'].forEach(canvasId => {
8912        const canvas = document.getElementById(canvasId);
8913        if (canvas && canvas.getContext) {
8914            try {
8915                const ctx = canvas.getContext('2d');
8916                ctx.clearRect(0, 0, canvas.width, canvas.height);
8917                // Remove Chart.js specific properties
8918                delete canvas.chart;
8919                canvas.removeAttribute('style');
8920            } catch(e) {
8921                console.warn('Failed to clear canvas:', canvasId, e);
8922            }
8923        }
8924    });
8925    
8926    // Step 4: Force garbage collection hint
8927    if (window.gc) { try { window.gc(); } catch(_) {} }
8928    
8929    // Step 5: Small delay to ensure cleanup, then create new charts
8930    setTimeout(() => {
8931        initEnhancedTypeChart(data);
8932        initTimelineChart(data);
8933    }, 10);
8934}
8935
8936document.addEventListener("DOMContentLoaded", () => {
8937    console.log('๐Ÿš€ MemScope Dashboard Loaded');
8938    
8939    // Initialize theme toggle
8940    try { 
8941        initThemeToggle(); 
8942        console.log('โœ… Theme toggle initialized');
8943    } catch(e) { 
8944        console.warn('โš ๏ธ Theme toggle initialization failed:', e?.message); 
8945    }
8946    
8947    // Initialize main dashboard with all original functions
8948    try { 
8949        // Use original dashboard functions
8950        renderKpis();
8951        renderTypeChart();
8952        renderTimelineChart();
8953        renderTreemap();
8954        renderLifetimes();
8955        updateEnhancedMetrics();
8956        renderEnhancedCharts();
8957        renderMemoryFragmentation();
8958        renderEnhancedDataInsights();
8959        renderAllocationTimelineDetail();
8960        updateLifecycleStatistics();
8961        renderMemoryHotspots();
8962        renderThreadAnalysis();
8963        populateAllocationsTable();
8964        populateUnsafeTable();
8965        renderVariableGraph();
8966        initEnhancedFFIVisualization();
8967        setupLifecycleVisualization();
8968        setupLifecycleToggle();
8969        
8970        // Optional hooks (no-op if undefined)
8971        try { updateEmbeddedFFISVG && updateEmbeddedFFISVG(); } catch(_) {}
8972        try { updatePerformanceMetrics && updatePerformanceMetrics(); } catch(_) {}
8973        
8974        console.log('โœ… All dashboard components initialized');
8975    } catch(e) { 
8976        console.error('โŒ Dashboard initialization failed:', e); 
8977    }
8978});
8979
8980// Setup FFI flow visualization interactivity
8981function setupFFIFlowInteractivity(allocs) {
8982    // Add click handlers to FFI flow nodes
8983    setTimeout(() => {
8984        const rustNodes = document.querySelectorAll('.rust-node');
8985        const ffiNodes = document.querySelectorAll('.ffi-node');
8986        
8987        [...rustNodes, ...ffiNodes].forEach(node => {
8988            node.addEventListener('click', (e) => {
8989                const ptr = e.target.getAttribute('data-ptr');
8990                const size = e.target.getAttribute('data-size');
8991                showFFIFlowNodeDetail(ptr, size, allocs);
8992            });
8993            
8994            node.addEventListener('mouseover', (e) => {
8995                e.target.style.transform = 'scale(1.3)';
8996                e.target.style.filter = 'drop-shadow(0 0 8px currentColor)';
8997            });
8998            
8999            node.addEventListener('mouseout', (e) => {
9000                e.target.style.transform = 'scale(1)';
9001                e.target.style.filter = 'none';
9002            });
9003        });
9004        
9005        // Setup flow animation toggle
9006        const flowToggle = document.getElementById('ffi-flow-toggle');
9007        if (flowToggle) {
9008            let isAnimating = true;
9009            flowToggle.onclick = () => {
9010                const particles = document.querySelectorAll('#flow-particles circle');
9011                const flows = document.querySelectorAll('#data-flows path');
9012                
9013                if (isAnimating) {
9014                    // Pause animations
9015                    [...particles, ...flows].forEach(el => {
9016                        el.style.animationPlayState = 'paused';
9017                    });
9018                    flowToggle.innerHTML = '<i class="fa fa-pause"></i> Paused';
9019                    flowToggle.style.background = 'var(--primary-red)';
9020                    isAnimating = false;
9021                } else {
9022                    // Resume animations
9023                    [...particles, ...flows].forEach(el => {
9024                        el.style.animationPlayState = 'running';
9025                    });
9026                    flowToggle.innerHTML = '<i class="fa fa-play"></i> Animate';
9027                    flowToggle.style.background = 'var(--primary-green)';
9028                    isAnimating = true;
9029                }
9030            };
9031        }
9032    }, 100);
9033}
9034
9035// Show FFI flow node detail
9036function showFFIFlowNodeDetail(ptr, size, allocs) {
9037    const alloc = allocs.find(a => a.ptr === ptr);
9038    if (!alloc) return;
9039    
9040    const modal = document.createElement('div');
9041    modal.style.cssText = `
9042        position: fixed; top: 0; left: 0; right: 0; bottom: 0; 
9043        background: rgba(0,0,0,0.6); z-index: 1000; 
9044        display: flex; align-items: center; justify-content: center;
9045    `;
9046    
9047    const isFFI = alloc.ffi_tracked;
9048    const bgGradient = isFFI ? 'linear-gradient(135deg, #1e40af, #3b82f6)' : 'linear-gradient(135deg, #ea580c, #f97316)';
9049    const icon = isFFI ? 'โš™๏ธ' : '๐Ÿฆ€';
9050    const title = isFFI ? 'FFI Allocation' : 'Rust Allocation';
9051    
9052    modal.innerHTML = `
9053        <div style="background: ${bgGradient}; border-radius: 16px; padding: 24px; min-width: 400px; color: white; position: relative; overflow: hidden;">
9054            <div style="position: absolute; top: -50px; right: -50px; font-size: 120px; opacity: 0.1;">${icon}</div>
9055            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; position: relative; z-index: 1;">
9056                <h3 style="margin: 0; font-size: 18px;">${icon} ${title}</h3>
9057                <button onclick="this.closest('div').parentNode.remove()" style="background: rgba(255,255,255,0.2); border: none; border-radius: 50%; width: 32px; height: 32px; color: white; cursor: pointer; font-size: 16px;">ร—</button>
9058            </div>
9059            <div style="position: relative; z-index: 1; line-height: 1.8;">
9060                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
9061                    <div style="background: rgba(255,255,255,0.1); padding: 12px; border-radius: 8px;">
9062                        <div style="font-size: 12px; opacity: 0.8;">Variable</div>
9063                        <div style="font-weight: 600;">${alloc.var_name || 'unnamed'}</div>
9064                    </div>
9065                    <div style="background: rgba(255,255,255,0.1); padding: 12px; border-radius: 8px;">
9066                        <div style="font-size: 12px; opacity: 0.8;">Size</div>
9067                        <div style="font-weight: 600; font-size: 16px;">${formatBytes(alloc.size || 0)}</div>
9068                    </div>
9069                </div>
9070                <div style="background: rgba(255,255,255,0.1); padding: 12px; border-radius: 8px; margin-bottom: 16px;">
9071                    <div style="font-size: 12px; opacity: 0.8; margin-bottom: 4px;">Type</div>
9072                    <div style="font-weight: 600; font-size: 14px; word-break: break-all;">${alloc.type_name || 'Unknown'}</div>
9073                </div>
9074                <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px;">
9075                    <div style="text-align: center; background: rgba(255,255,255,0.1); padding: 8px; border-radius: 6px;">
9076                        <div style="font-size: 16px; font-weight: 700;">${(alloc.borrow_info?.immutable_borrows || 0) + (alloc.borrow_info?.mutable_borrows || 0)}</div>
9077                        <div style="font-size: 10px; opacity: 0.8;">Borrows</div>
9078                    </div>
9079                    <div style="text-align: center; background: rgba(255,255,255,0.1); padding: 8px; border-radius: 6px;">
9080                        <div style="font-size: 16px; font-weight: 700;">${alloc.clone_info?.clone_count || 0}</div>
9081                        <div style="font-size: 10px; opacity: 0.8;">Clones</div>
9082                    </div>
9083                    <div style="text-align: center; background: rgba(255,255,255,0.1); padding: 8px; border-radius: 6px;">
9084                        <div style="font-size: 16px; font-weight: 700;">${alloc.thread_id || 1}</div>
9085                        <div style="font-size: 10px; opacity: 0.8;">Thread</div>
9086                    </div>
9087                </div>
9088            </div>
9089        </div>
9090    `;
9091    
9092    document.body.appendChild(modal);
9093    modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
9094}
9095
9096"#;