diff --git a/packages/chess/src/app/App.tsx b/packages/chess/src/app/App.tsx
index 6ee56c1..c9ac41c 100644
--- a/packages/chess/src/app/App.tsx
+++ b/packages/chess/src/app/App.tsx
@@ -1,5 +1,6 @@
import { useEffect } from 'react'
import { Routes, Route, useNavigate } from 'react-router-dom'
+import { Lobby } from '../ui/Lobby'
import { GameView } from '../ui/GameView'
import { RulesView } from '../ui/RulesView'
import { SavePanel } from '../ui/SavePanel'
@@ -26,7 +27,7 @@ export function App() {
return (
- } />
+ } />
} />
} />
} />
@@ -66,11 +67,3 @@ function SaveWrapper({ chessState }: { chessState: ReturnType
)
}
-
-function Home() {
- return (
-
- Home
-
- )
-}
\ No newline at end of file
diff --git a/packages/chess/src/ui/Lobby.tsx b/packages/chess/src/ui/Lobby.tsx
new file mode 100644
index 0000000..b84dd2a
--- /dev/null
+++ b/packages/chess/src/ui/Lobby.tsx
@@ -0,0 +1,187 @@
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { GameClient } from '../net/client.js';
+
+const WS_URL = (import.meta as { env?: Record }).env?.["VITE_WS_URL"] ?? "ws://localhost:7357/ws";
+
+export function Lobby() {
+ const [codeInput, setCodeInput] = useState('');
+ const [roomCode, setRoomCode] = useState(null);
+ const [error, setError] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const navigate = useNavigate();
+
+ const handleCreate = async () => {
+ setLoading(true);
+ setError(null);
+ const client = new GameClient(WS_URL);
+
+ client.on("room.created", (e) => {
+ setRoomCode(e.payload.code);
+ sessionStorage.setItem("room-code", e.payload.code);
+ sessionStorage.setItem("room-token", e.payload.token);
+ sessionStorage.setItem("player-color", e.payload.color);
+ setLoading(false);
+ });
+
+ client.on("error", (e) => {
+ setError(e.payload.message);
+ setLoading(false);
+ });
+
+ try {
+ if (typeof client.connectAndCreate === 'function') {
+ await client.connectAndCreate();
+ } else {
+ // Fallback approach if connectAndCreate doesn't exist
+ const ws = new WebSocket(WS_URL);
+ ws.onopen = () => {
+ ws.send(JSON.stringify({ v: 1, seq: 1, ts: Date.now(), type: "room.create", payload: {} }));
+ };
+ ws.onmessage = (e) => {
+ const msg = JSON.parse(e.data);
+ if (msg.type === "room.created") {
+ setRoomCode(msg.payload.code);
+ sessionStorage.setItem("room-code", msg.payload.code);
+ sessionStorage.setItem("room-token", msg.payload.token);
+ sessionStorage.setItem("player-color", msg.payload.color);
+ setLoading(false);
+ }
+ if (msg.type === "error") {
+ setError(msg.payload.message);
+ setLoading(false);
+ }
+ };
+ ws.onerror = (e) => {
+ console.error("WebSocket error:", e);
+ setError("Could not connect to server");
+ setLoading(false);
+ };
+ ws.onclose = (e) => {
+ console.error("WebSocket closed:", e.code, e.reason);
+ if (loading) {
+ setError("Could not connect to server");
+ setLoading(false);
+ }
+ };
+ }
+ } catch {
+ setError("Could not connect to server");
+ setLoading(false);
+ }
+ };
+
+ const handleJoin = async () => {
+ if (!codeInput.trim()) {
+ setError("Enter a room code");
+ return;
+ }
+ setLoading(true);
+ setError(null);
+
+ const client = new GameClient(WS_URL);
+ client.on("room.joined", (e) => {
+ sessionStorage.setItem("room-code", e.payload.code);
+ sessionStorage.setItem("room-token", e.payload.token);
+ sessionStorage.setItem("player-color", e.payload.color);
+ navigate("/game");
+ });
+
+ client.on("error", (e) => {
+ setError(e.payload.message || "Invalid room code");
+ setLoading(false);
+ });
+
+ try {
+ await client.connect(codeInput.trim().toUpperCase(), "");
+ } catch {
+ setError("Could not connect");
+ setLoading(false);
+ }
+ };
+
+ return (
+
+
+
+
Chess
+
Play realtime multiplayer
+
+
+
+ {/* Create Room Section */}
+
+ New Game
+
+ {!roomCode ? (
+
+ ) : (
+
+
+ Room code:
+
+ {roomCode}
+
+
+
+
+ )}
+
+
+
+
+
+ {/* Join Room Section */}
+
+
+
+ {/* Error Display */}
+ {error && (
+
+ )}
+
+
+ );
+}