const WS = { _ws: null, _url: '', _reconnectTimer: null, _reconnectDelay: 2000, _handlers: {}, _connected: false, _lastPong: 0, init() { const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; this._url = `${protocol}//${location.host}/ws`; this._connect(); document.addEventListener('visibilitychange', () => { if (!document.hidden && !this._connected) { this._reconnectDelay = 500; this._connect(); } if (!document.hidden && this._connected) { this.send({type: 'ping'}); } }); }, _connect() { if (this._ws && (this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CONNECTING)) return; try { this._ws = new WebSocket(this._url); } catch (e) { this._scheduleReconnect(); return; } this._ws.onopen = () => { this._connected = true; this._lastPong = Date.now(); this._reconnectDelay = 2000; this._emit('connection', true); this._emit('status', 'connected'); }; this._ws.onmessage = (event) => { try { const msg = JSON.parse(event.data); if (msg.type === 'pong') { this._lastPong = Date.now(); return; } this._emit(msg.type || 'message', msg.data || msg); if (msg.type === 'telemetry') { this._emit('telemetry', msg.data); } } catch (e) { console.error(e); } }; this._ws.onclose = () => { this._connected = false; this._emit('connection', false); this._emit('status', 'disconnected'); this._scheduleReconnect(); }; this._ws.onerror = () => { this._emit('status', 'disconnected'); this._connected = false; }; }, _scheduleReconnect() { if (this._reconnectTimer) return; this._reconnectTimer = setTimeout(() => { this._reconnectTimer = null; this._reconnectDelay = Math.min(this._reconnectDelay * 1.5, 10000); this._connect(); }, this._reconnectDelay); }, on(event, handler) { if (!this._handlers[event]) this._handlers[event] = []; this._handlers[event].push(handler); }, off(event, handler) { if (!this._handlers[event]) return; this._handlers[event] = this._handlers[event].filter(h => h !== handler); }, _emit(event, data) { if (!this._handlers[event]) return; this._handlers[event].forEach(h => h(data)); }, send(data) { if (this._ws && this._ws.readyState === WebSocket.OPEN) { this._ws.send(JSON.stringify(data)); } }, get connected() { return this._connected; } }; window.WS = WS;