From c04e82e0852c3885e233d33be92ae0de1d900db6 Mon Sep 17 00:00:00 2001
From: Eric Wagoner
Date: Wed, 23 Sep 2026 21:04:29 -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
---
src/lib/components/Hall.svelte | 10 +++++++---
src/lib/family.ts | 19 +++++++++++++------
static/family.json | 1 +
3 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/src/lib/components/Hall.svelte b/src/lib/components/Hall.svelte
index a8a0346..a244e4c 100644
--- a/src/lib/components/Hall.svelte
+++ b/src/lib/components/Hall.svelte
@@ -3,7 +3,7 @@
// bot, open a table, join by code, the ledger of your tables, and the
// about panel with the rules, the guide and a way to reach the keeper.
import { goto } from '$app/navigation';
- import { otherGames, type FamilyGame } from '$lib/family';
+ import { loadFamily, type FamilyGame } from '$lib/family';
import Preferences from './Preferences.svelte';
import { prefs, setPref } from '$lib/prefs.svelte';
import { allSeats, doubtSeat, forgetSeat, rememberSeat, ServerError, trustSeat } from '$lib/net/client';
@@ -35,8 +35,12 @@
const hours = (minutes: number) => (minutes < 90 ? `${minutes} minutes` : `${Math.round(minutes / 60)} hours`);
/** The other games of Kestrel's Hall, for the about panel. */
let family = $state([]);
+ let hall = $state('');
$effect(() => {
- void otherGames('hnefatafl').then((g) => (family = g));
+ void loadFamily('hnefatafl').then((f) => {
+ family = f.games;
+ hall = f.hall;
+ });
});
/** Reports this browser's seats have filed, with the keeper's replies. */
let reports = $state([]);
@@ -283,7 +287,7 @@
{/if}
{#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/src/lib/family.ts b/src/lib/family.ts
index b706a6b..596072d 100644
--- a/src/lib/family.ts
+++ b/src/lib/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: [] };
}
}
diff --git a/static/family.json b/static/family.json
index f9e6d33..48572da 100644
--- a/static/family.json
+++ b/static/family.json
@@ -1,5 +1,6 @@
{
"family": "Kestrel's Hall",
+ "hall": "https://gamehall.kestrelsnest.social",
"games": [
{
"slug": "wizwar",