From 659e42efeb6ae3bb0a3e650e0be59f0cb718c6a0 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Tue, 24 Mar 2026 00:06:04 -0400 Subject: [PATCH] feat: add agent status board showing active agents Adds a HUD panel (bottom-left) listing Timmy, Kimi, and Perplexity with live active/idle status indicators. Status updates on WebSocket events (player-joined, player-left, chat-message). Fixes #131 --- app.js | 49 ++++++++++++++++++++++++++++++++++ index.html | 6 +++++ style.css | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) diff --git a/app.js b/app.js index 5a028f6..c9850f5 100644 --- a/app.js +++ b/app.js @@ -209,6 +209,46 @@ document.getElementById('debug-toggle').addEventListener('click', () => { } }); +// === AGENT STATUS BOARD === +const AGENTS = [ + { id: 'timmy', name: 'Timmy', status: 'active' }, + { id: 'kimi', name: 'Kimi', status: 'idle' }, + { id: 'perplexity', name: 'Perplexity', status: 'idle' }, +]; + +/** + * Renders the agent list into the #agent-list element. + */ +function renderAgentBoard() { + const list = document.getElementById('agent-list'); + if (!list) return; + list.innerHTML = ''; + for (const agent of AGENTS) { + const li = document.createElement('li'); + li.className = `agent-item ${agent.status}`; + li.dataset.agentId = agent.id; + li.innerHTML = + `` + + `${agent.name}` + + `${agent.status.toUpperCase()}`; + list.appendChild(li); + } +} + +/** + * Updates a single agent's status and re-renders the board. + * @param {string} agentId + * @param {'active'|'idle'} status + */ +function setAgentStatus(agentId, status) { + const agent = AGENTS.find(a => a.id === agentId); + if (!agent) return; + agent.status = status; + renderAgentBoard(); +} + +renderAgentBoard(); + // === WEBSOCKET CLIENT === import { wsClient } from './ws-client.js'; @@ -216,14 +256,23 @@ wsClient.connect(); window.addEventListener('player-joined', (/** @type {CustomEvent} */ event) => { console.log('Player joined:', event.detail); + if (event.detail && event.detail.agentId) { + setAgentStatus(event.detail.agentId, 'active'); + } }); window.addEventListener('player-left', (/** @type {CustomEvent} */ event) => { console.log('Player left:', event.detail); + if (event.detail && event.detail.agentId) { + setAgentStatus(event.detail.agentId, 'idle'); + } }); window.addEventListener('chat-message', (/** @type {CustomEvent} */ event) => { console.log('Chat message:', event.detail); + if (event.detail && event.detail.agentId) { + setAgentStatus(event.detail.agentId, 'active'); + } }); window.addEventListener('beforeunload', () => { diff --git a/index.html b/index.html index 26344f3..a514583 100644 --- a/index.html +++ b/index.html @@ -41,6 +41,12 @@ [Tab] to exit + +
+
AGENTS
+ +
+ diff --git a/style.css b/style.css index 1d78e52..4adb133 100644 --- a/style.css +++ b/style.css @@ -106,3 +106,80 @@ canvas { 0%, 100% { opacity: 0.7; } 50% { opacity: 1; } } + +/* === AGENT STATUS BOARD === */ +#agent-status-board { + position: fixed; + bottom: 16px; + left: 16px; + z-index: 10; + background: rgba(0, 0, 8, 0.75); + border: 1px solid var(--color-secondary); + border-radius: 4px; + padding: 8px 12px; + min-width: 140px; + pointer-events: none; +} + +.agent-board-title { + font-family: var(--font-body); + font-size: 9px; + letter-spacing: 0.25em; + color: var(--color-primary); + text-transform: uppercase; + margin-bottom: 6px; + border-bottom: 1px solid var(--color-secondary); + padding-bottom: 4px; +} + +#agent-list { + list-style: none; + margin: 0; + padding: 0; +} + +.agent-item { + display: flex; + align-items: center; + gap: 7px; + font-family: var(--font-body); + font-size: 11px; + color: var(--color-text); + padding: 2px 0; +} + +.agent-dot { + width: 6px; + height: 6px; + border-radius: 50%; + flex-shrink: 0; + background: var(--color-text-muted); +} + +.agent-dot.active { + background: #44ff88; + animation: agent-pulse 1.8s ease-in-out infinite; +} + +.agent-dot.idle { + background: var(--color-secondary); +} + +.agent-name { + flex: 1; +} + +.agent-status-label { + font-size: 9px; + color: var(--color-text-muted); + letter-spacing: 0.1em; +} + +.agent-item.active .agent-status-label { + color: #44ff88; +} + +@keyframes agent-pulse { + 0%, 100% { opacity: 1; box-shadow: 0 0 4px #44ff88; } + 50% { opacity: 0.5; box-shadow: none; } +} -- 2.43.0