Skip to main content
Glama

CodeGraph CLI MCP Server

by Jakedismo
dashboard.html26.1 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>CodeGraph Memory Profiler Dashboard</title> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/date-fns@2.29.3/index.min.js"></script> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0f0f0f; color: #ffffff; line-height: 1.6; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; } .header { text-align: center; margin-bottom: 30px; padding: 20px; background: linear-gradient(135deg, #1a1a2e, #16213e); border-radius: 12px; border: 1px solid #333; } .header h1 { color: #00d4aa; font-size: 2.5rem; margin-bottom: 10px; } .header p { color: #aaa; font-size: 1.1rem; } .status-bar { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 30px; } .status-card { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 20px; text-align: center; transition: all 0.3s ease; } .status-card:hover { border-color: #00d4aa; transform: translateY(-2px); } .status-card h3 { color: #00d4aa; font-size: 1.8rem; margin-bottom: 5px; } .status-card p { color: #bbb; font-size: 0.9rem; } .pressure-low { border-left: 4px solid #00d4aa; } .pressure-medium { border-left: 4px solid #ffa500; } .pressure-high { border-left: 4px solid #ff6b6b; } .pressure-critical { border-left: 4px solid #ff1744; } .dashboard-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 30px; } .chart-container { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 20px; } .chart-container h2 { color: #00d4aa; margin-bottom: 15px; font-size: 1.3rem; } .chart { position: relative; height: 300px; } .side-panel { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 20px; } .leaks-list { max-height: 300px; overflow-y: auto; } .leak-item { background: #2a2a2a; border: 1px solid #ff6b6b; border-radius: 6px; padding: 10px; margin-bottom: 10px; } .leak-item h4 { color: #ff6b6b; font-size: 0.9rem; margin-bottom: 5px; } .leak-item p { color: #ccc; font-size: 0.8rem; } .recommendations { margin-top: 30px; } .recommendation-item { background: #1a1a1a; border: 1px solid #333; border-radius: 8px; padding: 15px; margin-bottom: 15px; border-left: 4px solid #00d4aa; } .recommendation-item.warning { border-left-color: #ffa500; } .recommendation-item.critical { border-left-color: #ff6b6b; } .recommendation-item h3 { color: #00d4aa; margin-bottom: 8px; font-size: 1.1rem; } .recommendation-item p { color: #bbb; margin-bottom: 5px; } .recommendation-item .savings { color: #00d4aa; font-weight: bold; } .controls { display: flex; justify-content: center; gap: 15px; margin-bottom: 20px; } .btn { background: #00d4aa; color: #000; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-weight: bold; transition: all 0.3s ease; } .btn:hover { background: #00b894; transform: translateY(-1px); } .btn-secondary { background: #333; color: #fff; } .btn-secondary:hover { background: #555; } .connection-status { position: fixed; top: 20px; right: 20px; padding: 10px; border-radius: 6px; font-size: 0.9rem; z-index: 1000; } .connection-status.connected { background: #00d4aa; color: #000; } .connection-status.disconnected { background: #ff6b6b; color: #fff; } .loading { display: flex; justify-content: center; align-items: center; height: 200px; color: #aaa; } .categories-chart { max-height: 250px; } @media (max-width: 768px) { .dashboard-grid { grid-template-columns: 1fr; } .status-bar { grid-template-columns: 1fr; } .header h1 { font-size: 2rem; } } </style> </head> <body> <div class="container"> <div class="header"> <h1>🧠 CodeGraph Memory Profiler</h1> <p>Real-time memory allocation tracking and optimization recommendations</p> </div> <div class="connection-status" id="connectionStatus"> 🔌 Connecting... </div> <div class="controls"> <button class="btn" onclick="refreshData()">🔄 Refresh</button> <button class="btn btn-secondary" onclick="exportData('json')">📄 Export JSON</button> <button class="btn btn-secondary" onclick="exportData('csv')">📊 Export CSV</button> <button class="btn btn-secondary" onclick="detectLeaks()">🔍 Detect Leaks</button> </div> <div class="status-bar" id="statusBar"> <div class="status-card"> <h3 id="currentUsage">--</h3> <p>Current Usage</p> </div> <div class="status-card"> <h3 id="peakUsage">--</h3> <p>Peak Usage</p> </div> <div class="status-card"> <h3 id="allocationCount">--</h3> <p>Total Allocations</p> </div> <div class="status-card" id="pressureCard"> <h3 id="memoryPressure">--</h3> <p>Memory Pressure</p> </div> </div> <div class="dashboard-grid"> <div class="chart-container"> <h2>📈 Memory Usage Over Time</h2> <div class="chart"> <canvas id="memoryChart"></canvas> </div> </div> <div class="side-panel"> <h2>🔥 Active Memory Leaks</h2> <div class="leaks-list" id="leaksList"> <div class="loading">Loading leaks...</div> </div> </div> </div> <div class="chart-container"> <h2>🎯 Memory by Category</h2> <div class="chart categories-chart"> <canvas id="categoriesChart"></canvas> </div> </div> <div class="recommendations" id="recommendations"> <h2>💡 Optimization Recommendations</h2> <div id="recommendationsList"> <div class="loading">Loading recommendations...</div> </div> </div> </div> <script> class MemoryDashboard { constructor() { this.ws = null; this.memoryChart = null; this.categoriesChart = null; this.isConnected = false; this.init(); } async init() { this.setupWebSocket(); this.setupCharts(); await this.loadInitialData(); // Auto-refresh every 30 seconds setInterval(() => this.refreshData(), 30000); } setupWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws`; this.ws = new WebSocket(wsUrl); this.ws.onopen = () => { this.isConnected = true; this.updateConnectionStatus(); }; this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.handleWebSocketMessage(data); }; this.ws.onclose = () => { this.isConnected = false; this.updateConnectionStatus(); // Attempt to reconnect after 5 seconds setTimeout(() => this.setupWebSocket(), 5000); }; this.ws.onerror = (error) => { console.error('WebSocket error:', error); this.isConnected = false; this.updateConnectionStatus(); }; } updateConnectionStatus() { const status = document.getElementById('connectionStatus'); if (this.isConnected) { status.textContent = '🟢 Connected'; status.className = 'connection-status connected'; } else { status.textContent = '🔴 Disconnected'; status.className = 'connection-status disconnected'; } } handleWebSocketMessage(data) { switch (data.type) { case 'LiveMetrics': this.updateLiveMetrics(data); break; case 'Event': this.handleProfilerEvent(data); break; } } updateLiveMetrics(data) { this.updateStatusCards({ current_usage: data.current_usage, memory_pressure: data.memory_pressure, allocation_count: data.allocation_count || 0, peak_usage: data.peak_usage || 0 }); // Update memory chart with new data point if (this.memoryChart && data.current_usage) { const chart = this.memoryChart; const now = new Date(); // Add new data point chart.data.labels.push(now.toLocaleTimeString()); chart.data.datasets[0].data.push(data.current_usage / (1024 * 1024)); // Convert to MB // Keep only last 20 points if (chart.data.labels.length > 20) { chart.data.labels.shift(); chart.data.datasets[0].data.shift(); } chart.update('none'); } } handleProfilerEvent(event) { console.log('Profiler event:', event); if (event.type === 'LeakDetected') { this.addLeakToList(event.leak); } else if (event.type === 'RecommendationGenerated') { this.addRecommendation(event.recommendation); } } setupCharts() { // Memory usage chart const memoryCtx = document.getElementById('memoryChart').getContext('2d'); this.memoryChart = new Chart(memoryCtx, { type: 'line', data: { labels: [], datasets: [{ label: 'Memory Usage (MB)', data: [], borderColor: '#00d4aa', backgroundColor: 'rgba(0, 212, 170, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: '#ffffff' } } }, scales: { x: { ticks: { color: '#aaaaaa' }, grid: { color: '#333333' } }, y: { ticks: { color: '#aaaaaa' }, grid: { color: '#333333' } } } } }); // Categories chart const categoriesCtx = document.getElementById('categoriesChart').getContext('2d'); this.categoriesChart = new Chart(categoriesCtx, { type: 'doughnut', data: { labels: [], datasets: [{ data: [], backgroundColor: [ '#00d4aa', '#ff6b6b', '#ffa500', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7', '#a29bfe' ], borderWidth: 2, borderColor: '#1a1a1a' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { color: '#ffffff' } } } } }); } async loadInitialData() { try { await Promise.all([ this.loadMetrics(), this.loadLeaks(), this.loadRecommendations(), this.loadHistory() ]); } catch (error) { console.error('Error loading initial data:', error); } } async loadMetrics() { try { const response = await fetch('/api/metrics'); const result = await response.json(); if (result.success) { this.updateStatusCards(result.data); this.updateCategoriesChart(result.data.categories); } } catch (error) { console.error('Error loading metrics:', error); } } async loadLeaks() { try { const response = await fetch('/api/leaks'); const result = await response.json(); if (result.success) { this.updateLeaksList(result.data); } } catch (error) { console.error('Error loading leaks:', error); } } async loadRecommendations() { try { const response = await fetch('/api/recommendations'); const result = await response.json(); if (result.success) { this.updateRecommendations(result.data); } } catch (error) { console.error('Error loading recommendations:', error); } } async loadHistory() { try { const response = await fetch('/api/history?hours=1&resolution=minute'); const result = await response.json(); if (result.success && result.data.length > 0) { const labels = result.data.map(point => new Date(point.timestamp * 1000).toLocaleTimeString() ); const data = result.data.map(point => point.total_usage / (1024 * 1024)); this.memoryChart.data.labels = labels; this.memoryChart.data.datasets[0].data = data; this.memoryChart.update(); } } catch (error) { console.error('Error loading history:', error); } } updateStatusCards(metrics) { document.getElementById('currentUsage').textContent = this.formatBytes(metrics.current_usage); document.getElementById('peakUsage').textContent = this.formatBytes(metrics.peak_usage); document.getElementById('allocationCount').textContent = metrics.allocation_count?.toLocaleString() || '--'; const pressureCard = document.getElementById('pressureCard'); const pressureElement = document.getElementById('memoryPressure'); pressureElement.textContent = this.formatPressure(metrics.memory_pressure); // Update pressure card styling pressureCard.className = `status-card pressure-${metrics.memory_pressure?.toLowerCase() || 'low'}`; } updateCategoriesChart(categories) { if (!categories || Object.keys(categories).length === 0) return; const labels = Object.keys(categories); const data = Object.values(categories).map(cat => cat.current / (1024 * 1024)); // Convert to MB this.categoriesChart.data.labels = labels; this.categoriesChart.data.datasets[0].data = data; this.categoriesChart.update(); } updateLeaksList(leaks) { const container = document.getElementById('leaksList'); if (leaks.length === 0) { container.innerHTML = '<p style="color: #00d4aa; text-align: center;">🎉 No memory leaks detected!</p>'; return; } container.innerHTML = leaks.map(leak => ` <div class="leak-item"> <h4>🚨 ${this.formatBytes(leak.size)} leak (${leak.age_seconds || 0}s old)</h4> <p><strong>Category:</strong> ${leak.category}</p> <p><strong>Impact:</strong> ${leak.estimated_impact}</p> <p><strong>Stack:</strong> ${leak.stack_trace?.slice(0, 2).join(' → ') || 'No trace'}</p> </div> `).join(''); } addLeakToList(leak) { // Add new leak to the top of the list const container = document.getElementById('leaksList'); const leakHtml = ` <div class="leak-item" style="animation: fadeIn 0.5s ease-in;"> <h4>🚨 ${this.formatBytes(leak.size)} leak (just detected)</h4> <p><strong>Category:</strong> ${leak.category}</p> <p><strong>Impact:</strong> ${leak.estimated_impact}</p> </div> `; if (container.innerHTML.includes('No memory leaks detected')) { container.innerHTML = leakHtml; } else { container.insertAdjacentHTML('afterbegin', leakHtml); } } updateRecommendations(recommendations) { const container = document.getElementById('recommendationsList'); if (recommendations.length === 0) { container.innerHTML = '<p style="color: #00d4aa; text-align: center;">✅ No optimization recommendations at this time</p>'; return; } container.innerHTML = recommendations.map(rec => ` <div class="recommendation-item ${rec.severity?.toLowerCase() || 'info'}"> <h3>💡 ${rec.action || 'Optimization'}</h3> <p>${rec.description}</p> <p><strong>Category:</strong> ${rec.category}</p> <p><strong>Estimated Savings:</strong> <span class="savings">${this.formatBytes(rec.estimated_savings || 0)}</span></p> <p><strong>Difficulty:</strong> ${rec.implementation_difficulty || 'Unknown'}</p> </div> `).join(''); } addRecommendation(recommendation) { // Add new recommendation to the top of the list const container = document.getElementById('recommendationsList'); const recHtml = ` <div class="recommendation-item ${recommendation.severity?.toLowerCase() || 'info'}" style="animation: fadeIn 0.5s ease-in;"> <h3>💡 ${recommendation.action || 'Optimization'} (New)</h3> <p>${recommendation.description}</p> <p><strong>Estimated Savings:</strong> <span class="savings">${this.formatBytes(recommendation.estimated_savings || 0)}</span></p> </div> `; if (container.innerHTML.includes('No optimization recommendations')) { container.innerHTML = recHtml; } else { container.insertAdjacentHTML('afterbegin', recHtml); } } formatBytes(bytes) { if (!bytes) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; } formatPressure(pressure) { if (!pressure) return 'Low'; return pressure.charAt(0).toUpperCase() + pressure.slice(1).toLowerCase(); } async refreshData() { await this.loadInitialData(); console.log('Data refreshed'); } async exportData(format) { try { const response = await fetch(`/api/export?format=${format}`); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `memory_profile.${format}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } catch (error) { console.error('Export error:', error); alert('Export failed. Please try again.'); } } async detectLeaks() { try { await this.loadLeaks(); console.log('Leak detection completed'); } catch (error) { console.error('Leak detection error:', error); } } } // Global functions for button clicks let dashboard; function refreshData() { dashboard.refreshData(); } function exportData(format) { dashboard.exportData(format); } function detectLeaks() { dashboard.detectLeaks(); } // Initialize dashboard when page loads document.addEventListener('DOMContentLoaded', () => { dashboard = new MemoryDashboard(); }); // Add fade-in animation const style = document.createElement('style'); style.textContent = ` @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } `; document.head.appendChild(style); </script> </body> </html>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jakedismo/codegraph-rust'

If you have feedback or need assistance with the MCP directory API, please join our Discord server