memscope_rs/export/binary/
html_template.rs

1use std::fs;
2use std::sync::OnceLock;
3
4pub static BINARY_DASHBOARD_TEMPLATE: OnceLock<String> = OnceLock::new();
5
6pub fn get_binary_dashboard_template() -> &'static str {
7    BINARY_DASHBOARD_TEMPLATE.get_or_init(|| {
8        // Try to load from external file first
9        if let Ok(external_path) = std::env::var("MEMSCOPE_BINARY_TEMPLATE") {
10            if let Ok(content) = fs::read_to_string(&external_path) {
11                println!("📁 Loaded external binary template: {}", external_path);
12                return content;
13            }
14        }
15
16        // Fall back to embedded template
17        EMBEDDED_BINARY_DASHBOARD_TEMPLATE.to_string()
18    })
19}
20
21/// Generate comprehensive dashboard JavaScript code
22pub fn generate_dashboard_javascript() -> String {
23    r#"
24// Dashboard initialization and chart rendering functions
25let charts = {};
26let memoryTimelineChart = null;
27let typeTreemapData = null;
28
29function initDashboard() {
30    console.log('🚀 Initializing dashboard...');
31    
32    if (!window.analysisData || !window.analysisData.memory_analysis) {
33        console.warn('No analysis data available');
34        return;
35    }
36    
37    const allocations = window.analysisData.memory_analysis.allocations || [];
38    console.log('📊 Processing', allocations.length, 'allocations');
39    
40    // Initialize all dashboard components
41    updateKPIs(allocations);
42    renderMemoryOperationsAnalysis(allocations);
43    renderMemoryOverTime(allocations);
44    renderEnhancedTypeTreemap(allocations);
45    renderEnhancedBorrowHeatmap(allocations);
46    renderInteractiveVariableGraph(allocations);
47    populateAllocationTable(allocations);
48    
49    // Update Performance Metrics
50    updatePerformanceMetrics(allocations);
51    
52    console.log('✅ Dashboard initialized successfully');
53}
54
55function updatePerformanceMetrics(allocations) {
56    console.log('⚡ Updating Performance Metrics...');
57    
58    // Calculate allocation efficiency (successful vs total attempts)
59    const totalAllocations = allocations.length;
60    const successfulAllocations = allocations.filter(a => a.size > 0).length;
61    const allocationEfficiency = totalAllocations > 0 ? 
62        Math.round((successfulAllocations / totalAllocations) * 100) : 100;
63    
64    // Calculate memory utilization (allocated vs deallocated)
65    const totalAllocated = allocations.reduce((sum, a) => sum + (a.size || 0), 0);
66    const totalDeallocated = allocations.filter(a => a.timestamp_dealloc)
67        .reduce((sum, a) => sum + (a.size || 0), 0);
68    const memoryUtilization = totalAllocated > 0 ? 
69        Math.round(((totalAllocated - totalDeallocated) / totalAllocated) * 100) : 0;
70    
71    // Calculate fragmentation index (estimate based on allocation sizes)
72    const allocationSizes = allocations.map(a => a.size || 0).filter(s => s > 0);
73    const avgSize = allocationSizes.length > 0 ? 
74        allocationSizes.reduce((sum, s) => sum + s, 0) / allocationSizes.length : 0;
75    const sizeVariance = allocationSizes.length > 0 ? 
76        allocationSizes.reduce((sum, s) => sum + Math.pow(s - avgSize, 2), 0) / allocationSizes.length : 0;
77    const fragmentation = avgSize > 0 ? Math.min(100, Math.round((Math.sqrt(sizeVariance) / avgSize) * 100)) : 0;
78    
79    // Calculate leak ratio
80    const leakedAllocations = allocations.filter(a => a.is_leaked).length;
81    const leakRatio = totalAllocations > 0 ? 
82        Math.round((leakedAllocations / totalAllocations) * 100) : 0;
83    
84    // Calculate thread efficiency (allocations per thread)
85    const uniqueThreads = new Set(allocations.map(a => a.thread_id)).size;
86    const threadEfficiency = uniqueThreads > 0 ? 
87        Math.round(totalAllocations / uniqueThreads) : 0;
88    
89    // Calculate borrow efficiency (safe borrows vs total)
90    const totalBorrows = allocations.reduce((sum, a) => sum + (a.borrow_count || 0), 0);
91    const immutableBorrows = allocations.reduce((sum, a) => {
92        return sum + (a.borrow_info ? (a.borrow_info.immutable_borrows || 0) : 0);
93    }, 0);
94    const borrowSafety = totalBorrows > 0 ? 
95        Math.round((immutableBorrows / totalBorrows) * 100) : 100;
96    
97    // Update UI elements
98    safeUpdateElement('allocation-efficiency', allocationEfficiency + '%');
99    safeUpdateElement('memory-utilization', memoryUtilization + '%');
100    safeUpdateElement('fragmentation-index', fragmentation + '%');
101    safeUpdateElement('leak-ratio', leakRatio + '%');
102    safeUpdateElement('thread-efficiency', threadEfficiency + ' allocs/thread');
103    safeUpdateElement('borrow-safety', borrowSafety + '%');
104    
105    console.log('✅ Performance Metrics updated');
106}
107
108function updateKPIs(allocations) {
109    console.log('📊 Updating KPIs...');
110    
111    const totalAllocations = allocations.length;
112    const activeAllocations = allocations.filter(a => !a.timestamp_dealloc).length;
113    const totalMemory = allocations.reduce((sum, a) => sum + (a.size || 0), 0);
114    const leakedCount = allocations.filter(a => a.is_leaked).length;
115    
116    // Calculate safety score (percentage of non-leaked allocations)
117    const safetyScore = totalAllocations > 0 ? 
118        Math.round(((totalAllocations - leakedCount) / totalAllocations) * 100) : 100;
119    
120    safeUpdateElement('total-allocations', totalAllocations);
121    safeUpdateElement('active-variables', activeAllocations);
122    safeUpdateElement('total-memory', formatBytes(totalMemory));
123    safeUpdateElement('safety-score', safetyScore + '%');
124    
125    console.log('✅ KPIs updated');
126}
127
128function renderMemoryOperationsAnalysis(allocations) {
129    console.log('🔧 Rendering Memory Operations Analysis...');
130    
131    // Calculate time span
132    const timestamps = allocations.map(a => a.timestamp_alloc).filter(t => t);
133    const timeSpan = timestamps.length > 0 ? 
134        Math.max(...timestamps) - Math.min(...timestamps) : 0;
135    
136    // Calculate allocation burst (max allocations in a time window)
137    const sortedAllocs = allocations.filter(a => a.timestamp_alloc).sort((a, b) => a.timestamp_alloc - b.timestamp_alloc);
138    let maxBurst = 0;
139    const windowSize = 1000000; // 1ms in nanoseconds
140    
141    for (let i = 0; i < sortedAllocs.length; i++) {
142        const windowStart = sortedAllocs[i].timestamp_alloc;
143        const windowEnd = windowStart + windowSize;
144        let count = 0;
145        
146        for (let j = i; j < sortedAllocs.length && sortedAllocs[j].timestamp_alloc <= windowEnd; j++) {
147            count++;
148        }
149        maxBurst = Math.max(maxBurst, count);
150    }
151    
152    // Calculate peak concurrency (max active allocations at any time)
153    let peakConcurrency = 0;
154    let currentActive = 0;
155    
156    const events = [];
157    allocations.forEach(alloc => {
158        if (alloc.timestamp_alloc) events.push({ time: alloc.timestamp_alloc, type: 'alloc' });
159        if (alloc.timestamp_dealloc) events.push({ time: alloc.timestamp_dealloc, type: 'dealloc' });
160    });
161    
162    events.sort((a, b) => a.time - b.time);
163    events.forEach(event => {
164        if (event.type === 'alloc') currentActive++;
165        else currentActive--;
166        peakConcurrency = Math.max(peakConcurrency, currentActive);
167    });
168    
169    // Calculate thread activity
170    const threads = new Set(allocations.map(a => a.thread_id));
171    const threadActivity = threads.size;
172    
173    // Calculate borrow operations with detailed analysis
174    const borrowOps = allocations.reduce((sum, a) => sum + (a.borrow_count || 0), 0);
175    let mutableBorrows = 0;
176    let immutableBorrows = 0;
177    
178    allocations.forEach(a => {
179        if (a.borrow_info) {
180            mutableBorrows += a.borrow_info.mutable_borrows || 0;
181            immutableBorrows += a.borrow_info.immutable_borrows || 0;
182        }
183    });
184    
185    // Calculate clone operations
186    const cloneOps = allocations.reduce((sum, a) => {
187        return sum + (a.clone_info ? (a.clone_info.clone_count || 0) : 0);
188    }, 0);
189    
190    // Calculate average allocation size
191    const totalSize = allocations.reduce((sum, a) => sum + (a.size || 0), 0);
192    const avgAllocSize = allocations.length > 0 ? totalSize / allocations.length : 0;
193    
194    // Better time span calculation - use realistic timestamps if available
195    let timeSpanDisplay = 'N/A';
196    if (timeSpan > 0) {
197        if (timeSpan > 1000000000) { // > 1 second
198            timeSpanDisplay = (timeSpan / 1000000000).toFixed(2) + 's';
199        } else if (timeSpan > 1000000) { // > 1 millisecond
200            timeSpanDisplay = (timeSpan / 1000000).toFixed(2) + 'ms';
201        } else if (timeSpan > 1000) { // > 1 microsecond
202            timeSpanDisplay = (timeSpan / 1000).toFixed(2) + 'Ξs';
203        } else {
204            timeSpanDisplay = timeSpan + 'ns';
205        }
206    } else if (allocations.length > 0) {
207        // If no timestamps, show based on allocation count
208        timeSpanDisplay = allocations.length + ' allocs';
209    }
210    
211    // Update UI elements
212    safeUpdateElement('time-span', timeSpanDisplay);
213    safeUpdateElement('allocation-burst', maxBurst || allocations.length);
214    safeUpdateElement('peak-concurrency', peakConcurrency || allocations.length);
215    safeUpdateElement('thread-activity', threadActivity + ' threads');
216    safeUpdateElement('borrow-ops', borrowOps);
217    safeUpdateElement('clone-ops', cloneOps);
218    
219    // Update the missing fields
220    safeUpdateElement('mut-immut', `${mutableBorrows}/${immutableBorrows}`);
221    safeUpdateElement('avg-alloc', formatBytes(avgAllocSize));
222    
223    console.log('✅ Memory Operations Analysis updated');
224}
225
226function renderMemoryOverTime(allocations) {
227    console.log('📈 Rendering Memory Over Time chart...');
228    
229    const canvas = document.getElementById('timelineChart');
230    if (!canvas) {
231        console.warn('timelineChart canvas not found');
232        return;
233    }
234    
235    const ctx = canvas.getContext('2d');
236    
237    // Destroy existing chart if it exists
238    if (memoryTimelineChart) {
239        memoryTimelineChart.destroy();
240    }
241    
242    // Sort allocations by timestamp
243    const sortedAllocs = allocations
244        .filter(a => a.timestamp_alloc)
245        .sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
246    
247    if (sortedAllocs.length === 0) {
248        console.warn('No allocations with timestamps found');
249        ctx.fillStyle = '#666';
250        ctx.font = '16px Arial';
251        ctx.textAlign = 'center';
252        ctx.fillText('No timeline data available', canvas.width / 2, canvas.height / 2);
253        return;
254    }
255    
256    // Create simple indexed timeline data (avoid time scale issues)
257    const timelineData = [];
258    let cumulativeMemory = 0;
259    
260    sortedAllocs.forEach((alloc, index) => {
261        cumulativeMemory += alloc.size || 0;
262        timelineData.push({
263            x: index,
264            y: cumulativeMemory
265        });
266        
267        // Add deallocation point if available
268        if (alloc.timestamp_dealloc) {
269            cumulativeMemory -= alloc.size || 0;
270            timelineData.push({
271                x: index + 0.5,
272                y: cumulativeMemory
273            });
274        }
275    });
276    
277    // Create labels from allocation names
278    const labels = sortedAllocs.map((alloc, index) => 
279        `${index}: ${alloc.var_name || 'unnamed'}`);
280    
281    memoryTimelineChart = new Chart(ctx, {
282        type: 'line',
283        data: {
284            labels: labels,
285            datasets: [{
286                label: 'Memory Usage',
287                data: timelineData.map(d => d.y),
288                borderColor: 'rgb(59, 130, 246)',
289                backgroundColor: 'rgba(59, 130, 246, 0.1)',
290                fill: true,
291                tension: 0.4,
292                pointRadius: 3,
293                pointHoverRadius: 5
294            }]
295        },
296        options: {
297            responsive: true,
298            maintainAspectRatio: false,
299            interaction: {
300                intersect: false,
301                mode: 'index'
302            },
303            scales: {
304                x: {
305                    title: {
306                        display: true,
307                        text: 'Allocation Sequence'
308                    },
309                    ticks: {
310                        maxTicksLimit: 10
311                    }
312                },
313                y: {
314                    title: {
315                        display: true,
316                        text: 'Memory (bytes)'
317                    },
318                    ticks: {
319                        callback: function(value) {
320                            return formatBytes(value);
321                        }
322                    }
323                }
324            },
325            plugins: {
326                tooltip: {
327                    callbacks: {
328                        title: function(context) {
329                            const index = context[0].dataIndex;
330                            if (sortedAllocs[index]) {
331                                return `${sortedAllocs[index].var_name || 'unnamed'} (${sortedAllocs[index].type_name || 'unknown'})`;
332                            }
333                            return 'Allocation ' + index;
334                        },
335                        label: function(context) {
336                            return 'Memory: ' + formatBytes(context.parsed.y);
337                        }
338                    }
339                }
340            }
341        }
342    });
343    
344    // Add growth rate toggle functionality
345    const growthRateToggle = document.getElementById('toggleGrowthRate');
346    if (growthRateToggle) {
347        growthRateToggle.addEventListener('change', function() {
348            updateTimelineChart(allocations, this.checked);
349        });
350    }
351    
352    console.log('✅ Memory Over Time chart rendered with', timelineData.length, 'data points');
353}
354
355function updateTimelineChart(allocations, showGrowthRate) {
356    const canvas = document.getElementById('timelineChart');
357    if (!canvas) return;
358    
359    const ctx = canvas.getContext('2d');
360    
361    // Destroy existing chart if it exists
362    if (memoryTimelineChart) {
363        memoryTimelineChart.destroy();
364    }
365    
366    // Sort allocations by timestamp
367    const sortedAllocs = allocations
368        .filter(a => a.timestamp_alloc)
369        .sort((a, b) => (a.timestamp_alloc || 0) - (b.timestamp_alloc || 0));
370    
371    if (sortedAllocs.length === 0) {
372        ctx.fillStyle = '#666';
373        ctx.font = '16px Arial';
374        ctx.textAlign = 'center';
375        ctx.fillText('No timeline data available', canvas.width / 2, canvas.height / 2);
376        return;
377    }
378    
379    const timelineData = [];
380    const growthRateData = [];
381    let cumulativeMemory = 0;
382    let previousMemory = 0;
383    
384    sortedAllocs.forEach((alloc, index) => {
385        previousMemory = cumulativeMemory;
386        cumulativeMemory += alloc.size || 0;
387        
388        timelineData.push({
389            x: index,
390            y: cumulativeMemory
391        });
392        
393        // Calculate growth rate (percentage change)
394        const growthRate = previousMemory > 0 ? 
395            ((cumulativeMemory - previousMemory) / previousMemory) * 100 : 0;
396        growthRateData.push({
397            x: index,
398            y: growthRate
399        });
400        
401        // Add deallocation point if available
402        if (alloc.timestamp_dealloc) {
403            previousMemory = cumulativeMemory;
404            cumulativeMemory -= alloc.size || 0;
405            timelineData.push({
406                x: index + 0.5,
407                y: cumulativeMemory
408            });
409            
410            const deallocGrowthRate = previousMemory > 0 ? 
411                ((cumulativeMemory - previousMemory) / previousMemory) * 100 : 0;
412            growthRateData.push({
413                x: index + 0.5,
414                y: deallocGrowthRate
415            });
416        }
417    });
418    
419    const labels = sortedAllocs.map((alloc, index) => 
420        `${index}: ${alloc.var_name || 'unnamed'}`);
421    
422    const datasets = [{
423        label: 'Memory Usage',
424        data: timelineData.map(d => d.y),
425        borderColor: 'rgb(59, 130, 246)',
426        backgroundColor: 'rgba(59, 130, 246, 0.1)',
427        fill: true,
428        tension: 0.4,
429        pointRadius: 3,
430        pointHoverRadius: 5,
431        yAxisID: 'y'
432    }];
433    
434    if (showGrowthRate) {
435        datasets.push({
436            label: 'Growth Rate (%)',
437            data: growthRateData.map(d => d.y),
438            borderColor: 'rgb(239, 68, 68)',
439            backgroundColor: 'rgba(239, 68, 68, 0.1)',
440            fill: false,
441            tension: 0.4,
442            pointRadius: 2,
443            pointHoverRadius: 4,
444            yAxisID: 'y1'
445        });
446    }
447    
448    const scales = {
449        x: {
450            title: {
451                display: true,
452                text: 'Allocation Sequence'
453            },
454            ticks: {
455                maxTicksLimit: 10
456            }
457        },
458        y: {
459            type: 'linear',
460            display: true,
461            position: 'left',
462            title: {
463                display: true,
464                text: 'Memory (bytes)'
465            },
466            ticks: {
467                callback: function(value) {
468                    return formatBytes(value);
469                }
470            }
471        }
472    };
473    
474    if (showGrowthRate) {
475        scales.y1 = {
476            type: 'linear',
477            display: true,
478            position: 'right',
479            title: {
480                display: true,
481                text: 'Growth Rate (%)'
482            },
483            grid: {
484                drawOnChartArea: false
485            },
486            ticks: {
487                callback: function(value) {
488                    return value.toFixed(1) + '%';
489                }
490            }
491        };
492    }
493    
494    memoryTimelineChart = new Chart(ctx, {
495        type: 'line',
496        data: {
497            labels: labels,
498            datasets: datasets
499        },
500        options: {
501            responsive: true,
502            maintainAspectRatio: false,
503            interaction: {
504                intersect: false,
505                mode: 'index'
506            },
507            scales: scales,
508            plugins: {
509                tooltip: {
510                    callbacks: {
511                        title: function(context) {
512                            const index = context[0].dataIndex;
513                            if (sortedAllocs[index]) {
514                                return `${sortedAllocs[index].var_name || 'unnamed'} (${sortedAllocs[index].type_name || 'unknown'})`;
515                            }
516                            return 'Allocation ' + index;
517                        },
518                        label: function(context) {
519                            if (context.dataset.label.includes('Growth Rate')) {
520                                return 'Growth Rate: ' + context.parsed.y.toFixed(2) + '%';
521                            }
522                            return 'Memory: ' + formatBytes(context.parsed.y);
523                        }
524                    }
525                }
526            }
527        }
528    });
529}
530
531function renderEnhancedTypeTreemap(allocations) {
532    console.log('ðŸŒģ Rendering Enhanced Type Treemap...');
533    
534    const container = document.getElementById('treemap');
535    if (!container) {
536        console.warn('treemap container not found');
537        return;
538    }
539    
540    // Clear existing content
541    container.innerHTML = '';
542    container.style.position = 'relative';
543    
544    // Aggregate by type
545    const typeData = {};
546    allocations.forEach(alloc => {
547        const type = alloc.type_name || 'unknown';
548        if (!typeData[type]) {
549            typeData[type] = { count: 0, totalSize: 0 };
550        }
551        typeData[type].count++;
552        typeData[type].totalSize += alloc.size || 0;
553    });
554    
555    // Convert to treemap format and sort by size
556    const treemapData = Object.entries(typeData)
557        .map(([type, data]) => ({
558            name: type,
559            value: data.totalSize,
560            count: data.count
561        }))
562        .sort((a, b) => b.value - a.value);
563    
564    if (treemapData.length === 0) {
565        container.innerHTML = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">No type data available</div>';
566        return;
567    }
568    
569    // Use squarified treemap algorithm for better layout
570    const containerRect = container.getBoundingClientRect();
571    const containerWidth = containerRect.width || 400;
572    const containerHeight = containerRect.height || 300;
573    const totalValue = treemapData.reduce((sum, d) => sum + d.value, 0);
574    
575    // Calculate areas proportional to values
576    treemapData.forEach(d => {
577        d.area = (d.value / totalValue) * containerWidth * containerHeight;
578        d.ratio = containerWidth / containerHeight;
579    });
580    
581    // Simple recursive treemap layout
582    function layoutTreemap(data, x, y, width, height) {
583        if (data.length === 0) return;
584        
585        if (data.length === 1) {
586            const item = data[0];
587            createTreemapTile(item, x, y, width, height);
588            return;
589        }
590        
591        // Split the data into two groups
592        const totalArea = data.reduce((sum, d) => sum + d.area, 0);
593        const midValue = totalArea / 2;
594        let currentSum = 0;
595        let splitIndex = 0;
596        
597        for (let i = 0; i < data.length; i++) {
598            currentSum += data[i].area;
599            if (currentSum >= midValue) {
600                splitIndex = i + 1;
601                break;
602            }
603        }
604        
605        const group1 = data.slice(0, splitIndex);
606        const group2 = data.slice(splitIndex);
607        
608        if (width > height) {
609            // Split vertically
610            const splitWidth = width * (currentSum / totalArea);
611            layoutTreemap(group1, x, y, splitWidth, height);
612            layoutTreemap(group2, x + splitWidth, y, width - splitWidth, height);
613        } else {
614            // Split horizontally
615            const splitHeight = height * (currentSum / totalArea);
616            layoutTreemap(group1, x, y, width, splitHeight);
617            layoutTreemap(group2, x, y + splitHeight, width, height - splitHeight);
618        }
619    }
620    
621    function createTreemapTile(item, x, y, width, height) {
622        const tile = document.createElement('div');
623        const minSize = Math.min(width, height);
624        const fontSize = Math.max(Math.min(minSize / 8, 14), 10);
625        
626        tile.style.cssText = `
627            position: absolute;
628            left: ${x + 1}px;
629            top: ${y + 1}px;
630            width: ${width - 2}px;
631            height: ${height - 2}px;
632            background: hsl(${(item.name.length * 37) % 360}, 65%, 55%);
633            border: 2px solid rgba(255,255,255,0.8);
634            border-radius: 6px;
635            display: flex;
636            flex-direction: column;
637            align-items: center;
638            justify-content: center;
639            font-size: ${fontSize}px;
640            font-weight: 600;
641            color: white;
642            text-shadow: 1px 1px 2px rgba(0,0,0,0.7);
643            cursor: pointer;
644            transition: all 0.3s ease;
645            overflow: hidden;
646            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
647        `;
648        
649        const shortName = item.name.length > 12 ? item.name.substring(0, 12) + '...' : item.name;
650        tile.innerHTML = `
651            <div style="text-align: center; padding: 4px;">
652                <div style="font-weight: 700; margin-bottom: 2px;" title="${item.name}">${shortName}</div>
653                <div style="font-size: ${Math.max(fontSize - 2, 8)}px; opacity: 0.9;">${formatBytes(item.value)}</div>
654                <div style="font-size: ${Math.max(fontSize - 3, 7)}px; opacity: 0.8;">(${item.count} items)</div>
655            </div>
656        `;
657        
658        tile.addEventListener('mouseenter', () => {
659            tile.style.transform = 'scale(1.05)';
660            tile.style.zIndex = '10';
661            tile.style.boxShadow = '0 4px 16px rgba(0,0,0,0.4)';
662        });
663        
664        tile.addEventListener('mouseleave', () => {
665            tile.style.transform = 'scale(1)';
666            tile.style.zIndex = '1';
667            tile.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
668        });
669        
670        tile.addEventListener('click', () => {
671            const totalMemorySize = treemapData.reduce((sum, d) => sum + d.value, 0);
672            const modalContent = `
673                <div style="text-align: center; margin-bottom: 20px;">
674                    <div style="font-size: 48px; margin-bottom: 10px;">📊</div>
675                    <div style="font-size: 24px; font-weight: 600; margin-bottom: 8px;">${item.name}</div>
676                </div>
677                <div style="background: rgba(255, 255, 255, 0.1); padding: 20px; border-radius: 12px; margin-bottom: 20px;">
678                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
679                        <div style="text-align: center;">
680                            <div style="font-size: 28px; font-weight: 700; color: #4ade80;">${formatBytes(item.value)}</div>
681                            <div style="opacity: 0.8; font-size: 14px;">Total Size</div>
682                        </div>
683                        <div style="text-align: center;">
684                            <div style="font-size: 28px; font-weight: 700; color: #60a5fa;">${item.count}</div>
685                            <div style="opacity: 0.8; font-size: 14px;">Allocations</div>
686                        </div>
687                    </div>
688                </div>
689                <div style="background: rgba(255, 255, 255, 0.05); padding: 16px; border-radius: 8px;">
690                    <div style="font-size: 14px; opacity: 0.9;">
691                        <div style="margin-bottom: 8px;"><strong>Average Size:</strong> ${formatBytes(item.value / item.count)}</div>
692                        <div style="margin-bottom: 8px;"><strong>Memory Share:</strong> ${((item.value / totalMemorySize) * 100).toFixed(1)}%</div>
693                        <div><strong>Type Category:</strong> ${item.name.includes('Vec') ? 'Dynamic Array' : item.name.includes('HashMap') ? 'Hash Map' : item.name.includes('String') ? 'String Type' : 'Custom Type'}</div>
694                    </div>
695                </div>
696            `;
697            createModal(`📋 Type Analysis`, modalContent);
698        });
699        
700        container.appendChild(tile);
701    }
702    
703    // Start the layout process
704    layoutTreemap(treemapData, 0, 0, containerWidth, containerHeight);
705    
706    console.log('✅ Enhanced Type Treemap rendered with', treemapData.length, 'types');
707}
708
709function renderEnhancedBorrowHeatmap(allocations) {
710    console.log('ðŸ”Ĩ Rendering Enhanced Borrow Activity Heatmap...');
711    
712    const container = document.getElementById('borrowPatternChart');
713    if (!container) {
714        console.warn('borrowPatternChart container not found');
715        return;
716    }
717    
718    container.innerHTML = '';
719    container.style.position = 'relative';
720    
721    // Enhanced borrow data collection - include borrow_info if available
722    const borrowData = allocations.map(alloc => {
723        const borrowCount = alloc.borrow_count || 0;
724        const borrowInfo = alloc.borrow_info || {};
725        const immutableBorrows = borrowInfo.immutable_borrows || 0;
726        const mutableBorrows = borrowInfo.mutable_borrows || 0;
727        const totalBorrows = Math.max(borrowCount, immutableBorrows + mutableBorrows);
728        
729        return {
730            ...alloc,
731            totalBorrows,
732            immutableBorrows,
733            mutableBorrows,
734            hasActivity: totalBorrows > 0 || borrowCount > 0
735        };
736    }).filter(a => a.hasActivity || allocations.length <= 20); // Show all if few allocations
737    
738    if (borrowData.length === 0) {
739        // Create synthetic data for demonstration
740        const syntheticData = allocations.slice(0, Math.min(50, allocations.length)).map((alloc, i) => ({
741            ...alloc,
742            totalBorrows: Math.floor(Math.random() * 10) + 1,
743            immutableBorrows: Math.floor(Math.random() * 5),
744            mutableBorrows: Math.floor(Math.random() * 3),
745            hasActivity: true
746        }));
747        
748        if (syntheticData.length > 0) {
749            renderHeatmapGrid(container, syntheticData, true);
750        } else {
751            container.innerHTML = `
752                <div style="display: flex; align-items: center; justify-content: center; height: 100%; 
753                            color: var(--text-secondary); font-size: 14px; text-align: center;">
754                    <div>
755                        <div style="margin-bottom: 8px;">📊 No borrow activity detected</div>
756                        <div style="font-size: 12px; opacity: 0.7;">This indicates efficient memory usage with minimal borrowing</div>
757                    </div>
758                </div>
759            `;
760        }
761        return;
762    }
763    
764    renderHeatmapGrid(container, borrowData, false);
765    
766    function renderHeatmapGrid(container, data, isSynthetic) {
767        const containerRect = container.getBoundingClientRect();
768        const containerWidth = containerRect.width || 400;
769        const containerHeight = containerRect.height || 300;
770        
771        // Calculate optimal cell size and grid dimensions
772        const maxCells = Math.min(data.length, 200);
773        const aspectRatio = containerWidth / containerHeight;
774        const cols = Math.floor(Math.sqrt(maxCells * aspectRatio));
775        const rows = Math.ceil(maxCells / cols);
776        const cellSize = Math.min((containerWidth - 10) / cols, (containerHeight - 10) / rows) - 2;
777        
778        const maxBorrows = Math.max(...data.map(a => a.totalBorrows), 1);
779        
780        // Add legend
781        const legend = document.createElement('div');
782        legend.style.cssText = `
783            position: absolute;
784            top: 5px;
785            right: 5px;
786            background: rgba(0,0,0,0.8);
787            color: white;
788            padding: 8px;
789            border-radius: 4px;
790            font-size: 10px;
791            z-index: 100;
792        `;
793        legend.innerHTML = `
794            <div>Borrow Activity ${isSynthetic ? '(Demo)' : ''}</div>
795            <div style="margin-top: 4px;">
796                <div style="display: flex; align-items: center; margin: 2px 0;">
797                    <div style="width: 12px; height: 12px; background: rgba(239, 68, 68, 0.3); margin-right: 4px;"></div>
798                    <span>Low</span>
799                </div>
800                <div style="display: flex; align-items: center; margin: 2px 0;">
801                    <div style="width: 12px; height: 12px; background: rgba(239, 68, 68, 0.7); margin-right: 4px;"></div>
802                    <span>Medium</span>
803                </div>
804                <div style="display: flex; align-items: center; margin: 2px 0;">
805                    <div style="width: 12px; height: 12px; background: rgba(239, 68, 68, 1.0); margin-right: 4px;"></div>
806                    <span>High</span>
807                </div>
808            </div>
809        `;
810        container.appendChild(legend);
811        
812        data.slice(0, maxCells).forEach((alloc, i) => {
813            const row = Math.floor(i / cols);
814            const col = i % cols;
815            const intensity = Math.max(0.1, alloc.totalBorrows / maxBorrows);
816            
817            const cell = document.createElement('div');
818            const x = col * (cellSize + 2) + 5;
819            const y = row * (cellSize + 2) + 30; // Offset for legend
820            
821            // Color based on borrow type
822            let backgroundColor;
823            if (alloc.mutableBorrows > alloc.immutableBorrows) {
824                backgroundColor = `rgba(239, 68, 68, ${intensity})`; // Red for mutable
825            } else if (alloc.immutableBorrows > 0) {
826                backgroundColor = `rgba(59, 130, 246, ${intensity})`; // Blue for immutable
827            } else {
828                backgroundColor = `rgba(16, 185, 129, ${intensity})`; // Green for mixed/unknown
829            }
830            
831            cell.style.cssText = `
832                position: absolute;
833                left: ${x}px;
834                top: ${y}px;
835                width: ${cellSize}px;
836                height: ${cellSize}px;
837                background: ${backgroundColor};
838                border: 1px solid rgba(255,255,255,0.3);
839                border-radius: 2px;
840                cursor: pointer;
841                transition: all 0.2s ease;
842            `;
843            
844            const tooltipText = `
845Variable: ${alloc.var_name || 'unnamed'}
846Type: ${alloc.type_name || 'unknown'}
847Total Borrows: ${alloc.totalBorrows}
848Immutable: ${alloc.immutableBorrows}
849Mutable: ${alloc.mutableBorrows}
850            `.trim();
851            
852            cell.title = tooltipText;
853            
854            cell.addEventListener('mouseenter', () => {
855                cell.style.transform = 'scale(1.2)';
856                cell.style.zIndex = '10';
857                cell.style.boxShadow = '0 2px 8px rgba(0,0,0,0.5)';
858            });
859            
860            cell.addEventListener('mouseleave', () => {
861                cell.style.transform = 'scale(1)';
862                cell.style.zIndex = '1';
863                cell.style.boxShadow = 'none';
864            });
865            
866            cell.addEventListener('click', () => {
867                const modalContent = `
868                    <div style="text-align: center; margin-bottom: 20px;">
869                        <div style="font-size: 48px; margin-bottom: 10px;">ðŸ”Ĩ</div>
870                        <div style="font-size: 24px; font-weight: 600; margin-bottom: 8px;">${alloc.var_name || 'unnamed'}</div>
871                        <div style="opacity: 0.8; font-size: 16px;">${alloc.type_name || 'unknown'}</div>
872                    </div>
873                    <div style="background: rgba(255, 255, 255, 0.1); padding: 20px; border-radius: 12px; margin-bottom: 20px;">
874                        <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; text-align: center;">
875                            <div>
876                                <div style="font-size: 24px; font-weight: 700; color: #f87171;">${alloc.totalBorrows}</div>
877                                <div style="opacity: 0.8; font-size: 12px;">Total Borrows</div>
878                            </div>
879                            <div>
880                                <div style="font-size: 24px; font-weight: 700; color: #60a5fa;">${alloc.immutableBorrows}</div>
881                                <div style="opacity: 0.8; font-size: 12px;">Immutable</div>
882                            </div>
883                            <div>
884                                <div style="font-size: 24px; font-weight: 700; color: #fb7185;">${alloc.mutableBorrows}</div>
885                                <div style="opacity: 0.8; font-size: 12px;">Mutable</div>
886                            </div>
887                        </div>
888                    </div>
889                    <div style="background: rgba(255, 255, 255, 0.05); padding: 16px; border-radius: 8px;">
890                        <div style="font-size: 14px; opacity: 0.9;">
891                            <div style="margin-bottom: 8px;"><strong>Variable Size:</strong> ${formatBytes(alloc.size || 0)}</div>
892                            <div style="margin-bottom: 8px;"><strong>Borrow Ratio:</strong> ${alloc.immutableBorrows > 0 ? (alloc.mutableBorrows / alloc.immutableBorrows).toFixed(2) : 'N/A'} (Mut/Immut)</div>
893                            <div style="margin-bottom: 8px;"><strong>Activity Level:</strong> ${alloc.totalBorrows > 10 ? 'High' : alloc.totalBorrows > 5 ? 'Medium' : 'Low'}</div>
894                            <div><strong>Safety:</strong> ${alloc.mutableBorrows === 0 ? '✅ Read-only' : alloc.mutableBorrows < alloc.immutableBorrows ? '⚠ïļ Mostly read' : 'ðŸ”Ĩ Write-heavy'}</div>
895                        </div>
896                    </div>
897                `;
898                createModal(`ðŸ”Ĩ Borrow Analysis`, modalContent);
899            });
900            
901            container.appendChild(cell);
902        });
903        
904        console.log(`✅ Enhanced Borrow Heatmap rendered with ${Math.min(data.length, maxCells)} cells${isSynthetic ? ' (synthetic data)' : ''}`);
905    }
906}
907
908function renderInteractiveVariableGraph(allocations) {
909    console.log('ðŸ•ļïļ Rendering Interactive Variable Relationships Graph...');
910    
911    const container = document.getElementById('graph');
912    if (!container) {
913        console.warn('graph container not found');
914        return;
915    }
916    
917    container.innerHTML = '';
918    container.style.position = 'relative';
919    container.style.overflow = 'hidden';
920    container.style.background = 'var(--bg-primary)';
921    container.style.border = '1px solid var(--border-light)';
922    container.style.borderRadius = '8px';
923    
924    // Create interactive graph with D3-like functionality
925    const containerRect = container.getBoundingClientRect();
926    const width = containerRect.width || 600;
927    const height = containerRect.height || 400;
928    
929    // Graph state
930    let zoomLevel = 1;
931    let panX = 0;
932    let panY = 0;
933    let selectedNode = null;
934    let isDragging = false;
935    let dragTarget = null;
936    
937    // Create nodes with relationship analysis
938    const nodes = allocations.slice(0, 100).map((alloc, i) => {
939        const baseSize = Math.sqrt(alloc.size || 100) / 10 + 8;
940        return {
941            id: i,
942            name: alloc.var_name || ('var_' + i),
943            type: alloc.type_name || 'unknown',
944            size: alloc.size || 0,
945            nodeSize: Math.max(baseSize, 12),
946            x: Math.random() * (width - 100) + 50,
947            y: Math.random() * (height - 100) + 50,
948            vx: 0,
949            vy: 0,
950            alloc: alloc,
951            isLeaked: alloc.is_leaked,
952            borrowCount: alloc.borrow_count || 0,
953            cloneInfo: alloc.clone_info,
954            fixed: false
955        };
956    });
957    
958    // Create relationships based on various criteria
959    const links = [];
960    for (let i = 0; i < nodes.length; i++) {
961        for (let j = i + 1; j < nodes.length; j++) {
962            const nodeA = nodes[i];
963            const nodeB = nodes[j];
964            let relationship = null;
965            let strength = 0;
966            
967            // Check for clone relationships
968            if (nodeA.cloneInfo && nodeB.cloneInfo) {
969                if (nodeA.cloneInfo.original_ptr === nodeB.alloc.ptr || 
970                    nodeB.cloneInfo.original_ptr === nodeA.alloc.ptr) {
971                    relationship = 'clone';
972                    strength = 0.8;
973                }
974            }
975            
976            // Check for type similarity
977            if (!relationship && nodeA.type === nodeB.type && nodeA.type !== 'unknown') {
978                relationship = 'type_similar';
979                strength = 0.3;
980            }
981            
982            // Check for thread affinity
983            if (!relationship && nodeA.alloc.thread_id === nodeB.alloc.thread_id && 
984                nodeA.alloc.thread_id !== undefined) {
985                relationship = 'thread_affinity';
986                strength = 0.2;
987            }
988            
989            // Check for temporal proximity (allocated around same time)
990            if (!relationship && nodeA.alloc.timestamp_alloc && nodeB.alloc.timestamp_alloc) {
991                const timeDiff = Math.abs(nodeA.alloc.timestamp_alloc - nodeB.alloc.timestamp_alloc);
992                if (timeDiff < 1000000) { // Within 1ms
993                    relationship = 'temporal';
994                    strength = 0.4;
995                }
996            }
997            
998            // Add link if relationship found
999            if (relationship && (strength > 0.2 || Math.random() < 0.05)) {
1000                links.push({
1001                    source: i,
1002                    target: j,
1003                    relationship,
1004                    strength,
1005                    sourceNode: nodeA,
1006                    targetNode: nodeB
1007                });
1008            }
1009        }
1010    }
1011    
1012    // Add control panel
1013    const controls = document.createElement('div');
1014    controls.style.cssText = `
1015        position: absolute;
1016        top: 10px;
1017        left: 10px;
1018        background: rgba(0,0,0,0.8);
1019        color: white;
1020        padding: 10px;
1021        border-radius: 6px;
1022        font-size: 12px;
1023        z-index: 1000;
1024        user-select: none;
1025    `;
1026    controls.innerHTML = `
1027        <div style="margin-bottom: 8px; font-weight: bold;">ðŸŽŪ Graph Controls</div>
1028        <button id="zoom-in" style="margin: 2px; padding: 4px 8px; font-size: 11px;">🔍+ Zoom In</button>
1029        <button id="zoom-out" style="margin: 2px; padding: 4px 8px; font-size: 11px;">🔍- Zoom Out</button>
1030        <button id="reset-view" style="margin: 2px; padding: 4px 8px; font-size: 11px;">🏠 Reset</button>
1031        <button id="auto-layout" style="margin: 2px; padding: 4px 8px; font-size: 11px;">🔄 Layout</button>
1032        <div style="margin-top: 8px; font-size: 10px;">
1033            <div>Nodes: ${nodes.length}</div>
1034            <div>Links: ${links.length}</div>
1035            <div>Zoom: <span id="zoom-display">100%</span></div>
1036        </div>
1037    `;
1038    container.appendChild(controls);
1039    
1040    // Add legend
1041    const legend = document.createElement('div');
1042    legend.style.cssText = `
1043        position: absolute;
1044        top: 10px;
1045        right: 10px;
1046        background: rgba(0,0,0,0.8);
1047        color: white;
1048        padding: 10px;
1049        border-radius: 6px;
1050        font-size: 11px;
1051        z-index: 1000;
1052        user-select: none;
1053    `;
1054    legend.innerHTML = `
1055        <div style="font-weight: bold; margin-bottom: 6px;">🔗 Relationships</div>
1056        <div style="margin: 3px 0;"><span style="color: #ff6b6b;">━━</span> Clone</div>
1057        <div style="margin: 3px 0;"><span style="color: #4ecdc4;">━━</span> Type Similar</div>
1058        <div style="margin: 3px 0;"><span style="color: #45b7d1;">━━</span> Thread Affinity</div>
1059        <div style="margin: 3px 0;"><span style="color: #f9ca24;">━━</span> Temporal</div>
1060        <div style="margin-top: 8px; font-weight: bold;">ðŸŽŊ Nodes</div>
1061        <div style="margin: 3px 0;"><span style="color: #ff6b6b;">●</span> Leaked</div>
1062        <div style="margin: 3px 0;"><span style="color: #6c5ce7;">●</span> High Borrow</div>
1063        <div style="margin: 3px 0;"><span style="color: #a8e6cf;">●</span> Normal</div>
1064    `;
1065    container.appendChild(legend);
1066    
1067    // Create info panel for selected node
1068    const infoPanel = document.createElement('div');
1069    infoPanel.style.cssText = `
1070        position: absolute;
1071        bottom: 10px;
1072        left: 10px;
1073        background: rgba(0,0,0,0.9);
1074        color: white;
1075        padding: 12px;
1076        border-radius: 6px;
1077        font-size: 11px;
1078        max-width: 250px;
1079        z-index: 1000;
1080        display: none;
1081    `;
1082    container.appendChild(infoPanel);
1083    
1084    // Render function
1085    function render() {
1086        // Clear existing nodes and links
1087        container.querySelectorAll('.graph-node, .graph-link').forEach(el => el.remove());
1088        
1089        // Render links first (behind nodes)
1090        links.forEach(link => {
1091            const sourceNode = nodes[link.source];
1092            const targetNode = nodes[link.target];
1093            
1094            const linkEl = document.createElement('div');
1095            linkEl.className = 'graph-link';
1096            
1097            const dx = (targetNode.x - sourceNode.x) * zoomLevel;
1098            const dy = (targetNode.y - sourceNode.y) * zoomLevel;
1099            const length = Math.sqrt(dx * dx + dy * dy);
1100            const angle = Math.atan2(dy, dx) * 180 / Math.PI;
1101            
1102            const x = sourceNode.x * zoomLevel + panX;
1103            const y = sourceNode.y * zoomLevel + panY;
1104            
1105            let color;
1106            switch(link.relationship) {
1107                case 'clone': color = '#ff6b6b'; break;
1108                case 'type_similar': color = '#4ecdc4'; break;
1109                case 'thread_affinity': color = '#45b7d1'; break;
1110                case 'temporal': color = '#f9ca24'; break;
1111                default: color = '#666';
1112            }
1113            
1114            linkEl.style.cssText = `
1115                position: absolute;
1116                left: ${x}px;
1117                top: ${y}px;
1118                width: ${length}px;
1119                height: ${Math.max(link.strength * 2, 1)}px;
1120                background: linear-gradient(90deg, ${color} 60%, transparent 60%);
1121                background-size: 8px 100%;
1122                opacity: ${0.4 + link.strength * 0.3};
1123                transform-origin: 0 50%;
1124                transform: rotate(${angle}deg);
1125                z-index: 1;
1126                pointer-events: none;
1127            `;
1128            
1129            container.appendChild(linkEl);
1130        });
1131        
1132        // Render nodes
1133        nodes.forEach((node, i) => {
1134            const nodeEl = document.createElement('div');
1135            nodeEl.className = 'graph-node';
1136            nodeEl.dataset.nodeId = i;
1137            
1138            const x = node.x * zoomLevel + panX - (node.nodeSize * zoomLevel) / 2;
1139            const y = node.y * zoomLevel + panY - (node.nodeSize * zoomLevel) / 2;
1140            const size = node.nodeSize * zoomLevel;
1141            
1142            // Determine node color based on properties
1143            let color;
1144            if (node.isLeaked) {
1145                color = '#ff6b6b'; // Red for leaked
1146            } else if (node.borrowCount > 5) {
1147                color = '#6c5ce7'; // Purple for high borrow activity
1148            } else {
1149                color = `hsl(${(node.type.length * 47) % 360}, 65%, 60%)`;
1150            }
1151            
1152            nodeEl.style.cssText = `
1153                position: absolute;
1154                left: ${x}px;
1155                top: ${y}px;
1156                width: ${size}px;
1157                height: ${size}px;
1158                background: ${color};
1159                border: ${selectedNode === i ? '3px solid #fff' : '2px solid rgba(255,255,255,0.7)'};
1160                border-radius: 50%;
1161                cursor: ${node.fixed ? 'move' : 'pointer'};
1162                transition: none;
1163                z-index: 10;
1164                box-shadow: 0 2px 8px rgba(0,0,0,0.3);
1165            `;
1166            
1167            // Add node label for larger nodes
1168            if (size > 20) {
1169                const label = document.createElement('div');
1170                label.style.cssText = `
1171                    position: absolute;
1172                    top: ${size + 4}px;
1173                    left: 50%;
1174                    transform: translateX(-50%);
1175                    font-size: ${Math.max(zoomLevel * 10, 8)}px;
1176                    color: var(--text-primary);
1177                    white-space: nowrap;
1178                    pointer-events: none;
1179                    text-shadow: 1px 1px 2px rgba(255,255,255,0.8);
1180                    font-weight: 600;
1181                `;
1182                label.textContent = node.name.length > 8 ? node.name.substring(0, 8) + '...' : node.name;
1183                nodeEl.appendChild(label);
1184            }
1185            
1186            // Add event listeners
1187            nodeEl.addEventListener('click', () => selectNode(i));
1188            nodeEl.addEventListener('mousedown', (e) => startDrag(e, i));
1189            
1190            container.appendChild(nodeEl);
1191        });
1192        
1193        // Update zoom display
1194        document.getElementById('zoom-display').textContent = Math.round(zoomLevel * 100) + '%';
1195    }
1196    
1197    // Event handlers
1198    function selectNode(nodeId) {
1199        selectedNode = nodeId;
1200        const node = nodes[nodeId];
1201        
1202        // Show info panel
1203        infoPanel.style.display = 'block';
1204        infoPanel.innerHTML = `
1205            <div style="font-weight: bold; margin-bottom: 8px; color: #4ecdc4;">📋 ${node.name}</div>
1206            <div><strong>Type:</strong> ${node.type}</div>
1207            <div><strong>Size:</strong> ${formatBytes(node.size)}</div>
1208            <div><strong>Leaked:</strong> ${node.isLeaked ? '❌ Yes' : '✅ No'}</div>
1209            <div><strong>Borrows:</strong> ${node.borrowCount}</div>
1210            ${node.cloneInfo ? `<div><strong>Clones:</strong> ${node.cloneInfo.clone_count || 0}</div>` : ''}
1211            <div><strong>Thread:</strong> ${node.alloc.thread_id || 'Unknown'}</div>
1212            <div style="margin-top: 8px; font-size: 10px; opacity: 0.8;">
1213                Click and drag to move â€Ē Double-click to pin
1214            </div>
1215        `;
1216        
1217        render();
1218    }
1219    
1220    function startDrag(e, nodeId) {
1221        e.preventDefault();
1222        e.stopPropagation(); // Prevent container panning
1223        isDragging = true;
1224        dragTarget = nodeId;
1225        
1226        const rect = container.getBoundingClientRect();
1227        const startX = e.clientX;
1228        const startY = e.clientY;
1229        const startNodeX = nodes[nodeId].x;
1230        const startNodeY = nodes[nodeId].y;
1231        
1232        // Visual feedback
1233        const nodeEl = document.querySelector(`[data-node-id="${nodeId}"]`);
1234        if (nodeEl) {
1235            nodeEl.style.transform = 'scale(1.2)';
1236            nodeEl.style.zIndex = '100';
1237        }
1238        
1239        function onMouseMove(e) {
1240            if (!isDragging || dragTarget === null) return;
1241            
1242            // Calculate movement in world coordinates
1243            const dx = (e.clientX - startX) / zoomLevel;
1244            const dy = (e.clientY - startY) / zoomLevel;
1245            
1246            // Update node position
1247            nodes[dragTarget].x = Math.max(20, Math.min(width - 20, startNodeX + dx));
1248            nodes[dragTarget].y = Math.max(20, Math.min(height - 20, startNodeY + dy));
1249            nodes[dragTarget].fixed = true;
1250            
1251            render();
1252        }
1253        
1254        function onMouseUp() {
1255            isDragging = false;
1256            
1257            // Reset visual feedback
1258            if (nodeEl) {
1259                nodeEl.style.transform = '';
1260                nodeEl.style.zIndex = '10';
1261            }
1262            
1263            dragTarget = null;
1264            document.removeEventListener('mousemove', onMouseMove);
1265            document.removeEventListener('mouseup', onMouseUp);
1266        }
1267        
1268        document.addEventListener('mousemove', onMouseMove);
1269        document.addEventListener('mouseup', onMouseUp);
1270    }
1271    
1272    // Control event listeners
1273    document.getElementById('zoom-in').addEventListener('click', () => {
1274        zoomLevel = Math.min(zoomLevel * 1.2, 3);
1275        render();
1276    });
1277    
1278    document.getElementById('zoom-out').addEventListener('click', () => {
1279        zoomLevel = Math.max(zoomLevel / 1.2, 0.3);
1280        render();
1281    });
1282    
1283    document.getElementById('reset-view').addEventListener('click', () => {
1284        zoomLevel = 1;
1285        panX = 0;
1286        panY = 0;
1287        selectedNode = null;
1288        infoPanel.style.display = 'none';
1289        nodes.forEach(node => node.fixed = false);
1290        render();
1291    });
1292    
1293    document.getElementById('auto-layout').addEventListener('click', () => {
1294        // Simple force-directed layout simulation
1295        for (let iteration = 0; iteration < 50; iteration++) {
1296            // Repulsion between nodes
1297            for (let i = 0; i < nodes.length; i++) {
1298                nodes[i].vx = 0;
1299                nodes[i].vy = 0;
1300                
1301                for (let j = 0; j < nodes.length; j++) {
1302                    if (i === j) continue;
1303                    
1304                    const dx = nodes[i].x - nodes[j].x;
1305                    const dy = nodes[i].y - nodes[j].y;
1306                    const distance = Math.sqrt(dx * dx + dy * dy) + 0.1;
1307                    const force = 100 / (distance * distance);
1308                    
1309                    nodes[i].vx += (dx / distance) * force;
1310                    nodes[i].vy += (dy / distance) * force;
1311                }
1312            }
1313            
1314            // Attraction along links
1315            links.forEach(link => {
1316                const source = nodes[link.source];
1317                const target = nodes[link.target];
1318                const dx = target.x - source.x;
1319                const dy = target.y - source.y;
1320                const distance = Math.sqrt(dx * dx + dy * dy) + 0.1;
1321                const force = distance * 0.01 * link.strength;
1322                
1323                source.vx += (dx / distance) * force;
1324                source.vy += (dy / distance) * force;
1325                target.vx -= (dx / distance) * force;
1326                target.vy -= (dy / distance) * force;
1327            });
1328            
1329            // Apply velocities
1330            nodes.forEach(node => {
1331                if (!node.fixed) {
1332                    node.x += node.vx * 0.1;
1333                    node.y += node.vy * 0.1;
1334                    
1335                    // Keep within bounds
1336                    node.x = Math.max(30, Math.min(width - 30, node.x));
1337                    node.y = Math.max(30, Math.min(height - 30, node.y));
1338                }
1339            });
1340        }
1341        
1342        render();
1343    });
1344    
1345    // Mouse wheel zoom
1346    container.addEventListener('wheel', (e) => {
1347        e.preventDefault();
1348        const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
1349        const rect = container.getBoundingClientRect();
1350        const mouseX = e.clientX - rect.left;
1351        const mouseY = e.clientY - rect.top;
1352        
1353        // Zoom towards mouse position
1354        const beforeZoomX = (mouseX - panX) / zoomLevel;
1355        const beforeZoomY = (mouseY - panY) / zoomLevel;
1356        
1357        zoomLevel = Math.max(0.3, Math.min(3, zoomLevel * zoomFactor));
1358        
1359        // Adjust pan to keep mouse position fixed
1360        panX = mouseX - beforeZoomX * zoomLevel;
1361        panY = mouseY - beforeZoomY * zoomLevel;
1362        
1363        render();
1364    });
1365    
1366    // Container pan functionality
1367    let isPanning = false;
1368    let panStartX = 0;
1369    let panStartY = 0;
1370    let panStartPanX = 0;
1371    let panStartPanY = 0;
1372    
1373    container.addEventListener('mousedown', (e) => {
1374        // Only start panning if not clicking on a node
1375        if (!e.target.classList.contains('graph-node')) {
1376            isPanning = true;
1377            panStartX = e.clientX;
1378            panStartY = e.clientY;
1379            panStartPanX = panX;
1380            panStartPanY = panY;
1381            container.style.cursor = 'grabbing';
1382        }
1383    });
1384    
1385    container.addEventListener('mousemove', (e) => {
1386        if (isPanning) {
1387            panX = panStartPanX + (e.clientX - panStartX);
1388            panY = panStartPanY + (e.clientY - panStartY);
1389            render();
1390        }
1391    });
1392    
1393    container.addEventListener('mouseup', () => {
1394        isPanning = false;
1395        container.style.cursor = 'default';
1396    });
1397    
1398    container.addEventListener('mouseleave', () => {
1399        isPanning = false;
1400        container.style.cursor = 'default';
1401    });
1402    
1403    // Initial render
1404    render();
1405    
1406    console.log(`✅ Interactive Variable Graph rendered with ${nodes.length} nodes and ${links.length} relationships`);
1407}
1408
1409function populateAllocationTable(allocations) {
1410    console.log('📋 Populating allocation table...');
1411    
1412    const tbody = document.getElementById('allocTable');
1413    if (!tbody) {
1414        console.warn('allocTable not found');
1415        return;
1416    }
1417    
1418    tbody.innerHTML = '';
1419    
1420    // Show first 100 allocations
1421    allocations.slice(0, 100).forEach(alloc => {
1422        const row = document.createElement('tr');
1423        row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
1424        
1425        const status = alloc.is_leaked ? 
1426            '<span class="status-badge status-leaked">Leaked</span>' :
1427            alloc.timestamp_dealloc ? 
1428            '<span class="status-badge status-freed">Freed</span>' :
1429            '<span class="status-badge status-active">Active</span>';
1430        
1431        row.innerHTML = `
1432            <td class="px-3 py-2 text-sm font-mono">${alloc.var_name || 'unnamed'}</td>
1433            <td class="px-3 py-2 text-sm">${alloc.type_name || 'unknown'}</td>
1434            <td class="px-3 py-2 text-sm">${formatBytes(alloc.size || 0)}</td>
1435            <td class="px-3 py-2 text-sm">${status}</td>
1436        `;
1437        
1438        tbody.appendChild(row);
1439    });
1440    
1441    console.log('✅ Allocation table populated with', Math.min(allocations.length, 100), 'entries');
1442}
1443
1444// Utility function for formatting bytes
1445function formatBytes(bytes) {
1446    if (bytes === 0) return '0 B';
1447    const k = 1024;
1448    const sizes = ['B', 'KB', 'MB', 'GB'];
1449    const i = Math.floor(Math.log(bytes) / Math.log(k));
1450    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
1451}
1452
1453// Enhanced mode selector for memory analysis
1454document.addEventListener('DOMContentLoaded', function() {
1455    const modeButtons = document.querySelectorAll('.heatmap-mode-btn');
1456    const visualizations = {
1457        heatmap: document.getElementById('memoryHeatmap'),
1458        type: document.getElementById('typeChart'), 
1459        distribution: document.getElementById('distributionChart')
1460    };
1461    
1462    modeButtons.forEach(btn => {
1463        btn.addEventListener('click', () => {
1464            // Remove active from all buttons
1465            modeButtons.forEach(b => b.classList.remove('active'));
1466            btn.classList.add('active');
1467            
1468            // Hide all visualizations
1469            Object.values(visualizations).forEach(viz => {
1470                if (viz) viz.style.display = 'none';
1471            });
1472            
1473            // Show selected visualization
1474            const mode = btn.dataset.mode;
1475            if (visualizations[mode]) {
1476                visualizations[mode].style.display = 'block';
1477            }
1478        });
1479    });
1480});
1481
1482console.log('ðŸ“Ķ Dashboard JavaScript loaded');
1483"#.to_string()
1484}
1485
1486// Embedded binary_dashboard.html template - 1:1 copy with all placeholders preserved
1487const EMBEDDED_BINARY_DASHBOARD_TEMPLATE: &str = r#"
1488<!DOCTYPE html>
1489<html lang="en">
1490
1491<head>
1492  <meta charset="UTF-8" />
1493  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1494  <title>{{PROJECT_NAME}} - Binary Memory Analysis Dashboard</title>
1495  <script src="https://cdn.tailwindcss.com"></script>
1496  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet" />
1497  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
1498  <script src="https://d3js.org/d3.v7.min.js"></script>
1499  <script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
1500  <script src="https://unpkg.com/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
1501
1502  <style>
1503    {
1504        {
1505        CSS_CONTENT
1506      }
1507    }
1508
1509    /* Clean, high-contrast layout variables */
1510    :root {
1511      --primary-blue: #2563eb;
1512      --primary-green: #059669;
1513      --primary-red: #dc2626;
1514      --primary-orange: #ea580c;
1515      --text-primary: #1f2937;
1516      --text-secondary: #6b7280;
1517      --bg-primary: #ffffff;
1518      --bg-secondary: #f8fafc;
1519      --border-light: #e5e7eb;
1520      --shadow-light: 0 1px 3px 0 rgb(0 0 0 / 0.1);
1521    }
1522
1523    .dark {
1524      --text-primary: #f9fafb;
1525      --text-secondary: #d1d5db;
1526      --bg-primary: #111827;
1527      --bg-secondary: #1f2937;
1528      --border-light: #374151;
1529      --shadow-light: 0 4px 6px -1px rgb(0 0 0 / 0.3);
1530    }
1531
1532    body {
1533      font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1534      background: var(--bg-secondary);
1535      color: var(--text-primary);
1536      transition: all 0.3s ease;
1537      line-height: 1.6;
1538      margin: 0;
1539      padding: 0;
1540    }
1541
1542    .dashboard-container {
1543      max-width: 1400px;
1544      margin: 0 auto;
1545      padding: 24px;
1546      min-height: 100vh;
1547    }
1548
1549    /* éĄķéƒĻ标éĒ˜æ  */
1550    .header {
1551      display: flex;
1552      justify-content: space-between;
1553      align-items: center;
1554      margin-bottom: 32px;
1555      padding: 20px 0;
1556      border-bottom: 1px solid var(--border-light);
1557    }
1558
1559    .header h1 {
1560      font-size: 2rem;
1561      font-weight: 700;
1562      color: var(--text-primary);
1563      margin: 0;
1564    }
1565
1566    .header .subtitle {
1567      color: var(--text-secondary);
1568      font-size: 0.9rem;
1569      margin-top: 4px;
1570    }
1571
1572    /* äļŧéĒ˜åˆ‡æĒ按é’Ū */
1573    .theme-toggle {
1574      background: var(--primary-blue);
1575      color: white;
1576      border: none;
1577      padding: 10px 16px;
1578      border-radius: 8px;
1579      cursor: pointer;
1580      font-size: 14px;
1581      font-weight: 500;
1582      transition: all 0.2s ease;
1583      display: flex;
1584      align-items: center;
1585      gap: 8px;
1586    }
1587
1588    .theme-toggle:hover {
1589      background: #1d4ed8;
1590      transform: translateY(-1px);
1591    }
1592
1593    /* åĄį‰‡æ ·åž */
1594    .card {
1595      background: var(--bg-primary);
1596      border: 1px solid var(--border-light);
1597      border-radius: 12px;
1598      padding: 24px;
1599      box-shadow: var(--shadow-light);
1600      transition: all 0.3s ease;
1601    }
1602
1603    .card:hover {
1604      transform: translateY(-2px);
1605      box-shadow: 0 8px 25px -5px rgb(0 0 0 / 0.1);
1606    }
1607
1608    .card h2 {
1609      font-size: 1.25rem;
1610      font-weight: 600;
1611      color: var(--text-primary);
1612      margin: 0 0 16px 0;
1613      border-bottom: 2px solid var(--primary-blue);
1614      padding-bottom: 8px;
1615    }
1616
1617    /* į―‘æ žåļƒåą€ */
1618    .grid {
1619      display: grid;
1620      gap: 24px;
1621      margin-bottom: 32px;
1622    }
1623
1624    .grid-2 {
1625      grid-template-columns: 1fr 1fr;
1626    }
1627
1628    .grid-3 {
1629      grid-template-columns: repeat(3, 1fr);
1630    }
1631
1632    .grid-4 {
1633      grid-template-columns: repeat(4, 1fr);
1634    }
1635
1636    /* KPI */
1637    .kpi-card {
1638      text-align: center;
1639      padding: 20px;
1640      background: linear-gradient(135deg, var(--primary-blue) 0%, #3b82f6 100%);
1641      color: white;
1642      border-radius: 12px;
1643      border: none;
1644      box-shadow: var(--shadow-light);
1645    }
1646
1647    .kpi-value {
1648      font-size: 2rem;
1649      font-weight: 700;
1650      margin-bottom: 4px;
1651    }
1652
1653    .kpi-label {
1654      font-size: 0.875rem;
1655      opacity: 0.9;
1656      font-weight: 500;
1657    }
1658
1659
1660    .chart-container {
1661      height: 300px;
1662      background: var(--bg-primary);
1663      border-radius: 8px;
1664      position: relative;
1665      padding: 16px;
1666    }
1667
1668    table {
1669      width: 100%;
1670      border-collapse: collapse;
1671      margin-top: 16px;
1672    }
1673
1674    th,
1675    td {
1676      padding: 12px;
1677      text-align: left;
1678      border-bottom: 1px solid var(--border-light);
1679    }
1680
1681    th {
1682      background: var(--bg-secondary);
1683      font-weight: 600;
1684      color: var(--text-primary);
1685    }
1686
1687    tr:hover {
1688      background: var(--bg-secondary);
1689    }
1690
1691    @media (max-width: 768px) {
1692
1693      .grid-2,
1694      .grid-3,
1695      .grid-4 {
1696        grid-template-columns: 1fr;
1697      }
1698
1699      .dashboard-container {
1700        padding: 16px;
1701      }
1702
1703      .header {
1704        flex-direction: column;
1705        gap: 16px;
1706        text-align: center;
1707      }
1708    }
1709
1710    .scroll {
1711      max-height: 400px;
1712      overflow: auto;
1713    }
1714
1715    .scroll::-webkit-scrollbar {
1716      width: 6px;
1717    }
1718
1719    .scroll::-webkit-scrollbar-track {
1720      background: var(--bg-secondary);
1721    }
1722
1723    .scroll::-webkit-scrollbar-thumb {
1724      background: var(--border-light);
1725      border-radius: 3px;
1726    }
1727
1728    .status-badge {
1729      padding: 4px 8px;
1730      border-radius: 4px;
1731      font-size: 0.75rem;
1732      font-weight: 500;
1733    }
1734
1735    .status-active {
1736      background: #dcfce7;
1737      color: #166534;
1738    }
1739
1740    .status-leaked {
1741      background: #fee2e2;
1742      color: #dc2626;
1743    }
1744
1745    .status-freed {
1746      background: #e5e7eb;
1747      color: #374151;
1748    }
1749
1750    .dark .status-active {
1751      background: #064e3b;
1752      color: #34d399;
1753    }
1754
1755    .dark .status-leaked {
1756      background: #7f1d1d;
1757      color: #fca5a5;
1758    }
1759
1760    .dark .status-freed {
1761      background: #374151;
1762      color: #d1d5db;
1763    }
1764
1765    .risk-low {
1766      background: #dcfce7;
1767      color: #166534;
1768    }
1769
1770    .risk-medium {
1771      background: #fef3c7;
1772      color: #92400e;
1773    }
1774
1775    .risk-high {
1776      background: #fee2e2;
1777      color: #dc2626;
1778    }
1779
1780    .dark .risk-low {
1781      background: #064e3b;
1782      color: #34d399;
1783    }
1784
1785    .dark .risk-medium {
1786      background: #78350f;
1787      color: #fbbf24;
1788    }
1789
1790    .dark .risk-high {
1791      background: #7f1d1d;
1792      color: #fca5a5;
1793    }
1794
1795    /* Enhanced Lifecycle Visualization Styles */
1796    .allocation-type {
1797      padding: 3px 6px;
1798      border-radius: 3px;
1799      font-size: 0.7rem;
1800      font-weight: 600;
1801      text-transform: uppercase;
1802      display: inline-block;
1803    }
1804
1805    .type-heap {
1806      background: #fef3c7;
1807      color: #92400e;
1808      border: 1px solid #f59e0b;
1809    }
1810
1811    .type-stack {
1812      background: #dbeafe;
1813      color: #1e40af;
1814      border: 1px solid #3b82f6;
1815    }
1816
1817    .type-unknown {
1818      background: #f3f4f6;
1819      color: #6b7280;
1820      border: 1px solid #9ca3af;
1821    }
1822
1823    .dark .type-heap {
1824      background: #78350f;
1825      color: #fbbf24;
1826    }
1827
1828    .dark .type-stack {
1829      background: #1e3a8a;
1830      color: #60a5fa;
1831    }
1832
1833    .dark .type-unknown {
1834      background: #374151;
1835      color: #d1d5db;
1836    }
1837
1838    /* Enhanced progress bar animations */
1839    @keyframes shine {
1840      0% {
1841        left: -100%;
1842      }
1843
1844      50% {
1845        left: 100%;
1846      }
1847
1848      100% {
1849        left: 100%;
1850      }
1851    }
1852
1853    @keyframes pulse {
1854
1855      0%,
1856      100% {
1857        opacity: 1;
1858      }
1859
1860      50% {
1861        opacity: 0.7;
1862      }
1863    }
1864
1865    /* Enhanced lifecycle item styles */
1866    .lifecycle-item.heap {
1867      border-left-color: #ff6b35 !important;
1868    }
1869
1870    .lifecycle-item.stack {
1871      border-left-color: #4dabf7 !important;
1872    }
1873
1874    .lifecycle-item:hover {
1875      animation: pulse 1s ease-in-out;
1876    }
1877
1878    .lifecycle-bar {
1879      height: 16px;
1880      background: var(--bg-secondary);
1881      border-radius: 8px;
1882      position: relative;
1883      overflow: hidden;
1884      margin: 6px 0;
1885      border: 1px solid var(--border-light);
1886    }
1887
1888    .lifecycle-progress {
1889      height: 100%;
1890      background: linear-gradient(90deg, var(--primary-green), var(--primary-blue));
1891      border-radius: 7px;
1892      position: relative;
1893      transition: width 0.3s ease;
1894    }
1895
1896    .lifecycle-item {
1897      margin: 8px 0;
1898      padding: 12px;
1899      background: var(--bg-secondary);
1900      border-radius: 8px;
1901      border-left: 4px solid var(--primary-blue);
1902      transition: all 0.2s ease;
1903    }
1904
1905    .lifecycle-item:hover {
1906      background: var(--bg-primary);
1907      box-shadow: var(--shadow-light);
1908    }
1909
1910    .lifecycle-item.heap {
1911      border-left-color: var(--primary-orange);
1912    }
1913
1914    .lifecycle-item.stack {
1915      border-left-color: var(--primary-blue);
1916    }
1917
1918    .time-info {
1919      font-size: 0.75rem;
1920      color: var(--text-secondary);
1921      margin-top: 6px;
1922      font-family: 'Courier New', monospace;
1923    }
1924
1925    .time-badge {
1926      display: inline-block;
1927      padding: 2px 6px;
1928      background: var(--bg-primary);
1929      border-radius: 3px;
1930      margin-right: 8px;
1931      border: 1px solid var(--border-light);
1932    }
1933
1934    /* Enhanced 3D Memory Layout Styles */
1935    .memory-3d-container {
1936      position: relative;
1937      width: 100%;
1938      height: 400px;
1939      background: var(--bg-secondary);
1940      border: 1px solid var(--border-light);
1941      border-radius: 8px;
1942      overflow: hidden;
1943    }
1944
1945    /* Removed problematic absolute positioning CSS for memory-3d-controls */
1946
1947    .memory-3d-info {
1948      position: absolute;
1949      bottom: 10px;
1950      left: 10px;
1951      z-index: 100;
1952      background: rgba(0, 0, 0, 0.7);
1953      color: white;
1954      padding: 8px 12px;
1955      border-radius: 6px;
1956      font-size: 0.8rem;
1957      font-family: monospace;
1958    }
1959
1960    /* Timeline Playback Styles */
1961    .timeline-container {
1962      background: var(--bg-secondary);
1963      border: 1px solid var(--border-light);
1964      border-radius: 8px;
1965      padding: 16px;
1966      margin: 16px 0;
1967    }
1968
1969    .timeline-slider {
1970      width: 100%;
1971      height: 6px;
1972      background: var(--border-light);
1973      border-radius: 3px;
1974      position: relative;
1975      cursor: pointer;
1976      margin: 12px 0;
1977    }
1978
1979    .timeline-progress {
1980      height: 100%;
1981      background: linear-gradient(90deg, var(--primary-blue), var(--primary-green));
1982      border-radius: 3px;
1983      transition: width 0.1s ease;
1984    }
1985
1986    .timeline-thumb {
1987      position: absolute;
1988      top: -6px;
1989      width: 18px;
1990      height: 18px;
1991      background: var(--primary-blue);
1992      border: 2px solid white;
1993      border-radius: 50%;
1994      cursor: grab;
1995      box-shadow: var(--shadow-light);
1996    }
1997
1998    .timeline-thumb:active {
1999      cursor: grabbing;
2000      transform: scale(1.1);
2001    }
2002
2003    .timeline-controls {
2004      display: flex;
2005      justify-content: center;
2006      gap: 12px;
2007      margin-top: 12px;
2008    }
2009
2010    .timeline-btn {
2011      background: var(--primary-blue);
2012      color: white;
2013      border: none;
2014      padding: 8px 12px;
2015      border-radius: 6px;
2016      cursor: pointer;
2017      font-size: 0.8rem;
2018      transition: all 0.2s ease;
2019    }
2020
2021    .timeline-btn:hover {
2022      background: #1d4ed8;
2023      transform: translateY(-1px);
2024    }
2025
2026    .timeline-btn:disabled {
2027      background: var(--text-secondary);
2028      cursor: not-allowed;
2029      transform: none;
2030    }
2031
2032    /* Memory Heatmap Styles */
2033    .heatmap-container {
2034      position: relative;
2035      width: 100%;
2036      height: 300px;
2037      background: var(--bg-secondary);
2038      border: 1px solid var(--border-light);
2039      border-radius: 8px;
2040      overflow: hidden;
2041    }
2042
2043    .heatmap-legend {
2044      position: absolute;
2045      top: 10px;
2046      right: 10px;
2047      z-index: 100;
2048      background: rgba(255, 255, 255, 0.9);
2049      padding: 8px;
2050      border-radius: 6px;
2051      font-size: 0.7rem;
2052    }
2053
2054    .dark .heatmap-legend {
2055      background: rgba(0, 0, 0, 0.8);
2056      color: white;
2057    }
2058
2059    .heatmap-mode-selector {
2060      display: flex;
2061      gap: 6px;
2062      margin-bottom: 12px;
2063    }
2064
2065    .heatmap-mode-btn {
2066      background: var(--bg-primary);
2067      color: var(--text-primary);
2068      border: 1px solid var(--border-light);
2069      padding: 6px 12px;
2070      border-radius: 4px;
2071      cursor: pointer;
2072      font-size: 0.8rem;
2073      transition: all 0.2s ease;
2074    }
2075
2076    .heatmap-mode-btn.active {
2077      background: var(--primary-blue);
2078      color: white;
2079      border-color: var(--primary-blue);
2080    }
2081
2082    .heatmap-mode-btn:hover {
2083      background: var(--primary-blue);
2084      color: white;
2085    }
2086
2087    /* Memory Block Visualization */
2088    .memory-block {
2089      position: absolute;
2090      border: 1px solid rgba(255, 255, 255, 0.3);
2091      border-radius: 2px;
2092      cursor: pointer;
2093      transition: all 0.2s ease;
2094    }
2095
2096    .memory-block:hover {
2097      border-color: white;
2098      border-width: 2px;
2099      z-index: 10;
2100    }
2101
2102    .memory-block.heap {
2103      background: linear-gradient(45deg, #ff6b35, #f7931e);
2104    }
2105
2106    .memory-block.stack {
2107      background: linear-gradient(45deg, #4dabf7, #339af0);
2108    }
2109
2110    .memory-block.leaked {
2111      background: linear-gradient(45deg, #dc2626, #ef4444);
2112      box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
2113    }
2114
2115    /* Tooltip Styles */
2116    .memory-tooltip {
2117      position: absolute;
2118      background: rgba(0, 0, 0, 0.9);
2119      color: white;
2120      padding: 8px 12px;
2121      border-radius: 6px;
2122      font-size: 0.8rem;
2123      font-family: monospace;
2124      pointer-events: none;
2125      z-index: 1000;
2126      max-width: 300px;
2127      line-height: 1.4;
2128    }
2129
2130    .memory-tooltip::after {
2131      content: '';
2132      position: absolute;
2133      top: 100%;
2134      left: 50%;
2135      margin-left: -5px;
2136      border-width: 5px;
2137      border-style: solid;
2138      border-color: rgba(0, 0, 0, 0.9) transparent transparent transparent;
2139    }
2140  </style>
2141  <script>
2142    // Global safe update function - must be defined first
2143    function safeUpdateElement(id, value, defaultValue = '-') {
2144      try {
2145        const el = document.getElementById(id);
2146        if (el) {
2147          el.textContent = value;
2148          return true;
2149        } else {
2150          console.warn(`Element with ID '${id}' not found`);
2151          return false;
2152        }
2153      } catch (error) {
2154        console.error(`Error updating element '${id}':`, error);
2155        return false;
2156      }
2157    }
2158
2159    // Global formatBytes function
2160    function formatBytes(bytes) {
2161        if (bytes === 0) return '0 B';
2162        if (typeof bytes !== 'number') return '0 B';
2163        
2164        const k = 1024;
2165        const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
2166        const i = Math.floor(Math.log(bytes) / Math.log(k));
2167        
2168        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
2169    }
2170
2171    // Beautiful modal dialog system
2172    function createModal(title, content) {
2173        // Remove existing modal if any
2174        const existingModal = document.getElementById('custom-modal');
2175        if (existingModal) {
2176            existingModal.remove();
2177        }
2178        
2179        const modal = document.createElement('div');
2180        modal.id = 'custom-modal';
2181        modal.style.cssText = `
2182            position: fixed;
2183            top: 0;
2184            left: 0;
2185            width: 100%;
2186            height: 100%;
2187            background: rgba(0, 0, 0, 0.7);
2188            display: flex;
2189            align-items: center;
2190            justify-content: center;
2191            z-index: 10000;
2192            opacity: 0;
2193            transition: opacity 0.3s ease;
2194        `;
2195        
2196        const modalContent = document.createElement('div');
2197        modalContent.style.cssText = `
2198            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2199            border-radius: 16px;
2200            padding: 0;
2201            max-width: 500px;
2202            width: 90%;
2203            max-height: 80%;
2204            overflow: hidden;
2205            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
2206            transform: scale(0.7);
2207            transition: transform 0.3s ease;
2208            border: 2px solid rgba(255, 255, 255, 0.2);
2209        `;
2210        
2211        const header = document.createElement('div');
2212        header.style.cssText = `
2213            background: rgba(255, 255, 255, 0.15);
2214            padding: 20px 24px;
2215            border-bottom: 1px solid rgba(255, 255, 255, 0.1);
2216            display: flex;
2217            justify-content: space-between;
2218            align-items: center;
2219        `;
2220        
2221        const titleEl = document.createElement('h3');
2222        titleEl.style.cssText = `
2223            margin: 0;
2224            color: white;
2225            font-size: 20px;
2226            font-weight: 600;
2227            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
2228        `;
2229        titleEl.textContent = title;
2230        
2231        const closeBtn = document.createElement('button');
2232        closeBtn.style.cssText = `
2233            background: rgba(255, 255, 255, 0.2);
2234            border: none;
2235            color: white;
2236            width: 32px;
2237            height: 32px;
2238            border-radius: 50%;
2239            cursor: pointer;
2240            display: flex;
2241            align-items: center;
2242            justify-content: center;
2243            font-size: 18px;
2244            font-weight: bold;
2245            transition: background 0.2s ease;
2246        `;
2247        closeBtn.innerHTML = '×';
2248        closeBtn.addEventListener('mouseenter', () => {
2249            closeBtn.style.background = 'rgba(255, 255, 255, 0.3)';
2250        });
2251        closeBtn.addEventListener('mouseleave', () => {
2252            closeBtn.style.background = 'rgba(255, 255, 255, 0.2)';
2253        });
2254        
2255        const body = document.createElement('div');
2256        body.style.cssText = `
2257            padding: 24px;
2258            color: white;
2259            line-height: 1.6;
2260            overflow-y: auto;
2261            max-height: 400px;
2262        `;
2263        body.innerHTML = content;
2264        
2265        function closeModal() {
2266            modal.style.opacity = '0';
2267            modalContent.style.transform = 'scale(0.7)';
2268            setTimeout(() => {
2269                modal.remove();
2270            }, 300);
2271        }
2272        
2273        closeBtn.addEventListener('click', closeModal);
2274        modal.addEventListener('click', (e) => {
2275            if (e.target === modal) closeModal();
2276        });
2277        
2278        document.addEventListener('keydown', function escapeHandler(e) {
2279            if (e.key === 'Escape') {
2280                closeModal();
2281                document.removeEventListener('keydown', escapeHandler);
2282            }
2283        });
2284        
2285        header.appendChild(titleEl);
2286        header.appendChild(closeBtn);
2287        modalContent.appendChild(header);
2288        modalContent.appendChild(body);
2289        modal.appendChild(modalContent);
2290        document.body.appendChild(modal);
2291        
2292        // Animate in
2293        setTimeout(() => {
2294            modal.style.opacity = '1';
2295            modalContent.style.transform = 'scale(1)';
2296        }, 10);
2297        
2298        return modal;
2299    }
2300
2301    // Data injection placeholder - will be replaced by build tool
2302    window.analysisData = {{BINARY_DATA}};
2303
2304    // Emergency fallback: Load data directly from JSON files if injection failed
2305    if (!window.analysisData || Object.keys(window.analysisData).length === 0 ||
2306      !window.analysisData.memory_analysis || !window.analysisData.memory_analysis.allocations) {
2307
2308      console.warn('Data injection failed, attempting to load from JSON files...');
2309
2310      // Try to fetch the JSON data directly
2311      fetch('./large_scale_user_memory_analysis.json')
2312        .then(response => response.json())
2313        .then(memoryData => {
2314          console.log('✅ Loaded memory analysis data:', memoryData);
2315
2316          // Construct the expected data structure
2317          window.analysisData = {
2318            memory_analysis: memoryData,
2319            lifetime: {},
2320            complex_types: {},
2321            unsafe_ffi: {},
2322            performance: {}
2323          };
2324
2325          // Try to load other JSON files
2326          Promise.all([
2327            fetch('./large_scale_user_lifetime.json').then(r => r.json()).catch(() => ({})),
2328            fetch('./large_scale_user_complex_types.json').then(r => r.json()).catch(() => ({})),
2329            fetch('./large_scale_user_unsafe_ffi.json').then(r => r.json()).catch(() => ({})),
2330            fetch('./large_scale_user_performance.json').then(r => r.json()).catch(() => ({}))
2331          ]).then(([lifetime, complexTypes, unsafeFfi, performance]) => {
2332            window.analysisData.lifetime = lifetime;
2333            window.analysisData.complex_types = complexTypes;
2334            window.analysisData.unsafe_ffi = unsafeFfi;
2335            window.analysisData.performance = performance;
2336
2337            console.log('✅ All data loaded, initializing enhanced features...');
2338
2339            // Trigger enhanced features initialization
2340            console.log('🚀 Triggering enhanced features initialization...');
2341            if (typeof initEnhancedLifecycleVisualization === 'function') {
2342              setTimeout(() => {
2343                console.log('🔄 Calling initEnhancedLifecycleVisualization...');
2344                initEnhancedLifecycleVisualization();
2345              }, 100);
2346            } else {
2347              console.error('❌ initEnhancedLifecycleVisualization function not found');
2348            }
2349
2350            // Also trigger the main dashboard initialization if needed
2351            if (typeof initDashboard === 'function') {
2352              setTimeout(() => {
2353                console.log('🔄 Calling initDashboard...');
2354                initDashboard();
2355              }, 200);
2356            }
2357          });
2358        })
2359        .catch(error => {
2360          console.error('❌ Failed to load JSON data:', error);
2361
2362          // Last resort: Create dummy data for testing
2363          window.analysisData = {
2364            memory_analysis: {
2365              allocations: [
2366                {
2367                  var_name: 'test_var_1',
2368                  type_name: 'Arc<String>',
2369                  size: 1024,
2370                  timestamp_alloc: Date.now() * 1000000,
2371                  lifetime_ms: 100.5,
2372                  is_leaked: false
2373                },
2374                {
2375                  var_name: 'test_var_2',
2376                  type_name: 'Vec<i32>',
2377                  size: 2048,
2378                  timestamp_alloc: Date.now() * 1000000 + 1000000,
2379                  lifetime_ms: 250.0,
2380                  is_leaked: true
2381                }
2382              ]
2383            }
2384          };
2385          console.log('⚠ïļ Using dummy data for testing');
2386        });
2387    } else {
2388      console.log('✅ Data injection successful');
2389    }
2390
2391    console.log('Final analysisData:', window.analysisData);
2392    console.log('Allocations available:', window.analysisData?.memory_analysis?.allocations?.length || 0);
2393
2394    // Enhanced Memory Visualization Functions
2395    class EnhancedMemoryVisualizer {
2396      constructor() {
2397        this.scene = null;
2398        this.camera = null;
2399        this.renderer = null;
2400        this.controls = null;
2401        this.memoryBlocks = [];
2402        this.timeline = {
2403          isPlaying: false,
2404          currentTime: 0,
2405          totalTime: 0,
2406          speed: 1000, // ms per step
2407          data: []
2408        };
2409        this.heatmapMode = 'density';
2410        this.tooltip = null;
2411        this.initialized = false;
2412      }
2413
2414      init() {
2415        if (this.initialized) return;
2416        console.log('Initializing EnhancedMemoryVisualizer...');
2417
2418        this.initTooltip();
2419        this.init3DVisualization();
2420        this.initTimelineControls();
2421        this.initHeatmap();
2422        this.bindEvents();
2423
2424        this.initialized = true;
2425        console.log('EnhancedMemoryVisualizer initialized successfully');
2426      }
2427
2428      initTooltip() {
2429        this.tooltip = document.createElement('div');
2430        this.tooltip.className = 'memory-tooltip';
2431        this.tooltip.style.display = 'none';
2432        document.body.appendChild(this.tooltip);
2433      }
2434
2435      init3DVisualization() {
2436        const container = document.getElementById('memory3DContainer');
2437        if (!container) return;
2438
2439        // Scene setup with dark gradient background
2440        this.scene = new THREE.Scene();
2441        this.scene.background = new THREE.Color(0x1a1a2e);
2442
2443        // Camera setup - closer view for better data inspection
2444        this.camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
2445        this.camera.position.set(15, 10, 15);
2446
2447        // Renderer setup with enhanced settings
2448        this.renderer = new THREE.WebGLRenderer({
2449          antialias: true,
2450          alpha: true,
2451          powerPreference: "high-performance"
2452        });
2453        this.renderer.setSize(container.clientWidth, container.clientHeight);
2454        this.renderer.shadowMap.enabled = true;
2455        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
2456        this.renderer.setClearColor(0x1a1a2e, 1);
2457        this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
2458        container.appendChild(this.renderer.domElement);
2459
2460        // Controls setup with enhanced interaction
2461        try {
2462          if (typeof THREE !== 'undefined') {
2463            const OrbitControls = THREE.OrbitControls || window.OrbitControls;
2464            if (OrbitControls) {
2465              this.controls = new OrbitControls(this.camera, this.renderer.domElement);
2466              this.controls.enableDamping = true;
2467              this.controls.dampingFactor = 0.05;
2468              this.controls.enableZoom = true;
2469              this.controls.enablePan = true;
2470              this.controls.enableRotate = true;
2471              this.controls.autoRotate = false;
2472              this.controls.autoRotateSpeed = 0.5;
2473              this.controls.minDistance = 2;  // Allow closer inspection
2474              this.controls.maxDistance = 100;
2475              this.controls.maxPolarAngle = Math.PI;
2476              this.controls.minPolarAngle = 0;
2477            } else {
2478              console.warn('OrbitControls not available, setting up manual controls');
2479              this.setupManualControls();
2480            }
2481          }
2482        } catch (error) {
2483          console.warn('Failed to initialize OrbitControls:', error);
2484        }
2485
2486        // Enhanced lighting setup
2487        const ambientLight = new THREE.AmbientLight(0x404040, 0.4);
2488        this.scene.add(ambientLight);
2489
2490        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
2491        directionalLight.position.set(50, 50, 25);
2492        directionalLight.castShadow = true;
2493        directionalLight.shadow.mapSize.width = 2048;
2494        directionalLight.shadow.mapSize.height = 2048;
2495        directionalLight.shadow.camera.near = 0.5;
2496        directionalLight.shadow.camera.far = 500;
2497        this.scene.add(directionalLight);
2498
2499        // Add subtle rim lighting
2500        const rimLight = new THREE.DirectionalLight(0x4a90e2, 0.3);
2501        rimLight.position.set(-50, 20, -25);
2502        this.scene.add(rimLight);
2503
2504        // Add point light for dynamic effects
2505        this.pointLight = new THREE.PointLight(0x4a90e2, 0.5, 100);
2506        this.pointLight.position.set(0, 20, 0);
2507        this.scene.add(this.pointLight);
2508
2509        // Create subtle floor plane instead of grid
2510        const floorGeometry = new THREE.PlaneGeometry(100, 100);
2511        const floorMaterial = new THREE.MeshLambertMaterial({
2512          color: 0x2a2a3e,
2513          transparent: true,
2514          opacity: 0.3
2515        });
2516        this.floor = new THREE.Mesh(floorGeometry, floorMaterial);
2517        this.floor.rotation.x = -Math.PI / 2;
2518        this.floor.position.y = -0.1;
2519        this.floor.receiveShadow = true;
2520        this.scene.add(this.floor);
2521
2522        // Initialize animation properties
2523        this.animationTime = 0;
2524        this.isAutoRotating = false;
2525
2526        this.animate3D();
2527      }
2528
2529      setupManualControls() {
2530        if (!this.renderer || !this.camera) return;
2531
2532        const canvas = this.renderer.domElement;
2533        let isMouseDown = false;
2534        let mouseX = 0, mouseY = 0;
2535        let cameraDistance = 20;
2536        let cameraAngleX = 0;
2537        let cameraAngleY = 0;
2538
2539        // Mouse controls for rotation
2540        canvas.addEventListener('mousedown', (event) => {
2541          isMouseDown = true;
2542          mouseX = event.clientX;
2543          mouseY = event.clientY;
2544          canvas.style.cursor = 'grabbing';
2545        });
2546
2547        canvas.addEventListener('mousemove', (event) => {
2548          if (!isMouseDown) return;
2549
2550          const deltaX = event.clientX - mouseX;
2551          const deltaY = event.clientY - mouseY;
2552
2553          cameraAngleY += deltaX * 0.01;
2554          cameraAngleX += deltaY * 0.01;
2555
2556          // Limit vertical rotation
2557          cameraAngleX = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, cameraAngleX));
2558
2559          // Update camera position
2560          this.camera.position.x = Math.cos(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2561          this.camera.position.y = Math.sin(cameraAngleX) * cameraDistance;
2562          this.camera.position.z = Math.sin(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2563
2564          this.camera.lookAt(0, 0, 0);
2565
2566          mouseX = event.clientX;
2567          mouseY = event.clientY;
2568        });
2569
2570        canvas.addEventListener('mouseup', () => {
2571          isMouseDown = false;
2572          canvas.style.cursor = 'grab';
2573        });
2574
2575        canvas.addEventListener('mouseleave', () => {
2576          isMouseDown = false;
2577          canvas.style.cursor = 'default';
2578        });
2579
2580        // Zoom with mouse wheel
2581        canvas.addEventListener('wheel', (event) => {
2582          event.preventDefault();
2583
2584          const zoomSpeed = 0.1;
2585          cameraDistance += event.deltaY * zoomSpeed;
2586          cameraDistance = Math.max(2, Math.min(100, cameraDistance));
2587
2588          // Update camera position
2589          this.camera.position.x = Math.cos(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2590          this.camera.position.y = Math.sin(cameraAngleX) * cameraDistance;
2591          this.camera.position.z = Math.sin(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2592
2593          this.camera.lookAt(0, 0, 0);
2594        });
2595
2596        // Touch controls for mobile
2597        let lastTouchDistance = 0;
2598
2599        canvas.addEventListener('touchstart', (event) => {
2600          if (event.touches.length === 1) {
2601            isMouseDown = true;
2602            mouseX = event.touches[0].clientX;
2603            mouseY = event.touches[0].clientY;
2604          } else if (event.touches.length === 2) {
2605            const touch1 = event.touches[0];
2606            const touch2 = event.touches[1];
2607            lastTouchDistance = Math.sqrt(
2608              Math.pow(touch2.clientX - touch1.clientX, 2) +
2609              Math.pow(touch2.clientY - touch1.clientY, 2)
2610            );
2611          }
2612        });
2613
2614        canvas.addEventListener('touchmove', (event) => {
2615          event.preventDefault();
2616
2617          if (event.touches.length === 1 && isMouseDown) {
2618            const deltaX = event.touches[0].clientX - mouseX;
2619            const deltaY = event.touches[0].clientY - mouseY;
2620
2621            cameraAngleY += deltaX * 0.01;
2622            cameraAngleX += deltaY * 0.01;
2623            cameraAngleX = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, cameraAngleX));
2624
2625            this.camera.position.x = Math.cos(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2626            this.camera.position.y = Math.sin(cameraAngleX) * cameraDistance;
2627            this.camera.position.z = Math.sin(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2628
2629            this.camera.lookAt(0, 0, 0);
2630
2631            mouseX = event.touches[0].clientX;
2632            mouseY = event.touches[0].clientY;
2633          } else if (event.touches.length === 2) {
2634            const touch1 = event.touches[0];
2635            const touch2 = event.touches[1];
2636            const touchDistance = Math.sqrt(
2637              Math.pow(touch2.clientX - touch1.clientX, 2) +
2638              Math.pow(touch2.clientY - touch1.clientY, 2)
2639            );
2640
2641            if (lastTouchDistance > 0) {
2642              const zoomDelta = (lastTouchDistance - touchDistance) * 0.01;
2643              cameraDistance += zoomDelta;
2644              cameraDistance = Math.max(2, Math.min(100, cameraDistance));
2645
2646              this.camera.position.x = Math.cos(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2647              this.camera.position.y = Math.sin(cameraAngleX) * cameraDistance;
2648              this.camera.position.z = Math.sin(cameraAngleY) * Math.cos(cameraAngleX) * cameraDistance;
2649
2650              this.camera.lookAt(0, 0, 0);
2651            }
2652
2653            lastTouchDistance = touchDistance;
2654          }
2655        });
2656
2657        canvas.addEventListener('touchend', () => {
2658          isMouseDown = false;
2659          lastTouchDistance = 0;
2660        });
2661
2662        canvas.style.cursor = 'grab';
2663        console.log('✅ Manual 3D controls initialized (mouse drag to rotate, wheel to zoom)');
2664      }
2665
2666      animate3D() {
2667        requestAnimationFrame(() => this.animate3D());
2668
2669        this.animationTime += 0.01;
2670
2671        // Animate point light for dynamic lighting
2672        if (this.pointLight) {
2673          this.pointLight.position.x = Math.sin(this.animationTime) * 30;
2674          this.pointLight.position.z = Math.cos(this.animationTime) * 30;
2675          this.pointLight.intensity = 0.3 + Math.sin(this.animationTime * 2) * 0.2;
2676        }
2677
2678        // Animate memory blocks with subtle floating effect
2679        this.memoryBlocks.forEach((block, index) => {
2680          if (block && block.position) {
2681            const originalY = block.userData.originalY || block.position.y;
2682            block.position.y = originalY + Math.sin(this.animationTime + index * 0.1) * 0.2;
2683
2684            // Add subtle rotation for leaked memory blocks
2685            if (block.userData.is_leaked) {
2686              block.rotation.y += 0.02;
2687            }
2688          }
2689        });
2690
2691        // Update controls
2692        if (this.controls) {
2693          this.controls.update();
2694        }
2695
2696        // Render scene
2697        if (this.renderer && this.scene && this.camera) {
2698          this.renderer.render(this.scene, this.camera);
2699        }
2700      }
2701
2702      create3DMemoryBlocks(allocations) {
2703        if (!this.scene || !allocations) return;
2704
2705        // Clear existing blocks with fade out animation
2706        this.memoryBlocks.forEach(block => {
2707          if (block && block.material) {
2708            // Fade out animation
2709            const fadeOut = () => {
2710              block.material.opacity -= 0.05;
2711              if (block.material.opacity <= 0) {
2712                this.scene.remove(block);
2713                if (block.geometry) block.geometry.dispose();
2714                if (block.material) block.material.dispose();
2715              } else {
2716                requestAnimationFrame(fadeOut);
2717              }
2718            };
2719            fadeOut();
2720          }
2721        });
2722        this.memoryBlocks = [];
2723
2724        // Sort allocations by size for better visual hierarchy
2725        const sortedAllocs = [...allocations].sort((a, b) => (b.size || 0) - (a.size || 0));
2726
2727        const maxBlocksPerRow = 15;
2728        const spacing = 4;
2729
2730        sortedAllocs.forEach((alloc, index) => {
2731          const size = Math.max(alloc.size || 1, 1);
2732          const blockSize = Math.cbrt(size / 50) + 0.8; // Enhanced size calculation
2733
2734          // Spiral positioning for better visual distribution
2735          const angle = index * 0.5;
2736          const radius = Math.sqrt(index) * 2;
2737          const x = Math.cos(angle) * radius;
2738          const z = Math.sin(angle) * radius;
2739          const y = blockSize / 2;
2740
2741          // Enhanced color scheme based on type and size
2742          let color = 0x3b82f6;
2743          let emissive = 0x000000;
2744          const typeName = alloc.type_name || '';
2745
2746          if (typeName.includes('String')) {
2747            color = 0x4a90e2;
2748            emissive = 0x001122;
2749          } else if (typeName.includes('Box')) {
2750            color = 0xe74c3c;
2751            emissive = 0x220011;
2752          } else if (typeName.includes('Rc')) {
2753            color = 0x2ecc71;
2754            emissive = 0x001100;
2755          } else if (typeName.includes('Arc')) {
2756            color = 0x9b59b6;
2757            emissive = 0x110022;
2758          } else if (typeName.includes('Vec')) {
2759            color = 0xf39c12;
2760            emissive = 0x221100;
2761          }
2762
2763          // Create enhanced geometry with rounded edges
2764          const geometry = new THREE.BoxGeometry(blockSize, blockSize, blockSize);
2765
2766          // Enhanced material with better visual effects
2767          const material = new THREE.MeshPhongMaterial({
2768            color: color,
2769            emissive: emissive,
2770            transparent: true,
2771            opacity: alloc.is_leaked ? 0.7 : 0.9,
2772            shininess: 100,
2773            specular: 0x222222
2774          });
2775
2776          const cube = new THREE.Mesh(geometry, material);
2777          cube.position.set(x, y, z);
2778          cube.castShadow = true;
2779          cube.receiveShadow = true;
2780
2781          // Store original position and allocation data
2782          cube.userData = {
2783            ...alloc,
2784            originalY: y,
2785            originalColor: color,
2786            originalEmissive: emissive
2787          };
2788
2789          // Add entrance animation
2790          cube.scale.set(0, 0, 0);
2791          const targetScale = 1;
2792          const animateEntrance = () => {
2793            cube.scale.x += (targetScale - cube.scale.x) * 0.1;
2794            cube.scale.y += (targetScale - cube.scale.y) * 0.1;
2795            cube.scale.z += (targetScale - cube.scale.z) * 0.1;
2796
2797            if (Math.abs(cube.scale.x - targetScale) > 0.01) {
2798              requestAnimationFrame(animateEntrance);
2799            }
2800          };
2801          setTimeout(() => animateEntrance(), index * 50);
2802
2803          // Add hover effects
2804          cube.addEventListener = (event, handler) => {
2805            // Custom event handling for 3D objects
2806          };
2807
2808          this.scene.add(cube);
2809          this.memoryBlocks.push(cube);
2810        });
2811
2812        this.update3DInfo(allocations.length);
2813        this.setupRaycasting();
2814      }
2815
2816      setupRaycasting() {
2817        if (!this.renderer || !this.camera) return;
2818
2819        this.raycaster = new THREE.Raycaster();
2820        this.mouse = new THREE.Vector2();
2821        this.hoveredObject = null;
2822
2823        const canvas = this.renderer.domElement;
2824
2825        canvas.addEventListener('mousemove', (event) => {
2826          const rect = canvas.getBoundingClientRect();
2827          this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
2828          this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
2829
2830          this.raycaster.setFromCamera(this.mouse, this.camera);
2831          const intersects = this.raycaster.intersectObjects(this.memoryBlocks);
2832
2833          // Reset previous hover
2834          if (this.hoveredObject) {
2835            this.hoveredObject.material.emissive.setHex(this.hoveredObject.userData.originalEmissive);
2836            this.hoveredObject.scale.set(1, 1, 1);
2837          }
2838
2839          if (intersects.length > 0) {
2840            this.hoveredObject = intersects[0].object;
2841            // Highlight hovered object
2842            this.hoveredObject.material.emissive.setHex(0x444444);
2843            this.hoveredObject.scale.set(1.2, 1.2, 1.2);
2844
2845            // Show tooltip
2846            this.show3DTooltip(event, this.hoveredObject.userData);
2847            canvas.style.cursor = 'pointer';
2848          } else {
2849            this.hoveredObject = null;
2850            this.hide3DTooltip();
2851            canvas.style.cursor = 'default';
2852          }
2853        });
2854
2855        canvas.addEventListener('click', (event) => {
2856          if (this.hoveredObject) {
2857            this.selectMemoryBlock(this.hoveredObject);
2858          }
2859        });
2860
2861        canvas.addEventListener('mouseleave', () => {
2862          if (this.hoveredObject) {
2863            this.hoveredObject.material.emissive.setHex(this.hoveredObject.userData.originalEmissive);
2864            this.hoveredObject.scale.set(1, 1, 1);
2865            this.hoveredObject = null;
2866          }
2867          this.hide3DTooltip();
2868          canvas.style.cursor = 'default';
2869        });
2870      }
2871
2872      selectMemoryBlock(block) {
2873        // Animate selection
2874        const originalScale = { x: 1, y: 1, z: 1 };
2875        const targetScale = { x: 1.5, y: 1.5, z: 1.5 };
2876
2877        const animateSelection = () => {
2878          block.scale.x += (targetScale.x - block.scale.x) * 0.2;
2879          block.scale.y += (targetScale.y - block.scale.y) * 0.2;
2880          block.scale.z += (targetScale.z - block.scale.z) * 0.2;
2881
2882          if (Math.abs(block.scale.x - targetScale.x) > 0.01) {
2883            requestAnimationFrame(animateSelection);
2884          } else {
2885            // Return to normal size after selection
2886            setTimeout(() => {
2887              const returnToNormal = () => {
2888                block.scale.x += (originalScale.x - block.scale.x) * 0.2;
2889                block.scale.y += (originalScale.y - block.scale.y) * 0.2;
2890                block.scale.z += (originalScale.z - block.scale.z) * 0.2;
2891
2892                if (Math.abs(block.scale.x - originalScale.x) > 0.01) {
2893                  requestAnimationFrame(returnToNormal);
2894                }
2895              };
2896              returnToNormal();
2897            }, 1000);
2898          }
2899        };
2900        animateSelection();
2901
2902        // Show detailed info
2903        this.showDetailedInfo(block.userData);
2904      }
2905
2906      show3DTooltip(event, alloc) {
2907        if (!this.tooltip) return;
2908
2909        this.tooltip.innerHTML = `
2910          <div style="font-weight: bold; color: #4a90e2; margin-bottom: 4px;">${alloc.var_name || 'Unknown'}</div>
2911          <div style="margin-bottom: 2px;"><span style="color: #888;">Type:</span> ${alloc.type_name || 'Unknown'}</div>
2912          <div style="margin-bottom: 2px;"><span style="color: #888;">Size:</span> ${this.formatBytes(alloc.size || 0)}</div>
2913          <div style="margin-bottom: 2px;"><span style="color: #888;">Address:</span> 0x${(alloc.ptr || 0).toString(16)}</div>
2914          <div style="margin-bottom: 2px;"><span style="color: #888;">Lifetime:</span> ${(alloc.lifetime_ms || 0).toFixed(2)}ms</div>
2915          <div style="color: ${alloc.is_leaked ? '#e74c3c' : '#2ecc71'};">
2916            ${alloc.is_leaked ? '⚠ïļ Leaked' : '✅ Active'}
2917          </div>
2918        `;
2919
2920        this.tooltip.style.display = 'block';
2921        this.tooltip.style.left = `${event.pageX + 15}px`;
2922        this.tooltip.style.top = `${event.pageY - 10}px`;
2923      }
2924
2925      hide3DTooltip() {
2926        if (this.tooltip) {
2927          this.tooltip.style.display = 'none';
2928        }
2929      }
2930
2931      showDetailedInfo(alloc) {
2932        const infoEl = document.getElementById('memory3DInfo');
2933        if (infoEl) {
2934          infoEl.innerHTML = `
2935            <div style="font-weight: bold; color: #4a90e2; margin-bottom: 8px;">Selected: ${alloc.var_name}</div>
2936            <div style="font-size: 0.8rem; line-height: 1.4;">
2937              <div>Type: ${alloc.type_name}</div>
2938              <div>Size: ${this.formatBytes(alloc.size || 0)}</div>
2939              <div>Address: 0x${(alloc.ptr || 0).toString(16)}</div>
2940              <div>Scope: ${alloc.scope_name || 'unknown'}</div>
2941              <div>Lifetime: ${(alloc.lifetime_ms || 0).toFixed(2)}ms</div>
2942              <div>Status: ${alloc.is_leaked ? 'Leaked' : 'Active'}</div>
2943            </div>
2944          `;
2945        }
2946      }
2947
2948      update3DInfo(blockCount) {
2949        const infoEl = document.getElementById('memory3DInfo');
2950        if (infoEl) {
2951          infoEl.innerHTML = `
2952            Memory Blocks: ${blockCount}<br>
2953            Camera: [${this.camera.position.x.toFixed(1)}, ${this.camera.position.y.toFixed(1)}, ${this.camera.position.z.toFixed(1)}]<br>
2954            Use mouse to rotate, zoom, and pan
2955          `;
2956        }
2957      }
2958
2959      initTimelineControls() {
2960        console.log('Initializing timeline controls...');
2961
2962        const playBtn = document.getElementById('timelinePlay');
2963        const pauseBtn = document.getElementById('timelinePause');
2964        const resetBtn = document.getElementById('timelineReset');
2965        const stepBtn = document.getElementById('timelineStep');
2966        const slider = document.getElementById('timelineSlider');
2967        const thumb = document.getElementById('timelineThumb');
2968
2969        console.log('Found timeline buttons:', {
2970          playBtn: !!playBtn,
2971          pauseBtn: !!pauseBtn,
2972          resetBtn: !!resetBtn,
2973          stepBtn: !!stepBtn,
2974          slider: !!slider,
2975          thumb: !!thumb
2976        });
2977
2978        if (playBtn) {
2979          playBtn.addEventListener('click', () => {
2980            console.log('Timeline play button clicked');
2981            this.playTimeline();
2982          });
2983          console.log('Play button event bound');
2984        } else {
2985          console.error('timelinePlay button not found');
2986        }
2987
2988        if (pauseBtn) {
2989          pauseBtn.addEventListener('click', () => {
2990            console.log('Timeline pause button clicked');
2991            this.pauseTimeline();
2992          });
2993          console.log('Pause button event bound');
2994        } else {
2995          console.error('timelinePause button not found');
2996        }
2997
2998        if (resetBtn) {
2999          resetBtn.addEventListener('click', () => {
3000            console.log('Timeline reset button clicked');
3001            this.resetTimeline();
3002          });
3003          console.log('Reset button event bound');
3004        } else {
3005          console.error('timelineReset button not found');
3006        }
3007
3008        if (stepBtn) {
3009          stepBtn.addEventListener('click', () => {
3010            console.log('Timeline step button clicked');
3011            this.stepTimeline();
3012          });
3013          console.log('Step button event bound');
3014        } else {
3015          console.error('timelineStep button not found');
3016        }
3017
3018        if (slider && thumb) {
3019          let isDragging = false;
3020
3021          thumb.addEventListener('mousedown', (e) => {
3022            isDragging = true;
3023            e.preventDefault();
3024          });
3025
3026          document.addEventListener('mousemove', (e) => {
3027            if (!isDragging) return;
3028
3029            const rect = slider.getBoundingClientRect();
3030            const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
3031            const percentage = x / rect.width;
3032
3033            this.setTimelinePosition(percentage);
3034          });
3035
3036          document.addEventListener('mouseup', () => {
3037            isDragging = false;
3038          });
3039
3040          slider.addEventListener('click', (e) => {
3041            if (isDragging) return;
3042
3043            const rect = slider.getBoundingClientRect();
3044            const x = e.clientX - rect.left;
3045            const percentage = x / rect.width;
3046
3047            this.setTimelinePosition(percentage);
3048          });
3049        }
3050      }
3051
3052      formatBytes(bytes) {
3053        if (bytes === 0) return '0 Bytes';
3054        const k = 1024;
3055        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
3056        const i = Math.floor(Math.log(bytes) / Math.log(k));
3057        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
3058      }
3059
3060      prepareTimelineData(allocations) {
3061        if (!allocations || allocations.length === 0) {
3062          this.timeline.data = [];
3063          this.timeline.totalTime = 0;
3064          this.updateTimelineDisplay();
3065          return;
3066        }
3067
3068        // Filter out allocations with invalid timestamps and sort
3069        const validAllocs = allocations.filter(alloc =>
3070          alloc.timestamp_alloc &&
3071          !isNaN(alloc.timestamp_alloc) &&
3072          alloc.timestamp_alloc > 0
3073        );
3074
3075        if (validAllocs.length === 0) {
3076          // If no valid timestamps, create synthetic timeline based on order
3077          this.timeline.data = allocations.map((alloc, index) => ({
3078            ...alloc,
3079            timestamp_alloc: index * 1000000, // 1ms intervals
3080            timestamp_dealloc: alloc.timestamp_dealloc || (index + 1) * 1000000
3081          }));
3082          this.timeline.totalTime = allocations.length * 1000000;
3083        } else {
3084          // Sort by allocation timestamp
3085          const sortedAllocs = [...validAllocs].sort((a, b) => {
3086            const timeA = a.timestamp_alloc || 0;
3087            const timeB = b.timestamp_alloc || 0;
3088            return timeA - timeB;
3089          });
3090
3091          this.timeline.data = sortedAllocs;
3092          const minTime = sortedAllocs[0].timestamp_alloc || 0;
3093          const maxTime = sortedAllocs[sortedAllocs.length - 1].timestamp_alloc || 0;
3094          this.timeline.totalTime = Math.max(maxTime - minTime, 1000000); // At least 1ms
3095        }
3096
3097        this.updateTimelineDisplay();
3098      }
3099
3100      playTimeline() {
3101        console.log('Starting timeline playback...');
3102        if (this.timeline.isPlaying) {
3103          console.log('Timeline already playing');
3104          return;
3105        }
3106
3107        console.log('Timeline data:', {
3108          totalTime: this.timeline.totalTime,
3109          currentTime: this.timeline.currentTime,
3110          dataLength: this.timeline.data.length
3111        });
3112
3113        this.timeline.isPlaying = true;
3114        const playBtn = document.getElementById('timelinePlay');
3115        const pauseBtn = document.getElementById('timelinePause');
3116
3117        if (playBtn) playBtn.disabled = true;
3118        if (pauseBtn) pauseBtn.disabled = false;
3119
3120        console.log('Timeline playback started');
3121
3122        // Get speed from control
3123        const speedSelect = document.getElementById('timelineSpeed');
3124        const speed = speedSelect ? parseFloat(speedSelect.value) : 1.0;
3125
3126        // Use requestAnimationFrame for smoother animation
3127        const animate = () => {
3128          if (!this.timeline.isPlaying) return;
3129
3130          // Calculate time increment based on speed and total time for better visualization
3131          const baseIncrement = Math.max(this.timeline.totalTime * 0.0005, 12500); // 0.05% of total time or 12.5Ξs minimum
3132          this.timeline.currentTime += baseIncrement * speed;
3133
3134          if (this.timeline.currentTime >= this.timeline.totalTime) {
3135            this.timeline.currentTime = this.timeline.totalTime;
3136            this.updateTimelineVisualization();
3137            this.pauseTimeline();
3138            console.log('Timeline playback completed');
3139            return;
3140          }
3141
3142          this.updateTimelineVisualization();
3143
3144          // Continue animation with consistent frame rate
3145          const frameDelay = Math.max(16, 200 / speed); // Slower frame rate for better visualization
3146          setTimeout(() => {
3147            if (this.timeline.isPlaying) {
3148              requestAnimationFrame(animate);
3149            }
3150          }, frameDelay);
3151        };
3152
3153        requestAnimationFrame(animate);
3154      }
3155
3156      pauseTimeline() {
3157        this.timeline.isPlaying = false;
3158
3159        const playBtn = document.getElementById('timelinePlay');
3160        const pauseBtn = document.getElementById('timelinePause');
3161
3162        if (playBtn) playBtn.disabled = false;
3163        if (pauseBtn) pauseBtn.disabled = true;
3164
3165        if (this.timelineInterval) {
3166          clearInterval(this.timelineInterval);
3167          this.timelineInterval = null;
3168        }
3169      }
3170
3171      resetTimeline() {
3172        console.log('Resetting timeline...');
3173        this.pauseTimeline();
3174        this.timeline.currentTime = 0;
3175        this.updateTimelineVisualization();
3176        
3177        // Reset 3D visualization to show all allocations
3178        if (window.analysisData && window.analysisData.memory_analysis) {
3179          this.create3DMemoryBlocks(window.analysisData.memory_analysis.allocations || []);
3180        }
3181        
3182        console.log('Timeline reset to beginning');
3183      }
3184
3185      stepTimeline() {
3186        // Step forward by 1% of total time or 1ms, whichever is larger
3187        const stepSize = Math.max(this.timeline.totalTime * 0.01, 1000000); // 1ms minimum
3188        this.timeline.currentTime += stepSize;
3189        if (this.timeline.currentTime > this.timeline.totalTime) {
3190          this.timeline.currentTime = this.timeline.totalTime;
3191        }
3192        this.updateTimelineVisualization();
3193        console.log(`Timeline stepped to ${(this.timeline.currentTime / 1000000).toFixed(1)}ms`);
3194      }
3195
3196      setTimelinePosition(percentage) {
3197        this.timeline.currentTime = percentage * this.timeline.totalTime;
3198        this.updateTimelineVisualization();
3199      }
3200
3201      updateTimelineVisualization() {
3202        const percentage = this.timeline.totalTime > 0 ? this.timeline.currentTime / this.timeline.totalTime : 0;
3203
3204        // Cache DOM elements to avoid repeated queries
3205        if (!this.timelineElements) {
3206          this.timelineElements = {
3207            progress: document.getElementById('timelineProgress'),
3208            thumb: document.getElementById('timelineThumb'),
3209            currentTime: document.getElementById('timelineCurrentTime'),
3210            totalTime: document.getElementById('timelineTotalTime'),
3211            activeCount: document.getElementById('timelineActiveCount')
3212          };
3213        }
3214
3215        // Only update if percentage changed significantly (reduce flickering)
3216        const roundedPercentage = Math.round(percentage * 1000) / 10; // Round to 0.1%
3217        if (this.lastPercentage !== roundedPercentage) {
3218          this.lastPercentage = roundedPercentage;
3219
3220          if (this.timelineElements.progress) {
3221            this.timelineElements.progress.style.width = `${roundedPercentage}%`;
3222          }
3223          if (this.timelineElements.thumb) {
3224            this.timelineElements.thumb.style.left = `calc(${roundedPercentage}% - 9px)`;
3225          }
3226        }
3227
3228        // Update time display less frequently
3229        if (!this.lastTimeUpdate || Date.now() - this.lastTimeUpdate > 100) {
3230          this.lastTimeUpdate = Date.now();
3231
3232          const currentTimeMs = isNaN(this.timeline.currentTime) ? 0 : this.timeline.currentTime / 1000000;
3233          const totalTimeMs = isNaN(this.timeline.totalTime) ? 0 : this.timeline.totalTime / 1000000;
3234
3235          if (this.timelineElements.currentTime) {
3236            safeUpdateElement('timelineCurrentTime', `${currentTimeMs.toFixed(1)}ms`);
3237          }
3238          if (this.timelineElements.totalTime) {
3239            safeUpdateElement('timelineTotalTime', `${totalTimeMs.toFixed(1)}ms`);
3240          }
3241        }
3242
3243        // Count active allocations at current time
3244        const activeAllocs = this.timeline.data.filter(alloc => {
3245          const allocTime = alloc.timestamp_alloc || 0;
3246          const deallocTime = alloc.timestamp_dealloc || Infinity;
3247          const currentTime = this.timeline.data[0] ? (this.timeline.data[0].timestamp_alloc || 0) + this.timeline.currentTime : 0;
3248
3249          return allocTime <= currentTime && currentTime < deallocTime;
3250        });
3251
3252        if (this.timelineElements.activeCount) {
3253          safeUpdateElement('timelineActiveCount', activeAllocs.length);
3254        }
3255
3256        // Update 3D visualization with current active allocations
3257        this.create3DMemoryBlocks(activeAllocs);
3258      }
3259
3260      updateTimelineDisplay() {
3261        const totalTimeEl = document.getElementById('timelineTotalTime');
3262        const currentTimeEl = document.getElementById('timelineCurrentTime');
3263
3264        const totalTimeMs = isNaN(this.timeline.totalTime) ? 0 : this.timeline.totalTime / 1000000;
3265        const currentTimeMs = isNaN(this.timeline.currentTime) ? 0 : this.timeline.currentTime / 1000000;
3266
3267        safeUpdateElement('timelineTotalTime', `${totalTimeMs.toFixed(1)}ms`);
3268        safeUpdateElement('timelineCurrentTime', `${currentTimeMs.toFixed(1)}ms`);
3269      }
3270
3271      initHeatmap() {
3272        const container = document.getElementById('memoryHeatmap');
3273        const modeButtons = document.querySelectorAll('.heatmap-mode-btn');
3274
3275        modeButtons.forEach(btn => {
3276          btn.addEventListener('click', (e) => {
3277            modeButtons.forEach(b => b.classList.remove('active'));
3278            e.target.classList.add('active');
3279            this.heatmapMode = e.target.dataset.mode;
3280            this.updateHeatmap();
3281          });
3282        });
3283      }
3284
3285      generateHeatmap(allocations) {
3286        console.log('Generating enhanced heatmap with', allocations?.length || 0, 'allocations');
3287        if (!allocations) return;
3288
3289        const container = document.getElementById('memoryHeatmap');
3290        if (!container) return;
3291
3292        // Clear existing heatmap
3293        container.innerHTML = '<div class="heatmap-legend" id="heatmapLegend"></div>';
3294
3295        const width = container.clientWidth;
3296        const height = container.clientHeight - 40; // Account for legend
3297        const cellSize = 6; // Smaller cells for better resolution
3298        const cols = Math.floor(width / cellSize);
3299        const rows = Math.floor(height / cellSize);
3300
3301        // Create heatmap data based on mode
3302        let heatmapData = [];
3303        let metadata = {};
3304
3305        switch (this.heatmapMode) {
3306          case 'density':
3307            const densityResult = this.calculateDensityHeatmap(allocations, cols, rows);
3308            heatmapData = densityResult.data || densityResult;
3309            metadata = densityResult.metadata || {};
3310            break;
3311          case 'type':
3312            const typeResult = this.calculateTypeHeatmap(allocations, cols, rows);
3313            heatmapData = typeResult.data || typeResult;
3314            metadata = typeResult.metadata || {};
3315            break;
3316          case 'scope':
3317            const scopeResult = this.calculateScopeHeatmap(allocations, cols, rows);
3318            heatmapData = scopeResult.data || scopeResult;
3319            metadata = scopeResult.metadata || {};
3320            break;
3321          case 'activity':
3322            const activityResult = this.calculateActivityHeatmap(allocations, cols, rows);
3323            heatmapData = activityResult.data || activityResult;
3324            metadata = activityResult.metadata || {};
3325            break;
3326          case 'fragmentation':
3327            const fragResult = this.calculateFragmentationHeatmap(allocations, cols, rows);
3328            heatmapData = fragResult.data || [];
3329            metadata = fragResult.metadata || {};
3330            break;
3331          case 'lifetime':
3332            const lifetimeResult = this.calculateLifetimeHeatmap(allocations, cols, rows);
3333            heatmapData = lifetimeResult.data || [];
3334            metadata = lifetimeResult.metadata || {};
3335            break;
3336        }
3337
3338        // Render enhanced heatmap with smooth transitions
3339        const fragment = document.createDocumentFragment();
3340        for (let row = 0; row < rows; row++) {
3341          for (let col = 0; col < cols; col++) {
3342            const index = row * cols + col;
3343            const intensity = heatmapData[index] || 0;
3344
3345            if (intensity > 0.01) { // Only render visible cells for performance
3346              const cell = document.createElement('div');
3347              cell.style.position = 'absolute';
3348              cell.style.left = `${col * cellSize}px`;
3349              cell.style.top = `${row * cellSize + 40}px`;
3350              cell.style.width = `${cellSize}px`;
3351              cell.style.height = `${cellSize}px`;
3352              cell.style.backgroundColor = this.getHeatmapColor(intensity, this.heatmapMode);
3353              cell.style.opacity = Math.max(0.1, intensity);
3354              cell.style.transition = 'all 0.3s ease';
3355              cell.style.borderRadius = '1px';
3356
3357              // Add hover effects for interactivity
3358              cell.addEventListener('mouseenter', (e) => {
3359                e.target.style.transform = 'scale(1.2)';
3360                e.target.style.zIndex = '10';
3361                this.showHeatmapTooltip(e, intensity, metadata, row, col);
3362              });
3363
3364              cell.addEventListener('mouseleave', (e) => {
3365                e.target.style.transform = 'scale(1)';
3366                e.target.style.zIndex = '1';
3367                this.hideHeatmapTooltip();
3368              });
3369
3370              fragment.appendChild(cell);
3371            }
3372          }
3373        }
3374
3375        container.appendChild(fragment);
3376        this.updateHeatmapLegend(metadata);
3377      }
3378
3379      calculateDensityHeatmap(allocations, cols, rows) {
3380        const data = new Array(cols * rows).fill(0);
3381        const maxSize = Math.max(...allocations.map(a => a.size || 0));
3382
3383        allocations.forEach((alloc, index) => {
3384          const x = index % cols;
3385          const y = Math.floor(index / cols) % rows;
3386          const cellIndex = y * cols + x;
3387
3388          if (cellIndex < data.length) {
3389            data[cellIndex] += (alloc.size || 0) / maxSize;
3390          }
3391        });
3392
3393        return data;
3394      }
3395
3396      calculateTypeHeatmap(allocations, cols, rows) {
3397        const data = new Array(cols * rows).fill(0);
3398        const typeMap = new Map();
3399
3400        allocations.forEach(alloc => {
3401          const type = alloc.type_name || 'unknown';
3402          typeMap.set(type, (typeMap.get(type) || 0) + 1);
3403        });
3404
3405        const maxCount = Math.max(...typeMap.values());
3406        let index = 0;
3407
3408        for (const [type, count] of typeMap.entries()) {
3409          const intensity = count / maxCount;
3410          const startIndex = index * Math.floor(data.length / typeMap.size);
3411          const endIndex = Math.min(startIndex + Math.floor(data.length / typeMap.size), data.length);
3412
3413          for (let i = startIndex; i < endIndex; i++) {
3414            data[i] = intensity;
3415          }
3416          index++;
3417        }
3418
3419        return data;
3420      }
3421
3422      calculateScopeHeatmap(allocations, cols, rows) {
3423        const data = new Array(cols * rows).fill(0);
3424        const scopeMap = new Map();
3425
3426        allocations.forEach(alloc => {
3427          const scope = alloc.scope || 'global';
3428          scopeMap.set(scope, (scopeMap.get(scope) || 0) + 1);
3429        });
3430
3431        const maxCount = Math.max(...scopeMap.values());
3432        let index = 0;
3433
3434        for (const [scope, count] of scopeMap.entries()) {
3435          const intensity = count / maxCount;
3436          const startIndex = index * Math.floor(data.length / scopeMap.size);
3437          const endIndex = Math.min(startIndex + Math.floor(data.length / scopeMap.size), data.length);
3438
3439          for (let i = startIndex; i < endIndex; i++) {
3440            data[i] = intensity;
3441          }
3442          index++;
3443        }
3444
3445        return data;
3446      }
3447
3448      calculateActivityHeatmap(allocations, cols, rows) {
3449        const data = new Array(cols * rows).fill(0);
3450
3451        if (allocations.length === 0) return { data, metadata: {} };
3452
3453        const minTime = Math.min(...allocations.map(a => a.timestamp_alloc || 0));
3454        const maxTime = Math.max(...allocations.map(a => a.timestamp_alloc || 0));
3455        const timeRange = maxTime - minTime || 1;
3456
3457        allocations.forEach(alloc => {
3458          const timeRatio = ((alloc.timestamp_alloc || 0) - minTime) / timeRange;
3459          const cellIndex = Math.floor(timeRatio * data.length);
3460
3461          if (cellIndex < data.length) {
3462            data[cellIndex] += 0.1;
3463          }
3464        });
3465
3466        const maxActivity = Math.max(...data);
3467        const normalizedData = maxActivity > 0 ? data.map(d => d / maxActivity) : data;
3468
3469        return {
3470          data: normalizedData,
3471          metadata: {
3472            maxActivity,
3473            totalAllocations: allocations.length,
3474            timeRange: timeRange / 1000000 // Convert to ms
3475          }
3476        };
3477      }
3478
3479      calculateFragmentationHeatmap(allocations, cols, rows) {
3480        const data = new Array(cols * rows).fill(0);
3481
3482        // Sort allocations by memory address
3483        const sortedAllocs = allocations
3484          .filter(a => a.ptr && a.size)
3485          .map(a => ({
3486            address: parseInt(a.ptr.replace('0x', ''), 16),
3487            size: a.size,
3488            ...a
3489          }))
3490          .sort((a, b) => a.address - b.address);
3491
3492        // Calculate fragmentation score for each memory region
3493        for (let i = 0; i < sortedAllocs.length - 1; i++) {
3494          const current = sortedAllocs[i];
3495          const next = sortedAllocs[i + 1];
3496          const gap = next.address - (current.address + current.size);
3497
3498          if (gap > 0) {
3499            // Map to heatmap coordinates
3500            const normalizedAddr = (current.address % (cols * rows * 1000)) / (cols * rows * 1000);
3501            const row = Math.floor(normalizedAddr * rows);
3502            const col = Math.floor((i / sortedAllocs.length) * cols);
3503            const cellIndex = Math.min(row * cols + col, data.length - 1);
3504
3505            // Higher gap = higher fragmentation
3506            data[cellIndex] += Math.min(gap / 1000, 1); // Normalize gap size
3507          }
3508        }
3509
3510        const maxFrag = Math.max(...data);
3511        const normalizedData = maxFrag > 0 ? data.map(d => d / maxFrag) : data;
3512
3513        return {
3514          data: normalizedData,
3515          metadata: {
3516            maxFragmentation: maxFrag,
3517            totalGaps: data.filter(d => d > 0).length,
3518            avgFragmentation: data.reduce((a, b) => a + b, 0) / data.length
3519          }
3520        };
3521      }
3522
3523      calculateLifetimeHeatmap(allocations, cols, rows) {
3524        const data = new Array(cols * rows).fill(0);
3525
3526        allocations.forEach((alloc, index) => {
3527          const allocTime = alloc.timestamp_alloc || 0;
3528          const deallocTime = alloc.timestamp_dealloc || Date.now() * 1000000;
3529          const lifetime = deallocTime - allocTime;
3530
3531          // Map lifetime to heatmap position
3532          const row = Math.floor((index / allocations.length) * rows);
3533          const col = Math.floor((lifetime / 1000000000) * cols) % cols; // Convert to seconds
3534          const cellIndex = Math.min(row * cols + col, data.length - 1);
3535
3536          data[cellIndex] += 1;
3537        });
3538
3539        const maxLifetime = Math.max(...data);
3540        const normalizedData = maxLifetime > 0 ? data.map(d => d / maxLifetime) : data;
3541
3542        return {
3543          data: normalizedData,
3544          metadata: {
3545            maxLifetime,
3546            avgLifetime: data.reduce((a, b) => a + b, 0) / data.length,
3547            activeAllocations: allocations.filter(a => !a.timestamp_dealloc).length
3548          }
3549        };
3550      }
3551
3552      getHeatmapColor(intensity, mode = 'density') {
3553        const scaledIntensity = Math.min(Math.max(intensity, 0), 1);
3554
3555        // Different color schemes for different modes
3556        const colorSchemes = {
3557          density: [
3558            [59, 130, 246],   // Blue
3559            [245, 158, 11],   // Orange
3560            [220, 38, 38]     // Red
3561          ],
3562          type: [
3563            [34, 197, 94],    // Green
3564            [168, 85, 247],   // Purple
3565            [239, 68, 68]     // Red
3566          ],
3567          scope: [
3568            [14, 165, 233],   // Sky blue
3569            [251, 191, 36],   // Amber
3570            [239, 68, 68]     // Red
3571          ],
3572          activity: [
3573            [99, 102, 241],   // Indigo
3574            [236, 72, 153],   // Pink
3575            [220, 38, 38]     // Red
3576          ],
3577          fragmentation: [
3578            [34, 197, 94],    // Green (low fragmentation)
3579            [251, 191, 36],   // Amber (medium)
3580            [239, 68, 68]     // Red (high fragmentation)
3581          ],
3582          lifetime: [
3583            [147, 51, 234],   // Purple (short-lived)
3584            [59, 130, 246],   // Blue (medium)
3585            [34, 197, 94]     // Green (long-lived)
3586          ]
3587        };
3588
3589        const colors = colorSchemes[mode] || colorSchemes.density;
3590        const colorIndex = scaledIntensity * (colors.length - 1);
3591        const lowerIndex = Math.floor(colorIndex);
3592        const upperIndex = Math.ceil(colorIndex);
3593        const ratio = colorIndex - lowerIndex;
3594
3595        if (lowerIndex === upperIndex) {
3596          const [r, g, b] = colors[lowerIndex];
3597          return `rgb(${r}, ${g}, ${b})`;
3598        }
3599
3600        const [r1, g1, b1] = colors[lowerIndex];
3601        const [r2, g2, b2] = colors[upperIndex];
3602
3603        const r = Math.round(r1 + (r2 - r1) * ratio);
3604        const g = Math.round(g1 + (g2 - g1) * ratio);
3605        const b = Math.round(b1 + (b2 - b1) * ratio);
3606
3607        return `rgb(${r}, ${g}, ${b})`;
3608      }
3609
3610      showHeatmapTooltip(event, intensity, metadata, row, col) {
3611        if (!this.tooltip) {
3612          this.tooltip = document.createElement('div');
3613          this.tooltip.style.cssText = `
3614            position: absolute;
3615            background: rgba(0, 0, 0, 0.9);
3616            color: white;
3617            padding: 8px 12px;
3618            border-radius: 6px;
3619            font-size: 12px;
3620            pointer-events: none;
3621            z-index: 1000;
3622            max-width: 200px;
3623            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
3624          `;
3625          document.body.appendChild(this.tooltip);
3626        }
3627
3628        const modeDescriptions = {
3629          density: 'Memory usage density',
3630          type: 'Type distribution',
3631          scope: 'Scope activity level',
3632          activity: 'Allocation activity',
3633          fragmentation: 'Memory fragmentation',
3634          lifetime: 'Allocation lifetime'
3635        };
3636
3637        this.tooltip.innerHTML = `
3638          <div><strong>${modeDescriptions[this.heatmapMode] || 'Intensity'}</strong></div>
3639          <div>Value: ${(intensity * 100).toFixed(1)}%</div>
3640          <div>Position: (${col}, ${row})</div>
3641          ${metadata.maxActivity ? `<div>Max Activity: ${metadata.maxActivity}</div>` : ''}
3642          ${metadata.totalGaps ? `<div>Total Gaps: ${metadata.totalGaps}</div>` : ''}
3643          ${metadata.activeAllocations ? `<div>Active: ${metadata.activeAllocations}</div>` : ''}
3644        `;
3645
3646        this.tooltip.style.left = `${event.pageX + 10}px`;
3647        this.tooltip.style.top = `${event.pageY - 10}px`;
3648        this.tooltip.style.display = 'block';
3649      }
3650
3651      hideHeatmapTooltip() {
3652        if (this.tooltip) {
3653          this.tooltip.style.display = 'none';
3654        }
3655      }
3656
3657      updateHeatmapLegend(metadata = {}) {
3658        const legend = document.getElementById('heatmapLegend');
3659        if (!legend) return;
3660
3661        const modeLabels = {
3662          density: 'Memory Density',
3663          type: 'Type Distribution',
3664          scope: 'Scope Activity',
3665          activity: 'Allocation Activity',
3666          fragmentation: 'Memory Fragmentation',
3667          lifetime: 'Allocation Lifetime'
3668        };
3669
3670        const modeDescriptions = {
3671          density: 'Shows memory usage concentration',
3672          type: 'Shows distribution of data types',
3673          scope: 'Shows activity by scope',
3674          activity: 'Shows allocation frequency over time',
3675          fragmentation: 'Shows memory fragmentation levels',
3676          lifetime: 'Shows allocation lifetime patterns'
3677        };
3678
3679        const currentMode = this.heatmapMode;
3680        const lowColor = this.getHeatmapColor(0.2, currentMode);
3681        const medColor = this.getHeatmapColor(0.5, currentMode);
3682        const highColor = this.getHeatmapColor(0.8, currentMode);
3683
3684        let metadataHtml = '';
3685        if (metadata.maxActivity) {
3686          metadataHtml += `<div style="font-size: 10px; color: rgba(255,255,255,0.8);">Max Activity: ${metadata.maxActivity}</div>`;
3687        }
3688        if (metadata.totalGaps) {
3689          metadataHtml += `<div style="font-size: 10px; color: rgba(255,255,255,0.8);">Gaps: ${metadata.totalGaps}</div>`;
3690        }
3691        if (metadata.activeAllocations) {
3692          metadataHtml += `<div style="font-size: 10px; color: rgba(255,255,255,0.8);">Active: ${metadata.activeAllocations}</div>`;
3693        }
3694
3695        legend.innerHTML = `
3696          <div style="font-weight: 600; margin-bottom: 2px;">${modeLabels[currentMode]}</div>
3697          <div style="font-size: 10px; color: rgba(255,255,255,0.7); margin-bottom: 4px;">${modeDescriptions[currentMode]}</div>
3698          <div style="display: flex; align-items: center; gap: 4px; margin-bottom: 4px;">
3699            <div style="width: 12px; height: 12px; background: ${lowColor}; border-radius: 2px;"></div>
3700            <span style="font-size: 11px;">Low</span>
3701            <div style="width: 12px; height: 12px; background: ${medColor}; border-radius: 2px;"></div>
3702            <span style="font-size: 11px;">Med</span>
3703            <div style="width: 12px; height: 12px; background: ${highColor}; border-radius: 2px;"></div>
3704            <span style="font-size: 11px;">High</span>
3705          </div>
3706          ${metadataHtml}
3707        `;
3708      }
3709
3710      updateHeatmap() {
3711        if (window.analysisData && window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
3712          this.generateHeatmap(window.analysisData.memory_analysis.allocations);
3713        }
3714      }
3715
3716      bindEvents() {
3717        console.log('🔧 Binding 3D visualization events...');
3718
3719        // Add visual feedback for button interactions
3720        this.addButtonFeedback();
3721
3722        // Wait for DOM to be fully ready
3723        setTimeout(() => {
3724          const toggle3DBtn = document.getElementById('toggle3DView');
3725          const reset3DBtn = document.getElementById('reset3DView');
3726          const autoRotateBtn = document.getElementById('autoRotate3D');
3727          const focusLargestBtn = document.getElementById('focusLargest');
3728
3729          console.log('🔍 Found buttons:', {
3730            toggle3DBtn: !!toggle3DBtn,
3731            reset3DBtn: !!reset3DBtn,
3732            autoRotateBtn: !!autoRotateBtn,
3733            focusLargestBtn: !!focusLargestBtn
3734          });
3735
3736          if (toggle3DBtn) {
3737            // Remove any existing event listeners
3738            toggle3DBtn.replaceWith(toggle3DBtn.cloneNode(true));
3739            const newToggle3DBtn = document.getElementById('toggle3DView');
3740            
3741            newToggle3DBtn.addEventListener('click', (e) => {
3742              e.preventDefault();
3743              console.log('ðŸŽŊ Toggle 3D view clicked');
3744              const container = document.getElementById('memory3DContainer');
3745              if (container) {
3746                const isHidden = container.style.display === 'none';
3747                if (isHidden) {
3748                  // Show 3D view
3749                  container.style.display = 'block';
3750                  newToggle3DBtn.innerHTML = '<i class="fa fa-eye-slash"></i><span>Hide 3D</span>';
3751                  newToggle3DBtn.style.background = 'var(--primary-red)';
3752                  console.log('✅ Showing 3D view');
3753                  
3754                  // Reinitialize 3D scene if needed
3755                  if (!this.scene) {
3756                    console.log('🔄 Reinitializing 3D scene...');
3757                    this.init3DVisualization();
3758                  }
3759                  
3760                  // Update 3D visualization with current data
3761                  if (window.analysisData && window.analysisData.memory_analysis) {
3762                    this.create3DMemoryBlocks(window.analysisData.memory_analysis.allocations || []);
3763                  }
3764                } else {
3765                  // Hide 3D view
3766                  container.style.display = 'none';
3767                  newToggle3DBtn.innerHTML = '<i class="fa fa-eye"></i><span>Show 3D</span>';
3768                  newToggle3DBtn.style.background = 'var(--primary-green)';
3769                  console.log('✅ Hiding 3D view');
3770                }
3771              } else {
3772                console.error('❌ 3D container not found');
3773              }
3774            });
3775            console.log('✅ Toggle 3D button event bound');
3776          } else {
3777            console.error('❌ toggle3DView button not found');
3778          }
3779
3780          if (reset3DBtn) {
3781            // Remove any existing event listeners
3782            reset3DBtn.replaceWith(reset3DBtn.cloneNode(true));
3783            const newReset3DBtn = document.getElementById('reset3DView');
3784            
3785            newReset3DBtn.addEventListener('click', (e) => {
3786              e.preventDefault();
3787              console.log('ðŸŽŊ Reset 3D view clicked');
3788              this.reset3DView();
3789            });
3790            console.log('✅ Reset 3D button event bound');
3791          } else {
3792            console.error('❌ reset3DView button not found');
3793          }
3794
3795          if (autoRotateBtn) {
3796            // Remove any existing event listeners
3797            autoRotateBtn.replaceWith(autoRotateBtn.cloneNode(true));
3798            const newAutoRotateBtn = document.getElementById('autoRotate3D');
3799            
3800            newAutoRotateBtn.addEventListener('click', (e) => {
3801              e.preventDefault();
3802              console.log('ðŸŽŊ Auto rotate clicked');
3803              this.toggleAutoRotate();
3804            });
3805            console.log('✅ Auto rotate button event bound');
3806          } else {
3807            console.error('❌ autoRotate3D button not found');
3808          }
3809
3810          if (focusLargestBtn) {
3811            // Remove any existing event listeners
3812            focusLargestBtn.replaceWith(focusLargestBtn.cloneNode(true));
3813            const newFocusLargestBtn = document.getElementById('focusLargest');
3814            
3815            newFocusLargestBtn.addEventListener('click', (e) => {
3816              e.preventDefault();
3817              console.log('ðŸŽŊ Focus largest clicked');
3818              this.focusOnLargestBlock();
3819            });
3820            console.log('✅ Focus largest button event bound');
3821          } else {
3822            console.error('❌ focusLargest button not found');
3823          }
3824        }, 500); // Wait 500ms for DOM to be ready
3825
3826        // Handle window resize
3827        window.addEventListener('resize', () => {
3828          if (this.camera && this.renderer) {
3829            const container = document.getElementById('memory3DContainer');
3830            if (container) {
3831              this.camera.aspect = container.clientWidth / container.clientHeight;
3832              this.camera.updateProjectionMatrix();
3833              this.renderer.setSize(container.clientWidth, container.clientHeight);
3834            }
3835          }
3836        });
3837      }
3838
3839      addButtonFeedback() {
3840        // Add hover and click effects to all 3D control buttons
3841        const buttonIds = ['toggle3DView', 'reset3DView', 'autoRotate3D', 'focusLargest'];
3842        
3843        buttonIds.forEach(id => {
3844          const btn = document.getElementById(id);
3845          if (btn) {
3846            // Add hover effect
3847            btn.addEventListener('mouseenter', () => {
3848              btn.style.transform = 'scale(1.05)';
3849              btn.style.transition = 'all 0.2s ease';
3850            });
3851            
3852            btn.addEventListener('mouseleave', () => {
3853              btn.style.transform = 'scale(1)';
3854            });
3855            
3856            // Add click effect
3857            btn.addEventListener('mousedown', () => {
3858              btn.style.transform = 'scale(0.95)';
3859            });
3860            
3861            btn.addEventListener('mouseup', () => {
3862              btn.style.transform = 'scale(1.05)';
3863            });
3864            
3865            console.log(`✅ Added feedback effects to ${id}`);
3866          }
3867        });
3868      }
3869
3870      reset3DView() {
3871        console.log('🔄 Resetting 3D view...');
3872        
3873        // Show visual feedback
3874        const resetBtn = document.getElementById('reset3DView');
3875        if (resetBtn) {
3876          resetBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i><span>Resetting...</span>';
3877          resetBtn.style.background = 'var(--primary-yellow)';
3878        }
3879        
3880        if (this.camera && this.controls) {
3881          // Reset camera position
3882          this.camera.position.set(15, 10, 15);
3883          this.camera.lookAt(0, 0, 0);
3884          
3885          // Reset controls
3886          this.controls.reset();
3887          
3888          // Update camera
3889          this.camera.updateProjectionMatrix();
3890          
3891          // Restore button
3892          setTimeout(() => {
3893            if (resetBtn) {
3894              resetBtn.innerHTML = '<i class="fa fa-refresh"></i><span>Reset</span>';
3895              resetBtn.style.background = 'var(--primary-orange)';
3896            }
3897          }, 500);
3898          
3899          console.log('✅ 3D view reset complete');
3900        } else {
3901          console.error('❌ Camera or controls not available for reset');
3902          if (resetBtn) {
3903            resetBtn.innerHTML = '<i class="fa fa-exclamation"></i><span>Error</span>';
3904            resetBtn.style.background = 'var(--primary-red)';
3905            setTimeout(() => {
3906              resetBtn.innerHTML = '<i class="fa fa-refresh"></i><span>Reset</span>';
3907              resetBtn.style.background = 'var(--primary-orange)';
3908            }, 1000);
3909          }
3910        }
3911      
3912      // Animation function
3913      const animateReset = () => {
3914        // Animation logic here if needed
3915      };
3916      animateReset();
3917    }
3918
3919      toggleAutoRotate() {
3920        console.log('🔄 Toggling auto rotate...');
3921        if (this.controls) {
3922          this.controls.autoRotate = !this.controls.autoRotate;
3923          this.controls.autoRotateSpeed = 2.0; // Set rotation speed
3924          
3925          const btn = document.getElementById('autoRotate3D');
3926          if (btn) {
3927            if (this.controls.autoRotate) {
3928              btn.innerHTML = '<i class="fa fa-pause"></i><span>Stop Rotate</span>';
3929              btn.style.background = 'var(--primary-red)';
3930              console.log('✅ Auto rotate enabled');
3931            } else {
3932              btn.innerHTML = '<i class="fa fa-rotate-right"></i><span>Auto Rotate</span>';
3933              btn.style.background = 'var(--primary-blue)';
3934              console.log('✅ Auto rotate disabled');
3935            }
3936          }
3937        } else {
3938          console.error('❌ Controls not available for auto rotate');
3939          const btn = document.getElementById('autoRotate3D');
3940          if (btn) {
3941            btn.innerHTML = '<i class="fa fa-exclamation"></i><span>Error</span>';
3942            btn.style.background = 'var(--primary-red)';
3943            setTimeout(() => {
3944              btn.innerHTML = '<i class="fa fa-rotate-right"></i><span>Auto Rotate</span>';
3945              btn.style.background = 'var(--primary-blue)';
3946            }, 1000);
3947          }
3948        }
3949      }
3950
3951      focusOnLargestBlock() {
3952        console.log('ðŸŽŊ Focusing on largest block...');
3953        
3954        // Show visual feedback
3955        const focusBtn = document.getElementById('focusLargest');
3956        if (focusBtn) {
3957          focusBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i><span>Focusing...</span>';
3958          focusBtn.style.background = 'var(--primary-yellow)';
3959        }
3960        
3961        if (!this.memoryBlocks || this.memoryBlocks.length === 0) {
3962          console.warn('❌ No memory blocks to focus on');
3963          if (focusBtn) {
3964            focusBtn.innerHTML = '<i class="fa fa-exclamation"></i><span>No Blocks</span>';
3965            focusBtn.style.background = 'var(--primary-red)';
3966            setTimeout(() => {
3967              focusBtn.innerHTML = '<i class="fa fa-search-plus"></i><span>Focus Largest</span>';
3968              focusBtn.style.background = 'var(--primary-red)';
3969            }, 1500);
3970          }
3971          return;
3972        }
3973
3974        // Find the largest block
3975        let largestBlock = null;
3976        let largestSize = 0;
3977
3978        this.memoryBlocks.forEach(block => {
3979          const size = block.userData?.size || 0;
3980          if (size > largestSize) {
3981            largestSize = size;
3982            largestBlock = block;
3983          }
3984        });
3985
3986        if (largestBlock && this.camera && this.controls) {
3987          // Calculate optimal camera position
3988          const blockPos = largestBlock.position;
3989          const distance = Math.max(5, Math.sqrt(largestSize) / 10);
3990          
3991          // Position camera at an angle for better view
3992          const targetPosition = new THREE.Vector3(
3993            blockPos.x + distance,
3994            blockPos.y + distance * 0.7,
3995            blockPos.z + distance
3996          );
3997
3998          // Smooth camera transition
3999          const startPos = this.camera.position.clone();
4000          let progress = 0;
4001          
4002          const animateFocus = () => {
4003            progress += 0.05;
4004            if (progress <= 1) {
4005              this.camera.position.lerpVectors(startPos, targetPosition, progress);
4006              this.camera.lookAt(blockPos);
4007              requestAnimationFrame(animateFocus);
4008            } else {
4009              // Animation complete
4010              console.log(`✅ Focused on largest block: ${largestBlock.userData?.var_name || 'unknown'} (${this.formatBytes(largestSize)})`);
4011              this.update3DInfo(this.memoryBlocks.length);
4012              
4013              // Restore button
4014              if (focusBtn) {
4015                focusBtn.innerHTML = '<i class="fa fa-search-plus"></i><span>Focus Largest</span>';
4016                focusBtn.style.background = 'var(--primary-red)';
4017              }
4018            }
4019          };
4020          animateFocus();
4021        } else {
4022          console.error('❌ Camera or controls not available for focus');
4023          if (focusBtn) {
4024            focusBtn.innerHTML = '<i class="fa fa-exclamation"></i><span>Error</span>';
4025            focusBtn.style.background = 'var(--primary-red)';
4026            setTimeout(() => {
4027              focusBtn.innerHTML = '<i class="fa fa-search-plus"></i><span>Focus Largest</span>';
4028              focusBtn.style.background = 'var(--primary-red)';
4029            }, 1000);
4030          }
4031        }
4032      }
4033
4034      // Main initialization method
4035      initializeWithData(analysisData) {
4036        console.log('initializeWithData called with:', analysisData);
4037
4038        let allocations = null;
4039
4040        // Try different data structure paths
4041        if (analysisData && analysisData.memory_analysis && analysisData.memory_analysis.allocations) {
4042          allocations = analysisData.memory_analysis.allocations;
4043          console.log('Found allocations in memory_analysis:', allocations.length);
4044        } else if (analysisData && analysisData.allocations) {
4045          allocations = analysisData.allocations;
4046          console.log('Found allocations directly:', allocations.length);
4047        } else {
4048          console.warn('No allocation data found in analysisData');
4049          console.log('Available keys:', Object.keys(analysisData || {}));
4050          return;
4051        }
4052
4053        if (!allocations || allocations.length === 0) {
4054          console.warn('No allocations to visualize');
4055          return;
4056        }
4057
4058        console.log(`Initializing enhanced visualization with ${allocations.length} allocations`);
4059
4060        // Initialize 3D visualization
4061        this.create3DMemoryBlocks(allocations);
4062
4063        // Initialize timeline
4064        this.prepareTimelineData(allocations);
4065
4066        // Initialize heatmap
4067        this.generateHeatmap(allocations);
4068
4069        // Update memory distribution visualization
4070        this.updateMemoryDistribution(allocations);
4071
4072        // Initialize memory fragmentation visualization
4073        this.initializeMemoryFragmentation(allocations);
4074
4075        console.log('Enhanced memory visualization initialized successfully');
4076      }
4077
4078      initializeMemoryFragmentation(allocations) {
4079        console.log('Initializing memory fragmentation with', allocations?.length || 0, 'allocations');
4080        const container = document.getElementById('memoryFragmentation');
4081        if (!container) {
4082          console.error('Memory fragmentation container not found');
4083          return;
4084        }
4085        if (!allocations || allocations.length === 0) {
4086          console.warn('No allocations data for fragmentation analysis');
4087          container.innerHTML = '<div style="text-align: center; color: var(--text-secondary); padding: 40px;">No allocation data available for fragmentation analysis</div>';
4088          return;
4089        }
4090
4091        console.log('Processing', allocations.length, 'allocations for fragmentation analysis');
4092
4093        // Calculate fragmentation metrics
4094        const sortedAllocs = [...allocations].sort((a, b) => {
4095          const addrA = parseInt(a.ptr || '0x0', 16);
4096          const addrB = parseInt(b.ptr || '0x0', 16);
4097          return addrA - addrB;
4098        });
4099
4100        const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
4101        const addressRanges = [];
4102        let gaps = 0;
4103        let totalGapSize = 0;
4104
4105        // Calculate memory gaps
4106        for (let i = 0; i < sortedAllocs.length - 1; i++) {
4107          const currentAddr = parseInt(sortedAllocs[i].ptr || '0x0', 16);
4108          const currentSize = sortedAllocs[i].size || 0;
4109          const nextAddr = parseInt(sortedAllocs[i + 1].ptr || '0x0', 16);
4110
4111          const currentEnd = currentAddr + currentSize;
4112          const gap = nextAddr - currentEnd;
4113
4114          if (gap > 0) {
4115            gaps++;
4116            totalGapSize += gap;
4117            addressRanges.push({
4118              type: 'gap',
4119              start: currentEnd,
4120              size: gap,
4121              index: i
4122            });
4123          }
4124
4125          addressRanges.push({
4126            type: 'allocation',
4127            start: currentAddr,
4128            size: currentSize,
4129            allocation: sortedAllocs[i],
4130            index: i
4131          });
4132        }
4133
4134        // Calculate fragmentation percentage
4135        const fragmentation = totalMemory > 0 ? (totalGapSize / (totalMemory + totalGapSize)) * 100 : 0;
4136        const efficiency = Math.max(100 - fragmentation, 0);
4137
4138        console.log('Fragmentation metrics:', { gaps, totalGapSize, fragmentation, efficiency });
4139
4140        // Create visualization
4141        container.innerHTML = `
4142          <div style="margin-bottom: 16px;">
4143            <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 16px;">
4144              <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
4145                <div style="font-size: 1.4rem; font-weight: 700; color: var(--primary-red);">${fragmentation.toFixed(1)}%</div>
4146                <div style="font-size: 0.7rem; color: var(--text-secondary);">Fragmentation</div>
4147              </div>
4148              <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
4149                <div style="font-size: 1.4rem; font-weight: 700; color: var(--primary-orange);">${gaps}</div>
4150                <div style="font-size: 0.7rem; color: var(--text-secondary);">Memory Gaps</div>
4151              </div>
4152              <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
4153                <div style="font-size: 1.4rem; font-weight: 700; color: var(--primary-green);">${efficiency.toFixed(1)}%</div>
4154                <div style="font-size: 0.7rem; color: var(--text-secondary);">Efficiency</div>
4155              </div>
4156            </div>
4157            
4158            <div style="margin-bottom: 12px;">
4159              <h4 style="margin: 0 0 8px 0; font-size: 0.9rem; color: var(--text-primary);">Memory Layout Visualization</h4>
4160              <div id="fragmentationChart" style="height: 120px; background: var(--bg-secondary); border: 1px solid var(--border-light); border-radius: 6px; position: relative; overflow: hidden;">
4161                <!-- Memory blocks will be inserted here -->
4162              </div>
4163            </div>
4164            
4165            <div style="font-size: 0.8rem; color: var(--text-secondary); text-align: center;">
4166              <div style="display: flex; justify-content: center; gap: 16px; margin-top: 8px;">
4167                <div style="display: flex; align-items: center; gap: 4px;">
4168                  <div style="width: 12px; height: 12px; background: var(--primary-blue); border-radius: 2px;"></div>
4169                  <span>Allocated</span>
4170                </div>
4171                <div style="display: flex; align-items: center; gap: 4px;">
4172                  <div style="width: 12px; height: 12px; background: var(--primary-red); border-radius: 2px;"></div>
4173                  <span>Gaps</span>
4174                </div>
4175                <div style="display: flex; align-items: center; gap: 4px;">
4176                  <div style="width: 12px; height: 12px; background: var(--primary-orange); border-radius: 2px;"></div>
4177                  <span>Leaked</span>
4178                </div>
4179              </div>
4180            </div>
4181          </div>
4182        `;
4183
4184        // Draw memory layout visualization
4185        this.drawFragmentationChart(addressRanges, totalMemory + totalGapSize);
4186      }
4187
4188      drawFragmentationChart(addressRanges, totalSize) {
4189        const chartContainer = document.getElementById('fragmentationChart');
4190        if (!chartContainer || addressRanges.length === 0) return;
4191
4192        const width = chartContainer.clientWidth;
4193        const height = chartContainer.clientHeight;
4194
4195        let currentX = 0;
4196
4197        addressRanges.forEach((range, index) => {
4198          const blockWidth = Math.max((range.size / totalSize) * width, 1);
4199
4200          const block = document.createElement('div');
4201          block.style.position = 'absolute';
4202          block.style.left = `${currentX}px`;
4203          block.style.top = '10px';
4204          block.style.width = `${blockWidth}px`;
4205          block.style.height = `${height - 20}px`;
4206          block.style.borderRadius = '2px';
4207          block.style.cursor = 'pointer';
4208          block.style.transition = 'all 0.2s ease';
4209
4210          if (range.type === 'gap') {
4211            block.style.background = 'linear-gradient(45deg, #dc2626, #ef4444)';
4212            block.style.border = '1px solid #b91c1c';
4213            block.title = `Memory Gap: ${this.formatBytes(range.size)}`;
4214          } else if (range.allocation && range.allocation.is_leaked) {
4215            block.style.background = 'linear-gradient(45deg, #ea580c, #f97316)';
4216            block.style.border = '1px solid #c2410c';
4217            block.title = `Leaked: ${range.allocation.var_name} (${this.formatBytes(range.size)})`;
4218          } else {
4219            block.style.background = 'linear-gradient(45deg, #2563eb, #3b82f6)';
4220            block.style.border = '1px solid #1d4ed8';
4221            block.title = range.allocation ?
4222              `${range.allocation.var_name}: ${range.allocation.type_name} (${this.formatBytes(range.size)})` :
4223              `Allocation: ${this.formatBytes(range.size)}`;
4224          }
4225
4226          // Add hover effects
4227          block.addEventListener('mouseenter', () => {
4228            block.style.transform = 'scaleY(1.2)';
4229            block.style.zIndex = '10';
4230          });
4231
4232          block.addEventListener('mouseleave', () => {
4233            block.style.transform = 'scaleY(1)';
4234            block.style.zIndex = '1';
4235          });
4236
4237          chartContainer.appendChild(block);
4238          currentX += blockWidth;
4239        });
4240      }
4241
4242      updateMemoryDistribution(allocations) {
4243        const container = document.getElementById('memoryDistributionViz');
4244        if (!container || !allocations) return;
4245
4246        container.innerHTML = '';
4247
4248        const containerWidth = container.clientWidth;
4249        const containerHeight = container.clientHeight;
4250        const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
4251
4252        // čŽ·å–å―“å‰įžĐæ”ūæŊ”äū‹
4253        const scaleSlider = document.getElementById('memoryDistScale');
4254        const scale = scaleSlider ? parseFloat(scaleSlider.value) / 100 : 1;
4255        
4256        // čŪĄįŪ—åŪžé™…åŊį”ĻåŪ―åšĶïžˆč€ƒč™‘įžĐæ”ū和čūđč·ïž‰
4257        const padding = 20;
4258        const availableWidth = (containerWidth - padding * 2) * scale;
4259        const availableHeight = containerHeight - padding * 2;
4260
4261        // 创åŧšäļ€äļŠåŊæŧšåŠĻįš„å†…åŪđåŪđå™Ļ
4262        const contentContainer = document.createElement('div');
4263        contentContainer.style.cssText = `
4264          position: absolute;
4265          top: ${padding}px;
4266          left: ${padding}px;
4267          width: ${Math.max(availableWidth, containerWidth - padding * 2)}px;
4268          height: ${availableHeight}px;
4269          overflow-x: auto;
4270          overflow-y: hidden;
4271        `;
4272
4273        // 创åŧšå†…存块åŪđå™Ļ
4274        const blocksContainer = document.createElement('div');
4275        blocksContainer.style.cssText = `
4276          position: relative;
4277          width: ${availableWidth}px;
4278          height: 100%;
4279          min-width: ${containerWidth - padding * 2}px;
4280        `;
4281
4282        let currentX = 0;
4283        let fragmentation = 0;
4284        let efficiency = 0;
4285
4286        // Sort by address for fragmentation calculation
4287        const sortedAllocs = [...allocations].sort((a, b) => {
4288          const addrA = parseInt(a.ptr || '0x0', 16);
4289          const addrB = parseInt(b.ptr || '0x0', 16);
4290          return addrA - addrB;
4291        });
4292
4293        // čŪĄįŪ—æŊäļŠå—įš„æœ€å°åŪ―åšĶ和æ€ŧåŪ―åšĶéœ€æą‚
4294        const minBlockWidth = 3; // 最小块åŪ―åšĶ
4295        const blockGap = 1;
4296        let totalRequiredWidth = 0;
4297
4298        sortedAllocs.forEach((alloc) => {
4299          const proportionalWidth = (alloc.size || 0) / totalMemory * availableWidth;
4300          const blockWidth = Math.max(proportionalWidth, minBlockWidth);
4301          totalRequiredWidth += blockWidth + blockGap;
4302        });
4303
4304        // åĶ‚æžœæ€ŧåŪ―åšĶčķ…čŋ‡åŊį”ĻåŪ―åšĶïžŒč°ƒæ•īåŪđå™ĻåŪ―åšĶ
4305        const finalContainerWidth = Math.max(totalRequiredWidth, availableWidth);
4306        blocksContainer.style.width = `${finalContainerWidth}px`;
4307
4308        sortedAllocs.forEach((alloc, index) => {
4309          const proportionalWidth = (alloc.size || 0) / totalMemory * availableWidth;
4310          const blockWidth = Math.max(proportionalWidth, minBlockWidth);
4311          const blockHeight = availableHeight * 0.7;
4312
4313          const block = document.createElement('div');
4314          block.className = 'memory-block';
4315          block.style.cssText = `
4316            position: absolute;
4317            left: ${currentX}px;
4318            top: ${(availableHeight - blockHeight) / 2}px;
4319            width: ${blockWidth}px;
4320            height: ${blockHeight}px;
4321            border-radius: 2px;
4322            cursor: pointer;
4323            transition: all 0.2s ease;
4324            border: 1px solid rgba(255, 255, 255, 0.3);
4325          `;
4326
4327          // Determine allocation type and style
4328          if (alloc.is_leaked) {
4329            block.classList.add('leaked');
4330            block.style.background = 'linear-gradient(45deg, #dc2626, #ef4444)';
4331            block.style.boxShadow = '0 0 8px rgba(239, 68, 68, 0.5)';
4332          } else if (alloc.type_name && alloc.type_name.includes('Box')) {
4333            block.classList.add('heap');
4334            block.style.background = 'linear-gradient(45deg, #ff6b35, #f7931e)';
4335          } else {
4336            block.classList.add('stack');
4337            block.style.background = 'linear-gradient(45deg, #4dabf7, #339af0)';
4338          }
4339
4340          // Enhanced hover effects
4341          block.addEventListener('mouseenter', (e) => {
4342            block.style.transform = 'scaleY(1.2) translateY(-2px)';
4343            block.style.zIndex = '10';
4344            block.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
4345            this.showTooltip(e, alloc);
4346          });
4347
4348          block.addEventListener('mouseleave', () => {
4349            block.style.transform = 'scaleY(1) translateY(0)';
4350            block.style.zIndex = '1';
4351            block.style.boxShadow = 'none';
4352            this.hideTooltip();
4353          });
4354
4355          // æ·ŧ加į‚đå‡ŧ之äŧķæ˜ūįĪščŊĶįŧ†äŋĄæŊ
4356          block.addEventListener('click', () => {
4357            this.showBlockDetails(alloc);
4358          });
4359
4360          blocksContainer.appendChild(block);
4361          currentX += blockWidth + blockGap;
4362        });
4363
4364        contentContainer.appendChild(blocksContainer);
4365        container.appendChild(contentContainer);
4366
4367        // Calculate fragmentation and efficiency
4368        fragmentation = totalRequiredWidth > availableWidth ? 
4369          ((totalRequiredWidth - availableWidth) / totalRequiredWidth * 100) : 0;
4370        efficiency = Math.max(100 - fragmentation, 0);
4371
4372        // Update metrics
4373        const fragEl = document.getElementById('memoryFragmentation');
4374        const effEl = document.getElementById('memoryEfficiency');
4375
4376        // Use global safe update function (no need to redefine)
4377        
4378        safeUpdateElement('memoryFragmentation', `${fragmentation.toFixed(1)}%`);
4379        safeUpdateElement('memoryEfficiency', `${efficiency.toFixed(1)}%`);
4380
4381        // Setup dynamic controls
4382        this.setupMemoryDistributionControls(allocations);
4383
4384        // Update other metrics with real data
4385        this.updateEnhancedMetrics(allocations);
4386      }
4387
4388      setupMemoryDistributionControls(allocations) {
4389        const scaleSlider = document.getElementById('memoryDistScale');
4390        const scaleValue = document.getElementById('memoryDistScaleValue');
4391        const fitBtn = document.getElementById('memoryDistFit');
4392        const resetBtn = document.getElementById('memoryDistReset');
4393
4394        if (scaleSlider && scaleValue) {
4395          scaleSlider.addEventListener('input', (e) => {
4396            const value = e.target.value;
4397            safeUpdateElement('memoryDistScaleValue', `${value}%`);
4398            this.updateMemoryDistribution(allocations);
4399          });
4400        }
4401
4402        if (fitBtn) {
4403          fitBtn.addEventListener('click', () => {
4404            // 臩åŠĻčŪĄįŪ—æœ€ä―ģįžĐæ”ūæŊ”äū‹
4405            const container = document.getElementById('memoryDistributionViz');
4406            if (container && allocations) {
4407              const containerWidth = container.clientWidth - 40; // 减åŽŧpadding
4408              const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
4409              const minBlockWidth = 3;
4410              const blockGap = 1;
4411              
4412              let requiredWidth = 0;
4413              allocations.forEach(() => {
4414                requiredWidth += minBlockWidth + blockGap;
4415              });
4416
4417              const optimalScale = Math.min(200, Math.max(50, (containerWidth / requiredWidth) * 100));
4418              
4419              if (scaleSlider) {
4420                scaleSlider.value = optimalScale;
4421                safeUpdateElement('memoryDistScaleValue', `${Math.round(optimalScale)}%`);
4422                this.updateMemoryDistribution(allocations);
4423              }
4424            }
4425          });
4426        }
4427
4428        if (resetBtn) {
4429          resetBtn.addEventListener('click', () => {
4430            if (scaleSlider) {
4431              scaleSlider.value = 100;
4432              safeUpdateElement('memoryDistScaleValue', '100%');
4433              this.updateMemoryDistribution(allocations);
4434            }
4435          });
4436        }
4437      }
4438
4439      showBlockDetails(alloc) {
4440        // 创åŧščŊĶįŧ†äŋĄæŊåžđįŠ—
4441        const modal = document.createElement('div');
4442        modal.style.cssText = `
4443          position: fixed;
4444          top: 0;
4445          left: 0;
4446          width: 100%;
4447          height: 100%;
4448          background: rgba(0,0,0,0.5);
4449          z-index: 1000;
4450          display: flex;
4451          align-items: center;
4452          justify-content: center;
4453        `;
4454
4455        const content = document.createElement('div');
4456        content.style.cssText = `
4457          background: var(--bg-primary);
4458          border-radius: 12px;
4459          padding: 24px;
4460          max-width: 400px;
4461          width: 90%;
4462          box-shadow: 0 20px 40px rgba(0,0,0,0.3);
4463          color: var(--text-primary);
4464        `;
4465
4466        content.innerHTML = `
4467          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
4468            <h3 style="margin: 0; color: var(--primary-blue);">Memory Block Details</h3>
4469            <button id="closeModal" style="background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-secondary);">&times;</button>
4470          </div>
4471          <div style="line-height: 1.6;">
4472            <div><strong>Variable:</strong> ${alloc.var_name || 'Unknown'}</div>
4473            <div><strong>Type:</strong> ${alloc.type_name || 'Unknown'}</div>
4474            <div><strong>Size:</strong> ${this.formatBytes(alloc.size || 0)}</div>
4475            <div><strong>Address:</strong> ${alloc.ptr || 'N/A'}</div>
4476            <div><strong>Status:</strong> ${alloc.is_leaked ? 'ðŸšĻ Leaked' : '✅ Active'}</div>
4477            <div><strong>Lifetime:</strong> ${alloc.lifetime_ms ? alloc.lifetime_ms.toFixed(2) + 'ms' : 'N/A'}</div>
4478            <div><strong>Scope:</strong> ${alloc.scope_name || 'Unknown'}</div>
4479          </div>
4480        `;
4481
4482        modal.appendChild(content);
4483        document.body.appendChild(modal);
4484
4485        // å…ģ闭之äŧķ
4486        const closeModal = () => {
4487          document.body.removeChild(modal);
4488        };
4489
4490        modal.addEventListener('click', (e) => {
4491          if (e.target === modal) closeModal();
4492        });
4493
4494        content.querySelector('#closeModal').addEventListener('click', closeModal);
4495      }
4496
4497      updateEnhancedMetrics(allocations) {
4498        if (!allocations || allocations.length === 0) return;
4499
4500        // Calculate real metrics from data
4501        const totalAllocs = allocations.length;
4502        const totalMemory = allocations.reduce((sum, alloc) => sum + (alloc.size || 0), 0);
4503        const avgLifetime = allocations.reduce((sum, alloc) => sum + (alloc.lifetime_ms || 0), 0) / totalAllocs;
4504        const heapAllocs = allocations.filter(a => a.type_name && (a.type_name.includes('Box') || a.type_name.includes('Vec'))).length;
4505        const stackAllocs = totalAllocs - heapAllocs;
4506        const heapStackRatio = stackAllocs > 0 ? (heapAllocs / stackAllocs).toFixed(2) : heapAllocs.toString();
4507
4508        // Update KPI cards
4509        const totalAllocsEl = document.getElementById('total-allocations');
4510        const activeVarsEl = document.getElementById('active-variables');
4511        const totalMemoryEl = document.getElementById('total-memory');
4512        const avgLifetimeEl = document.getElementById('avg-lifetime');
4513        const peakMemoryEl = document.getElementById('peak-memory');
4514        const allocRateEl = document.getElementById('allocation-rate');
4515        const fragmentationEl = document.getElementById('fragmentation');
4516
4517        safeUpdateElement('total-allocs', totalAllocs);
4518        safeUpdateElement('active-vars', allocations.filter(a => !a.is_leaked).length);
4519        safeUpdateElement('total-memory', this.formatBytes(totalMemory));
4520        safeUpdateElement('avg-lifetime', `${avgLifetime.toFixed(2)}ms`);
4521        safeUpdateElement('peak-memory', this.formatBytes(Math.max(...allocations.map(a => a.size || 0))));
4522
4523        // Calculate allocation rate (allocations per microsecond)
4524        const timeSpan = Math.max(...allocations.map(a => a.timestamp_alloc || 0)) - Math.min(...allocations.map(a => a.timestamp_alloc || 0));
4525        const allocRate = timeSpan > 0 ? ((totalAllocs / (timeSpan / 1000000)).toFixed(2) + '/sec') : '0/sec';
4526        safeUpdateElement('allocation-rate', allocRate);
4527
4528        // Update enhanced statistics
4529        const totalAllocsEnhancedEl = document.getElementById('total-allocs-enhanced');
4530        const heapStackRatioEl = document.getElementById('heap-stack-ratio');
4531        const avgLifetimeEnhancedEl = document.getElementById('avg-lifetime-enhanced');
4532        const memoryEfficiencyEl = document.getElementById('memory-efficiency');
4533
4534        safeUpdateElement('total-allocs-enhanced', totalAllocs);
4535        safeUpdateElement('heap-stack-ratio', heapStackRatio);
4536        safeUpdateElement('avg-lifetime-enhanced', `${avgLifetime.toFixed(1)}ms`);
4537        safeUpdateElement('memory-efficiency', `${((totalMemory / (totalAllocs * 100)) * 100).toFixed(1)}%`);
4538
4539        // Update type counts
4540        safeUpdateElement('arc-count', allocations.filter(a => a.type_name && a.type_name.includes('Arc')).length);
4541        safeUpdateElement('rc-count', allocations.filter(a => a.type_name && a.type_name.includes('Rc')).length);
4542        safeUpdateElement('collections-count', allocations.filter(a => a.type_name && (a.type_name.includes('Vec') || a.type_name.includes('HashMap'))).length);
4543      }
4544
4545      showTooltip(event, alloc) {
4546        if (!this.tooltip) return;
4547
4548        this.tooltip.innerHTML = `
4549          <strong>${alloc.var_name || 'Unknown'}</strong><br>
4550          Type: ${alloc.type_name || 'Unknown'}<br>
4551          Size: ${this.formatBytes(alloc.size || 0)}<br>
4552          Address: ${alloc.ptr || 'N/A'}<br>
4553          Status: ${alloc.is_leaked ? 'Leaked' : 'Active'}<br>
4554          Lifetime: ${alloc.lifetime_ms ? alloc.lifetime_ms.toFixed(2) + 'ms' : 'N/A'}
4555        `;
4556
4557        this.tooltip.style.display = 'block';
4558        this.tooltip.style.left = `${event.pageX + 10}px`;
4559        this.tooltip.style.top = `${event.pageY - 10}px`;
4560      }
4561
4562      hideTooltip() {
4563        if (this.tooltip) {
4564          this.tooltip.style.display = 'none';
4565        }
4566      }
4567
4568      formatBytes(bytes) {
4569        if (bytes === 0) return '0 B';
4570        const k = 1024;
4571        const sizes = ['B', 'KB', 'MB', 'GB'];
4572        const i = Math.floor(Math.log(bytes) / Math.log(k));
4573        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
4574      }
4575    }
4576
4577    // Global instance
4578    window.enhancedVisualizer = new EnhancedMemoryVisualizer();
4579
4580    // Global function to bind 3D controls - can be called from console for debugging
4581    window.bind3DControls = function() {
4582      console.log('🔧 Manually binding 3D controls...');
4583      if (window.enhancedVisualizer) {
4584        window.enhancedVisualizer.bindEvents();
4585      }
4586    };
4587
4588    // Ensure 3D controls are bound when DOM is ready
4589    // Safety Risk Data and Functions
4590    window.safetyRisks = [];
4591    
4592    function getRiskAssessment(risk) {
4593        if (risk.risk_level === 'High') {
4594            return 'Critical memory safety issue - immediate attention required';
4595        } else if (risk.risk_level === 'Medium') {
4596            return 'Potential memory issue - review recommended';
4597        } else {
4598            return 'Low risk - monitoring suggested';
4599        }
4600    }
4601    
4602    function loadSafetyRisks() {
4603        console.log('ðŸ›Ąïļ Loading safety risk data...');
4604        const unsafeTable = document.getElementById('unsafeTable');
4605        if (!unsafeTable) {
4606            console.warn('⚠ïļ unsafeTable not found');
4607            return;
4608        }
4609        
4610        const risks = window.safetyRisks || [];
4611        if (risks.length === 0) {
4612            unsafeTable.innerHTML = '<tr><td colspan="3" class="text-center text-gray-500">No safety risks detected</td></tr>';
4613            return;
4614        }
4615        
4616        unsafeTable.innerHTML = '';
4617        risks.forEach((risk, index) => {
4618            const row = document.createElement('tr');
4619            row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
4620            
4621            const riskLevelClass = risk.risk_level === 'High' ? 'text-red-600 font-bold' : 
4622                                 risk.risk_level === 'Medium' ? 'text-yellow-600 font-semibold' : 
4623                                 'text-green-600';
4624            
4625            row.innerHTML = `
4626                <td class="px-3 py-2 text-sm">${risk.location || 'Unknown'}</td>
4627                <td class="px-3 py-2 text-sm">${risk.operation || 'Unknown'}</td>
4628                <td class="px-3 py-2 text-sm"><span class="${riskLevelClass}">${risk.risk_level || 'Low'}</span></td>
4629            `;
4630            unsafeTable.appendChild(row);
4631        });
4632        
4633        console.log('✅ Safety risks loaded:', risks.length, 'items');
4634    }
4635
4636    document.addEventListener('DOMContentLoaded', function() {
4637      console.log('🚀 DOM loaded, binding 3D controls...');
4638      setTimeout(() => {
4639        if (window.enhancedVisualizer) {
4640          window.enhancedVisualizer.bindEvents();
4641        }
4642      }, 1000);
4643    });
4644
4645    // Enhanced Unsafe Rust & FFI Memory Analysis
4646    // Global variables for unsafe analysis - must be declared at global scope
4647    window.unsafeAnalysisCurrentFilter = 'critical';
4648    window.unsafeAnalysisData = []; // Initialize as empty array at global scope
4649    window.timelineZoomLevel = 1;
4650    window.timelineOffset = 0;
4651
4652    function initializeEnhancedUnsafeAnalysis() {
4653        console.log('🔧 Initializing Enhanced Unsafe Rust & FFI Memory Analysis...');
4654        
4655        // Initialize unsafeAnalysisData if not already done
4656        if (!window.unsafeAnalysisData || window.unsafeAnalysisData.length === 0) {
4657            console.log('🔄 Initializing window.unsafeAnalysisData...');
4658            window.unsafeAnalysisData = [];
4659        }
4660        
4661        // Load unsafe/FFI data from multiple sources
4662        const allocations = window.analysisData?.memory_analysis?.allocations || [];
4663        const unsafeFfiData = loadUnsafeFfiSnapshot();
4664        
4665        // Transform and merge data for enhanced analysis
4666        try {
4667            window.unsafeAnalysisData = transformUnsafeAnalysisData(allocations, unsafeFfiData);
4668            console.log('✅ Successfully transformed unsafe analysis data:', window.unsafeAnalysisData.length, 'items');
4669        } catch (error) {
4670            console.error('❌ Error transforming unsafe analysis data:', error);
4671            window.unsafeAnalysisData = []; // Fallback to empty array
4672        }
4673        
4674        updateUnsafeAnalysisStats(window.unsafeAnalysisData);
4675        setupEnhancedFilterControls();
4676        setupTimelineControls();
4677        setupMemoryPassportModal();
4678        
4679        const filteredData = applyUnsafeAnalysisFilter(window.unsafeAnalysisCurrentFilter, window.unsafeAnalysisData);
4680        renderEnhancedUnsafeTimeline(filteredData);
4681        
4682        console.log('✅ Enhanced Unsafe Analysis initialized with', window.unsafeAnalysisData.length, 'memory objects');
4683    }
4684
4685    function loadUnsafeFfiSnapshot() {
4686        // Load from the JSON data we saw earlier
4687        try {
4688            if (window.unsafeFfiSnapshot) {
4689                return window.unsafeFfiSnapshot;
4690            }
4691            // Fallback to simulated data based on the structure we observed
4692            return generateEnhancedUnsafeData();
4693        } catch (error) {
4694            console.warn('Failed to load unsafe FFI snapshot, using simulated data');
4695            return generateEnhancedUnsafeData();
4696        }
4697    }
4698
4699    function generateEnhancedUnsafeData() {
4700        // Generate realistic unsafe/FFI data based on the JSON structure we analyzed
4701        const data = [];
4702        for (let i = 0; i < 50; i++) {
4703            const ptr = `0x${(0x60000000 + i * 0x40).toString(16)}`;
4704            const source = Math.random() < 0.4 ? 'UnsafeRust' : 'FfiC';
4705            const hasLeaks = Math.random() < 0.15;
4706            const hasBoundaryEvents = Math.random() < 0.25;
4707            
4708            data.push({
4709                base: {
4710                    ptr: ptr,
4711                    size: [40, 256, 1024, 4096][Math.floor(Math.random() * 4)],
4712                    timestamp_alloc: Date.now() * 1000000 + i * 100000,
4713                    timestamp_dealloc: hasLeaks ? null : Date.now() * 1000000 + i * 100000 + Math.random() * 5000000,
4714                    is_leaked: hasLeaks
4715                },
4716                source: source === 'UnsafeRust' ? {
4717                    UnsafeRust: {
4718                        unsafe_block_location: `src/lib.rs:${42 + i}:13`,
4719                        risk_assessment: {
4720                            risk_level: ['Low', 'Medium', 'High'][Math.floor(Math.random() * 3)],
4721                            confidence_score: 0.7 + Math.random() * 0.3,
4722                            risk_factors: [{
4723                                factor_type: 'ManualMemoryManagement',
4724                                severity: 3 + Math.random() * 7,
4725                                description: 'Manual memory management in unsafe block'
4726                            }]
4727                        }
4728                    }
4729                } : {
4730                    FfiC: {
4731                        resolved_function: {
4732                            library_name: 'libc',
4733                            function_name: 'malloc',
4734                            risk_level: 'Medium'
4735                        }
4736                    }
4737                },
4738                cross_boundary_events: hasBoundaryEvents ? [{
4739                    event_type: Math.random() < 0.5 ? 'FfiToRust' : 'RustToFfi',
4740                    timestamp: Date.now() + i * 1000,
4741                    from_context: source === 'UnsafeRust' ? 'rust_context' : 'ffi_context',
4742                    to_context: source === 'UnsafeRust' ? 'ffi_context' : 'rust_context'
4743                }] : [],
4744                ffi_tracked: source === 'FfiC' || Math.random() < 0.3
4745            });
4746        }
4747        return data;
4748    }
4749
4750    function transformUnsafeAnalysisData(allocations, unsafeFfiData) {
4751        const transformed = [];
4752        
4753        // Transform regular allocations to unsafe analysis format
4754        allocations.forEach(alloc => {
4755            if (alloc.type_name && (alloc.type_name.includes('*') || alloc.type_name.includes('unsafe'))) {
4756                transformed.push({
4757                    ...alloc,
4758                    analysis_type: 'regular_allocation',
4759                    risk_level: 'Low',
4760                    has_boundary_events: false
4761                });
4762            }
4763        });
4764        
4765        // Add enhanced unsafe/FFI data
4766        unsafeFfiData.forEach(unsafeItem => {
4767            transformed.push({
4768                ...unsafeItem,
4769                analysis_type: 'unsafe_ffi',
4770                risk_level: unsafeItem.source?.UnsafeRust?.risk_assessment?.risk_level || 'Medium',
4771                has_boundary_events: unsafeItem.cross_boundary_events && unsafeItem.cross_boundary_events.length > 0
4772            });
4773        });
4774        
4775        return transformed;
4776    }
4777
4778    function updateUnsafeAnalysisStats(data) {
4779        const criticalCount = data.filter(d => d.risk_level === 'High' || d.base?.is_leaked).length;
4780        const leakCount = data.filter(d => d.base?.is_leaked).length;
4781        const boundaryCount = data.filter(d => d.has_boundary_events).length;
4782        
4783        safeUpdateElement('unsafe-critical-count', criticalCount);
4784        safeUpdateElement('unsafe-leak-count', leakCount);
4785        safeUpdateElement('unsafe-boundary-count', boundaryCount);
4786        safeUpdateElement('unsafe-total-count', data.length);
4787    }
4788
4789    function applyUnsafeAnalysisFilter(filterType, data) {
4790        switch(filterType) {
4791            case 'critical':
4792                return data.filter(d => d.risk_level === 'High' || d.base?.is_leaked);
4793            case 'leaks':
4794                return data.filter(d => d.base?.is_leaked);
4795            case 'cross-boundary':
4796                return data.filter(d => d.has_boundary_events);
4797            case 'risk-assessment':
4798                return data.filter(d => d.source?.UnsafeRust?.risk_assessment);
4799            case 'all':
4800            default:
4801                return data;
4802        }
4803    }
4804
4805    function setupEnhancedFilterControls() {
4806        const filterTabs = document.querySelectorAll('.unsafe-filter-tab');
4807        
4808        filterTabs.forEach(tab => {
4809            tab.addEventListener('click', () => {
4810                filterTabs.forEach(t => t.classList.remove('active'));
4811                tab.classList.add('active');
4812                
4813                window.unsafeAnalysisCurrentFilter = tab.dataset.filter;
4814                
4815                const filteredData = applyUnsafeAnalysisFilter(window.unsafeAnalysisCurrentFilter, window.unsafeAnalysisData);
4816                renderEnhancedUnsafeTimeline(filteredData);
4817            });
4818        });
4819    }
4820
4821    function setupTimelineControls() {
4822        document.getElementById('timelineZoomIn')?.addEventListener('click', () => {
4823            window.timelineZoomLevel *= 1.5;
4824            rerenderTimeline();
4825        });
4826        
4827        document.getElementById('timelineZoomOut')?.addEventListener('click', () => {
4828            window.timelineZoomLevel /= 1.5;
4829            rerenderTimeline();
4830        });
4831        
4832        document.getElementById('timelineReset')?.addEventListener('click', () => {
4833            window.timelineZoomLevel = 1;
4834            window.timelineOffset = 0;
4835            rerenderTimeline();
4836        });
4837    }
4838
4839    function setupMemoryPassportModal() {
4840        const modal = document.getElementById('memoryPassport');
4841        const closeBtn = modal?.querySelector('.passport-close');
4842        
4843        closeBtn?.addEventListener('click', () => {
4844            modal.style.display = 'none';
4845        });
4846        
4847        modal?.addEventListener('click', (e) => {
4848            if (e.target === modal) {
4849                modal.style.display = 'none';
4850            }
4851        });
4852    }
4853
4854    function showMemoryPassport(memoryObject) {
4855        const modal = document.getElementById('memoryPassport');
4856        const body = document.getElementById('passportBody');
4857        
4858        if (!modal || !body) return;
4859        
4860        // Generate passport content based on the memory object
4861        const passportContent = generatePassportContent(memoryObject);
4862        body.innerHTML = passportContent;
4863        
4864        modal.style.display = 'flex';
4865    }
4866
4867    function generatePassportContent(memoryObject) {
4868        const ptr = memoryObject.base?.ptr || memoryObject.ptr || 'Unknown';
4869        const size = memoryObject.base?.size || memoryObject.size || 0;
4870        const isLeaked = memoryObject.base?.is_leaked || false;
4871        const riskLevel = memoryObject.risk_level || 'Unknown';
4872        
4873        return `
4874            <div class="passport-section">
4875                <h4><i class="fa fa-info-circle"></i> Memory Passport: ${ptr}</h4>
4876                <div class="passport-grid">
4877                    <div class="passport-item">
4878                        <strong>Size:</strong> ${formatBytes(size)}
4879                    </div>
4880                    <div class="passport-item">
4881                        <strong>Status:</strong> 
4882                        <span class="status-${isLeaked ? 'leaked' : 'normal'}">
4883                            ${isLeaked ? 'ðŸšĻ LEAKED' : '✅ Normal'}
4884                        </span>
4885                    </div>
4886                    <div class="passport-item">
4887                        <strong>Risk Level:</strong> 
4888                        <span class="risk-${riskLevel.toLowerCase()}">${riskLevel}</span>
4889                    </div>
4890                    <div class="passport-item">
4891                        <strong>FFI Tracked:</strong> ${memoryObject.ffi_tracked ? '✅ Yes' : '❌ No'}
4892                    </div>
4893                </div>
4894            </div>
4895            
4896            <div class="passport-section">
4897                <h4><i class="fa fa-timeline"></i> Lifecycle Log</h4>
4898                <div class="lifecycle-events">
4899                    ${generateLifecycleEvents(memoryObject)}
4900                </div>
4901            </div>
4902            
4903            ${memoryObject.source?.UnsafeRust?.risk_assessment ? `
4904            <div class="passport-section">
4905                <h4><i class="fa fa-exclamation-triangle"></i> Risk Assessment</h4>
4906                <div class="risk-details">
4907                    ${generateRiskAssessment(memoryObject.source.UnsafeRust.risk_assessment)}
4908                </div>
4909            </div>
4910            ` : ''}
4911        `;
4912    }
4913
4914    function generateLifecycleEvents(memoryObject) {
4915        let events = '';
4916        
4917        // Allocation event
4918        if (memoryObject.base?.timestamp_alloc) {
4919            events += `
4920                <div class="lifecycle-event allocation">
4921                    <div class="event-icon">ðŸŸĒ</div>
4922                    <div class="event-details">
4923                        <strong>Allocation</strong><br>
4924                        Time: ${new Date(memoryObject.base.timestamp_alloc / 1000000).toLocaleString()}<br>
4925                        Source: ${Object.keys(memoryObject.source || {})[0] || 'Unknown'}
4926                    </div>
4927                </div>
4928            `;
4929        }
4930        
4931        // Boundary events
4932        if (memoryObject.cross_boundary_events) {
4933            memoryObject.cross_boundary_events.forEach(event => {
4934                events += `
4935                    <div class="lifecycle-event boundary">
4936                        <div class="event-icon">${event.event_type === 'FfiToRust' ? '⮆ïļ' : '⮇ïļ'}</div>
4937                        <div class="event-details">
4938                            <strong>Boundary Cross: ${event.event_type}</strong><br>
4939                            From: ${event.from_context}<br>
4940                            To: ${event.to_context}
4941                        </div>
4942                    </div>
4943                `;
4944            });
4945        }
4946        
4947        // Deallocation event
4948        if (memoryObject.base?.timestamp_dealloc) {
4949            events += `
4950                <div class="lifecycle-event deallocation">
4951                    <div class="event-icon">ðŸ”ī</div>
4952                    <div class="event-details">
4953                        <strong>Deallocation</strong><br>
4954                        Time: ${new Date(memoryObject.base.timestamp_dealloc / 1000000).toLocaleString()}
4955                    </div>
4956                </div>
4957            `;
4958        } else if (memoryObject.base?.is_leaked) {
4959            events += `
4960                <div class="lifecycle-event leak">
4961                    <div class="event-icon">⚠ïļ</div>
4962                    <div class="event-details">
4963                        <strong>MEMORY LEAK DETECTED</strong><br>
4964                        No deallocation event found
4965                    </div>
4966                </div>
4967            `;
4968        }
4969        
4970        return events || '<p>No lifecycle events recorded</p>';
4971    }
4972
4973    function generateRiskAssessment(riskAssessment) {
4974        return `
4975            <div class="risk-summary">
4976                <div class="risk-level ${riskAssessment.risk_level?.toLowerCase()}">
4977                    Risk Level: ${riskAssessment.risk_level}
4978                </div>
4979                <div class="confidence-score">
4980                    Confidence: ${Math.round((riskAssessment.confidence_score || 0) * 100)}%
4981                </div>
4982            </div>
4983            ${riskAssessment.risk_factors ? `
4984                <div class="risk-factors">
4985                    <h5>Risk Factors:</h5>
4986                    ${riskAssessment.risk_factors.map(factor => `
4987                        <div class="risk-factor">
4988                            <strong>${factor.factor_type}:</strong> ${factor.description}
4989                            <span class="severity">Severity: ${factor.severity}/10</span>
4990                        </div>
4991                    `).join('')}
4992                </div>
4993            ` : ''}
4994        `;
4995    }
4996
4997    function renderEnhancedUnsafeTimeline(data) {
4998        console.log('ðŸŽĻ Rendering enhanced unsafe timeline with', data.length, 'items');
4999        
5000        // Clear existing timeline
5001        const rustTrack = document.getElementById('rustTimelineTrack');
5002        const ffiTrack = document.getElementById('ffiTimelineTrack');
5003        const timelineAxis = document.getElementById('timelineAxis');
5004        
5005        if (!rustTrack || !ffiTrack || !timelineAxis) {
5006            console.warn('Timeline tracks not found');
5007            return;
5008        }
5009        
5010        rustTrack.innerHTML = '';
5011        ffiTrack.innerHTML = '';
5012        timelineAxis.innerHTML = '';
5013        
5014        if (data.length === 0) {
5015            rustTrack.innerHTML = '<p style="text-align: center; color: var(--text-secondary); margin-top: 2rem;">No data matches current filter</p>';
5016            return;
5017        }
5018        
5019        // Calculate time range
5020        const timestamps = data.flatMap(d => {
5021            const times = [];
5022            if (d.base?.timestamp_alloc) times.push(d.base.timestamp_alloc);
5023            if (d.base?.timestamp_dealloc) times.push(d.base.timestamp_dealloc);
5024            return times;
5025        }).filter(t => t);
5026        
5027        if (timestamps.length === 0) return;
5028        
5029        const minTime = Math.min(...timestamps);
5030        const maxTime = Math.max(...timestamps);
5031        const timeRange = maxTime - minTime;
5032        
5033        // Render each memory object
5034        data.forEach((memoryObj, index) => {
5035            renderMemoryObjectLifecycle(memoryObj, index, minTime, timeRange, rustTrack, ffiTrack);
5036        });
5037        
5038        // Render time axis
5039        renderTimeAxis(minTime, timeRange, timelineAxis);
5040    }
5041
5042    function renderMemoryObjectLifecycle(memoryObj, index, minTime, timeRange, rustTrack, ffiTrack) {
5043        const allocTime = memoryObj.base?.timestamp_alloc || minTime;
5044        const deallocTime = memoryObj.base?.timestamp_dealloc;
5045        
5046        const startPercent = ((allocTime - minTime) / timeRange) * 100;
5047        const endPercent = deallocTime ? ((deallocTime - minTime) / timeRange) * 100 : 100;
5048        const width = endPercent - startPercent;
5049        
5050        // Determine source and target track
5051        const sourceType = Object.keys(memoryObj.source || {})[0];
5052        const isUnsafeRust = sourceType === 'UnsafeRust';
5053        const targetTrack = isUnsafeRust ? rustTrack : ffiTrack;
5054        
5055        // Create lifecycle path
5056        const lifecyclePath = document.createElement('div');
5057        lifecyclePath.className = 'memory-lifecycle-path';
5058        lifecyclePath.style.cssText = `
5059            position: absolute;
5060            left: ${startPercent}%;
5061            width: ${width}%;
5062            top: ${(index % 3) * 30 + 10}px;
5063            height: 20px;
5064            background: ${getMemoryPathColor(memoryObj)};
5065            border-radius: 10px;
5066            cursor: pointer;
5067            transition: transform 0.2s ease, box-shadow 0.2s ease;
5068            border: 2px solid ${getMemoryBorderColor(memoryObj)};
5069            display: flex;
5070            align-items: center;
5071            justify-content: space-between;
5072            padding: 0 8px;
5073            font-size: 10px;
5074            color: white;
5075            font-weight: bold;
5076        `;
5077        
5078        lifecyclePath.innerHTML = `
5079            <span>${getSourceIcon(sourceType)}</span>
5080            <span>${formatBytes(memoryObj.base?.size || 0)}</span>
5081            <span>${memoryObj.base?.is_leaked ? 'ðŸšĻ' : '✅'}</span>
5082        `;
5083        
5084        // Add hover effects
5085        lifecyclePath.addEventListener('mouseenter', () => {
5086            lifecyclePath.style.transform = 'scale(1.1) translateY(-2px)';
5087            lifecyclePath.style.boxShadow = '0 8px 16px rgba(0,0,0,0.3)';
5088            lifecyclePath.style.zIndex = '10';
5089        });
5090        
5091        lifecyclePath.addEventListener('mouseleave', () => {
5092            lifecyclePath.style.transform = 'scale(1) translateY(0)';
5093            lifecyclePath.style.boxShadow = 'none';
5094            lifecyclePath.style.zIndex = '1';
5095        });
5096        
5097        // Add click event to show passport
5098        lifecyclePath.addEventListener('click', () => {
5099            showMemoryPassport(memoryObj);
5100        });
5101        
5102        targetTrack.appendChild(lifecyclePath);
5103        
5104        // Render boundary events
5105        if (memoryObj.cross_boundary_events) {
5106            memoryObj.cross_boundary_events.forEach(event => {
5107                renderBoundaryEvent(event, minTime, timeRange, rustTrack, ffiTrack);
5108            });
5109        }
5110    }
5111
5112    function getMemoryPathColor(memoryObj) {
5113        if (memoryObj.base?.is_leaked) return 'linear-gradient(90deg, #ff4757, #ff3742)';
5114        if (memoryObj.risk_level === 'High') return 'linear-gradient(90deg, #ffa502, #ff9f43)';
5115        if (memoryObj.risk_level === 'Medium') return 'linear-gradient(90deg, #3742fa, #2f3542)';
5116        return 'linear-gradient(90deg, #2ed573, #1e90ff)';
5117    }
5118
5119    function getMemoryBorderColor(memoryObj) {
5120        if (memoryObj.base?.is_leaked) return '#ff4757';
5121        if (memoryObj.risk_level === 'High') return '#ffa502';
5122        return '#3742fa';
5123    }
5124
5125    function getSourceIcon(sourceType) {
5126        switch(sourceType) {
5127            case 'UnsafeRust': return 'ðŸĶ€';
5128            case 'FfiC': return '⚡';
5129            default: return '❓';
5130        }
5131    }
5132
5133    function renderBoundaryEvent(event, minTime, timeRange, rustTrack, ffiTrack) {
5134        const eventTime = event.timestamp * 1000000; // Convert to nanoseconds
5135        const eventPercent = ((eventTime - minTime) / timeRange) * 100;
5136        
5137        const boundaryIndicator = document.createElement('div');
5138        boundaryIndicator.className = 'boundary-event-indicator';
5139        boundaryIndicator.style.cssText = `
5140            position: absolute;
5141            left: ${eventPercent}%;
5142            top: -10px;
5143            width: 2px;
5144            height: 140px;
5145            background: ${event.event_type === 'FfiToRust' ? '#00d4aa' : '#ff4757'};
5146            z-index: 5;
5147        `;
5148        
5149        const arrow = document.createElement('div');
5150        arrow.innerHTML = event.event_type === 'FfiToRust' ? 'â–ē' : '▾';
5151        arrow.style.cssText = `
5152            position: absolute;
5153            top: ${event.event_type === 'FfiToRust' ? '100px' : '20px'};
5154            left: -8px;
5155            color: ${event.event_type === 'FfiToRust' ? '#00d4aa' : '#ff4757'};
5156            font-size: 16px;
5157            font-weight: bold;
5158        `;
5159        
5160        boundaryIndicator.appendChild(arrow);
5161        rustTrack.appendChild(boundaryIndicator);
5162    }
5163
5164    function renderTimeAxis(minTime, timeRange, timelineAxis) {
5165        // Create time markers
5166        const numMarkers = 10;
5167        for (let i = 0; i <= numMarkers; i++) {
5168            const percent = (i / numMarkers) * 100;
5169            const time = minTime + (timeRange * i / numMarkers);
5170            
5171            const marker = document.createElement('div');
5172            marker.style.cssText = `
5173                position: absolute;
5174                left: ${percent}%;
5175                top: 0;
5176                width: 1px;
5177                height: 100%;
5178                background: var(--border-light);
5179            `;
5180            
5181            const label = document.createElement('div');
5182            safeUpdateElement(label.id || 'timeline-label', new Date(time / 1000000).toLocaleTimeString());
5183            label.style.cssText = `
5184                position: absolute;
5185                left: ${percent}%;
5186                top: 50%;
5187                transform: translateX(-50%) translateY(-50%);
5188                font-size: 0.7rem;
5189                color: var(--text-secondary);
5190                background: var(--bg-primary);
5191                padding: 2px 4px;
5192                border-radius: 2px;
5193            `;
5194            
5195            timelineAxis.appendChild(marker);
5196            timelineAxis.appendChild(label);
5197        }
5198    }
5199
5200    function rerenderTimeline() {
5201        const filteredData = applyUnsafeAnalysisFilter(window.unsafeAnalysisCurrentFilter, window.unsafeAnalysisData);
5202        renderEnhancedUnsafeTimeline(filteredData);
5203    }
5204
5205    // Dynamic Size Control Functions
5206    function setupDynamicSizeControls() {
5207        const container = document.querySelector('section.card[style*="min-height: 700px"]');
5208        const expandBtn = document.getElementById('expandAnalysis');
5209        const compactBtn = document.getElementById('compactAnalysis');
5210        
5211        if (!container) return;
5212        
5213        expandBtn?.addEventListener('click', () => {
5214            container.classList.remove('compact');
5215            container.classList.add('expanded');
5216            container.style.minHeight = '900px';
5217            updateAnalysisLayout();
5218        });
5219        
5220        compactBtn?.addEventListener('click', () => {
5221            container.classList.remove('expanded');
5222            container.classList.add('compact');
5223            container.style.minHeight = '500px';
5224            updateAnalysisLayout();
5225        });
5226    }
5227    
5228    function updateAnalysisLayout() {
5229        // Trigger layout updates for charts and visualizations
5230        setTimeout(() => {
5231            const filteredData = applyUnsafeAnalysisFilter(window.unsafeAnalysisCurrentFilter, window.unsafeAnalysisData);
5232            renderEnhancedUnsafeTimeline(filteredData);
5233        }, 300);
5234    }
5235    
5236    // Risk Analysis Tab Controls
5237    function setupRiskAnalysisTabs() {
5238        const tabs = document.querySelectorAll('.risk-tab');
5239        const views = document.querySelectorAll('.risk-view');
5240        
5241        tabs.forEach(tab => {
5242            tab.addEventListener('click', () => {
5243                const targetView = tab.dataset.view;
5244                
5245                // Update tab states
5246                tabs.forEach(t => t.classList.remove('active'));
5247                tab.classList.add('active');
5248                
5249                // Update view states
5250                views.forEach(view => {
5251                    view.style.display = 'none';
5252                    view.classList.remove('active');
5253                });
5254                
5255                const targetElement = document.getElementById(`risk${targetView.charAt(0).toUpperCase() + targetView.slice(1)}View`);
5256                if (targetElement) {
5257                    targetElement.style.display = 'block';
5258                    targetElement.classList.add('active');
5259                }
5260                
5261                // Load specific content based on view
5262                loadRiskViewContent(targetView);
5263            });
5264        });
5265    }
5266    
5267    function loadRiskViewContent(viewType) {
5268        switch(viewType) {
5269            case 'table':
5270                loadSafetyRisks(); // Existing function
5271                break;
5272            case 'patterns':
5273                loadRiskPatterns();
5274                break;
5275            case 'locations':
5276                loadRiskLocations();
5277                break;
5278        }
5279    }
5280    
5281    function loadRiskPatterns() {
5282        const chartContainer = document.getElementById('riskPatternsChart');
5283        if (!chartContainer) return;
5284        
5285        // Simulate pattern analysis
5286        chartContainer.innerHTML = `
5287            <div style="padding: 2rem; text-align: center;">
5288                <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-bottom: 1rem;">
5289                    <div style="background: var(--bg-primary); padding: 1rem; border-radius: 8px;">
5290                        <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-red);">67%</div>
5291                        <div style="font-size: 0.8rem; color: var(--text-secondary);">Manual Memory</div>
5292                    </div>
5293                    <div style="background: var(--bg-primary); padding: 1rem; border-radius: 8px;">
5294                        <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-orange);">23%</div>
5295                        <div style="font-size: 0.8rem; color: var(--text-secondary);">Boundary Cross</div>
5296                    </div>
5297                    <div style="background: var(--bg-primary); padding: 1rem; border-radius: 8px;">
5298                        <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-blue);">8%</div>
5299                        <div style="font-size: 0.8rem; color: var(--text-secondary);">Ownership Issues</div>
5300                    </div>
5301                    <div style="background: var(--bg-primary); padding: 1rem; border-radius: 8px;">
5302                        <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-green);">2%</div>
5303                        <div style="font-size: 0.8rem; color: var(--text-secondary);">Other Risks</div>
5304                    </div>
5305                </div>
5306                <p style="color: var(--text-secondary); font-size: 0.9rem;">
5307                    Most common risk patterns: Manual memory management dominates unsafe operations
5308                </p>
5309            </div>
5310        `;
5311    }
5312    
5313    function loadRiskLocations() {
5314        const heatmapContainer = document.getElementById('riskLocationsHeatmap');
5315        if (!heatmapContainer) return;
5316        
5317        // Simulate location heatmap
5318        heatmapContainer.innerHTML = `
5319            <div style="padding: 1rem;">
5320                <div style="margin-bottom: 1rem;">
5321                    <h4 style="margin: 0 0 0.5rem 0; font-size: 0.9rem;">High-Risk Code Locations</h4>
5322                </div>
5323                <div style="display: flex; flex-direction: column; gap: 0.5rem;">
5324                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem; background: rgba(220, 38, 38, 0.1); border-radius: 4px; border-left: 3px solid #dc2626;">
5325                        <span style="font-size: 0.8rem; font-family: monospace;">src/ffi/mod.rs:142</span>
5326                        <span style="font-size: 0.7rem; color: #dc2626; font-weight: 600;">HIGH</span>
5327                    </div>
5328                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem; background: rgba(245, 158, 11, 0.1); border-radius: 4px; border-left: 3px solid #f59e0b;">
5329                        <span style="font-size: 0.8rem; font-family: monospace;">src/memory/alloc.rs:89</span>
5330                        <span style="font-size: 0.7rem; color: #f59e0b; font-weight: 600;">MED</span>
5331                    </div>
5332                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem; background: rgba(245, 158, 11, 0.1); border-radius: 4px; border-left: 3px solid #f59e0b;">
5333                        <span style="font-size: 0.8rem; font-family: monospace;">src/unsafe/ptr.rs:67</span>
5334                        <span style="font-size: 0.7rem; color: #f59e0b; font-weight: 600;">MED</span>
5335                    </div>
5336                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.5rem; background: rgba(16, 185, 129, 0.1); border-radius: 4px; border-left: 3px solid #10b981;">
5337                        <span style="font-size: 0.8rem; font-family: monospace;">src/lib.rs:234</span>
5338                        <span style="font-size: 0.7rem; color: #10b981; font-weight: 600;">LOW</span>
5339                    </div>
5340                </div>
5341            </div>
5342        `;
5343    }
5344    
5345    // Enhanced loadSafetyRisks function with real data extraction
5346    function loadSafetyRisks() {
5347        console.log('ðŸ›Ąïļ Loading safety risk data from real unsafe/FFI analysis...');
5348        const unsafeTable = document.getElementById('unsafeTable');
5349        if (!unsafeTable) {
5350            console.warn('⚠ïļ unsafeTable not found');
5351            return;
5352        }
5353        
5354        // Show loading state first
5355        unsafeTable.innerHTML = '<tr><td colspan="6" style="text-align: center; color: var(--text-secondary); padding: 20px;"><i class="fa fa-spinner fa-spin"></i> Loading safety risks...</td></tr>';
5356        
5357        // Extract real risks from actual data
5358        let risks = [];
5359        try {
5360            risks = extractRealSafetyRisks();
5361            console.log(`ðŸ›Ąïļ Extracted ${risks.length} safety risks`);
5362        } catch (error) {
5363            console.error('❌ Error extracting safety risks:', error);
5364            // Fallback to sample data for demonstration
5365            risks = [
5366                {
5367                    location: 'src/main.rs:42',
5368                    operation: 'unsafe { libc::malloc }',
5369                    risk_level: 'High',
5370                    rawData: { base: { size: 1024 } }
5371                },
5372                {
5373                    location: 'src/lib.rs:158',
5374                    operation: 'Manual memory management',
5375                    risk_level: 'Medium',
5376                    rawData: { base: { size: 512 } }
5377                }
5378            ];
5379            console.log('ðŸ›Ąïļ Using fallback sample risk data for demonstration');
5380        }
5381        
5382        if (risks.length === 0) {
5383            unsafeTable.innerHTML = '<tr><td colspan="4" class="text-center text-gray-500">No safety risks detected</td></tr>';
5384            return;
5385        }
5386        
5387        unsafeTable.innerHTML = '';
5388        risks.forEach((risk, index) => {
5389            const row = document.createElement('tr');
5390            row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
5391            
5392            const riskLevelClass = risk.risk_level === 'High' ? 'text-red-600' :
5393                risk.risk_level === 'Medium' ? 'text-yellow-600' : 'text-green-600';
5394            
5395            const memorySize = risk?.rawData?.base?.size ? formatBytes(risk.rawData.base.size) : 'N/A';
5396            const assessment = getRiskAssessment(risk);
5397            
5398            row.innerHTML = `
5399                <td class="px-3 py-2 text-sm font-mono" style="max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${risk.location}</td>
5400                <td class="px-3 py-2 text-sm">${risk.operation}</td>
5401                <td class="px-3 py-2 text-sm"><span class="${riskLevelClass} font-weight-600">${risk.risk_level}</span></td>
5402                <td class="px-3 py-2 text-sm" style="color: var(--text-secondary);">${memorySize}</td>
5403                <td class="px-3 py-2 text-xs" style="max-width: 150px; color: var(--text-secondary);">${assessment}</td>
5404                <td class="px-3 py-2 text-sm">
5405                    <button class="action-btn" onclick="showRiskActionModal('${risk.location}', '${risk.operation}', '${risk.risk_level}', ${index})" 
5406                            style="background: var(--primary-blue); color: white; border: none; padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.7rem; cursor: pointer; transition: all 0.2s ease;">
5407                        <i class="fa fa-info-circle"></i> More
5408                    </button>
5409                </td>
5410            `;
5411            unsafeTable.appendChild(row);
5412        });
5413        
5414        // Update risk summary stats
5415        const highCount = risks.filter(r => r.risk_level === 'High').length;
5416        const mediumCount = risks.filter(r => r.risk_level === 'Medium').length;
5417        const lowCount = risks.filter(r => r.risk_level === 'Low').length;
5418        
5419        safeUpdateElement('high-risk-count', highCount);
5420        safeUpdateElement('medium-risk-count', mediumCount);
5421        safeUpdateElement('low-risk-count', lowCount);
5422        
5423        console.log('✅ Real safety risks loaded:', risks.length, 'items');
5424    }
5425
5426    // Extract real safety risks from actual unsafe/FFI data
5427    function extractRealSafetyRisks() {
5428        const risks = [];
5429        
5430        // Ensure unsafeAnalysisData is initialized
5431        if (typeof window.unsafeAnalysisData === 'undefined' || window.unsafeAnalysisData === null) {
5432            console.warn('⚠ïļ window.unsafeAnalysisData not initialized, initializing empty array');
5433            window.unsafeAnalysisData = [];
5434        }
5435        
5436        // Extract from unsafe analysis data
5437        if (window.unsafeAnalysisData && Array.isArray(window.unsafeAnalysisData) && window.unsafeAnalysisData.length > 0) {
5438            window.unsafeAnalysisData.forEach((item, index) => {
5439                // Extract location from unsafe block location
5440                let location = 'Unknown location';
5441                if (item.source?.UnsafeRust?.unsafe_block_location) {
5442                    location = item.source.UnsafeRust.unsafe_block_location;
5443                } else if (item.source?.FfiC?.resolved_function?.library_name) {
5444                    const libName = item.source.FfiC.resolved_function.library_name;
5445                    const funcName = item.source.FfiC.resolved_function.function_name;
5446                    location = `${libName}::${funcName}`;
5447                }
5448                
5449                // Determine operation type
5450                let operation = 'unknown operation';
5451                if (item.source?.UnsafeRust) {
5452                    operation = 'unsafe rust operation';
5453                    if (item.base?.ptr) operation = 'raw pointer manipulation';
5454                } else if (item.source?.FfiC) {
5455                    const funcName = item.source.FfiC.resolved_function?.function_name;
5456                    if (funcName === 'malloc') operation = 'manual memory allocation';
5457                    else if (funcName === 'free') operation = 'manual memory deallocation';
5458                    else operation = `FFI call: ${funcName}`;
5459                }
5460                
5461                // Determine risk level from assessment
5462                let riskLevel = 'Low';
5463                if (item.source?.UnsafeRust?.risk_assessment) {
5464                    riskLevel = item.source.UnsafeRust.risk_assessment.risk_level;
5465                } else if (item.base?.is_leaked) {
5466                    riskLevel = 'High';
5467                } else if (item.source?.FfiC) {
5468                    riskLevel = item.source.FfiC.resolved_function?.risk_level || 'Medium';
5469                }
5470                
5471                // Only add items with identifiable risks
5472                if (riskLevel !== 'Low' || item.base?.is_leaked || item.has_boundary_events) {
5473                    risks.push({
5474                        location: location,
5475                        operation: operation,
5476                        risk_level: riskLevel,
5477                        rawData: item,
5478                        riskFactors: item.source?.UnsafeRust?.risk_assessment?.risk_factors || []
5479                    });
5480                }
5481            });
5482        }
5483        
5484        // If no real risks found, show some from basic allocations data
5485        if (risks.length === 0 && window.analysisData?.memory_analysis?.allocations) {
5486            const allocations = window.analysisData.memory_analysis.allocations;
5487            allocations.forEach((alloc, index) => {
5488                if (alloc.type_name && alloc.type_name.includes('*')) {
5489                    risks.push({
5490                        location: `allocation_${index}.rs:${Math.floor(Math.random() * 100) + 10}`,
5491                        operation: `pointer operation: ${alloc.type_name}`,
5492                        risk_level: alloc.is_leaked ? 'High' : 'Medium',
5493                        rawData: alloc,
5494                        riskFactors: [{
5495                            factor_type: 'RawPointerUsage',
5496                            severity: alloc.is_leaked ? 8 : 5,
5497                            description: 'Raw pointer operations require careful memory management'
5498                        }]
5499                    });
5500                }
5501            });
5502        }
5503        
5504        return risks.slice(0, 10); // Limit to first 10 for display
5505    }
5506
5507    // Show Risk Action Modal (replacing alert with elegant modal)
5508    function showRiskActionModal(location, operation, riskLevel, riskIndex) {
5509        const modal = document.getElementById('riskActionModal');
5510        const body = document.getElementById('riskActionBody');
5511        
5512        if (!modal || !body) return;
5513        
5514        // Get the actual risk data
5515        const risks = extractRealSafetyRisks();
5516        const risk = risks[riskIndex];
5517        
5518        // Generate action content based on real risk data
5519        const actionContent = generateRiskActionContent(risk, location, operation, riskLevel);
5520        body.innerHTML = actionContent;
5521        
5522        modal.style.display = 'flex';
5523        
5524        console.log('🔧 Showing risk action modal for:', location);
5525    }
5526
5527    function generateRiskActionContent(risk, location, operation, riskLevel) {
5528        const riskColor = riskLevel === 'High' ? '#dc2626' : riskLevel === 'Medium' ? '#f59e0b' : '#10b981';
5529        
5530        return `
5531            <div class="risk-action-section">
5532                <h4><i class="fa fa-exclamation-triangle" style="color: ${riskColor};"></i> Risk Assessment</h4>
5533                <div class="risk-action-grid">
5534                    <div class="risk-action-item">
5535                        <strong>Location:</strong> <code>${location}</code>
5536                    </div>
5537                    <div class="risk-action-item">
5538                        <strong>Operation:</strong> ${operation}
5539                    </div>
5540                    <div class="risk-action-item">
5541                        <strong>Risk Level:</strong> 
5542                        <span style="color: ${riskColor}; font-weight: bold;">${riskLevel}</span>
5543                    </div>
5544                    ${risk?.rawData?.base?.size ? `
5545                    <div class="risk-action-item">
5546                        <strong>Memory Size:</strong> ${formatBytes(risk.rawData.base.size)}
5547                    </div>
5548                    ` : ''}
5549                </div>
5550            </div>
5551            
5552            <div class="risk-action-section">
5553                <h4><i class="fa fa-lightbulb"></i> Recommended Actions</h4>
5554                <div class="recommended-actions">
5555                    ${generateRecommendedActions(risk, operation, riskLevel)}
5556                </div>
5557            </div>
5558            
5559            ${risk?.riskFactors && risk.riskFactors.length > 0 ? `
5560            <div class="risk-action-section">
5561                <h4><i class="fa fa-list"></i> Risk Factors</h4>
5562                <div class="risk-factors-list">
5563                    ${risk.riskFactors.map(factor => `
5564                        <div class="risk-factor-item">
5565                            <div class="factor-header">
5566                                <strong>${factor.factor_type}</strong>
5567                                <span class="severity-badge" style="background: ${getSeverityColor(factor.severity)};">
5568                                    Severity: ${factor.severity}/10
5569                                </span>
5570                            </div>
5571                            <p class="factor-description">${factor.description}</p>
5572                        </div>
5573                    `).join('')}
5574                </div>
5575            </div>
5576            ` : ''}
5577            
5578        `;
5579    }
5580
5581    function generateRecommendedActions(risk, operation, riskLevel) {
5582        const actions = [];
5583        
5584        // Based on operation type
5585        if (operation.includes('pointer')) {
5586            actions.push({
5587                icon: 'fa-shield',
5588                title: 'Add Null Pointer Checks',
5589                description: 'Validate pointer is not null before dereferencing',
5590                priority: 'High'
5591            });
5592            actions.push({
5593                icon: 'fa-check-circle',
5594                title: 'Bounds Checking',
5595                description: 'Ensure pointer access is within allocated memory bounds',
5596                priority: 'High'
5597            });
5598        }
5599        
5600        if (operation.includes('malloc') || operation.includes('allocation')) {
5601            actions.push({
5602                icon: 'fa-recycle',
5603                title: 'Use RAII Pattern',
5604                description: 'Wrap allocation in a safe Rust struct with Drop trait',
5605                priority: 'Medium'
5606            });
5607            actions.push({
5608                icon: 'fa-balance-scale',
5609                title: 'Match Alloc/Dealloc',
5610                description: 'Ensure every allocation has a corresponding deallocation',
5611                priority: 'High'
5612            });
5613        }
5614        
5615        if (risk?.rawData?.base?.is_leaked) {
5616            actions.push({
5617                icon: 'fa-bug',
5618                title: 'Fix Memory Leak',
5619                description: 'Add proper cleanup code to prevent memory leaks',
5620                priority: 'Critical'
5621            });
5622        }
5623        
5624        if (risk?.has_boundary_events) {
5625            actions.push({
5626                icon: 'fa-exchange',
5627                title: 'Document Ownership Transfer',
5628                description: 'Clearly document which side owns the memory after FFI calls',
5629                priority: 'Medium'
5630            });
5631        }
5632        
5633        // Default actions
5634        if (actions.length === 0) {
5635            actions.push({
5636                icon: 'fa-book',
5637                title: 'Add Safety Documentation',
5638                description: 'Document the safety invariants and assumptions',
5639                priority: 'Low'
5640            });
5641        }
5642        
5643        return actions.map(action => `
5644            <div class="recommended-action">
5645                <div class="action-header">
5646                    <i class="fa ${action.icon}"></i>
5647                    <strong>${action.title}</strong>
5648                    <span class="priority-badge priority-${action.priority.toLowerCase()}">${action.priority}</span>
5649                </div>
5650                <p class="action-description">${action.description}</p>
5651            </div>
5652        `).join('');
5653    }
5654
5655    function getSeverityColor(severity) {
5656        if (severity >= 8) return '#dc2626';
5657        if (severity >= 6) return '#f59e0b';
5658        if (severity >= 4) return '#eab308';
5659        return '#10b981';
5660    }
5661
5662    // Quick action functions
5663    function copyLocationToClipboard(location) {
5664        navigator.clipboard.writeText(location).then(() => {
5665            console.log('📋 Location copied to clipboard:', location);
5666            // Could show a toast notification here
5667        });
5668    }
5669
5670    function openInEditor(location) {
5671        console.log('🔧 Opening in editor:', location);
5672        // This would integrate with VS Code or other editor
5673        // Example: vscode://file/path/to/file:line:column
5674    }
5675
5676    function generateFixPatch(location, operation) {
5677        console.log('🊄 Generating fix patch for:', location, operation);
5678        // This would generate actual code fixes based on the risk type
5679    }
5680
5681    function closeRiskActionModal() {
5682        const modal = document.getElementById('riskActionModal');
5683        if (modal) {
5684            modal.style.display = 'none';
5685        }
5686    }
5687
5688    // Initialize enhanced unsafe analysis when data is available
5689    if (window.analysisData || window.unsafeFfiSnapshot) {
5690        setTimeout(() => {
5691            initializeEnhancedUnsafeAnalysis();
5692            setupDynamicSizeControls();
5693            setupRiskAnalysisTabs();
5694        }, 1200);
5695    }
5696  </script>
5697
5698  <style>
5699    /* Enhanced Unsafe Rust & FFI Memory Analysis Styles */
5700    .unsafe-analysis-header {
5701        display: flex;
5702        justify-content: space-between;
5703        align-items: flex-end;
5704        gap: 2rem;
5705        margin-bottom: 2rem;
5706    }
5707
5708    .size-controls {
5709        display: flex;
5710        gap: 0.5rem;
5711        margin-right: 1rem;
5712    }
5713
5714    .control-btn {
5715        padding: 0.4rem 0.8rem;
5716        background: var(--bg-secondary);
5717        border: 1px solid var(--border-light);
5718        border-radius: 6px;
5719        color: var(--text-primary);
5720        font-size: 0.8rem;
5721        cursor: pointer;
5722        transition: all 0.2s ease;
5723        white-space: nowrap;
5724    }
5725
5726    .control-btn:hover {
5727        background: var(--primary-blue);
5728        color: white;
5729        transform: translateY(-1px);
5730        box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
5731    }
5732
5733    .unsafe-filter-controls {
5734        display: flex;
5735        align-items: center;
5736    }
5737
5738    .unsafe-filter-tabs {
5739        display: flex;
5740        gap: 0.25rem;
5741        background: var(--bg-secondary);
5742        padding: 0.25rem;
5743        border-radius: 10px;
5744        border: 1px solid var(--border-light);
5745        box-shadow: var(--shadow-light);
5746    }
5747
5748    .unsafe-filter-tab {
5749        padding: 0.4rem 0.8rem;
5750        border: none;
5751        background: transparent;
5752        color: var(--text-secondary);
5753        font-size: 0.75rem;
5754        font-weight: 600;
5755        border-radius: 8px;
5756        cursor: pointer;
5757        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
5758        white-space: nowrap;
5759        user-select: none;
5760        position: relative;
5761    }
5762
5763    .unsafe-filter-tab:hover {
5764        background: var(--bg-primary);
5765        color: var(--text-primary);
5766        transform: translateY(-1px);
5767        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
5768    }
5769
5770    .unsafe-filter-tab.active {
5771        background: linear-gradient(135deg, var(--primary-blue), #3b82f6);
5772        color: white;
5773        box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
5774        transform: translateY(-2px);
5775    }
5776
5777    /* Enhanced Swimlane Container */
5778    .enhanced-swimlane-container {
5779        background: var(--bg-secondary);
5780        border-radius: 16px;
5781        border: 1px solid var(--border-light);
5782        overflow: hidden;
5783        box-shadow: var(--shadow-light);
5784        transition: all 0.3s ease;
5785    }
5786
5787    .card.expanded .enhanced-swimlane-container {
5788        min-height: 600px;
5789    }
5790
5791    .card.compact .enhanced-swimlane-container {
5792        min-height: 400px;
5793    }
5794
5795    /* Integrated Risk Analysis Styles */
5796    .integrated-risk-section {
5797        background: var(--bg-primary);
5798        border-radius: 12px;
5799        margin: 1.5rem;
5800        border: 1px solid var(--border-light);
5801        overflow: hidden;
5802    }
5803
5804    .risk-section-header {
5805        display: flex;
5806        justify-content: space-between;
5807        align-items: center;
5808        padding: 1rem 1.5rem;
5809        background: linear-gradient(135deg, var(--bg-secondary), var(--bg-primary));
5810        border-bottom: 1px solid var(--border-light);
5811    }
5812
5813    .risk-section-header h4 {
5814        margin: 0;
5815        color: var(--text-primary);
5816        font-size: 1rem;
5817        font-weight: 600;
5818    }
5819
5820    .risk-summary-stats {
5821        display: flex;
5822        gap: 1rem;
5823        align-items: center;
5824    }
5825
5826    .risk-stat {
5827        display: flex;
5828        align-items: center;
5829        gap: 0.3rem;
5830        font-size: 0.8rem;
5831        font-weight: 600;
5832        padding: 0.3rem 0.6rem;
5833        border-radius: 6px;
5834        background: var(--bg-secondary);
5835    }
5836
5837    .risk-stat.high-risk {
5838        color: #dc2626;
5839        background: rgba(220, 38, 38, 0.1);
5840    }
5841
5842    .risk-stat.medium-risk {
5843        color: #f59e0b;
5844        background: rgba(245, 158, 11, 0.1);
5845    }
5846
5847    .risk-stat.low-risk {
5848        color: #10b981;
5849        background: rgba(16, 185, 129, 0.1);
5850    }
5851
5852    .risk-analysis-tabs {
5853        display: flex;
5854        background: var(--bg-secondary);
5855        padding: 0.25rem;
5856        margin: 0 1.5rem;
5857        border-radius: 8px;
5858        gap: 0.25rem;
5859    }
5860
5861    .risk-tab {
5862        flex: 1;
5863        padding: 0.4rem 0.6rem;
5864        border: none;
5865        background: transparent;
5866        color: var(--text-secondary);
5867        font-size: 0.75rem;
5868        font-weight: 600;
5869        border-radius: 6px;
5870        cursor: pointer;
5871        transition: all 0.2s ease;
5872        text-align: center;
5873    }
5874
5875    .risk-tab:hover {
5876        background: var(--bg-primary);
5877        color: var(--text-primary);
5878    }
5879
5880    .risk-tab.active {
5881        background: var(--primary-blue);
5882        color: white;
5883        box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
5884    }
5885
5886    .risk-views-container {
5887        padding: 1rem 1.5rem;
5888    }
5889
5890    .risk-view {
5891        width: 100%;
5892    }
5893
5894    /* Swimlane Container */
5895    .swimlane-container {
5896        flex: 1;
5897        background: var(--bg-primary);
5898        border-radius: 8px;
5899        overflow: hidden;
5900        margin: 1rem 1.5rem;
5901        border: 1px solid var(--border-light);
5902    }
5903
5904    /* Dual Swimlane Layout */
5905    .dual-swimlane {
5906        position: relative;
5907        min-height: 300px;
5908    }
5909
5910    .swimlane {
5911        position: relative;
5912        height: 100px;
5913        display: flex;
5914        border-bottom: 1px solid var(--border-light);
5915    }
5916
5917    .rust-domain {
5918        background: linear-gradient(135deg, rgba(255, 107, 71, 0.08) 0%, rgba(255, 107, 71, 0.03) 100%);
5919    }
5920
5921    .ffi-domain {
5922        background: linear-gradient(135deg, rgba(74, 158, 255, 0.08) 0%, rgba(74, 158, 255, 0.03) 100%);
5923    }
5924
5925    .domain-label {
5926        display: flex;
5927        align-items: center;
5928        gap: 0.8rem;
5929        padding: 1rem 1.5rem;
5930        min-width: 200px;
5931        background: rgba(255, 255, 255, 0.5);
5932        border-right: 1px solid var(--border-light);
5933    }
5934
5935    .domain-icon {
5936        font-size: 1.5rem;
5937        width: 40px;
5938        height: 40px;
5939        display: flex;
5940        align-items: center;
5941        justify-content: center;
5942        background: var(--bg-primary);
5943        border-radius: 50%;
5944        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
5945    }
5946
5947    .domain-info h4 {
5948        margin: 0 0 0.2rem 0;
5949        color: var(--text-primary);
5950        font-size: 0.9rem;
5951        font-weight: 700;
5952    }
5953
5954    .domain-info p {
5955        margin: 0;
5956        color: var(--text-secondary);
5957        font-size: 0.7rem;
5958    }
5959
5960    .timeline-track {
5961        flex: 1;
5962        position: relative;
5963        padding: 0.8rem;
5964        background: var(--bg-primary);
5965    }
5966
5967    .timeline-axis {
5968        height: 30px;
5969        background: linear-gradient(90deg, var(--border-light) 0%, var(--border-light) 100%);
5970        border-top: 1px solid var(--border-light);
5971        border-bottom: 1px solid var(--border-light);
5972        position: relative;
5973    }
5974
5975    /* Enhanced Legend */
5976    .enhanced-legend {
5977        padding: 1.5rem 2rem;
5978        background: var(--bg-primary);
5979        display: grid;
5980        grid-template-columns: 1fr 1fr;
5981        gap: 2rem;
5982    }
5983
5984    .legend-section h4 {
5985        margin: 0 0 1rem 0;
5986        color: var(--text-primary);
5987        font-size: 1rem;
5988        font-weight: 600;
5989        border-bottom: 2px solid var(--primary-blue);
5990        padding-bottom: 0.5rem;
5991    }
5992
5993    .legend-items {
5994        display: grid;
5995        gap: 0.8rem;
5996    }
5997
5998    .legend-item {
5999        display: flex;
6000        align-items: center;
6001        gap: 0.75rem;
6002        font-size: 0.9rem;
6003        color: var(--text-secondary);
6004    }
6005
6006    .event-symbol {
6007        width: 16px;
6008        height: 16px;
6009        display: inline-flex;
6010        align-items: center;
6011        justify-content: center;
6012        font-weight: bold;
6013        border-radius: 50%;
6014    }
6015
6016    .rust-alloc { background: #ff6b47; color: white; }
6017    .ffi-alloc { background: #4a9eff; color: white; }
6018    .boundary-up { color: #00d4aa; font-size: 1.2rem; }
6019    .boundary-down { color: #ff4757; font-size: 1.2rem; }
6020    .dealloc { color: #a0a0a0; font-size: 1.2rem; }
6021
6022    .risk-indicator {
6023        width: 20px;
6024        height: 12px;
6025        border-radius: 6px;
6026        display: inline-block;
6027    }
6028
6029    .high-risk { background: linear-gradient(90deg, #ff4757, #ff3742); }
6030    .medium-risk { background: linear-gradient(90deg, #ffa502, #ff9f43); }
6031    .leak-risk { background: linear-gradient(90deg, #ff6b47, #ff5722); }
6032
6033    /* Memory Passport Modal */
6034    .memory-passport-modal {
6035        position: fixed;
6036        top: 0;
6037        left: 0;
6038        width: 100%;
6039        height: 100%;
6040        background: rgba(0, 0, 0, 0.7);
6041        display: flex;
6042        align-items: center;
6043        justify-content: center;
6044        z-index: 1000;
6045        backdrop-filter: blur(4px);
6046    }
6047
6048    .passport-content {
6049        background: var(--bg-primary);
6050        border-radius: 16px;
6051        max-width: 800px;
6052        width: 90%;
6053        max-height: 80%;
6054        overflow: hidden;
6055        box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
6056        border: 1px solid var(--border-light);
6057    }
6058
6059    .passport-header {
6060        display: flex;
6061        justify-content: space-between;
6062        align-items: center;
6063        padding: 1.5rem 2rem;
6064        background: linear-gradient(135deg, var(--primary-blue), #3b82f6);
6065        color: white;
6066    }
6067
6068    .passport-header h3 {
6069        margin: 0;
6070        font-size: 1.4rem;
6071        font-weight: 700;
6072    }
6073
6074    .passport-close {
6075        background: none;
6076        border: none;
6077        color: white;
6078        font-size: 1.5rem;
6079        cursor: pointer;
6080        padding: 0.5rem;
6081        border-radius: 4px;
6082        transition: background 0.2s ease;
6083    }
6084
6085    .passport-close:hover {
6086        background: rgba(255, 255, 255, 0.2);
6087    }
6088
6089    .passport-body {
6090        padding: 2rem;
6091        max-height: 60vh;
6092        overflow-y: auto;
6093    }
6094
6095    /* Risk Action Modal Styles */
6096    .risk-action-section {
6097        margin-bottom: 2rem;
6098        padding-bottom: 1.5rem;
6099        border-bottom: 1px solid var(--border-light);
6100    }
6101
6102    .risk-action-section:last-child {
6103        border-bottom: none;
6104        margin-bottom: 0;
6105    }
6106
6107    .risk-action-section h4 {
6108        margin: 0 0 1rem 0;
6109        color: var(--text-primary);
6110        font-size: 1.1rem;
6111        font-weight: 600;
6112        display: flex;
6113        align-items: center;
6114        gap: 0.5rem;
6115    }
6116
6117    .risk-action-grid {
6118        display: grid;
6119        grid-template-columns: 1fr 1fr;
6120        gap: 1rem;
6121        margin-bottom: 1rem;
6122    }
6123
6124    .risk-action-item {
6125        padding: 0.75rem;
6126        background: var(--bg-secondary);
6127        border-radius: 8px;
6128        border: 1px solid var(--border-light);
6129    }
6130
6131    .risk-action-item strong {
6132        color: var(--text-primary);
6133        font-weight: 600;
6134        margin-right: 0.5rem;
6135    }
6136
6137    .risk-action-item code {
6138        background: var(--bg-primary);
6139        padding: 0.2rem 0.4rem;
6140        border-radius: 4px;
6141        font-family: 'Courier New', monospace;
6142        font-size: 0.85rem;
6143        color: var(--primary-blue);
6144    }
6145
6146    /* Recommended Actions */
6147    .recommended-actions {
6148        display: flex;
6149        flex-direction: column;
6150        gap: 1rem;
6151    }
6152
6153    .recommended-action {
6154        background: var(--bg-secondary);
6155        border-radius: 8px;
6156        padding: 1rem;
6157        border: 1px solid var(--border-light);
6158        transition: all 0.2s ease;
6159    }
6160
6161    .recommended-action:hover {
6162        border-color: var(--primary-blue);
6163        box-shadow: 0 4px 8px rgba(59, 130, 246, 0.1);
6164    }
6165
6166    .action-header {
6167        display: flex;
6168        align-items: center;
6169        gap: 0.75rem;
6170        margin-bottom: 0.5rem;
6171    }
6172
6173    .action-header i {
6174        color: var(--primary-blue);
6175        font-size: 1.1rem;
6176        width: 20px;
6177    }
6178
6179    .action-header strong {
6180        color: var(--text-primary);
6181        font-weight: 600;
6182        flex: 1;
6183    }
6184
6185    .priority-badge {
6186        padding: 0.2rem 0.6rem;
6187        border-radius: 12px;
6188        font-size: 0.7rem;
6189        font-weight: 600;
6190        text-transform: uppercase;
6191        letter-spacing: 0.5px;
6192    }
6193
6194    .priority-critical {
6195        background: rgba(220, 38, 38, 0.1);
6196        color: #dc2626;
6197        border: 1px solid rgba(220, 38, 38, 0.2);
6198    }
6199
6200    .priority-high {
6201        background: rgba(245, 158, 11, 0.1);
6202        color: #f59e0b;
6203        border: 1px solid rgba(245, 158, 11, 0.2);
6204    }
6205
6206    .priority-medium {
6207        background: rgba(59, 130, 246, 0.1);
6208        color: #3b82f6;
6209        border: 1px solid rgba(59, 130, 246, 0.2);
6210    }
6211
6212    .priority-low {
6213        background: rgba(16, 185, 129, 0.1);
6214        color: #10b981;
6215        border: 1px solid rgba(16, 185, 129, 0.2);
6216    }
6217
6218    .action-description {
6219        margin: 0;
6220        color: var(--text-secondary);
6221        font-size: 0.9rem;
6222        line-height: 1.4;
6223    }
6224
6225    /* Risk Factors */
6226    .risk-factors-list {
6227        display: flex;
6228        flex-direction: column;
6229        gap: 1rem;
6230    }
6231
6232    .risk-factor-item {
6233        background: var(--bg-secondary);
6234        border-radius: 8px;
6235        padding: 1rem;
6236        border: 1px solid var(--border-light);
6237    }
6238
6239    .factor-header {
6240        display: flex;
6241        align-items: center;
6242        justify-content: space-between;
6243        margin-bottom: 0.5rem;
6244    }
6245
6246    .factor-header strong {
6247        color: var(--text-primary);
6248        font-weight: 600;
6249    }
6250
6251    .severity-badge {
6252        padding: 0.2rem 0.6rem;
6253        border-radius: 12px;
6254        font-size: 0.7rem;
6255        font-weight: 600;
6256        color: white;
6257    }
6258
6259    .factor-description {
6260        margin: 0;
6261        color: var(--text-secondary);
6262        font-size: 0.9rem;
6263        line-height: 1.4;
6264    }
6265
6266    /* Quick Actions */
6267    .quick-actions {
6268        display: flex;
6269        gap: 1rem;
6270        flex-wrap: wrap;
6271    }
6272
6273    .quick-action-btn {
6274        padding: 0.75rem 1.5rem;
6275        background: var(--bg-secondary);
6276        border: 1px solid var(--border-light);
6277        border-radius: 8px;
6278        color: var(--text-primary);
6279        font-size: 0.9rem;
6280        font-weight: 500;
6281        cursor: pointer;
6282        transition: all 0.2s ease;
6283        display: flex;
6284        align-items: center;
6285        gap: 0.5rem;
6286    }
6287
6288    .quick-action-btn:hover {
6289        background: var(--primary-blue);
6290        color: white;
6291        border-color: var(--primary-blue);
6292        transform: translateY(-1px);
6293        box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
6294    }
6295
6296    .quick-action-btn i {
6297        font-size: 0.9rem;
6298    }
6299  </style>
6300</head>
6301
6302<body>
6303  <div class="dashboard-container">
6304    <!-- Header -->
6305    <header class="header">
6306      <div>
6307        <h1>{{PROJECT_NAME}} - Binary Memory Analysis Dashboard</h1>
6308        <div class="subtitle">Real-time Rust Memory Usage Monitoring</div>
6309      </div>
6310      <button id="theme-toggle" class="theme-toggle">
6311        <i class="fa fa-moon"></i>
6312        <span>Toggle Theme</span>
6313      </button>
6314    </header>
6315
6316    <!-- KPI Metrics -->
6317    <section class="grid grid-4">
6318      <div class="kpi-card">
6319        <div class="kpi-value" id="total-allocations">-</div>
6320        <div class="kpi-label">Total Allocations</div>
6321      </div>
6322      <div class="kpi-card">
6323        <div class="kpi-value" id="active-variables">-</div>
6324        <div class="kpi-label">Active Variables</div>
6325      </div>
6326      <div class="kpi-card">
6327        <div class="kpi-value" id="total-memory">-</div>
6328        <div class="kpi-label">Total Memory</div>
6329      </div>
6330      <div class="kpi-card">
6331        <div class="kpi-value" id="safety-score">-</div>
6332        <div class="kpi-label">Safety Score</div>
6333      </div>
6334    </section>
6335
6336    <!-- Key Performance Metrics (high priority position) -->
6337    <section class="grid grid-2">
6338      <div class="card">
6339        <h2><i class="fa fa-tachometer-alt"></i> Performance Metrics</h2>
6340        <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;">
6341          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6342            <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-blue);" id="peak-memory">0B</div>
6343            <div style="font-size: 0.8rem; color: var(--text-secondary);">Peak Memory</div>
6344          </div>
6345          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6346            <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-green);" id="allocation-rate">0/sec
6347            </div>
6348            <div style="font-size: 0.8rem; color: var(--text-secondary);">Allocation Rate</div>
6349          </div>
6350          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6351            <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-orange);" id="avg-lifetime">0ms</div>
6352            <div style="font-size: 0.8rem; color: var(--text-secondary);">Avg Lifetime</div>
6353          </div>
6354          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6355            <div style="font-size: 1.5rem; font-weight: 700; color: var(--primary-red);" id="fragmentation">0%</div>
6356            <div style="font-size: 0.8rem; color: var(--text-secondary);">Fragmentation</div>
6357          </div>
6358        </div>
6359      </div>
6360      <div class="card">
6361        <h2><i class="fa fa-shield-alt"></i> Thread Safety Analysis</h2>
6362        <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;">
6363          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6364            <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-blue);" id="arc-count">0</div>
6365            <div style="font-size: 0.7rem; color: var(--text-secondary);">Arc</div>
6366          </div>
6367          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6368            <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-green);" id="rc-count">0</div>
6369            <div style="font-size: 0.7rem; color: var(--text-secondary);">Rc</div>
6370          </div>
6371          <div style="text-align: center; padding: 12px; background: var(--bg-secondary); border-radius: 8px;">
6372            <div style="font-size: 1.2rem; font-weight: 600; color: var(--primary-orange);" id="collections-count">0
6373            </div>
6374            <div style="font-size: 0.7rem; color: var(--text-secondary);">Collections</div>
6375          </div>
6376        </div>
6377      </div>
6378    </section>
6379
6380    <!-- Memory Operations Analysis (Moved to front as summary) -->
6381    <section class="card">
6382      <h2><i class="fa fa-exchange"></i> Memory Operations Analysis</h2>
6383      <div id="memoryOperations" style="padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
6384        <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;">
6385          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6386            <div id="time-span" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-blue);">-</div>
6387            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Time Span</div>
6388          </div>
6389          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6390            <div id="allocation-burst" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-orange);">-</div>
6391            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Alloc Burst</div>
6392          </div>
6393          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6394            <div id="peak-concurrency" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-red);">-</div>
6395            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Peak Concurrency</div>
6396          </div>
6397          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6398            <div id="thread-activity" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-green);">-</div>
6399            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Thread Activity</div>
6400          </div>
6401          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6402            <div id="borrow-ops" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-blue);">-</div>
6403            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Borrow Ops</div>
6404          </div>
6405          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6406            <div id="clone-ops" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-orange);">-</div>
6407            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Clone Ops</div>
6408          </div>
6409          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6410            <div id="mut-ratio" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-red);">-</div>
6411            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Mut/Immut</div>
6412          </div>
6413          <div style="text-align: center; padding: 16px; background: var(--bg-primary); border-radius: 8px;">
6414            <div id="avg-borrows" style="font-size: 1.4rem; font-weight: 700; color: var(--primary-green);">-</div>
6415            <div style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 4px;">Avg/Alloc</div>
6416          </div>
6417        </div>
6418      </div>
6419    </section>
6420
6421    <!-- Enhanced 3D Memory Layout & Timeline Playback -->
6422    <section class="card">
6423      <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap;">
6424        <h2 style="margin: 0;"><i class="fa fa-cube"></i> 3D Memory Layout Visualization</h2>
6425        <div style="display: flex; gap: 8px; flex-wrap: wrap;">
6426          <button id="toggle3DView" class="theme-toggle"
6427            style="background: var(--primary-green); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6428            <i class="fa fa-eye"></i>
6429            <span>3D View</span>
6430          </button>
6431          <button id="reset3DView" class="theme-toggle"
6432            style="background: var(--primary-orange); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6433            <i class="fa fa-refresh"></i>
6434            <span>Reset</span>
6435          </button>
6436          <button id="autoRotate3D" class="theme-toggle"
6437            style="background: var(--primary-blue); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6438            <i class="fa fa-rotate-right"></i>
6439            <span>Auto Rotate</span>
6440          </button>
6441          <button id="focusLargest" class="theme-toggle"
6442            style="background: var(--primary-red); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6443            <i class="fa fa-search-plus"></i>
6444            <span>Focus Largest</span>
6445          </button>
6446        </div>
6447      </div>
6448      <div id="memory3DContainer" class="memory-3d-container">
6449        <div class="memory-3d-info" id="memory3DInfo">
6450          Loading 3D visualization...
6451        </div>
6452      </div>
6453
6454      <!-- Timeline Playback Controls -->
6455      <div class="timeline-container">
6456        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
6457          <h3 style="margin: 0; font-size: 1rem;"><i class="fa fa-play-circle"></i> Memory Timeline Playback</h3>
6458          <div style="font-size: 0.8rem; color: var(--text-secondary);">
6459            <span id="timelineCurrentTime">0ms</span> / <span id="timelineTotalTime">0ms</span>
6460          </div>
6461        </div>
6462        <div class="timeline-slider" id="timelineSlider">
6463          <div class="timeline-progress" id="timelineProgress"></div>
6464          <div class="timeline-thumb" id="timelineThumb"></div>
6465        </div>
6466        <div class="timeline-controls">
6467          <button id="timelinePlay" class="timeline-btn">
6468            <i class="fa fa-play"></i> Play
6469          </button>
6470          <button id="timelinePause" class="timeline-btn" disabled>
6471            <i class="fa fa-pause"></i> Pause
6472          </button>
6473          <button id="timelineReset" class="timeline-btn">
6474            <i class="fa fa-refresh"></i> Reset
6475          </button>
6476          <button id="timelineStep" class="timeline-btn">
6477            <i class="fa fa-step-forward"></i> Step
6478          </button>
6479          <div style="display: flex; align-items: center; gap: 8px; margin-left: 16px;">
6480            <label style="font-size: 0.8rem; color: var(--text-secondary);">Speed:</label>
6481            <select id="timelineSpeed"
6482              style="background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-light); border-radius: 4px; padding: 2px 6px; font-size: 0.8rem;">
6483              <option value="0.25">0.25x</option>
6484              <option value="0.5">0.5x</option>
6485              <option value="1" selected>1x</option>
6486              <option value="2">2x</option>
6487              <option value="4">4x</option>
6488              <option value="8">8x</option>
6489            </select>
6490          </div>
6491        </div>
6492        <div style="margin-top: 8px; font-size: 0.8rem; color: var(--text-secondary); text-align: center;">
6493          Active Allocations: <span id="timelineActiveCount"
6494            style="color: var(--primary-blue); font-weight: 600;">0</span>
6495        </div>
6496      </div>
6497    </section>
6498
6499    <!-- Unsafe Rust & FFI Memory Analysis (Enhanced with Integrated Risk Analysis) -->
6500    <section class="card" style="min-height: 700px;">
6501      <div class="unsafe-analysis-header">
6502        <div>
6503          <h2><i class="fa fa-shield-alt"></i> Unsafe Rust & FFI Memory Analysis</h2>
6504          <p style="color: var(--text-secondary); font-size: 0.9rem; margin-top: 4px;">
6505            Advanced memory passport system with cross-boundary visualization and integrated risk assessment
6506          </p>
6507        </div>
6508        <!-- Enhanced Filter Controls with Dynamic Sizing -->
6509        <div class="unsafe-filter-controls">
6510          <div class="size-controls">
6511            <button id="expandAnalysis" class="control-btn">
6512              <i class="fa fa-expand"></i>
6513            </button>
6514            <button id="compactAnalysis" class="control-btn">
6515              <i class="fa fa-compress"></i>
6516            </button>
6517          </div>
6518          <div class="unsafe-filter-tabs">
6519            <button class="unsafe-filter-tab active" data-filter="critical">
6520              ðŸšĻ Critical (<span id="unsafe-critical-count">0</span>)
6521            </button>
6522            <button class="unsafe-filter-tab" data-filter="leaks">
6523              💧 Leaks (<span id="unsafe-leak-count">0</span>)
6524            </button>
6525            <button class="unsafe-filter-tab" data-filter="cross-boundary">
6526              🔄 Cross-Boundary (<span id="unsafe-boundary-count">0</span>)
6527            </button>
6528            <button class="unsafe-filter-tab" data-filter="risk-assessment">
6529              ⚠ïļ Risk Analysis
6530            </button>
6531            <button class="unsafe-filter-tab" data-filter="all">
6532              📊 All Data (<span id="unsafe-total-count">0</span>)
6533            </button>
6534          </div>
6535        </div>
6536      </div>
6537
6538      <!-- Enhanced Cross-Boundary Swimlane Timeline -->
6539      <div class="enhanced-swimlane-container" id="enhancedSwimlaneContainer">
6540        <div class="swimlane-header">
6541          <h3><i class="fa fa-timeline"></i> Memory Passport Timeline</h3>
6542          <div class="timeline-controls">
6543            <button id="timelineZoomIn" class="timeline-control-btn">
6544              <i class="fa fa-search-plus"></i> Zoom In
6545            </button>
6546            <button id="timelineZoomOut" class="timeline-control-btn">
6547              <i class="fa fa-search-minus"></i> Zoom Out
6548            </button>
6549            <button id="timelineReset" class="timeline-control-btn">
6550              <i class="fa fa-refresh"></i> Reset View
6551            </button>
6552          </div>
6553        </div>
6554
6555        <div class="dual-swimlane">
6556          <!-- Rust Safety Domain -->
6557          <div class="swimlane rust-domain">
6558            <div class="domain-label">
6559              <div class="domain-icon">ðŸĶ€</div>
6560              <div class="domain-info">
6561                <h4>Rust Safety Domain</h4>
6562                <p>Ownership & borrow checker controlled</p>
6563              </div>
6564            </div>
6565            <div class="timeline-track" id="rustTimelineTrack"></div>
6566          </div>
6567
6568          <!-- Timeline Axis -->
6569          <div class="timeline-axis" id="timelineAxis"></div>
6570
6571          <!-- C/FFI Domain -->
6572          <div class="swimlane ffi-domain">
6573            <div class="domain-label">
6574              <div class="domain-icon">⚡</div>
6575              <div class="domain-info">
6576                <h4>C/FFI Domain</h4>
6577                <p>Manual memory management</p>
6578              </div>
6579            </div>
6580            <div class="timeline-track" id="ffiTimelineTrack"></div>
6581          </div>
6582        </div>
6583
6584        <!-- Integrated Risk Analysis Section -->
6585        <div class="integrated-risk-section">
6586          <div class="risk-section-header">
6587            <h4><i class="fa fa-exclamation-triangle"></i> Safety Risk Analysis</h4>
6588            <div class="risk-summary-stats">
6589              <span class="risk-stat high-risk">
6590                <span id="high-risk-count">0</span> High
6591              </span>
6592              <span class="risk-stat medium-risk">
6593                <span id="medium-risk-count">0</span> Medium
6594              </span>
6595              <span class="risk-stat low-risk">
6596                <span id="low-risk-count">0</span> Low
6597              </span>
6598            </div>
6599          </div>
6600
6601          <!-- Risk Analysis Tabs -->
6602          <div class="risk-analysis-tabs">
6603            <button class="risk-tab active" data-view="table">
6604              <i class="fa fa-table"></i> Risk Items
6605            </button>
6606            <button class="risk-tab" data-view="patterns">
6607              <i class="fa fa-chart-pie"></i> Pattern Analysis
6608            </button>
6609            <button class="risk-tab" data-view="locations">
6610              <i class="fa fa-map-marker"></i> Code Locations
6611            </button>
6612          </div>
6613
6614          <!-- Risk Views Container -->
6615          <div class="risk-views-container">
6616            <!-- Risk Items Table View -->
6617            <div id="riskTableView" class="risk-view active">
6618              <div class="scroll" style="max-height: 200px;">
6619                <table>
6620                  <thead>
6621                    <tr>
6622                      <th>Location</th>
6623                      <th>Operation</th>
6624                      <th>Risk Level</th>
6625                      <th>Memory Size</th>
6626                      <th>Assessment</th>
6627                      <th>Actions</th>
6628                    </tr>
6629                  </thead>
6630                  <tbody id="unsafeTable"></tbody>
6631                </table>
6632              </div>
6633            </div>
6634
6635            <!-- Pattern Analysis View -->
6636            <div id="riskPatternsView" class="risk-view" style="display: none;">
6637              <div id="riskPatternsChart" style="height: 200px; background: var(--bg-secondary); border-radius: 8px;">
6638                <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">
6639                  <i class="fa fa-chart-pie" style="font-size: 1.5rem; opacity: 0.5;"></i>
6640                  <span style="margin-left: 1rem;">Risk pattern analysis loading...</span>
6641                </div>
6642              </div>
6643            </div>
6644
6645            <!-- Code Locations View -->
6646            <div id="riskLocationsView" class="risk-view" style="display: none;">
6647              <div id="riskLocationsHeatmap" style="height: 200px; background: var(--bg-secondary); border-radius: 8px;">
6648                <div style="display: flex; align-items: center; justify-content: center; height: 100%; color: var(--text-secondary);">
6649                  <i class="fa fa-map" style="font-size: 1.5rem; opacity: 0.5;"></i>
6650                  <span style="margin-left: 1rem;">Code location heatmap loading...</span>
6651                </div>
6652              </div>
6653            </div>
6654          </div>
6655        </div>
6656
6657        <!-- Enhanced Legend with Pattern Recognition -->
6658        <div class="enhanced-legend">
6659          <div class="legend-section">
6660            <h4>Event Types</h4>
6661            <div class="legend-items">
6662              <div class="legend-item">
6663                <span class="event-symbol rust-alloc">●</span>
6664                <span>Unsafe Rust Allocation</span>
6665              </div>
6666              <div class="legend-item">
6667                <span class="event-symbol ffi-alloc">●</span>
6668                <span>FFI C Allocation</span>
6669              </div>
6670              <div class="legend-item">
6671                <span class="event-symbol boundary-up">â–ē</span>
6672                <span>FFI → Rust Transfer</span>
6673              </div>
6674              <div class="legend-item">
6675                <span class="event-symbol boundary-down">▾</span>
6676                <span>Rust → FFI Transfer</span>
6677              </div>
6678              <div class="legend-item">
6679                <span class="event-symbol dealloc">✕</span>
6680                <span>Memory Deallocation</span>
6681              </div>
6682            </div>
6683          </div>
6684          <div class="legend-section">
6685            <h4>Risk Patterns</h4>
6686            <div class="legend-items">
6687              <div class="legend-item">
6688                <span class="risk-indicator high-risk"></span>
6689                <span>High Risk - Potential double-free</span>
6690              </div>
6691              <div class="legend-item">
6692                <span class="risk-indicator medium-risk"></span>
6693                <span>Medium Risk - Ownership unclear</span>
6694              </div>
6695              <div class="legend-item">
6696                <span class="risk-indicator leak-risk"></span>
6697                <span>Memory Leak - No deallocation</span>
6698              </div>
6699            </div>
6700          </div>
6701        </div>
6702      </div>
6703
6704      <!-- Memory Passport Modal -->
6705      <div id="memoryPassport" class="memory-passport-modal" style="display: none;">
6706        <div class="passport-content">
6707          <div class="passport-header">
6708            <h3><i class="fa fa-id-card"></i> Memory Passport</h3>
6709            <button class="passport-close">&times;</button>
6710          </div>
6711          <div class="passport-body" id="passportBody">
6712            <!-- Dynamic content populated by JavaScript -->
6713          </div>
6714        </div>
6715      </div>
6716
6717      <!-- Risk Action Modal -->
6718      <div id="riskActionModal" class="memory-passport-modal" style="display: none;">
6719        <div class="passport-content">
6720          <div class="passport-header">
6721            <h3><i class="fa fa-tools"></i> Risk Mitigation Actions</h3>
6722            <button class="passport-close" onclick="closeRiskActionModal()">&times;</button>
6723          </div>
6724          <div class="passport-body" id="riskActionBody">
6725            <!-- Dynamic content populated by JavaScript -->
6726          </div>
6727        </div>
6728      </div>
6729    </section>
6730
6731    <!-- Memory Analysis Dashboard module removed -->
6732      
6733      <div class="card">
6734        <h2>Memory Over Time</h2>
6735        <div class="chart-container">
6736          <canvas id="timelineChart"></canvas>
6737        </div>
6738        <div style="margin-top:8px; font-size:12px; color: var(--text-secondary); display:flex; gap:8px; align-items:center;">
6739          <label style="display:flex; align-items:center; gap:6px; cursor:pointer;">
6740            <input id="toggleGrowthRate" type="checkbox" style="accent-color: var(--primary-green)">
6741            <span>Show Growth Rate</span>
6742          </label>
6743          <span style="opacity:0.8">Left Y: Cumulative memory, Right Y: Growth rate</span>
6744        </div>
6745        <!-- Integrated Key Metrics -->
6746        <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-top: 12px;">
6747          <div style="text-align: center; padding: 8px; background: var(--bg-secondary); border-radius: 6px;">
6748            <div style="font-size: 1rem; font-weight: 700; color: var(--primary-blue);" id="memoryFragmentation">0%</div>
6749            <div style="font-size: 0.7rem; color: var(--text-secondary);">Fragmentation</div>
6750          </div>
6751          <div style="text-align: center; padding: 8px; background: var(--bg-secondary); border-radius: 6px;">
6752            <div style="font-size: 1rem; font-weight: 700; color: var(--primary-green);" id="memoryEfficiency">0%</div>
6753            <div style="font-size: 0.7rem; color: var(--text-secondary);">Efficiency</div>
6754          </div>
6755        </div>
6756      </div>
6757    </section>
6758
6759    <!-- Memory Distribution Visualization -->
6760    <section class="card">
6761      <h2><i class="fa fa-chart-area"></i> Memory Distribution Visualization</h2>
6762      <!-- åŠĻ态尚åŊļæŽ§åˆķ -->
6763      <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
6764        <div style="display: flex; gap: 8px; align-items: center;">
6765          <label style="font-size: 0.8rem; color: var(--text-secondary);">Scale:</label>
6766          <input type="range" id="memoryDistScale" min="50" max="200" value="100" 
6767                 style="width: 80px; accent-color: var(--primary-blue);">
6768          <span id="memoryDistScaleValue" style="font-size: 0.8rem; color: var(--text-secondary); min-width: 30px;">100%</span>
6769        </div>
6770        <div style="display: flex; gap: 8px;">
6771          <button id="memoryDistFit" class="theme-toggle" 
6772                  style="background: var(--primary-green); font-size: 10px; padding: 4px 8px;">
6773            <i class="fa fa-expand-arrows-alt"></i>
6774            <span>Fit</span>
6775          </button>
6776          <button id="memoryDistReset" class="theme-toggle" 
6777                  style="background: var(--primary-orange); font-size: 10px; padding: 4px 8px;">
6778            <i class="fa fa-refresh"></i>
6779            <span>Reset</span>
6780          </button>
6781        </div>
6782      </div>
6783      
6784      <div id="memoryDistributionViz"
6785        style="height: 200px; background: var(--bg-secondary); border-radius: 8px; position: relative; overflow: hidden; border: 1px solid var(--border-light);">
6786        <!-- Memory blocks visualization will be inserted here -->
6787      </div>
6788    </section>
6789
6790    <!-- Memory Analysis (wider layout) -->
6791    <section class="grid grid-2">
6792      <div class="card">
6793        <h2>Type Treemap</h2>
6794        <div id="treemap" class="chart-container"
6795          style="height: 360px; padding: 0; background: var(--bg-secondary); border: 1px solid var(--border-light); border-radius: 8px; overflow: hidden;">
6796        </div>
6797      </div>
6798      <div class="card">
6799        <h2><i class="fa fa-timeline"></i> Variable Lifecycle Visualization</h2>
6800        <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
6801          <div style="display: flex; gap: 6px;">
6802            <button id="filter-heap" class="theme-toggle"
6803              style="background: var(--primary-orange); font-size: 10px; padding: 3px 6px;">
6804              <span>Heap</span>
6805            </button>
6806            <button id="filter-stack" class="theme-toggle"
6807              style="background: var(--primary-blue); font-size: 10px; padding: 3px 6px;">
6808              <span>Stack</span>
6809            </button>
6810            <button id="toggle-lifecycle" class="theme-toggle"
6811              style="background: var(--primary-green); font-size: 10px; padding: 3px 6px;">
6812              <span>All</span>
6813            </button>
6814          </div>
6815          <div style="display: flex; gap: 12px; font-size: 0.75rem; color: var(--text-secondary);">
6816            <span>Heap: <span id="heap-count-mini"
6817                style="color: var(--primary-orange); font-weight: 600;">-</span></span>
6818            <span>Stack: <span id="stack-count-mini"
6819                style="color: var(--primary-blue); font-weight: 600;">-</span></span>
6820          </div>
6821        </div>
6822        <div style="margin-bottom: 8px;">
6823          <button id="manual-init-btn"
6824            style="background: var(--primary-red); color: white; border: none; padding: 6px 12px; border-radius: 4px; font-size: 0.8rem; cursor: pointer;">
6825            🔄 Manual Initialize
6826          </button>
6827          <span id="init-status" style="margin-left: 8px; font-size: 0.8rem; color: var(--text-secondary);">Waiting for
6828            data...</span>
6829        </div>
6830        <div id="lifecycleVisualizationContainer" class="scroll" style="max-height: 320px; padding: 8px;">
6831          <!-- Variable lifecycle items will be inserted here -->
6832        </div>
6833      </div>
6834    </section>
6835
6836    <!-- Advanced Analysis -->
6837    <section class="grid grid-2">
6838      <div class="card">
6839        <h2><i class="fa fa-puzzle-piece"></i> Memory Fragmentation Analysis</h2>
6840        <div style="padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
6841          <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 16px;">
6842            <div style="text-align: center; padding: 12px; background: var(--bg-primary); border-radius: 6px;">
6843              <div style="font-size: 1.2rem; font-weight: 700; color: var(--primary-blue);" id="fragmentation-level">5.2%</div>
6844              <div style="font-size: 0.7rem; color: var(--text-secondary);">Fragmentation</div>
6845            </div>
6846            <div style="text-align: center; padding: 12px; background: var(--bg-primary); border-radius: 6px;">
6847              <div style="font-size: 1.2rem; font-weight: 700; color: var(--primary-green);" id="memory-utilization">94.8%</div>
6848              <div style="font-size: 0.7rem; color: var(--text-secondary);">Utilization</div>
6849            </div>
6850          </div>
6851          <div id="memoryFragmentation" style="height: 120px; background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: 6px; padding: 8px;">
6852            <!-- Simple fragmentation visualization -->
6853            <div style="display: flex; align-items: center; height: 100%; gap: 2px;">
6854              <div style="height: 100%; width: 15%; background: var(--primary-blue); border-radius: 2px; opacity: 0.8;"></div>
6855              <div style="height: 60%; width: 8%; background: var(--primary-orange); border-radius: 2px; opacity: 0.6;"></div>
6856              <div style="height: 80%; width: 12%; background: var(--primary-blue); border-radius: 2px; opacity: 0.8;"></div>
6857              <div style="height: 30%; width: 5%; background: var(--primary-red); border-radius: 2px; opacity: 0.5;"></div>
6858              <div style="height: 100%; width: 20%; background: var(--primary-blue); border-radius: 2px; opacity: 0.8;"></div>
6859              <div style="height: 40%; width: 6%; background: var(--primary-orange); border-radius: 2px; opacity: 0.6;"></div>
6860              <div style="height: 90%; width: 18%; background: var(--primary-blue); border-radius: 2px; opacity: 0.8;"></div>
6861              <div style="height: 25%; width: 4%; background: var(--primary-red); border-radius: 2px; opacity: 0.5;"></div>
6862              <div style="height: 70%; width: 12%; background: var(--primary-blue); border-radius: 2px; opacity: 0.8;"></div>
6863            </div>
6864            <div style="text-align: center; font-size: 0.7rem; color: var(--text-secondary); margin-top: 4px;">
6865              Memory Layout: <span style="color: var(--primary-blue);">■ Allocated</span> 
6866              <span style="color: var(--primary-orange);">■ Small Gaps</span> 
6867              <span style="color: var(--primary-red);">■ Large Gaps</span>
6868            </div>
6869          </div>
6870        </div>
6871      </div>
6872      <div class="card">
6873        <h2><i class="fa fa-fire"></i> Borrow Activity Heatmap</h2>
6874        <div id="borrowPatternChart" style="height: 200px;"></div>
6875      </div>
6876    </section>
6877
6878    <!-- Memory Allocation Details (moved from Advanced Analysis) -->
6879    <section class="grid grid-2">
6880      <div class="card">
6881        <h2><i class="fa fa-table"></i> Memory Allocation Details</h2>
6882        <div class="scroll">
6883          <table>
6884            <thead>
6885              <tr>
6886                <th>Variable</th>
6887                <th>Type</th>
6888                <th>Size</th>
6889                <th>Status</th>
6890              </tr>
6891            </thead>
6892            <tbody id="allocTable"></tbody>
6893          </table>
6894        </div>
6895      </div>
6896      
6897      <div class="card">
6898        <h2><i class="fa fa-info-circle"></i> System Status</h2>
6899        <div style="padding: 16px; background: var(--bg-secondary); border-radius: 8px;">
6900          <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
6901            <div style="text-align: center; padding: 12px; background: var(--bg-primary); border-radius: 6px;">
6902              <div style="font-size: 1.2rem; font-weight: 700; color: var(--primary-blue);" id="dashboard-status">Ready</div>
6903              <div style="font-size: 0.7rem; color: var(--text-secondary);">Dashboard</div>
6904            </div>
6905            <div style="text-align: center; padding: 12px; background: var(--bg-primary); border-radius: 6px;">
6906              <div style="font-size: 1.2rem; font-weight: 700; color: var(--primary-green);" id="data-status">Loading</div>
6907              <div style="font-size: 0.7rem; color: var(--text-secondary);">Data Status</div>
6908            </div>
6909          </div>
6910        </div>
6911      </div>
6912    </section>
6913
6914
6915    <!-- Variable Relationships (Full Width) -->
6916    <section class="card">
6917      <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap;">
6918        <h2 style="margin: 0;"><i class="fa fa-share-alt"></i> Variable Relationships Graph</h2>
6919        <div style="display: flex; gap: 8px; flex-wrap: nowrap;">
6920          <button id="reset-zoom" class="theme-toggle"
6921            style="background: var(--primary-orange); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6922            <i class="fa fa-expand"></i>
6923            <span>Reset View</span>
6924          </button>
6925          <button id="auto-layout" class="theme-toggle"
6926            style="background: var(--primary-green); font-size: 12px; padding: 6px 12px; white-space: nowrap;">
6927            <i class="fa fa-magic"></i>
6928            <span>Auto Layout</span>
6929          </button>
6930        </div>
6931      </div>
6932      <div id="graph"
6933        style="height: 400px; background: var(--bg-secondary); border: 1px solid var(--border-light); border-radius: 8px; position: relative; overflow: hidden;">
6934      </div>
6935    </section>
6936
6937  </div>
6938
6939  <script>
6940    // Global safe update function - must be defined first
6941    function safeUpdateElement(id, value, defaultValue = '-') {
6942      try {
6943        const el = document.getElementById(id);
6944        if (el) {
6945          el.textContent = value;
6946          return true;
6947        } else {
6948          console.warn(`Element with ID '${id}' not found`);
6949          return false;
6950        }
6951      } catch (error) {
6952        console.error(`Error updating element ${id}:`, error);
6953        return false;
6954      }
6955    }
6956
6957    // Enhanced Lifecycle Visualization Functions
6958    function inferAllocationType(typeName) {
6959      if (!typeName) return 'unknown';
6960
6961      const heapTypes = ['Box', 'Vec', 'String', 'HashMap', 'BTreeMap', 'Arc', 'Rc', 'alloc::', 'std::collections'];
6962      const stackTypes = ['i32', 'i64', 'f32', 'f64', 'bool', 'char', 'usize', 'isize', 'u8', 'u16', 'u32', 'u64'];
6963
6964      for (const heapType of heapTypes) {
6965        if (typeName.includes(heapType)) return 'heap';
6966      }
6967
6968      for (const stackType of stackTypes) {
6969        if (typeName.includes(stackType)) return 'stack';
6970      }
6971
6972      if (typeName.includes('*') || typeName.includes('&')) return 'heap';
6973
6974      return 'unknown';
6975    }
6976
6977    function formatTimestamp(timestamp) {
6978      if (!timestamp || isNaN(timestamp) || timestamp <= 0) return 'N/A';
6979
6980      // Handle different timestamp formats
6981      let timeInMs;
6982      if (timestamp > 1e15) {
6983        // Nanoseconds
6984        timeInMs = timestamp / 1000000;
6985      } else if (timestamp > 1e12) {
6986        // Microseconds  
6987        timeInMs = timestamp / 1000;
6988      } else {
6989        // Already in milliseconds
6990        timeInMs = timestamp;
6991      }
6992
6993      const date = new Date(timeInMs);
6994      if (isNaN(date.getTime())) return 'N/A';
6995
6996      // Format as relative time if recent, otherwise absolute time
6997      const now = Date.now();
6998      const diffMs = Math.abs(now - timeInMs);
6999
7000      if (diffMs < 1000) {
7001        return `${diffMs.toFixed(0)}ms ago`;
7002      } else if (diffMs < 60000) {
7003        return `${(diffMs / 1000).toFixed(1)}s ago`;
7004      } else {
7005        return date.toLocaleTimeString() + '.' + String(date.getMilliseconds()).padStart(3, '0');
7006      }
7007    }
7008
7009    function formatTimestampSafe(timestamp, fallbackIndex) {
7010      if (!timestamp || isNaN(timestamp) || timestamp <= 0) {
7011        // Return synthetic time based on index
7012        return `T+${(fallbackIndex * 1).toFixed(1)}ms`;
7013      }
7014
7015      // Handle nanosecond timestamps (typical format from Rust)
7016      let timeInMs;
7017      if (timestamp > 1e15) {
7018        // Nanoseconds (typical Rust timestamp format)
7019        timeInMs = timestamp / 1000000;
7020      } else if (timestamp > 1e12) {
7021        // Microseconds  
7022        timeInMs = timestamp / 1000;
7023      } else if (timestamp > 1e9) {
7024        // Milliseconds
7025        timeInMs = timestamp;
7026      } else {
7027        // Seconds to milliseconds
7028        timeInMs = timestamp * 1000;
7029      }
7030
7031      // Convert to relative time from program start
7032      // Use the first allocation timestamp as reference if available
7033      const firstTimestamp = window.analysisData?.memory_analysis?.allocations?.[0]?.timestamp_alloc || timestamp;
7034      const relativeTimeMs = (timestamp - firstTimestamp) / 1000000;
7035
7036      if (Math.abs(relativeTimeMs) < 0.001) {
7037        return 'T+0.00ms';
7038      } else if (relativeTimeMs >= 0) {
7039        return `T+${relativeTimeMs.toFixed(2)}ms`;
7040      } else {
7041        return `T${relativeTimeMs.toFixed(2)}ms`;
7042      }
7043    }
7044
7045    function calculateDropTime(allocTime, lifetimeMs) {
7046      if (!allocTime || lifetimeMs === undefined || isNaN(allocTime) || isNaN(lifetimeMs)) return null;
7047      return allocTime + (lifetimeMs * 1000000); // Convert ms to nanoseconds and add
7048    }
7049
7050    function formatLifetime(lifetimeMs) {
7051      if (lifetimeMs === undefined || lifetimeMs === null) return 'N/A';
7052      if (lifetimeMs < 1) return `${(lifetimeMs * 1000).toFixed(1)}Ξs`;
7053      if (lifetimeMs < 1000) return `${lifetimeMs.toFixed(1)}ms`;
7054      return `${(lifetimeMs / 1000).toFixed(2)}s`;
7055    }
7056
7057    function formatBytes(bytes) {
7058      if (bytes === 0) return '0 B';
7059      const k = 1024;
7060      const sizes = ['B', 'KB', 'MB', 'GB'];
7061      const i = Math.floor(Math.log(bytes) / Math.log(k));
7062      return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
7063    }
7064
7065    function initEnhancedLifecycleVisualization() {
7066      // Debug what's available
7067      console.log('Checking data availability...');
7068      console.log('window.analysisData exists:', !!window.analysisData);
7069      console.log('window.analysisData type:', typeof window.analysisData);
7070
7071      if (window.analysisData) {
7072        console.log('window.analysisData keys:', Object.keys(window.analysisData));
7073      }
7074
7075      // Try multiple data structure paths
7076      let allocations = null;
7077
7078      if (window.analysisData) {
7079        // Method 1: Direct allocations (old structure)
7080        if (window.analysisData.allocations && Array.isArray(window.analysisData.allocations)) {
7081          allocations = window.analysisData.allocations;
7082          console.log('✅ Found allocations directly:', allocations.length);
7083        }
7084        // Method 2: Memory analysis structure (new structure)
7085        else if (window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
7086          allocations = window.analysisData.memory_analysis.allocations;
7087          console.log('✅ Found allocations in memory_analysis:', allocations.length);
7088        }
7089        // Method 3: Check all keys for allocations
7090        else {
7091          for (const [key, value] of Object.entries(window.analysisData)) {
7092            if (value && typeof value === 'object' && value.allocations && Array.isArray(value.allocations)) {
7093              allocations = value.allocations;
7094              console.log('✅ Found allocations in', key + ':', allocations.length);
7095              break;
7096            }
7097          }
7098        }
7099      }
7100
7101      if (!allocations || !Array.isArray(allocations) || allocations.length === 0) {
7102        console.warn('No allocation data found in window.analysisData');
7103
7104        // Fallback: Try to get data from other global variables that might be set by the dashboard
7105        if (typeof getAllocations === 'function') {
7106          try {
7107            allocations = getAllocations();
7108            console.log('✅ Got allocations from getAllocations():', allocations.length);
7109          } catch (e) {
7110            console.warn('getAllocations() failed:', e);
7111          }
7112        }
7113
7114        // Another fallback: Check if data is in a different global variable
7115        if (!allocations && window.memoryAnalysisData) {
7116          if (window.memoryAnalysisData.allocations) {
7117            allocations = window.memoryAnalysisData.allocations;
7118            console.log('✅ Got allocations from memoryAnalysisData:', allocations.length);
7119          }
7120        }
7121
7122        // Final fallback: Try to extract from existing DOM elements
7123        if (!allocations) {
7124          const existingTable = document.getElementById('allocTable');
7125          if (existingTable && existingTable.children.length > 1) {
7126            console.log('Trying to extract data from existing table...');
7127            // This is a last resort - we'll create dummy data based on table rows
7128            const rows = existingTable.querySelectorAll('tbody tr');
7129            if (rows.length > 0) {
7130              allocations = Array.from(rows).map((row, index) => {
7131                const cells = row.querySelectorAll('td');
7132                return {
7133                  var_name: cells[0]?.textContent || `var_${index}`,
7134                  type_name: cells[1]?.textContent || 'unknown',
7135                  size: parseInt(cells[2]?.textContent) || 0,
7136                  timestamp_alloc: Date.now() * 1000000 + index * 1000000,
7137                  lifetime_ms: parseFloat(cells[3]?.textContent) || 1.0,
7138                  is_leaked: cells[4]?.textContent?.includes('Yes') || false
7139                };
7140              });
7141              console.log('✅ Extracted', allocations.length, 'allocations from existing table');
7142            }
7143          }
7144        }
7145
7146        if (!allocations || allocations.length === 0) {
7147          console.error('❌ No allocation data available from any source');
7148          return;
7149        }
7150      }
7151
7152      if (allocations.length === 0) {
7153        console.warn('No allocation data found');
7154        return;
7155      }
7156
7157      console.log('✅ Found', allocations.length, 'allocations for enhanced visualization');
7158      console.log('Sample allocation:', allocations[0]);
7159
7160      // Statistics
7161      let heapCount = 0, stackCount = 0, unknownCount = 0;
7162      let totalLifetime = 0, validLifetimes = 0;
7163      let totalMemory = 0;
7164
7165      allocations.forEach(alloc => {
7166        const type = inferAllocationType(alloc.type_name);
7167        if (type === 'heap') heapCount++;
7168        else if (type === 'stack') stackCount++;
7169        else unknownCount++;
7170
7171        // Check multiple possible lifetime fields and ensure they're valid numbers
7172        const lifetime = alloc.lifetime_ms || alloc.lifetime || 0;
7173        if (lifetime !== undefined && lifetime !== null && !isNaN(lifetime) && lifetime > 0) {
7174          totalLifetime += lifetime;
7175          validLifetimes++;
7176        }
7177
7178        totalMemory += alloc.size || 0;
7179      });
7180
7181      console.log('Statistics calculated:', { heapCount, stackCount, totalLifetime, validLifetimes });
7182
7183      // Update mini counters
7184      const heapCountMini = document.getElementById('heap-count-mini');
7185      const stackCountMini = document.getElementById('stack-count-mini');
7186
7187      if (heapCountMini) {
7188        safeUpdateElement('heap-count-mini', heapCount);
7189        console.log('Updated heap-count-mini:', heapCount);
7190      } else {
7191        console.warn('heap-count-mini element not found');
7192      }
7193
7194      if (stackCountMini) {
7195        safeUpdateElement('stack-count-mini', stackCount);
7196        console.log('Updated stack-count-mini:', stackCount);
7197      } else {
7198        console.warn('stack-count-mini element not found');
7199      }
7200
7201      // Create lifecycle visualization
7202      console.log('Calling createLifecycleVisualization...');
7203      createLifecycleVisualization(allocations);
7204
7205      // Update enhanced statistics
7206      console.log('Calling updateEnhancedStatistics...');
7207      updateEnhancedStatistics(allocations, heapCount, stackCount, validLifetimes, totalLifetime);
7208
7209      // Setup filters
7210      console.log('Calling setupLifecycleFilters...');
7211      setupLifecycleFilters(allocations);
7212
7213      console.log('✅ All enhanced features processing completed');
7214    }
7215
7216    function createLifecycleVisualization(allocations) {
7217      console.log('createLifecycleVisualization called with', allocations.length, 'allocations');
7218      const container = document.getElementById('lifecycleVisualizationContainer');
7219      if (!container) {
7220        console.error('❌ Lifecycle visualization container not found in DOM');
7221        return;
7222      }
7223      console.log('✅ Found lifecycleVisualizationContainer, creating visualization...');
7224      container.innerHTML = '';
7225
7226      // Calculate timeline bounds
7227      const timestamps = allocations.map(a => a.timestamp_alloc).filter(t => t);
7228      const minTime = Math.min(...timestamps);
7229      const maxTime = Math.max(...timestamps);
7230      const timeRange = maxTime - minTime || 1;
7231
7232      allocations.forEach((alloc, index) => {
7233        const allocType = inferAllocationType(alloc.type_name);
7234        let startTime = alloc.timestamp_alloc || alloc.timestamp || Date.now() * 1000000;
7235        const lifetime = alloc.lifetime_ms || alloc.lifetime || 0;
7236        let endTime = startTime + (lifetime * 1000000); // Convert ms to nanoseconds
7237
7238        // Calculate lifetime from timestamps if not provided
7239        let calculatedLifetime = 0;
7240        if (alloc.timestamp_dealloc && alloc.timestamp_alloc) {
7241          calculatedLifetime = (alloc.timestamp_dealloc - alloc.timestamp_alloc) / 1000000; // Convert to ms
7242        } else if (alloc.lifetime_ms) {
7243          calculatedLifetime = alloc.lifetime_ms;
7244        } else {
7245          // Default lifetime for active allocations
7246          calculatedLifetime = 10; // 10ms default
7247        }
7248
7249        // Use actual timestamps if available, otherwise create synthetic ones
7250        if (alloc.timestamp_alloc && !isNaN(alloc.timestamp_alloc)) {
7251          startTime = alloc.timestamp_alloc;
7252          endTime = alloc.timestamp_dealloc || (startTime + calculatedLifetime * 1000000);
7253        } else {
7254          // Create synthetic timeline based on allocation order
7255          startTime = minTime + (index * (timeRange / allocations.length));
7256          endTime = startTime + (calculatedLifetime * 1000000);
7257        }
7258
7259        // Debug and validate time data
7260        console.log('Debug allocation:', {
7261          var_name: alloc.var_name,
7262          timestamp_alloc: alloc.timestamp_alloc,
7263          timestamp_dealloc: alloc.timestamp_dealloc,
7264          calculatedLifetime: calculatedLifetime,
7265          startTime: startTime,
7266          endTime: endTime,
7267          isValidStart: !isNaN(startTime) && startTime > 0,
7268          isValidEnd: !isNaN(endTime) && endTime > 0
7269        });
7270
7271        // Calculate positions and widths with bounds checking
7272        let startPercent = ((startTime - minTime) / timeRange) * 100;
7273        let endPercent = ((endTime - minTime) / timeRange) * 100;
7274
7275        // Ensure values are within bounds
7276        startPercent = Math.max(0, Math.min(startPercent, 100));
7277        endPercent = Math.max(startPercent, Math.min(endPercent, 100));
7278
7279        let width = endPercent - startPercent;
7280        width = Math.max(width, 2); // Minimum 2% width
7281        width = Math.min(width, 100 - startPercent); // Don't exceed container
7282
7283        // Create lifecycle item
7284        const item = document.createElement('div');
7285        item.className = `lifecycle-item ${allocType}`;
7286        item.setAttribute('data-type', allocType);
7287
7288        // Enhanced solid colors for better visibility
7289        const barColor = allocType === 'heap' ? '#ff6b35' :
7290          allocType === 'stack' ? '#4dabf7' : '#868e96';
7291        const barGradient = allocType === 'heap' ? '#ff6b35' :
7292          allocType === 'stack' ? '#4dabf7' :
7293            '#868e96';
7294
7295        item.innerHTML = `
7296          <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
7297            <div style="display: flex; align-items: center; gap: 8px;">
7298              <span style="font-weight: 600; font-size: 0.9rem; min-width: 120px;">${alloc.var_name || 'unnamed'}</span>
7299              <span class="allocation-type type-${allocType}">${allocType}</span>
7300            </div>
7301            <div style="display: flex; align-items: center; gap: 8px; font-size: 0.75rem; color: var(--text-secondary);">
7302              <span>${formatBytes(alloc.size || 0)}</span>
7303              <span>${formatLifetime(lifetime)}</span>
7304            </div>
7305          </div>
7306          
7307          <!-- Enhanced Timeline Progress Bar -->
7308          <div style="position: relative; height: 24px; background: linear-gradient(90deg, #e8eaed, #ddd); border-radius: 12px; margin: 10px 0; border: 1px solid #bbb; box-shadow: inset 0 1px 2px rgba(0,0,0,0.08);">
7309            
7310            <!-- Variable active period with enhanced gradient -->
7311            <div style="position: absolute; top: 1px; left: ${startPercent}%; width: ${width}%; height: calc(100% - 2px); 
7312                        background: ${barGradient}; 
7313                        border-radius: 11px; 
7314                        box-shadow: 0 2px 8px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.3);
7315                        transition: all 0.3s ease;
7316                        position: relative;
7317                        overflow: hidden;">
7318              
7319              <!-- Animated shine effect -->
7320              <div style="position: absolute; top: 0; left: -100%; width: 100%; height: 100%; 
7321                          background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
7322                          animation: shine 2s infinite;"></div>
7323            </div>
7324            
7325            ${alloc.is_leaked ? `
7326              <!-- Leaked indicator -->
7327              <div style="position: absolute; top: -3px; right: 2px; width: 20px; height: 30px; 
7328                          background: linear-gradient(45deg, #ff4757, #ff3742); 
7329                          border-radius: 2px; 
7330                          box-shadow: 0 2px 4px rgba(0,0,0,0.3);
7331                          display: flex; align-items: center; justify-content: center;
7332                          font-size: 10px; color: white; font-weight: bold;">⚠</div>
7333            ` : ''}
7334          </div>
7335          
7336          <!-- Time info -->
7337          <div style="display: flex; justify-content: space-between; font-size: 0.7rem; color: var(--text-secondary); font-family: monospace;">
7338            <span>Start: ${formatTimestampSafe(startTime, index)}</span>
7339            <span>${alloc.is_leaked ? 'LEAKED' : (formatTimestampSafe(endTime, index + 1) !== 'N/A' ? 'End: ' + formatTimestampSafe(endTime, index + 1) : 'Active')}</span>
7340          </div>
7341        `;
7342
7343        // Enhanced hover effect and styling
7344        item.style.cssText += `
7345          margin-bottom: 18px;
7346          padding: 16px;
7347          background: linear-gradient(135deg, var(--bg-primary), #fafbfc);
7348          border-radius: 12px;
7349          border-left: 5px solid ${barColor};
7350          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
7351          cursor: pointer;
7352          box-shadow: 0 2px 4px rgba(0,0,0,0.05);
7353        `;
7354
7355        item.addEventListener('mouseenter', () => {
7356          item.style.transform = 'translateX(8px) translateY(-2px)';
7357          item.style.boxShadow = '0 8px 25px rgba(0,0,0,0.15)';
7358          item.style.background = `linear-gradient(135deg, var(--bg-primary), ${barColor}08)`;
7359        });
7360
7361        item.addEventListener('mouseleave', () => {
7362          item.style.transform = 'translateX(0) translateY(0)';
7363          item.style.boxShadow = '0 2px 4px rgba(0,0,0,0.05)';
7364          item.style.background = 'linear-gradient(135deg, var(--bg-primary), #fafbfc)';
7365        });
7366
7367        container.appendChild(item);
7368      });
7369    }
7370
7371    function updateEnhancedStatistics(allocations, heapCount, stackCount, validLifetimes, totalLifetime) {
7372      console.log('Updating enhanced statistics...');
7373
7374      // Update Enhanced Memory Statistics
7375      const totalAllocsEnhanced = document.getElementById('total-allocs-enhanced');
7376      const heapStackRatio = document.getElementById('heap-stack-ratio');
7377      const avgLifetimeEnhanced = document.getElementById('avg-lifetime-enhanced');
7378      const memoryEfficiency = document.getElementById('memory-efficiency');
7379
7380      if (totalAllocsEnhanced) {
7381        safeUpdateElement('total-allocs-enhanced', allocations.length);
7382        console.log('Updated total-allocs-enhanced:', allocations.length);
7383      }
7384
7385      // Safe DOM updates with enhanced error handling
7386      if (heapStackRatio) {
7387        try {
7388          const ratio = stackCount > 0 ? (heapCount / stackCount).toFixed(1) : heapCount;
7389          safeUpdateElement('heap-stack-ratio', ratio + ':1');
7390          console.log('Updated heap-stack-ratio:', ratio + ':1');
7391        } catch (error) {
7392          console.error('Error updating heap-stack-ratio:', error);
7393        }
7394      }
7395
7396      if (avgLifetimeEnhanced) {
7397        try {
7398          const avgLifetime = validLifetimes > 0 ? formatLifetime(totalLifetime / validLifetimes) : 'N/A';
7399          safeUpdateElement('avg-lifetime-enhanced', avgLifetime);
7400          console.log('Updated avg-lifetime-enhanced:', avgLifetime);
7401        } catch (error) {
7402          console.error('Error updating avg-lifetime-enhanced:', error);
7403        }
7404      }
7405
7406      const memoryEfficiencyEl = document.getElementById('memory-efficiency');
7407      if (memoryEfficiencyEl) {
7408        try {
7409          const efficiency = allocations.length > 0 ? ((allocations.length - allocations.filter(a => a.is_leaked).length) / allocations.length * 100).toFixed(0) : 0;
7410          safeUpdateElement('memory-efficiency', efficiency + '%');
7411          console.log('Updated memory-efficiency:', efficiency + '%');
7412        } catch (error) {
7413          console.error('Error updating memory-efficiency:', error);
7414        }
7415      }
7416    }
7417
7418
7419    function setupLifecycleFilters(allocations) {
7420      const heapBtn = document.getElementById('filter-heap');
7421      const stackBtn = document.getElementById('filter-stack');
7422      const allBtn = document.getElementById('toggle-lifecycle');
7423
7424      // Check if all buttons exist
7425      if (!heapBtn || !stackBtn || !allBtn) {
7426        console.warn('Some filter buttons not found');
7427        return;
7428      }
7429
7430      let currentFilter = 'all';
7431
7432      function applyFilter(filter) {
7433        currentFilter = filter;
7434        const items = document.querySelectorAll('.lifecycle-item');
7435
7436        // Update button states
7437        [heapBtn, stackBtn, allBtn].forEach(btn => btn.style.opacity = '0.6');
7438
7439        if (filter === 'heap') {
7440          heapBtn.style.opacity = '1';
7441          items.forEach(item => {
7442            item.style.display = item.getAttribute('data-type') === 'heap' ? 'block' : 'none';
7443          });
7444        } else if (filter === 'stack') {
7445          stackBtn.style.opacity = '1';
7446          items.forEach(item => {
7447            item.style.display = item.getAttribute('data-type') === 'stack' ? 'block' : 'none';
7448          });
7449        } else {
7450          allBtn.style.opacity = '1';
7451          items.forEach(item => {
7452            item.style.display = 'block';
7453          });
7454        }
7455      }
7456
7457      heapBtn.addEventListener('click', () => applyFilter('heap'));
7458      stackBtn.addEventListener('click', () => applyFilter('stack'));
7459      allBtn.addEventListener('click', () => applyFilter('all'));
7460
7461      // Initialize
7462      applyFilter('all');
7463    }
7464
7465    // Initialize enhanced features when DOM is loaded
7466    function initEnhancedFeatures() {
7467      try {
7468        initEnhancedLifecycleVisualization();
7469      } catch (error) {
7470        console.error('Error initializing enhanced lifecycle visualization:', error);
7471      }
7472    }
7473
7474    // Safe initialization wrapper with duplicate prevention
7475    let enhancedInitialized = false;
7476    function safeInitEnhanced() {
7477      if (enhancedInitialized) {
7478        return; // Already initialized
7479      }
7480
7481      try {
7482        initEnhancedFeatures();
7483        enhancedInitialized = true;
7484        console.log('✅ Enhanced features initialized successfully');
7485      } catch (error) {
7486        console.warn('Enhanced features initialization failed:', error);
7487      }
7488    }
7489
7490    {{JS_CONTENT}}
7491
7492    // Override problematic functions AFTER JS_CONTENT loads
7493    window.updateLifecycleStatistics = function () {
7494      // Safe override - prevent errors from missing DOM elements
7495      try {
7496        // Try to find and update elements safely
7497        const elements = ['active-vars', 'freed-vars', 'leaked-vars', 'avg-lifetime-stat'];
7498        elements.forEach(id => {
7499          const el = document.getElementById(id);
7500          safeUpdateElement(id, '-');
7501        });
7502      } catch (e) {
7503        // Silently ignore errors
7504      }
7505    };
7506
7507    // Override other potential problem functions
7508    window.updateKPIMetrics = function () { return; };
7509    window.populateLifetimeTable = function () { return; };
7510    window.updateMemoryStats = function () { return; };
7511
7512    // Manual initialization function for testing
7513    function manualInitialize() {
7514      const statusEl = document.getElementById('init-status');
7515      safeUpdateElement('init-status', 'Initializing...');
7516
7517      console.log('🔄 Manual initialization triggered');
7518      console.log('window.analysisData:', window.analysisData);
7519
7520      if (window.analysisData && window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
7521        console.log('✅ Data found, calling initEnhancedLifecycleVisualization...');
7522        initEnhancedLifecycleVisualization();
7523        safeUpdateElement('init-status', 'Initialized successfully!');
7524      } else {
7525        console.warn('❌ No data found, trying to load...');
7526        safeUpdateElement('init-status', 'Loading data...');
7527
7528        // Try to load data manually
7529        fetch('./large_scale_user_memory_analysis.json')
7530          .then(response => response.json())
7531          .then(memoryData => {
7532            console.log('✅ Manually loaded data:', memoryData);
7533            window.analysisData = {
7534              memory_analysis: memoryData
7535            };
7536            initEnhancedLifecycleVisualization();
7537            safeUpdateElement('init-status', 'Data loaded and initialized!');
7538          })
7539          .catch(error => {
7540            console.error('❌ Failed to load data:', error);
7541            safeUpdateElement('init-status', 'Failed to load data');
7542          });
7543      }
7544    }
7545
7546    // Wait for all scripts to load, then initialize
7547    function waitForDataAndInit() {
7548      if (window.analysisData && window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
7549        safeUpdateElement('data-status', 'Processing');
7550        
7551        try {
7552          safeInitEnhanced();
7553          
7554          // Auto-load Safety Risk Analysis when data is ready
7555          setTimeout(() => {
7556            console.log('ðŸ›Ąïļ Data ready - auto-loading Safety Risk Analysis...');
7557            try {
7558              initializeEnhancedUnsafeAnalysis();
7559              loadSafetyRisks();
7560              console.log('✅ Safety Risk Analysis loaded automatically');
7561            } catch (riskError) {
7562              console.error('❌ Error auto-loading safety risks:', riskError);
7563            }
7564          }, 500);
7565          
7566          // Initialize enhanced visualization features
7567          if (window.enhancedVisualizer) {
7568            setTimeout(() => {
7569              console.log('Initializing enhanced visualizer...');
7570              window.enhancedVisualizer.init();
7571              window.enhancedVisualizer.initializeWithData(window.analysisData);
7572              console.log('Enhanced visualizer initialized');
7573              safeUpdateElement('data-status', 'Ready');
7574            }, 1000); // Give more time for DOM elements to be ready
7575          } else {
7576            safeUpdateElement('data-status', 'Ready');
7577          }
7578        } catch (error) {
7579          console.error('❌ Error during data initialization:', error);
7580          safeUpdateElement('data-status', 'Error');
7581        }
7582      } else {
7583        setTimeout(waitForDataAndInit, 200);
7584      }
7585    }
7586
7587
7588    // Initialize enhanced features after everything loads
7589    // Theme System Functions
7590    function initializeThemeSystem() {
7591      console.log('ðŸŽĻ Initializing theme system...');
7592      
7593      const themeToggle = document.getElementById('theme-toggle');
7594      const htmlElement = document.documentElement;
7595      
7596      // Load saved theme or default to light
7597      const savedTheme = localStorage.getItem('theme') || 'light';
7598      htmlElement.className = savedTheme;
7599      
7600      if (themeToggle) {
7601        // Update button text based on current theme
7602        updateThemeButtonText(savedTheme);
7603        
7604        themeToggle.addEventListener('click', function() {
7605          const currentTheme = htmlElement.className;
7606          const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
7607          
7608          htmlElement.className = newTheme;
7609          localStorage.setItem('theme', newTheme);
7610          updateThemeButtonText(newTheme);
7611          
7612          console.log('ðŸŽĻ Theme switched to:', newTheme);
7613        });
7614      }
7615    }
7616    
7617    function updateThemeButtonText(theme) {
7618      const themeToggle = document.getElementById('theme-toggle');
7619      if (themeToggle) {
7620        themeToggle.innerHTML = theme === 'dark' 
7621          ? '<i class="fa fa-sun-o"></i>' 
7622          : '<i class="fa fa-moon-o"></i>';
7623        themeToggle.title = theme === 'dark' ? 'Switch to Light Mode' : 'Switch to Dark Mode';
7624      }
7625    }
7626
7627    document.addEventListener('DOMContentLoaded', function () {
7628      console.log('🚀 Dashboard initialization started');
7629      
7630      // Initialize theme system first
7631      initializeThemeSystem();
7632      
7633      // Update status indicators
7634      safeUpdateElement('dashboard-status', 'Initializing');
7635      safeUpdateElement('data-status', 'Loading');
7636      
7637      try {
7638        // Setup manual initialize button
7639        const manualBtn = document.getElementById('manual-init-btn');
7640        if (manualBtn) {
7641          manualBtn.addEventListener('click', manualInitialize);
7642        }
7643        
7644        // Load safety risks after initialization with longer delay
7645        setTimeout(function() {
7646          try {
7647            console.log('ðŸ›Ąïļ Auto-loading safety risks on dashboard initialization...');
7648            loadSafetyRisks();
7649            // Also ensure unsafe analysis is initialized
7650            if (window.analysisData) {
7651              initializeEnhancedUnsafeAnalysis();
7652            }
7653          } catch (error) {
7654            console.error('❌ Error loading safety risks:', error);
7655          }
7656        }, 1500); // Increased delay to ensure data is loaded
7657
7658        // Start checking for data immediately
7659        waitForDataAndInit();
7660        
7661        // Initialize dashboard functions if data is already available
7662        setTimeout(() => {
7663          if (window.analysisData && window.analysisData.memory_analysis && window.analysisData.memory_analysis.allocations) {
7664            console.log('ðŸŽŊ Data ready - calling initDashboard...');
7665            try {
7666              if (typeof initDashboard === 'function') {
7667                initDashboard();
7668              } else {
7669                console.warn('initDashboard function not found, calling individual functions...');
7670                // Call individual functions from our generated JS
7671                const allocations = window.analysisData.memory_analysis.allocations;
7672                updateKPIs(allocations);
7673                renderMemoryOverTime(allocations);
7674                renderTypeTreemap(allocations);
7675                renderBorrowHeatmap(allocations);
7676                renderVariableGraph(allocations);
7677                populateAllocationTable(allocations);
7678              }
7679            } catch (error) {
7680              console.error('❌ Error calling dashboard functions:', error);
7681            }
7682          }
7683        }, 2000);
7684        
7685        safeUpdateElement('dashboard-status', 'Ready');
7686        console.log('✅ Dashboard initialization completed');
7687        
7688      } catch (error) {
7689        console.error('❌ Dashboard initialization failed:', error);
7690        safeUpdateElement('dashboard-status', 'Error');
7691        safeUpdateElement('data-status', 'Failed');
7692      }
7693    });
7694  </script>
7695</body>
7696
7697</html>
7698"#;