Download index.html from cackerman/tom_game: direct link, hf CLI and curl.
- Browser
- Download file 3.46 kB
-
https://huggingface.co/spaces/cackerman/tom_game/resolve/main/index.html
- Command line
-
hf download hf://spaces/cackerman/tom_game/index.html
-
curl -L -o index.html https://huggingface.co/spaces/cackerman/tom_game/resolve/main/index.html
3.46 kB
| <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> |