Add undo/redo for all encounter actions

Memento-based undo/redo with full encounter snapshots. Undo stack
capped at 50 entries, persisted to localStorage. Triggered via
buttons in the top bar (inboard of turn navigation) and keyboard
shortcuts (Ctrl+Z / Ctrl+Shift+Z, Cmd on Mac, case-insensitive key
matching). Clear encounter resets both stacks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-26 23:30:33 +01:00
parent 9d81c8ad27
commit 17cc6ed72c
22 changed files with 1127 additions and 61 deletions

View File

@@ -1,11 +1,19 @@
import { StepBack, StepForward, Trash2 } from "lucide-react";
import { Redo2, StepBack, StepForward, Trash2, Undo2 } from "lucide-react";
import { useEncounterContext } from "../contexts/encounter-context.js";
import { Button } from "./ui/button.js";
import { ConfirmButton } from "./ui/confirm-button.js";
export function TurnNavigation() {
const { encounter, advanceTurn, retreatTurn, clearEncounter } =
useEncounterContext();
const {
encounter,
advanceTurn,
retreatTurn,
clearEncounter,
undo,
redo,
canUndo,
canRedo,
} = useEncounterContext();
const hasCombatants = encounter.combatants.length > 0;
const isAtStart = encounter.roundNumber === 1 && encounter.activeIndex === 0;
@@ -24,6 +32,29 @@ export function TurnNavigation() {
<StepBack className="h-5 w-5" />
</Button>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
onClick={undo}
disabled={!canUndo}
title="Undo"
aria-label="Undo"
>
<Undo2 className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
onClick={redo}
disabled={!canRedo}
title="Redo"
aria-label="Redo"
>
<Redo2 className="h-4 w-4" />
</Button>
</div>
<div className="flex min-w-0 flex-1 items-center justify-center gap-2 text-sm">
<span className="shrink-0 rounded-md bg-muted px-2 py-0.5 font-semibold text-foreground text-sm">
<span className="-mt-[3px] inline-block">