// Header components: Clock and Backend Status Indicator "use strict"; // ========================================================================= // Global Variables // ========================================================================= var clockInterval = null; var statusUpdateCallback = null; // ========================================================================= // Clock // ========================================================================= function initClock() { function updateClock() { var now = new Date(); var hours = String(now.getHours()).padStart(2, '0'); var minutes = String(now.getMinutes()).padStart(2, '0'); var seconds = String(now.getSeconds()).padStart(2, '0'); var timeString = hours + ':' + minutes + ':' + seconds; var clockElement = document.getElementById('clockTime'); if (clockElement) { clockElement.textContent = timeString; } } updateClock(); if (clockInterval) { clearInterval(clockInterval); } clockInterval = setInterval(updateClock, 1000); } // ========================================================================= // Status Indicator // ========================================================================= function initStatusIndicator() { updateStatusIndicator('connecting', 'Connecting...'); function registerStatusCallback() { if (typeof wsManager !== 'undefined' && wsManager) { wsManager.onStatusChange(function(state, text) { updateStatusIndicator(state, text); }); var currentState = wsManager.getConnectionState(); var currentText = 'Connecting...'; if (currentState === 'connected' && wsManager.isConnected) { currentText = 'Connected'; } else if (currentState === 'connecting') { currentText = 'Connecting...'; } else if (currentState === 'disconnected') { currentText = 'Disconnected'; } else if (currentState === 'disconnecting') { currentText = 'Disconnecting...'; } updateStatusIndicator(currentState, currentText); return true; } return false; } if (!registerStatusCallback()) { var checkInterval = setInterval(function() { if (registerStatusCallback()) { clearInterval(checkInterval); } }, 100); setTimeout(function() { clearInterval(checkInterval); }, 5000); } } function updateStatusIndicator(state, text) { var statusDot = document.getElementById('statusDot'); var statusText = document.getElementById('statusText'); if (!statusDot || !statusText) { return; } statusDot.classList.remove('bg-green-500', 'bg-yellow-500', 'bg-red-500', 'bg-gray-400'); switch (state) { case 'connected': statusDot.classList.add('bg-green-500'); statusText.textContent = text || 'Connected'; break; case 'connecting': case 'reconnecting': statusDot.classList.add('bg-yellow-500'); statusText.textContent = text || 'Connecting...'; break; case 'disconnected': case 'error': statusDot.classList.add('bg-red-500'); statusText.textContent = text || 'Disconnected'; break; default: statusDot.classList.add('bg-gray-400'); statusText.textContent = text || 'Unknown'; } } // ========================================================================= // WebSocket Tooltip // ========================================================================= function createWebSocketTooltip() { const tooltip = document.createElement('div'); tooltip.id = 'wsTooltip'; tooltip.className = 'fixed z-50 px-3 py-2 bg-gray-900 text-white text-xs rounded shadow-lg pointer-events-none opacity-0 transition-opacity duration-200'; tooltip.style.display = 'none'; tooltip.style.minWidth = '200px'; document.body.appendChild(tooltip); const statusEl = document.getElementById('backendStatus'); if (!statusEl) { return; } let tooltipUpdateInterval = null; function updateTooltipContent() { if (!wsManager || !wsManager.isConnected) { return; } const info = wsManager.getConnectionInfo(); if (!info) { return; } tooltip.innerHTML = `
WebSocket Connection
Duration: ${info.duration}
Last Heartbeat: ${info.lastHeartbeat}
Messages: ${info.messages}
Reconnects: ${info.reconnects}
${info.protocol}
${info.url}
`; } function showTooltip(e) { if (!wsManager || !wsManager.isConnected) { return; } updateTooltipContent(); const rect = statusEl.getBoundingClientRect(); const tooltipRect = tooltip.getBoundingClientRect(); let left = rect.left + (rect.width / 2) - (tooltipRect.width / 2); let top = rect.bottom + 8; if (left < 8) left = 8; if (left + tooltipRect.width > window.innerWidth - 8) { left = window.innerWidth - tooltipRect.width - 8; } if (top + tooltipRect.height > window.innerHeight - 8) { top = rect.top - tooltipRect.height - 8; } tooltip.style.left = left + 'px'; tooltip.style.top = top + 'px'; tooltip.style.display = 'block'; setTimeout(() => { tooltip.style.opacity = '1'; }, 10); if (tooltipUpdateInterval) { clearInterval(tooltipUpdateInterval); } tooltipUpdateInterval = setInterval(updateTooltipContent, 1000); } function hideTooltip() { tooltip.style.opacity = '0'; setTimeout(() => { tooltip.style.display = 'none'; }, 200); if (tooltipUpdateInterval) { clearInterval(tooltipUpdateInterval); tooltipUpdateInterval = null; } } statusEl.addEventListener('mouseenter', showTooltip); statusEl.addEventListener('mouseleave', hideTooltip); if (typeof wsManager !== 'undefined' && wsManager) { wsManager.onStatusChange(function(state, text) { if (state !== 'connected') { hideTooltip(); } }); } else { var checkInterval = setInterval(function() { if (typeof wsManager !== 'undefined' && wsManager) { wsManager.onStatusChange(function(state, text) { if (state !== 'connected') { hideTooltip(); } }); clearInterval(checkInterval); } }, 100); } } // ========================================================================= // Initialization // ========================================================================= function initHeader() { initClock(); initStatusIndicator(); createWebSocketTooltip(); } if (typeof window !== 'undefined') { window.addEventListener('beforeunload', function() { if (clockInterval) { clearInterval(clockInterval); } }); }