import React, { useEffect, useState } from "react"; import { newGame, castSpell } from "./api.js"; export default function App() { const [snap, setSnap] = useState(null); const [selected, setSelected] = useState([]); const [spell, setSpell] = useState(null); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const start = async () => { setError(null); setSpell(null); setSelected([]); try { setSnap(await newGame()); } catch (e) { setError(`Backend unreachable. Start it with: uv run uvicorn api.server:app --port 8000`); } }; useEffect(() => { start(); }, []); const toggleRune = (key) => { setSelected((cur) => { if (cur.includes(key)) return cur.filter((k) => k !== key); if (cur.length >= 4) return cur; return [...cur, key]; }); }; const cast = async () => { if (!snap || selected.length === 0 || busy) return; setBusy(true); setError(null); try { const res = await castSpell(snap.session_id, selected); setSpell(res.spell); setSnap(res); setSelected([]); } catch (e) { setError(String(e)); } finally { setBusy(false); } }; if (!snap) { return (
{error ?? "Summoning the dungeon…"}
Draw forbidden spells. Regret efficiently.
weak: {enemy.weakness.join(", ")} · resists: {enemy.resistance.join(", ")}
mood: {enemy.mood}
{room.name}
score: {snap.score} · 🎒 {player.inventory.join(", ") || "empty"}
{spell.flavor}
{spell.effect}
{spell.side_effect &&⚠ {spell.side_effect}
}enemy {spell.enemy_hp_delta} · player {spell.player_hp_delta} · chaos {spell.chaos}/10
{log.join("\n")}
{error && {error}
}