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 = ''; html += ''; stats.forEach(stat => { html += ``; }); html += '
FunctionCallsTotal (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)}
'; statsContent.innerHTML = html; } function updateStatus(message, type = 'info') { const statusEl = document.getElementById('status'); statusEl.textContent = message; statusEl.className = `status ${type}`; // Auto-hide after 5 seconds setTimeout(() => { statusEl.textContent = ''; statusEl.className = 'status'; }, 5000); } // Initialize on page load document.addEventListener('DOMContentLoaded', function() { // Check if we're in all mode and get current mode fetch('/api/current-mode') .then(response => response.json()) .then(data => { if (data.mode) { currentMode = data.mode; updateModeDisplay(); } refreshImage(); }) .catch(error => { // If endpoint doesn't exist, we're not in all mode console.log('Not in all mode, using default'); refreshImage(); }); });