// Undercooked — the help overlay (phase 07, task 05; the task's // `app/static/ui/help.js`, served from `js/ui/` like the HUD). // // The `?`-key in-game help: a body-level, fixed, dismissible panel // listing the key map. Created once at boot, persists across screens // (it is not a screen — the state machine never knows it), hidden by // default. `?` toggles it; `Escape` closes it (the keyboard module's // `Esc` branch defers to it while it is open); a click on the panel's // backdrop or its Close button dismisses it. No external docs needed // mid-game. // // The rows are `getKeyMap()`'s — `input/keyboard.js` re-exports it // (the canonical source) and the README's controls section renders the // same rows (a test keeps them identical: the browser-rendered map // against the README's block). import { el } from "../screens/dom.js"; /** * The key map the help panel (and the README's controls section — a * test keeps them identical) renders: `{ keys, action }` rows in * display order. `keys` are joined with " / " when rendered. */ export function getKeyMap() { return [ { keys: ["W", "A", "S", "D"], action: "move (or the arrow keys)" }, { keys: ["Space", "Enter"], action: "action (pick, work, set down, serve)" }, { keys: ["1", "2", "3", "4"], action: "emote (wave, cheer, thumbs up, thumbs down)" }, { keys: ["?"], action: "help overlay" }, { keys: ["Esc"], action: "close help / leave the room" }, ]; } /** * The help overlay itself. * * `onToggle` — optional `(visible) => void` (the census seam; the * keyboard module's `?`/`Esc` branches need no hook — they call * `toggle()` / `hide()` directly). * * Returns `{ show, hide, toggle, visible }`. */ export function createHelpOverlay({ onToggle = null } = {}) { let panel = null; let visible = false; function build() { if (panel !== null) return panel; const title = el( "div", { class: "help-title" }, el("span", { class: "help-heading", text: "Controls" }), el("button", { class: "btn help-close", type: "button", "aria-label": "Close help", text: "Close", onClick: () => hide(), }) ); const rows = getKeyMap().map((row) => el( "div", { class: "help-row" }, el("kbd", { class: "help-keys", text: row.keys.join(" / ") }), el("span", { class: "help-action", text: row.action }) ) ); panel = el( "div", { id: "help-overlay", class: "help-overlay", role: "dialog", "aria-label": "Help", "aria-hidden": "true", }, el( "div", { class: "panel help-panel", onClick: (e) => e.stopPropagation() }, title, el("div", { class: "help-rows" }, ...rows), el("p", { class: "help-foot", text: "Press ? or Esc to close. On the play screen, Esc leaves the room.", }) ) ); panel.addEventListener("click", () => hide()); return panel; } function show() { const node = build(); if (visible) return; node.setAttribute("aria-hidden", "false"); document.body.append(node); visible = true; if (onToggle !== null) onToggle(true); } function hide() { if (!visible) return; visible = false; if (panel !== null) { panel.setAttribute("aria-hidden", "true"); panel.remove(); } if (onToggle !== null) onToggle(false); } function toggle() { if (visible) hide(); else show(); } return { show, hide, toggle, visible: () => visible }; }