From 9dee01d393a4a4751beee6baf6374ce82de1ce1e Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Wed, 16 Sep 2026 22:13:44 -0400 Subject: [PATCH] The help becomes a reference desk: searchable, linkable, and it remembers its place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rules tab is one body — the condensed rules, the rulebook, the expansion, and the FAQ — with a contents list, a search that labels each hit by source, and a link on every section. The rulebook's emphasis renders, and the (6E: …) notes are set apart from the original words. The card library gains filters for set, type, and target, ranks an exact name first, counts its results, and says so when there are none; a card opens as an entry: the card itself beside reading-size text with the printed wording, deck counts, the official FAQ, this table's rulings, and links to the cards it names. The house rulings are organized by card, in the present tense, with the revision history kept off the page. Every page and card has a link (/?help=rules/…, /?card=…) that opens the desk to it, and the desk reopens on the tab, search, and scroll position it was closed at. About leads with the worn box and the friends, shows the workshops as a small gallery with a direct link to the demo reel, and says plainly what an online ledger keeps and what a passed-around device keeps to itself. The tally leads with finished games, treasure and combat wins, and hours played, and explains that wizards are counted by name and table time by the clock between moves. Escape closes only the foremost layer and returns focus to the card that opened it; the tabs wrap on a phone instead of clipping. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG --- packages/web/src/App.svelte | 40 +- packages/web/src/Help.svelte | 684 ++++++++++++++++++++++----------- packages/web/src/help-state.ts | 21 + packages/web/src/reference.ts | 296 ++++++++++++++ 4 files changed, 816 insertions(+), 225 deletions(-) create mode 100644 packages/web/src/help-state.ts create mode 100644 packages/web/src/reference.ts diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 557a721..45e7555 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -9,6 +9,7 @@ import Faq from "./Faq.svelte"; import Card from "./Card.svelte"; import Help from "./Help.svelte"; + import type { HelpTab } from "./help-state"; import Replay from "./Replay.svelte"; import FxGallery from "./FxGallery.svelte"; import TokenGallery from "./TokenGallery.svelte"; @@ -257,7 +258,39 @@ let faqCardId = $state(null); /** A discard-pile card enlarged above the pile. */ let discardPeek = $state(null); - let helpTab = $state<"play" | "rules" | "cards" | "rulings" | "about" | "tally">("play"); + /** The help's opening tab; null reopens it where the reader left off. */ + let helpTab = $state(null); + let helpAnchor = $state(null); + let helpCard = $state(null); + // A link into the reference — /?help=rules/rulebook-line-of-sight or + // /?card=force-field — opens the desk to that page. + { + const q = new URLSearchParams(location.search); + const card = q.get("card"); + const help = q.get("help"); + const tabs: HelpTab[] = ["play", "rules", "cards", "rulings", "about", "tally"]; + if (card) { helpCard = card; showHelp = true; } + else if (help) { + const [t, ...rest] = help.split("/"); + if (tabs.includes(t as HelpTab)) { helpTab = t as HelpTab; helpAnchor = rest.join("/") || null; showHelp = true; } + } + } + /** The address bar follows the reader through the help, so any page is a link. */ + function helpNavigate(tab: HelpTab, anchor: string | null, card: string | null) { + const u = new URL(location.href); + u.searchParams.delete("help"); u.searchParams.delete("card"); + if (card) u.searchParams.set("card", card); + else u.searchParams.set("help", anchor ? `${tab}/${anchor}` : tab); + // The slash stays a slash: /?help=rules/the-cards reads as a path should. + u.search = u.searchParams.toString().replace(/%2F/g, "/"); + history.replaceState(null, "", u); + } + function helpClose() { + showHelp = false; helpAnchor = null; helpCard = null; helpTab = null; + const u = new URL(location.href); + u.searchParams.delete("help"); u.searchParams.delete("card"); + history.replaceState(null, "", u); + } let hotseatCount = $state(2); let setupName = $state(""); let setupColor = $state(0); @@ -1695,7 +1728,7 @@ {/if} - @@ -1969,7 +2002,8 @@ {/if} {#if showHelp} - net.requestStats()} onclose={() => (showHelp = false)} /> + net.requestStats()} onnavigate={helpNavigate} onclose={helpClose} /> {/if} {#if view?.phase === "finished" && view.winner && victoryCurtain && !victorySeen} diff --git a/packages/web/src/Help.svelte b/packages/web/src/Help.svelte index f1ff169..ea97ca0 100644 --- a/packages/web/src/Help.svelte +++ b/packages/web/src/Help.svelte @@ -1,130 +1,231 @@ -{#if libPeek} -
(libPeek = null)} onkeydown={() => {}}> -
- (faqCardId = id)} /> +{#if entryDef} + {/if} -{#if faqCardId} - (faqCardId = null)} /> -{/if} - -
{}}> -