Spaces:
Sleeping
Sleeping
| import { createSmoothRenderer } from "./shared.js"; | |
| const socket = io(); | |
| const display = document.getElementById("display"); | |
| const clientCountEl = document.getElementById("clientCount"); | |
| const tableBody = document.getElementById("clientsTable"); | |
| const colorPicker = document.getElementById("colorPicker"); | |
| const blackoutBtn = document.getElementById("blackout"); | |
| const wakeBtn = document.getElementById("wake"); | |
| const renderer = createSmoothRenderer({ | |
| setText: (t) => (display.textContent = t), | |
| }); | |
| socket.on("connect", () => { | |
| // Identify as admin to receive client list updates | |
| socket.emit("admin:join"); | |
| }); | |
| // Stopwatch state (also brings color & blackout) | |
| socket.on("state", (s) => { | |
| renderer.applyServerState(s); | |
| // Color reflection | |
| if (s.color) { | |
| display.style.color = s.color; | |
| if (colorPicker && colorPicker.value.toLowerCase() !== s.color.toLowerCase()) { | |
| colorPicker.value = s.color; | |
| } | |
| } | |
| // Blackout button label reflect state | |
| if (typeof s.blackout === "boolean" && blackoutBtn) { | |
| blackoutBtn.textContent = s.blackout ? "Show Clock" : "Blackout"; | |
| blackoutBtn.style.background = s.blackout ? "#550000" : "#111"; | |
| } | |
| }); | |
| // Render clients table | |
| socket.on("clients:list", ({ count, clients }) => { | |
| clientCountEl.textContent = count; | |
| if (!clients || clients.length === 0) { | |
| tableBody.innerHTML = `<tr><td colspan="6" class="muted">No clients connected yet.</td></tr>`; | |
| return; | |
| } | |
| const rows = clients.map((c) => { | |
| const since = new Date(c.connectedAt).toLocaleTimeString(); | |
| const lat = (c.latencyMs == null) ? "—" : `${c.latencyMs} ms`; | |
| const shortId = c.id.slice(0, 6); | |
| const ua = (c.ua || "").slice(0, 80); | |
| const url = (c.url || "").slice(0, 64); | |
| return ` | |
| <tr> | |
| <td class="mono" title="${c.id}">${shortId}</td> | |
| <td class="mono">${c.ip}</td> | |
| <td>${lat}</td> | |
| <td title="${c.url}">${url}</td> | |
| <td title="${c.ua}">${ua}</td> | |
| <td class="mono">${since}</td> | |
| </tr> | |
| `; | |
| }); | |
| tableBody.innerHTML = rows.join(""); | |
| }); | |
| // Admin buttons | |
| document.getElementById("start").onclick = () => socket.emit("cmd:start"); | |
| document.getElementById("stop").onclick = () => socket.emit("cmd:stop"); | |
| document.getElementById("reset").onclick = () => socket.emit("cmd:reset"); | |
| // Color (debounced a bit) | |
| let colorDebounce; | |
| colorPicker?.addEventListener("input", (e) => { | |
| const hex = String(e.target.value || "").trim(); | |
| clearTimeout(colorDebounce); | |
| colorDebounce = setTimeout(() => { | |
| socket.emit("cmd:color", hex); | |
| }, 50); | |
| }); | |
| // Blackout toggle | |
| let blackoutEnabled = false; | |
| blackoutBtn?.addEventListener("click", () => { | |
| blackoutEnabled = !blackoutEnabled; | |
| socket.emit("cmd:blackout", blackoutEnabled); | |
| blackoutBtn.textContent = blackoutEnabled ? "Show Clock" : "Blackout"; | |
| blackoutBtn.style.background = blackoutEnabled ? "#550000" : "#111"; | |
| }); | |
| // 🚀 Wake button | |
| wakeBtn?.addEventListener("click", () => { | |
| socket.emit("cmd:wake"); | |
| // Visual feedback pulse | |
| wakeBtn.style.background = "#008800"; | |
| setTimeout(() => (wakeBtn.style.background = "#004400"), 300); | |
| }); | |