diff --git a/public/game.html b/public/game.html index 391613b..16f5662 100644 --- a/public/game.html +++ b/public/game.html @@ -371,6 +371,30 @@ logs... { + el.classList.remove("last-move-dest"); + el.classList.remove("last-move-source"); + }); + } catch (_) {} + try { + const prevCircles = boardEl.querySelectorAll(".last-move-circle"); + prevCircles.forEach((c) => c.remove()); + } catch (_) {} + + if (data && data.boardState) renderBoardFromState(data.boardState); if (data && data.boardState) updateTurnBanner(data.boardState); + + try { + // only highlight the destination square (not the source). create a proper .last-move-circle + if (data && data.from && data.to && data.from !== data.to) { + const dest = boardEl.querySelector(`.square[data-square="${data.to}"]`); + if (dest) { + dest.classList.add("last-move-dest"); + } + const fromEl = boardEl.querySelector(`.square[data-square="${data.from}"]`); + if (fromEl) { + fromEl.classList.add("last-move-source"); + } + // persist last move per room so a refresh can restore highlights + try { + if (roomId) { + const key = `cn:lastMove:${roomId}`; + localStorage.setItem(key, JSON.stringify({ from: data.from, to: data.to, ts: Date.now() })); + } + } catch (e) {} + } + } catch (_) {} } catch (e) { console.warn("move:moved handler error", e); } @@ -1412,7 +1469,20 @@ logs... (s.innerHTML = "")); + squares.forEach((s) => { + // remove any last-move visual state from previous turns + try { + s.classList.remove("last-move-dest"); + s.classList.remove("last-move-source"); + } catch (_) {} + // remove any lingering circle markers + try { + const prev = s.querySelectorAll('.last-move-circle'); + prev.forEach((c) => c.remove()); + } catch (_) {} + // clear square contents (pieces/markers) + s.innerHTML = ""; + }); // place pieces according to boardState.pieces (expects algebraic square names) (boardState.pieces || []).forEach((p) => { diff --git a/public/styles/style.css b/public/styles/style.css index f499c78..8e18fc2 100644 --- a/public/styles/style.css +++ b/public/styles/style.css @@ -49,8 +49,8 @@ a:hover{text-decoration:underline} #board{border:1px solid rgba(11,18,32,0.06);position:relative;background-image: url('/assets/chess-pieces/boards/8x8_wood.svg');background-size:cover} /* square cells and piece images (used by renderFen) */ -.square{width:100%;height:100%;display:flex;align-items:center;justify-content:center} -.square img, .piece{max-width:78%;max-height:78%;user-select:none} +.square{width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden} +.square img, .piece{max-width:78%;max-height:78%;user-select:none;position:relative;z-index:3} /* marker shown on target squares for legal moves */ .square{position:relative} @@ -245,4 +245,23 @@ button:hover{transform:translateY(-3px);transition:all 180ms ease} } #board.shuffle-play .square img{ transition: transform 700ms cubic-bezier(.2,.9,.2,1); transform: rotate(2deg) scale(0.995); filter: drop-shadow(0 6px 18px rgba(0,0,0,0.06)); } +/* overlay that fills the whole square (subtle green) so the entire cell is highlighted */ +.square.last-move-dest::after{ + content: ''; + position: absolute; + left: 0; top: 0; right: 0; bottom: 0; + background: rgb(164, 229, 208); + pointer-events: none; + z-index: 2; +} + +/* subtle highlight for the source square of the last move (less prominent) */ +.square.last-move-source::after{ + content: ''; + position: absolute; + left: 0; top: 0; right: 0; bottom: 0; + background: rgb(164, 229, 208); + pointer-events: none; + z-index: 2; +}