const WS = { _ws: null, _url: '', _reconnectTimer: null, _reconnectDelay: 2000, _handlers: {}, _connected: false, init() { const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; this._url = `${protocol}//${location.host}/ws`; this._connect(); }, _connect() { if (this._ws && this._ws.readyState === WebSocket.OPEN) return; try { this._ws = new WebSocket(this._url); } catch (e) { this._scheduleReconnect(); return; } this._ws.onopen = () => { this._connected = true; this._reconnectDelay = 2000; this._emit('connection', true); this._emit('status', 'connected'); }; this._ws.onmessage = (event) => { try { const msg = JSON.parse(event.data); 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', 'error'); }; }, _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;