// screens_menu.jsx — Player Menu window.S = window.S || {}; (() => { const { useState, useRef, useEffect } = React; const { T, panelStyle, Btn, Tooltip, NarrativePanel, IconFloppy, IconContinue, IconPlay, IconFileTransfer, IconSearch, IconJournal, IconTalking } = window.UI; const A = window.E.A; const L = window.L; // ── Feedback form as a separate component ──────────────────── const FeedbackPanel = ({ popPanel, state }) => { const formRef = useRef(null); const [includeSave, setIncludeSave] = useState(false); // Detect itch.io embedding const isOnItchio = window.self !== window.top || window.location.hostname.includes("itch.zone"); useEffect(() => { if (!isOnItchio && formRef.current) { const now = new Date().toISOString(); formRef.current.elements["date"].value = now; formRef.current.elements["os"].value = navigator.platform || "unknown"; formRef.current.elements["browser"].value = navigator.userAgent; formRef.current.elements["url"].value = window.location.href; formRef.current.elements["playtime"].value = state.day + " days"; } }, [isOnItchio]); useEffect(() => { if (!isOnItchio && formRef.current) { formRef.current.elements["saveData"].value = includeSave ? JSON.stringify(state) : ""; } }, [includeSave, isOnItchio]); if (isOnItchio) { return (
← Back

External feedback forms are not supported inside itch.io. Please use the comments section at the bottom of the itch.io game page to share your feedback. Thank you!

); } return (
← Back
setIncludeSave(e.target.checked)} />
); }; // ── Simple markdown‑to‑JSX for changelog ──────────────────── const renderChangelogContent = (md) => { const lines = md.split("\n"); const elements = []; let i = 0; while (i < lines.length) { const line = lines[i]; if (/^###\s/.test(line)) { const heading = line.replace(/^###\s+/, ""); elements.push(
{heading}
); i++; continue; } if (line.trim() === "") { i++; continue; } const processed = line.replace(/\*\*(.*?)\*\*/g, "$1"); elements.push(
); i++; } return elements; }; // ── Main menu modal ───────────────────────────────────────── function MenuModal({ state, dispatch, onClose }) { const [panel, setPanel] = useState("menu"); const [changelogContent, setChangelogContent] = useState(null); const [isNarrow, setIsNarrow] = useState(window.innerWidth < 500); const importRef = useRef(null); useEffect(() => { const h = () => setIsNarrow(window.innerWidth < 500); window.addEventListener("resize", h); return () => window.removeEventListener("resize", h); }, []); useEffect(() => { if (panel === "changelog" && !changelogContent) { fetch("docs/changelog.md") .then(r => r.text()) .then(md => setChangelogContent(md)) .catch(() => setChangelogContent("*Changelog not available.*")); } }, [panel]); const handleImport = (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = () => { dispatch({ type: A.IMPORT_SAVE, fileContent: reader.result }); onClose(); }; reader.readAsText(file); e.target.value = ""; }; const pushPanel = (p) => setPanel(p); const popPanel = () => setPanel("menu"); const closeAll = () => { setPanel("menu"); onClose(); }; // ── Panel renderers ───────────────────────────────────── const renderMenu = () => ( <>
Resume { dispatch({ type: A.SAVE_GAME }); closeAll(); }}> Save Game {L.hasSave() && ( { dispatch({ type: A.LOAD_GAME }); closeAll(); }}> Load Game )} pushPanel("newGame")}> Start New Game { dispatch({ type: A.EXPORT_SAVE }); closeAll(); }}> Export Save importRef.current?.click()}> Import Save
{/* Auto‑save toggle */}
dispatch({ type: A.TOGGLE_AUTO_SAVE })} /> Auto‑save on port entry
Reference & Community
window.open("handbook.html", "_blank")}> Captain's Handbook pushPanel("changelog")}> Changelog pushPanel("feedback")}> Send Feedback
{/* GitHub */} GitHub repo {/* itch.io */} Play on itch.io {/* Ko‑fi */} Buy Me a Coffee at ko-fi.com Not expected but appreciated
); const renderNewGame = () => (
Are you sure? Your current browser‑stored save will be lost. If you want to keep it, use the Export Game button.
{ dispatch({ type: A.NAVIGATE, screen: "newgame" }); closeAll(); }}>Yes, start a new game Cancel
); const renderChangelog = () => (
← Back
{changelogContent ? renderChangelogContent(changelogContent) :
Loading...
}
); // ── Main render ───────────────────────────────────────── return (
e.stopPropagation()}>
{panel === "menu" ? "Menu" : panel === "changelog" ? "Changelog" : panel === "feedback" ? "Send Feedback" : panel === "newGame" ? "Start New Game" : ""}
{panel === "menu" && renderMenu()} {panel === "newGame" && renderNewGame()} {panel === "changelog" && renderChangelog()} {panel === "feedback" && }
); } window.S.MenuModal = MenuModal; })();