Render the board from pushed state

Clients now render from the state delivered with each move/update and
drop stale or echoed updates via a ply-version guard, instead of issuing
a full game-state fetch per event. Adds captured-piece trays, a material
advantage badge, a move list, a status line, and the mobile menu toggle.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Josh-Heaps
2026-06-10 16:58:22 -06:00
co-authored by Claude Opus 4.8
parent e8a3bfe432
commit c92c46e6d4
6 changed files with 187 additions and 28 deletions
@@ -11,8 +11,8 @@ const ChessSignalR = {
console.error("❌ SignalR connection closed:", err?.message);
});
this.connection.on("ReceiveMoveUpdate", async (gameId, moveDto, moveResultDto) => {
await this.handleMoveUpdate(gameId, moveDto, moveResultDto);
this.connection.on("ReceiveMoveUpdate", async (gameId, moveDto, moveResultDto, state) => {
await this.handleMoveUpdate(gameId, moveDto, moveResultDto, state);
});
this.connection.on("ReceiveGameOver", async (gameId, winner, reason) => {
@@ -28,12 +28,14 @@ const ChessSignalR = {
}
},
async handleMoveUpdate(gameId, moveDto, moveResultDto) {
async handleMoveUpdate(gameId, moveDto, moveResultDto, state) {
if (gameId !== GameState.currentGameId) return;
const gameState = await ChessAPI.getGameState(gameId);
// Drop the echo of our own move and any out-of-order delivery.
if (!GameState.shouldApply(state?.version)) return;
GameState.setPreviousMove([moveDto.sourceRow, moveDto.sourceCol], [moveDto.targetRow, moveDto.targetCol]);
ChessBoard.renderPieces(gameState.pieces);
ChessBoard.renderState(state);
ChessAPI.alertGameStatusChange(moveResultDto);
},
@@ -50,12 +52,6 @@ const ChessSignalR = {
await this.leaveGame();
},
async notifyMoveMade(moveDto, moveResult) {
if (this.connection) {
await this.connection.invoke("MoveMade", GameState.currentGameId, moveDto, moveResult);
}
},
async leaveGame() {
if (this.connection) {
await this.connection.invoke("LeaveWebsocketGroup", GameState.currentGameId);