Add Phase 1 MVP: D&D text-adventure server

FastAPI + Postgres backend with fastapi-users auth, games/characters
CRUD, and a WebSocket chat endpoint where an x.ai Grok DM narrates
play, rolls dice, and maintains character sheets via tool calls.
React + Tailwind frontend covers registration/login, game list and
creation, participation-code join flow, character viewer, and a
real-time chat session view. Docker Compose skeleton (Postgres +
backend) included; Caddy/frontend container wiring is deferred to
the deploy milestone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Thorsten
2026-08-31 18:43:08 +02:00
commit f37dc9fa76
75 changed files with 106988 additions and 0 deletions
@@ -0,0 +1,40 @@
import { useEffect, useRef } from "react";
import { Message } from "../api/games";
function senderLabel(message: Message): string {
if (message.sender_type === "dm") return "Dungeon Master";
if (message.sender_type === "system") return "System";
if (message.character_name) return `${message.player_name} (${message.character_name})`;
return message.player_name ?? "Spieler";
}
export default function TranscriptPane({ messages }: { messages: Message[] }) {
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
bottomRef.current?.scrollIntoView({ block: "end" });
}, [messages]);
return (
<div className="flex h-full flex-col gap-3 overflow-y-auto p-4">
{messages.map((m) => (
<div key={m.id} className={m.sender_type === "system" ? "text-red-400 italic" : m.sender_type === "dm" ? "text-slate-100" : "text-slate-300"}>
<span
className={
m.sender_type === "dm"
? "font-semibold text-amber-400"
: m.sender_type === "system"
? "font-semibold text-red-400"
: "font-semibold text-sky-400"
}
>
{senderLabel(m)}:
</span>{" "}
<span className="whitespace-pre-wrap">{m.content}</span>
</div>
))}
<div ref={bottomRef} />
</div>
);
}