tom_game / index.html
cackerman's picture
Update index.html
d3d402f verified
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Room Scenario Game</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root { --w: 1000px; }
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 16px; }
#wrap { max-width: var(--w); margin: 0 auto; }
#log { width: 100%; height: 60vh; border: 1px solid #ccc; padding: 12px; overflow: auto; white-space: pre-wrap; }
#row { margin-top: 12px; display: flex; gap: 8px; }
#cmd { flex: 1; padding: 8px; font-size: 16px; }
button { padding: 8px 12px; font-size: 16px; }
#small { color: #666; font-size: 12px; margin-top: 6px; }
</style>
</head>
<body>
<div id="wrap">
<div id="log"></div>
<div id="row">
<input id="cmd" type="text" placeholder="[Press Enter to start]" autocomplete="off" />
<button id="primary">Start</button>
</div>
<div id="small">Enter starts, continues, or submits your move. The button does the same.</div>
</div>
<script>
const log = document.getElementById("log");
const cmd = document.getElementById("cmd");
const btn = document.getElementById("primary");
let SID = "";
let MODE = "awaiting_start"; // authoritative client state
function scrollBottom(){ log.scrollTop = log.scrollHeight; }
function render(s){
if (s.sid) SID = s.sid;
if (s.mode) MODE = s.mode;
log.textContent = s.transcript || "";
btn.textContent = s.primary_label || "Start";
cmd.placeholder = s.placeholder || "";
btn.style.display = (MODE === "awaiting_action") ? "none" : "inline-block";
cmd.focus();
scrollBottom();
}
async function fetchState(){
const r = await fetch(SID ? `/state?sid=${SID}` : "/state");
if (!r.ok) throw new Error("state fetch failed");
return r.json();
}
async function callPrimary(){
btn.disabled = true;
try{
const r = await fetch(SID ? `/primary?sid=${SID}` : "/primary", { method: "POST" });
render(await r.json());
cmd.value = "";
} finally { btn.disabled = false; }
}
async function callAction(text){
btn.disabled = true;
try{
const r = await fetch(SID ? `/action?sid=${SID}` : "/action", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text })
});
render(await r.json());
cmd.value = "";
} finally { btn.disabled = false; }
}
async function init(){
// Single initial state fetch. After that, trust MODE from the last successful response.
render(await fetchState());
btn.addEventListener("click", async () => { await callPrimary(); });
// Enter uses local MODE. No pre-Enter GET /state (prevents the /primary loop).
cmd.addEventListener("keydown", async (e) => {
if (e.key !== "Enter") return;
e.preventDefault();
if (MODE === "awaiting_action"){
await callAction(cmd.value.trim());
} else if (MODE === "awaiting_continue" || MODE === "awaiting_start"){
await callPrimary();
}
});
cmd.focus();
scrollBottom();
}
init();
</script>
</body>
</html>