'use strict'; // ── WebSocket Setup ────────────────────────────────────────────────────────── let ws; let connected = false; function connectWS() { const proto = location.protocol === 'https:' ? 'wss' : 'ws'; ws = new WebSocket(`${proto}://${location.host}/ws`); ws.onopen = () => { connected = true; updateStatus(); }; ws.onclose = () => { connected = false; updateStatus(); setTimeout(connectWS, 2000); }; ws.onerror = () => {}; ws.onmessage = (e) => { let msg; try { msg = JSON.parse(e.data); } catch { return; } handleServerMessage(msg); }; } function handleServerMessage(msg) { if (msg.type === 'status') { updateStatus(); } } function updateStatus() { const dot = document.getElementById('status-dot'); const lbl = document.getElementById('status-label'); if (connected) { dot.className = 'ok'; lbl.textContent = 'Ready'; } else { dot.className = ''; lbl.textContent = 'Connecting…'; } } // ── Connection Management ──────────────────────────────────────────────────── let selectedRobot = null; let selectedMode = null; let connections = []; const STORAGE_KEY = 're-commander-connections'; // Load connections from localStorage function loadConnections() { const stored = localStorage.getItem(STORAGE_KEY); connections = stored ? JSON.parse(stored) : []; renderConnections(); } // Save connections to localStorage function saveConnections() { localStorage.setItem(STORAGE_KEY, JSON.stringify(connections)); } // Render connections list function renderConnections() { const list = document.getElementById('connections-list'); if (connections.length === 0) { list.innerHTML = '
No saved connections
'; return; } list.innerHTML = connections.map((conn, idx) => `
${conn.name}
${conn.ip}
`).join(''); // Add click handlers to select connection document.querySelectorAll('.connection-item').forEach(item => { const idx = parseInt(item.dataset.index); item.addEventListener('click', (e) => { if (!e.target.classList.contains('connection-delete')) { selectConnection(idx); } }); }); // Add click handlers to delete button document.querySelectorAll('.connection-delete').forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); const idx = parseInt(btn.dataset.index); deleteConnection(idx); }); }); // Highlight selected connection if (selectedRobot !== null && selectedRobot < connections.length) { document.querySelector(`.connection-item[data-index="${selectedRobot}"]`)?.classList.add('selected'); } } function selectConnection(idx) { selectedRobot = idx; const conn = connections[idx]; sessionStorage.setItem('selectedRobot', conn.ip); sessionStorage.setItem('selectedRobotName', conn.name); renderConnections(); updateLaunchButton(); } function deleteConnection(idx) { connections.splice(idx, 1); saveConnections(); if (selectedRobot === idx) { selectedRobot = null; sessionStorage.removeItem('selectedRobot'); sessionStorage.removeItem('selectedRobotName'); } renderConnections(); updateLaunchButton(); } // Add new connection document.getElementById('btn-add-connection').addEventListener('click', () => { const ip = document.getElementById('robot-ip').value.trim(); if (!ip) { alert('Please enter a robot IP or hostname'); return; } // Check if already exists if (connections.find(c => c.ip === ip)) { alert('This connection already exists'); return; } // Generate a name const name = `Robot (${ip})`; connections.push({ name, ip }); saveConnections(); renderConnections(); // Clear input and select the new connection document.getElementById('robot-ip').value = ''; selectConnection(connections.length - 1); }); // Handle Enter key in IP input document.getElementById('robot-ip').addEventListener('keypress', (e) => { if (e.key === 'Enter') { document.getElementById('btn-add-connection').click(); } }); // ── Mode Selection ─────────────────────────────────────────────────────────── document.querySelectorAll('.mode-btn').forEach(btn => { btn.addEventListener('click', function () { const mode = this.dataset.mode; selectedMode = mode; // Remove active class from all mode buttons document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active')); this.classList.add('active'); updateLaunchButton(); }); }); function updateLaunchButton() { const btn = document.getElementById('btn-launch'); if (selectedRobot !== null && selectedMode) { btn.disabled = false; } else { btn.disabled = true; } } // ── Launch ─────────────────────────────────────────────────────────────────── document.getElementById('btn-launch').addEventListener('click', () => { if (selectedRobot === null || !selectedMode) { alert('Please select both a robot and a mode'); return; } const conn = connections[selectedRobot]; // Store selection in sessionStorage sessionStorage.setItem('selectedRobot', conn.ip); sessionStorage.setItem('selectedRobotName', conn.name); sessionStorage.setItem('selectedMode', selectedMode); // Navigate to the appropriate page const modeFile = selectedMode + '.html'; window.location.href = modeFile; }); // ── Initialization ─────────────────────────────────────────────────────────── connectWS(); loadConnections();