The front page and the about page point to the other games of the Kestrel's Nest

The list is the kit's family.json, shipped as /family.json and refreshed
at deploy when the kit is beside this checkout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-23 12:26:49 -04:00
co-authored by Claude Fable 5.1
parent 13a6fc2603
commit 9d087ccaa1
5 changed files with 77 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
{
"family": "the Kestrel's Nest",
"games": [
{
"slug": "wizwar",
"name": "Wiz-War",
"url": "https://wizwar.kestrelsnest.social",
"pitch": "Steal treasure. Blast through walls. Outwit your friends.",
"players": "2 to 6",
"origin": "Tom Jolly's board game, sixth edition, 1993"
},
{
"slug": "waving-hands",
"name": "Waving Hands",
"url": "https://hands.kestrelsnest.social",
"pitch": "Duelling wizards. Two hands each. The right run of gestures is a spell.",
"players": "2 to 12",
"origin": "Richard Bartle's pencil-and-paper duel, 1977"
}
]
}
+12
View File
@@ -10,6 +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 { summarizeMissed } from "./catchup";
import Replay from "./Replay.svelte";
import FxGallery from "./FxGallery.svelte";
@@ -26,6 +27,11 @@
net.startGamePolling();
let name = $state(prefs.wizardName);
/** The other games of the Kestrel's Nest, for the front page. */
let family = $state<FamilyGame[]>([]);
$effect(() => {
void otherGames("wizwar").then((g) => (family = g));
});
let joinCode = $state("");
let claimPhrase = $state("");
/** A /join/<CODE> recruiting link. The visitor lands looking at the
@@ -2561,6 +2567,10 @@
<img src="/hero.jpg" alt="Through a wizard's eyes: an ambush watches its corridor through a wormhole" />
</a>
<a class="about-link clips-link" href="/clips">watch clips from the labyrinth</a>
{#if family.length}
<p class="family-line">Also at the Kestrel's Nest:
{#each family as g, i (g.slug)}{i > 0 ? ", " : " "}<a href={g.url} title={g.pitch}>{g.name}</a>{/each}</p>
{/if}
</div>
<details class="more-ways" open={local.hasSave()}>
@@ -4108,6 +4118,8 @@
margin-bottom: 1.2rem;
}
.about-link:hover { color: #43331f; }
.family-line { margin: 0.5rem 0 0; font-size: 0.85rem; color: #7b6a51; }
.family-line a { color: #5a4630; }
.clips-link { display: inline-block; margin: 0 0 0.9rem; }
.field { display: block; text-align: left; margin-bottom: 1rem; }
.field span {
+17
View File
@@ -3,6 +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 { cardDef } from "@wizwar/engine";
import { tick } from "svelte";
import Card from "./Card.svelte";
@@ -127,6 +128,11 @@
if (initialAnchor) void jumpTo(initialAnchor);
else if (bodyEl) bodyEl.scrollTop = helpState.scroll[tab] ?? 0;
});
/** The other games of the Kestrel's Nest, for the about page. */
let family = $state<FamilyGame[]>([]);
$effect(() => {
void otherGames("wizwar").then((g) => (family = g));
});
</script>
{#snippet section(s: RefSection, withSource: boolean)}
@@ -311,6 +317,14 @@
The keeper of this table roosts at
<a href="https://kestrelsnest.social" target="_blank" rel="noreferrer">kestrelsnest.social</a>.
</p>
{#if family.length}
<h3>More games at the Kestrel's Nest</h3>
<ul class="family">
{#each family as g (g.slug)}
<li><a href={g.url}>{g.name}</a>: {g.pitch} <span class="family-note">{g.players} players; {g.origin}.</span></li>
{/each}
</ul>
{/if}
<p class="colophon">
Wiz-War © Tom Jolly. Sixth edition published by Chessex, 1993.
Built by Eric and a very enthusiastic AI, 2026.
@@ -691,4 +705,7 @@
.entry-card :global(.card:hover) { transform: none; }
.contents { columns: 1; }
}
.family { margin: 0.4rem 0 0.8rem 1.2rem; padding: 0; }
.family li + li { margin-top: 0.3rem; }
.family-note { color: #7b6a51; }
</style>
+23
View File
@@ -0,0 +1,23 @@
// The other games of the Kestrel's Nest, 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.
export interface FamilyGame {
slug: string;
name: string;
url: string;
pitch: string;
players: string;
origin: string;
}
export async function otherGames(self: string): Promise<FamilyGame[]> {
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);
} catch {
return [];
}
}