From 0244cffb4bf0a53faa0101fe6024b70c9141f551 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Wed, 23 Sep 2026 21:04:31 -0400 Subject: [PATCH] The family list knows the hall: about panels link to gamehall.kestrelsnest.social, and a new game gets a card there Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG --- packages/web/public/family.json | 1 + packages/web/src/App.svelte | 10 +++++++--- packages/web/src/Help.svelte | 10 +++++++--- packages/web/src/family.ts | 19 +++++++++++++------ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/packages/web/public/family.json b/packages/web/public/family.json index f9e6d33..48572da 100644 --- a/packages/web/public/family.json +++ b/packages/web/public/family.json @@ -1,5 +1,6 @@ { "family": "Kestrel's Hall", + "hall": "https://gamehall.kestrelsnest.social", "games": [ { "slug": "wizwar", diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 985ee0f..9f5e661 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -10,7 +10,7 @@ import Card from "./Card.svelte"; import Help from "./Help.svelte"; import { HELP_TABS, type HelpTab } from "./help-state"; - import { otherGames, type FamilyGame } from "./family"; + import { loadFamily, type FamilyGame } from "./family"; import { summarizeMissed } from "./catchup"; import Replay from "./Replay.svelte"; import FxGallery from "./FxGallery.svelte"; @@ -29,8 +29,12 @@ let name = $state(prefs.wizardName); /** The other games of Kestrel's Hall, for the front page. */ let family = $state([]); + let hall = $state(""); $effect(() => { - void otherGames("wizwar").then((g) => (family = g)); + void loadFamily("wizwar").then((f) => { + family = f.games; + hall = f.hall; + }); }); let joinCode = $state(""); let claimPhrase = $state(""); @@ -2568,7 +2572,7 @@ watch clips from the labyrinth {#if family.length} -

Also in Kestrel's Hall: +

Also in {#if hall}Kestrel's Hall{:else}Kestrel's Hall{/if}: {#each family as g, i (g.slug)}{i > 0 ? ", " : " "}{g.name}{/each}

{/if} diff --git a/packages/web/src/Help.svelte b/packages/web/src/Help.svelte index e5915b1..6ce5a56 100644 --- a/packages/web/src/Help.svelte +++ b/packages/web/src/Help.svelte @@ -3,7 +3,7 @@ // body, the card library with proper entries, the house rulings, the // story of the table, and the tally. Every section and card has a link // a player can send; the desk remembers where the reader was. - import { otherGames, type FamilyGame } from "./family"; + import { loadFamily, type FamilyGame } from "./family"; import { cardDef } from "@wizwar/engine"; import { tick } from "svelte"; import Card from "./Card.svelte"; @@ -130,8 +130,12 @@ }); /** The other games of Kestrel's Hall, for the about page. */ let family = $state([]); + let hall = $state(""); $effect(() => { - void otherGames("wizwar").then((g) => (family = g)); + void loadFamily("wizwar").then((f) => { + family = f.games; + hall = f.hall; + }); }); @@ -318,7 +322,7 @@ kestrelsnest.social.

{#if family.length} -

More games in Kestrel's Hall

+

More games in {#if hall}Kestrel's Hall{:else}Kestrel's Hall{/if}

    {#each family as g (g.slug)}
  • {g.name}: {g.pitch} {g.players} players; {g.origin}.
  • diff --git a/packages/web/src/family.ts b/packages/web/src/family.ts index ded73dc..cc83776 100644 --- a/packages/web/src/family.ts +++ b/packages/web/src/family.ts @@ -1,6 +1,7 @@ // The other games of Kestrel's Hall, from the family list the kit keeps // (family.json, one copy shipped with every game), so a player who liked one -// table can find the next. This game is left out of its own list. +// table can find the next, and the hall's front door. This game is left out +// of its own list. export interface FamilyGame { slug: string; @@ -11,13 +12,19 @@ export interface FamilyGame { origin: string; } -export async function otherGames(self: string): Promise { +/** The family list: the hall's front door and every game but this one. */ +export interface Family { + hall: string; + games: FamilyGame[]; +} + +export async function loadFamily(self: string): Promise { try { const res = await fetch('/family.json', { cache: 'no-cache' }); - if (!res.ok) return []; - const data = (await res.json()) as { games?: FamilyGame[] }; - return (data.games ?? []).filter((g) => g.slug !== self); + if (!res.ok) return { hall: '', games: [] }; + const data = (await res.json()) as { hall?: string; games?: FamilyGame[] }; + return { hall: data.hall ?? '', games: (data.games ?? []).filter((g) => g.slug !== self) }; } catch { - return []; + return { hall: '', games: [] }; } }