let evtSource = null; let currentInstalledModels = []; // Initialize SSE Stream function initSSE() { if (evtSource) { evtSource.close(); } evtSource = new EventSource('/api/stream'); evtSource.onopen = () => { document.getElementById('sse-badge').textContent = 'SSE LIVE'; document.getElementById('sse-badge').className = 'text-emerald-400 font-bold'; }; evtSource.onmessage = (event) => { try { const data = JSON.parse(event.data); updateDashboard(data); } catch (err) { console.error('Error parsing SSE event:', err); } }; evtSource.onerror = (err) => { console.warn('SSE disconnected, retrying...', err); document.getElementById('sse-badge').textContent = 'RECONNECTING...'; document.getElementById('sse-badge').className = 'text-rose-400 font-bold'; }; } function updateDashboard(data) { if (!data) return; // 1. GPU VRAM Stats const gpu = data.gpu || {}; if (gpu.available) { pushOverclockSample(gpu); document.getElementById('gpu-chip-name').textContent = gpu.device_name || 'NVIDIA GPU'; document.getElementById('vram-total-used').textContent = gpu.vram_used_gb || '0.0'; document.getElementById('vram-used-pct').textContent = `${gpu.vram_used_pct || 0}% USED`; const bd = gpu.breakdown || {}; const totalBytes = gpu.vram_total_bytes || (16 * 1024**3); const ollamaPct = ((bd.ollama_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const comfyPct = ((bd.comfyui_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const systemPct = ((bd.system_gb || 0) / (gpu.vram_total_gb || 16)) * 100; const freePct = Math.max(0, 100 - (ollamaPct + comfyPct + systemPct)); document.getElementById('bar-ollama').style.width = `${ollamaPct}%`; document.getElementById('bar-comfy').style.width = `${comfyPct}%`; document.getElementById('bar-system').style.width = `${systemPct}%`; document.getElementById('bar-free').style.width = `${freePct}%`; document.getElementById('tooltip-ollama').textContent = `${bd.ollama_gb || 0} GB`; document.getElementById('tooltip-comfy').textContent = `${bd.comfyui_gb || 0} GB`; document.getElementById('tooltip-system').textContent = `${bd.system_gb || 0} GB`; document.getElementById('legend-ollama').textContent = `${bd.ollama_gb || 0} GB`; document.getElementById('legend-comfy').textContent = `${bd.comfyui_gb || 0} GB`; document.getElementById('legend-system').textContent = `${bd.system_gb || 0} GB`; document.getElementById('legend-free').textContent = `${bd.free_gb || 0} GB`; // Hardware sensors document.getElementById('gpu-util-val').textContent = `${gpu.gpu_util_pct || 0}%`; document.getElementById('gpu-temp-val').textContent = `${gpu.temperature_c || 0}°C`; document.getElementById('gpu-power-val').textContent = `${gpu.power_w || 0} W`; document.getElementById('gpu-fan-val').textContent = `${gpu.fan_pct || 0}%`; // Per-fan and animations const fan0 = (gpu.fans && gpu.fans.length > 0) ? gpu.fans[0] : (gpu.fan_pct || 0); const fan1 = (gpu.fans && gpu.fans.length > 1) ? gpu.fans[1] : (gpu.fan_pct || 0); const fanSub = document.getElementById('gpu-fan-sub'); if (fanSub) fanSub.textContent = `Fan 0: ${fan0}% | Fan 1: ${fan1}%`; const fan0Val = document.getElementById('oc-fan0-val'); if (fan0Val) fan0Val.textContent = `${fan0}%`; const fan0Bar = document.getElementById('oc-fan0-bar'); if (fan0Bar) fan0Bar.style.width = `${fan0}%`; const fan1Val = document.getElementById('oc-fan1-val'); if (fan1Val) fan1Val.textContent = `${fan1}%`; const fan1Bar = document.getElementById('oc-fan1-bar'); if (fan1Bar) fan1Bar.style.width = `${fan1}%`; const fanCurrent = document.getElementById('oc-fan-current'); if (fanCurrent) fanCurrent.textContent = `${gpu.fan_pct || 0}%`; const spinSpeed = Math.max(0.2, (100 - (gpu.fan_pct || 0)) / 100 * 1.6 + 0.3); const fanIcon = document.getElementById('gpu-fan-icon'); if (fanIcon) { if ((gpu.fan_pct || 0) > 0) { fanIcon.classList.add('fan-spinning'); fanIcon.style.animationDuration = `${spinSpeed.toFixed(2)}s`; } else { fanIcon.classList.remove('fan-spinning'); } } const ocFanCardIcon = document.getElementById('oc-fan-card-icon'); if (ocFanCardIcon) { if ((gpu.fan_pct || 0) > 0) { ocFanCardIcon.classList.add('fan-spinning'); ocFanCardIcon.style.animationDuration = `${spinSpeed.toFixed(2)}s`; } else { ocFanCardIcon.classList.remove('fan-spinning'); } } // Processes table const tbody = document.getElementById('gpu-proc-table'); if (bd.processes && bd.processes.length > 0) { tbody.innerHTML = bd.processes.map(p => { let tagClass = 'text-slate-400'; let badge = ''; if (p.is_ollama) { tagClass = 'text-purple-400 font-bold'; badge = 'OLLAMA'; } else if (p.is_comfy) { tagClass = 'text-cyan-400 font-bold'; badge = 'COMFY'; } return `