let autoRefreshInterval = null; let currentMode = 'gradient'; function refreshImage() { const img = document.getElementById('displayImage'); // Check if server has finished encoding fetch('/api/image-ready') .then(response => response.json()) .then(data => { if (data.ready) { performSmoothImageSwap(); } else { // Try again in 1 second setTimeout(refreshImage, 1000); } }) .catch(error => { console.error('Error checking image status:', error); updateStatus('Error checking image status', 'error'); }); } function performSmoothImageSwap() { const img = document.getElementById('displayImage'); const newImage = new Image(); newImage.onload = function() { // Crossfade transition img.style.opacity = '0'; setTimeout(() => { img.src = './output/display.jxl'; img.style.opacity = '1'; updateStatus('Image refreshed at ' + new Date().toLocaleTimeString()); }, 300); }; newImage.onerror = function() { updateStatus('Error loading image', 'error'); }; // Load with cache busting only when we know it's ready newImage.src = './output/display.jxl?' + new Date().getTime(); } function refreshTerrain() { fetch('/api/refresh-terrain', { method: 'POST' }) .then(response => response.json()) .then(data => { if (data.status === 'success') { refreshImage(); } else { updateStatus('Error refreshing terrain', 'error'); } }) .catch(error => { console.error('Error refreshing terrain:', error); updateStatus('Error refreshing terrain', 'error'); }); } function toggleAutoRefresh() { const button = document.getElementById('autoRefreshBtn'); if (autoRefreshInterval) { clearInterval(autoRefreshInterval); autoRefreshInterval = null; button.textContent = 'Start Auto-Refresh'; button.classList.remove('danger'); updateStatus('Auto-refresh stopped'); } else { // Faster refresh for terrain (5 seconds) autoRefreshInterval = setInterval(currentMode === 'terrain' ? refreshTerrain : refreshImage, 100); button.textContent = 'Stop Auto-Refresh'; button.classList.add('danger'); updateStatus('Auto-refresh started'); } } function switchMode() { fetch('/api/switch-mode', { method: 'POST' }) .then(response => response.json()) .then(data => { if (data.status === 'success') { currentMode = data.mode; updateModeDisplay(); refreshImage(); updateStatus('Switched to ' + data.mode + ' mode'); // Restart auto-refresh if active if (autoRefreshInterval) { clearInterval(autoRefreshInterval); autoRefreshInterval = setInterval(currentMode === 'terrain' ? refreshTerrain : refreshImage, 5000); } } else { updateStatus('Error switching mode', 'error'); } }) .catch(error => { console.error('Error switching mode:', error); updateStatus('Error switching mode', 'error'); }); } function updateModeDisplay() { const modeDisplay = document.getElementById('modeDisplay'); const switchBtn = document.getElementById('switchModeBtn'); const refreshBtn = document.getElementById('refreshBtn'); if (modeDisplay) { modeDisplay.textContent = 'Current Mode: ' + currentMode; } if (switchBtn) { switchBtn.textContent = 'Switch to ' + (currentMode === 'gradient' ? 'Terrain' : 'Gradient'); } if (refreshBtn) { if (currentMode === 'terrain') { refreshBtn.textContent = 'Refresh Terrain'; refreshBtn.onclick = refreshTerrain; } else { refreshBtn.textContent = 'Refresh Image'; refreshBtn.onclick = refreshImage; } } } function showStats() { fetch('/api/timing-stats') .then(response => response.json()) .then(data => { displayStats(data); document.getElementById('statsPanel').style.display = 'block'; }) .catch(error => { console.error('Error fetching stats:', error); updateStatus('Error loading stats', 'error'); }); } function hideStats() { document.getElementById('statsPanel').style.display = 'none'; } function clearStats() { fetch('/api/clear-stats', { method: 'POST' }) .then(response => response.json()) .then(data => { updateStatus('Statistics cleared'); hideStats(); }) .catch(error => { console.error('Error clearing stats:', error); updateStatus('Error clearing stats', 'error'); }); } function displayStats(stats) { const statsContent = document.getElementById('statsContent'); if (stats.length === 0) { statsContent.innerHTML = '
No timing data available.
'; return; } let html = '| Function | Calls | Total (s) | Avg (s) | Min (s) | Max (s) | Median (s) | P90 (s) | P95 (s) | P99 (s) |
|---|---|---|---|---|---|---|---|---|---|
| ${stat.function} | ${stat.call_count} | ${parseFloat(stat.total_time).toFixed(6)} | ${parseFloat(stat.avg_time).toFixed(6)} | ${parseFloat(stat.min_time).toFixed(6)} | ${parseFloat(stat.max_time).toFixed(6)} | ${parseFloat(stat.median_time).toFixed(6)} | ${parseFloat(stat.p90_time).toFixed(6)} | ${parseFloat(stat.p95_time).toFixed(6)} | ${parseFloat(stat.p99_time).toFixed(6)} |