feat(web): add undo button directly on the game board
Build and Deploy / build-and-push (push) Successful in 4m37s
Build and Deploy / build-and-push (push) Successful in 4m37s
Places a floating "↩ Undo" button at the bottom-right of the green felt surface so it is visible without looking in the header. Both the board button and the header button share the same handler; both track undo_stack_len and disable when nothing can be undone. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,16 @@ button:disabled { opacity: 0.4; cursor: default; }
|
|||||||
|
|
||||||
/* ── Board ───────────────────────────────────────────────────────────── */
|
/* ── Board ───────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.board-undo {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 50;
|
||||||
|
font-size: 15px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -108,6 +118,7 @@ main {
|
|||||||
|
|
||||||
/* Full-bleed felt surface — flex:1 reliably fills main's remaining height. */
|
/* Full-bleed felt surface — flex:1 reliably fills main's remaining height. */
|
||||||
#board {
|
#board {
|
||||||
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: var(--felt);
|
background: var(--felt);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
<main>
|
<main>
|
||||||
<section id="board">
|
<section id="board">
|
||||||
<div id="card-area"></div>
|
<div id="card-area"></div>
|
||||||
|
<button id="btn-board-undo" class="board-undo" title="Undo (Z)" disabled>↩ Undo</button>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ const hudTimer = document.getElementById("hud-timer");
|
|||||||
const hudStock = document.getElementById("hud-stock");
|
const hudStock = document.getElementById("hud-stock");
|
||||||
const hudSeed = document.getElementById("hud-seed");
|
const hudSeed = document.getElementById("hud-seed");
|
||||||
const btnUndo = document.getElementById("btn-undo");
|
const btnUndo = document.getElementById("btn-undo");
|
||||||
|
const btnBoardUndo = document.getElementById("btn-board-undo");
|
||||||
const btnNew = document.getElementById("btn-new");
|
const btnNew = document.getElementById("btn-new");
|
||||||
const chkDraw3 = document.getElementById("chk-draw3");
|
const chkDraw3 = document.getElementById("chk-draw3");
|
||||||
const btnTheme = document.getElementById("btn-theme");
|
const btnTheme = document.getElementById("btn-theme");
|
||||||
@@ -244,6 +245,7 @@ function render(s) {
|
|||||||
hudMoves.textContent = `Moves: ${s.move_count}`;
|
hudMoves.textContent = `Moves: ${s.move_count}`;
|
||||||
if (hudStock) hudStock.textContent = `Stock: ${s.stock.length}`;
|
if (hudStock) hudStock.textContent = `Stock: ${s.stock.length}`;
|
||||||
btnUndo.disabled = s.undo_stack_len === 0;
|
btnUndo.disabled = s.undo_stack_len === 0;
|
||||||
|
btnBoardUndo.disabled = s.undo_stack_len === 0;
|
||||||
|
|
||||||
const visible = new Map();
|
const visible = new Map();
|
||||||
const addPile = (name, cards) =>
|
const addPile = (name, cards) =>
|
||||||
@@ -385,10 +387,9 @@ function flashIllegal(cardIds) {
|
|||||||
|
|
||||||
// ── Input ─────────────────────────────────────────────────────────────────────
|
// ── Input ─────────────────────────────────────────────────────────────────────
|
||||||
function attachHandlers() {
|
function attachHandlers() {
|
||||||
btnUndo.addEventListener("click", () => {
|
const doUndo = () => { const r = game.undo(); if (r.ok) render(r.snapshot); };
|
||||||
const r = game.undo();
|
btnUndo.addEventListener("click", doUndo);
|
||||||
if (r.ok) render(r.snapshot);
|
btnBoardUndo.addEventListener("click", doUndo);
|
||||||
});
|
|
||||||
btnNew.addEventListener("click", () => startGame(randomSeed()));
|
btnNew.addEventListener("click", () => startGame(randomSeed()));
|
||||||
btnWinNew.addEventListener("click", () => startGame(randomSeed()));
|
btnWinNew.addEventListener("click", () => startGame(randomSeed()));
|
||||||
chkDraw3.addEventListener("change", () => {
|
chkDraw3.addEventListener("change", () => {
|
||||||
@@ -404,7 +405,7 @@ function attachHandlers() {
|
|||||||
|
|
||||||
document.addEventListener("keydown", (e) => {
|
document.addEventListener("keydown", (e) => {
|
||||||
if (e.target.tagName === "INPUT") return;
|
if (e.target.tagName === "INPUT") return;
|
||||||
if (e.key === "z" || e.key === "Z") { const r = game.undo(); if (r.ok) render(r.snapshot); }
|
if (e.key === "z" || e.key === "Z") doUndo();
|
||||||
if (e.key === "n" || e.key === "N") startGame(randomSeed());
|
if (e.key === "n" || e.key === "N") startGame(randomSeed());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user