File size: 3,461 Bytes
720ecf7
5e45c59
720ecf7
 
 
91e6663
720ecf7
91e6663
720ecf7
91e6663
720ecf7
 
 
 
 
 
 
 
 
 
 
 
 
 
91e6663
720ecf7
91e6663
720ecf7
 
 
 
 
d3d402f
 
5e45c59
 
d3d402f
5e45c59
 
d3d402f
720ecf7
 
 
d3d402f
720ecf7
91e6663
 
 
5e45c59
 
91e6663
 
 
5e45c59
91e6663
5e45c59
 
 
91e6663
5e45c59
91e6663
5e45c59
 
 
 
91e6663
 
 
 
5e45c59
91e6663
5e45c59
720ecf7
 
5e45c59
d3d402f
5e45c59
720ecf7
5e45c59
720ecf7
d3d402f
720ecf7
 
 
d3d402f
5e45c59
d3d402f
5e45c59
720ecf7
 
91e6663
 
 
720ecf7
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!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>