feat(soak): dashboard status grid UI

Static HTML page served by DashboardServer. Renders the 2×2 room
grid with progress bars and player tiles, subscribes to WS events,
updates tiles live. Click-to-watch modal is wired but receives
frames once the CDP screencaster ships in Task 22.

Adds escapeHtml() on all user-controlled strings (roomId, player
key) — not strictly needed for our trusted bot traffic but cheap
XSS hardening against future scenarios that accept user input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-11 18:55:21 -04:00
parent 9d1d4f899b
commit a35e789eb9
3 changed files with 386 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Golf Soak Dashboard</title>
<link rel="stylesheet" href="/dashboard.css">
</head>
<body>
<header class="dash-header">
<h1>⛳ Golf Soak Dashboard</h1>
<div class="meta">
<span id="run-id">run —</span>
<span id="elapsed">00:00:00</span>
</div>
</header>
<div class="meta-bar">
<div class="stat"><span class="label">Games</span><span id="metric-games">0</span></div>
<div class="stat"><span class="label">Moves</span><span id="metric-moves">0</span></div>
<div class="stat"><span class="label">Errors</span><span id="metric-errors">0</span></div>
<div class="stat"><span class="label">WS</span><span id="ws-status">connecting</span></div>
</div>
<div class="rooms" id="rooms">
<!-- Room tiles injected by dashboard.js -->
</div>
<section class="log">
<div class="log-header">Activity Log</div>
<ul id="log-list"></ul>
</section>
<!-- Modal for focused live video (screencast arrives in Task 22/23) -->
<div id="video-modal" class="video-modal hidden">
<div class="video-modal-content">
<div class="video-modal-header">
<span id="video-modal-title">Watching —</span>
<button id="video-modal-close">Close</button>
</div>
<img id="video-frame" alt="Live screencast" />
</div>
</div>
<script src="/dashboard.js"></script>
</body>
</html>