From ea945a37f86531c322545c0de67a9facda216801 Mon Sep 17 00:00:00 2001 From: truetype Date: Wed, 1 Jul 2026 11:19:52 +0200 Subject: [PATCH] feat(ui): selettore PG nella chat - Selettore personaggio sopra la barra input - Solo PG vivi, nome abbreviato - Click seleziona/deseleziona - Nome preposto all azione: 'Druso: esploro...' - Label azione mostra il nome del PG --- src/web/static/index.html | 84 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/src/web/static/index.html b/src/web/static/index.html index a0b6600..a1e9c36 100644 --- a/src/web/static/index.html +++ b/src/web/static/index.html @@ -476,6 +476,44 @@ html, body { text-align: center; } +/* ============================================================ + Character selector (above input) + ============================================================ */ +.char-selector { + display: flex; + gap: 6px; + padding: 6px 20px; + background: var(--bg-surface); + border-top: 1px solid var(--border); + overflow-x: auto; + scrollbar-width: none; +} +.char-selector::-webkit-scrollbar { display: none; } +.char-selector.hidden { display: none; } +.char-chip { + flex-shrink: 0; + background: var(--bg-elevated); + border: 1px solid var(--border); + border-radius: 12px; + padding: 3px 10px; + font-size: 12px; + color: var(--text-secondary); + cursor: pointer; + transition: border-color 0.2s, color 0.2s, background 0.2s; + white-space: nowrap; +} +.char-chip:hover { border-color: var(--gold-dim); color: var(--text-primary); } +.char-chip.active { + background: var(--gold-dim); + border-color: var(--gold); + color: #fff; +} +.char-chip.dead { + opacity: 0.3; + text-decoration: line-through; + cursor: not-allowed; +} + /* ============================================================ Input Area ============================================================ */ @@ -717,6 +755,15 @@ html, body { .msg { max-width: 95%; } .msg-dm-narration { font-size: 15px; } + .char-selector { + padding: 4px 12px; + gap: 4px; + } + .char-chip { + font-size: 11px; + padding: 3px 8px; + } + #input-area { padding: 10px 12px; padding-bottom: max(12px, env(safe-area-inset-bottom)); @@ -825,6 +872,7 @@ html, body {
Benvenuto, avventuriero. Scrivi la tua azione per iniziare.
+
@@ -843,6 +891,7 @@ let ws = null; let turn = 0; let reconnectTimer = null; let currentAreaId = ''; +let selectedCharId = null; let state = { characters: [], quests: [], areas: [] }; const HOST = window.location.host; @@ -1070,6 +1119,29 @@ function renderChar(chars) { if (dead.length > 0) { charDisplay.innerHTML += `
`; } + renderCharSelector(chars); +} + +function renderCharSelector(chars) { + const container = document.getElementById('char-selector'); + const alive = chars.filter(c => c.status !== 'dead'); + if (alive.length <= 1) { + container.classList.add('hidden'); + selectedCharId = null; + return; + } + container.classList.remove('hidden'); + container.innerHTML = alive.map(c => ` + + `).join(''); +} + +function selectChar(id) { + selectedCharId = selectedCharId === id ? null : id; + // Re-render selector to update active state + renderCharSelector(state.characters); + input.focus(); } async function replaceFunnelDead() { @@ -1238,14 +1310,22 @@ function sendAction() { const text = input.value.trim(); if (!text || !ws || ws.readyState !== WebSocket.OPEN) return; + const chars = state.characters || []; + const sel = selectedCharId ? chars.find(c => c.id === selectedCharId) : null; + + // Prepend selected character name if any + let actionText = text; + if (sel) actionText = `${sel.name}: ${text}`; + // Optimistic display const div = document.createElement('div'); div.className = 'msg player'; - div.innerHTML = `
⚔️ Azione
${escapeHtml(text)}
`; + const label = sel ? `⚔️ ${sel.name}` : '⚔️ Azione'; + div.innerHTML = `
${label}
${escapeHtml(text)}
`; chat.appendChild(div); scrollChat(); - ws.send(JSON.stringify({ type: 'action', payload: text })); + ws.send(JSON.stringify({ type: 'action', payload: actionText })); input.value = ''; input.focus(); }