# Egyptian Escape Room — Digital Lock A touch-friendly React escape room for homeschool: artifact room door lock (`/`) and museum path drag puzzle (`/museum-path`). ## Quick start (local) ```bash npm install ./scripts/setup-dev-media.sh npm run dev ``` Open http://localhost:5173. For iPad testing on your LAN: `npm run dev -- --host`. ## Test checklist **Puzzle 1** (`/`) - Wrong code → jiggle + error banner - Correct code (`8`, `9`, `10`, `6`, `8`, `6`, `12`, `4` in order) → confetti → unlock video → map fade-in + **HELP TED ESCAPE** **Puzzle 2** (`/museum-path`) - Refresh stays on puzzle 2 (does not reset puzzle 1) - Drag 8 artifacts into ordered slots (touch + mouse) - Wrong path + CHECK PATH → error feedback - Correct path → confetti + “Ted escaped the museum!” ## Media Media is copied from the Egyptian Escape Room documents folder. `setup-dev-media.sh` also stitches the puzzle 5 map from `EgyptEscapeRoomPrintables.pdf` (pages 9–10). Requires `pip install pymupdf` once. Dev setup: ```bash ./scripts/setup-dev-media.sh ``` Config lives at `public/media/config.json` (see `config.json.example`). ### Map artifacts (puzzle 5) The 16 display images on PDF pages 9–10 are embedded in the printables PDF and can be extracted individually: ```bash pip install -r scripts/requirements.txt # once python3 scripts/extract-artifact-images.py "/path/to/EgyptEscapeRoomPrintables.pdf" ``` Output: `public/media/artifacts/*.png` and `manifest.json` (slug, display name, dimensions). Use `--mode embedded` for raw JPEG/PNG bytes from the PDF instead of page renders. ## Docker (local smoke test) ```bash ./scripts/setup-dev-media.sh docker compose -f docker-compose.local.yml up --build ``` → http://localhost:8080 ## Deploy to homelab See [AGENTS.md](AGENTS.md) for stack name, hostname, and appdata paths. ```bash # On Unraid ./scripts/setup-prod-media.sh # Build and push ./scripts/build-push.sh ``` Then deploy the `egyptian-escape-room` Portainer stack from the homelab compose snapshot. **URL:** https://egyptian-escape-room.locklear.net (LAN only)