feat(chess): drop-target hover indicator + personalized turn banner

Board now renders three visual states during a drag: faint dot for
legal quiet-move targets, ring for legal capture targets, and a bright
emerald fill when the cursor is actually over a valid drop square so
the user sees exactly where the piece will land. Hovering an invalid
square shows a subtle red tint telling the player the drag will snap
back on release.

Turn indicator shows "Your turn" (with a pulsing emerald dot and
green card treatment) when it`s the local player`s turn in
multiplayer, and "Opponent`s turn" otherwise. Solo play falls back
to the neutral "White`s turn" / "Black`s turn" phrasing.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-17 14:41:01 -06:00
commit ae87772277
No known key found for this signature in database
3 changed files with 148 additions and 15 deletions

View file

@ -270,8 +270,11 @@ test("multiplayer: two contexts, reconnect at move 7, Scholar's Mate checkmate",
await drag(pageB, 'b8', 'c6'); // 6. black Nc6
await expect(pageA.locator('[data-square="c6"] [data-piece="black-knight"]')).toBeVisible();
await expect(pageA.locator('[data-testid="turn-indicator"]')).toContainText('White');
await expect(pageB.locator('[data-testid="turn-indicator"]')).toContainText('White');
// After 6 half-moves the turn returns to white. In multiplayer the
// indicator is personalized: the white player (pageA) sees "Your
// turn"; the black player (pageB) sees "Opponent's turn".
await expect(pageA.locator('[data-testid="turn-indicator"]')).toContainText("Your turn");
await expect(pageB.locator('[data-testid="turn-indicator"]')).toContainText("Opponent's turn");
// ── Step 4: ctx B disconnects (simulates network drop) ───────────────────
await pageB.close();
@ -279,7 +282,8 @@ test("multiplayer: two contexts, reconnect at move 7, Scholar's Mate checkmate",
// ── Step 5: ctx A plays move 7 (Qh5) during B's grace window ─────────────
await drag(pageA, 'd1', 'h5'); // 7. white Qh5
await expect(pageA.locator('[data-square="h5"] [data-piece="white-queen"]')).toBeVisible();
await expect(pageA.locator('[data-testid="turn-indicator"]')).toContainText('Black');
// After Qh5 it's black to move. PageA is white → sees "Opponent's turn".
await expect(pageA.locator('[data-testid="turn-indicator"]')).toContainText("Opponent's turn");
// ── Step 6: ctx B reconnects ─────────────────────────────────────────────
// A new page in the SAME browser context inherits sessionStorage