diff --git a/lyra/poker.py b/lyra/poker.py index 2722e22..da42314 100644 --- a/lyra/poker.py +++ b/lyra/poker.py @@ -1264,7 +1264,7 @@ def timeline(session_id: int | None = None) -> list[dict]: def _session_villains(sid: int) -> list[dict]: """Players read this session, with their standing dossier fields.""" rows = _c().execute( - "SELECT p.name AS name, p.category AS category, p.tendencies AS tendencies, " + "SELECT p.id AS id, p.name AS name, p.category AS category, p.tendencies AS tendencies, " "p.adjustment AS adjustment, " "(SELECT note FROM player_reads r2 WHERE r2.player_id = p.id " " AND r2.session_id = ? ORDER BY r2.id DESC LIMIT 1) AS last_note " diff --git a/lyra/web/static/session.html b/lyra/web/static/session.html index a3b46eb..1cc5a1b 100644 --- a/lyra/web/static/session.html +++ b/lyra/web/static/session.html @@ -114,6 +114,16 @@ li.start .tl-body { color: var(--accent); font-weight: 600; } li.scar .tl-body, li.confidence .tl-body { font-style: italic; } .tl-body a.hand { color: var(--accent); text-decoration: none; white-space: nowrap; } + /* quick-capture (no LLM) + inline correction controls */ + .quick { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; } + .quick input { width: 100px; background: var(--bg-line); border: 1px solid var(--border); + border-radius: 8px; padding: 8px 10px; color: var(--text); } + .quick input:focus { outline: none; border-color: var(--accent); } + .quick button { background: var(--accent); color: #0a0a0a; border: 1px solid var(--accent); + border-radius: 8px; padding: 8px 12px; cursor: pointer; font-weight: 600; } + button.mini { background: none; border: none; color: var(--fade); cursor: pointer; + font-size: .9rem; padding: 0 6px; } + button.mini:active { color: var(--accent); } @@ -217,6 +227,30 @@ } catch(e){ alert('Delete failed: '+e.message); } } + // Quick-capture (no LLM): post a number to a direct endpoint, then refresh. + function numVal(id){ const el = document.getElementById(id); return Number(((el && el.value) || '').replace(/[^0-9.]/g,'')); } + async function postQuick(url, amount){ + const r = await fetch(url, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ amount }) }); + const d = await r.json(); + if(!d.ok){ alert(d.error || 'failed'); return false; } + return true; + } + async function postStack(){ const v = numVal('qStack'); if(v && await postQuick('/session/stack', v)){ document.getElementById('qStack').value=''; refresh(); } } + async function postBuyin(){ const v = numVal('qBuyin'); if(v && await postQuick('/session/buyin', v)){ document.getElementById('qBuyin').value=''; refresh(); } } + async function postCashout(){ + if(!curSession) return; + const v = numVal('qCashout'); if(!v) return; + const r = await fetch('/session/'+curSession.id, { method:'PATCH', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ cash_out: v }) }); + if(!(await r.json()).ok){ alert('failed'); return; } + document.getElementById('qCashout').value=''; refresh(); + } + async function renamePlayer(id, current){ + const name = prompt('Rename player', current || ''); if(!name) return; + const r = await fetch('/player/'+id, { method:'PATCH', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ name }) }); + if(!(await r.json()).ok){ alert('failed'); return; } + refresh(); + } + function render(data){ const s = data.session; if (!s) { @@ -285,7 +319,15 @@ bought in ${money(stack.buy_in)}
${(stack.log||[]).length} update(s)
${sparkline(stack.log || [])} - ${stack.current == null ? '

No stack logged yet — tell Lyra your stack ("I\'m at 350").

' : ''} + ${stack.current == null ? '

No stack logged yet — log it below or tell Lyra ("I\'m at 350").

' : ''} +
+ + + + + + +
@@ -332,6 +374,7 @@ ${villains.length ? ``