The hall puts games in play first for someone coming back, finished ones further down, and reads a game looking up as looking, not unreachable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
Eric Wagoner
2026-09-29 12:25:07 -04:00
co-authored by Claude Opus 5.5
parent f131f292ae
commit f78ff3ef8c
2 changed files with 48 additions and 18 deletions
+2 -1
View File
@@ -10,7 +10,8 @@ README.
- **The hall** is the landing page (`/`). Name, *Play now against the bot*, - **The hall** is the landing page (`/`). Name, *Play now against the bot*,
*Open a table*, *or join one* with a four-letter code, then *your games* *Open a table*, *or join one* with a four-letter code, then *your games*
(the ledger of tables this browser holds a seat at, with whose move it (the ledger of tables this browser holds a seat at, with whose move it
is and unread talk), *your reports to the keeper*, and *about this game — is and unread talk; for someone coming back it comes first, and finished
games wait further down), *your reports to the keeper*, and *about this game —
a labor of love*, which opens the about panel: a dialog of tabs (The a labor of love*, which opens the about panel: a dialog of tabs (The
game, The tally, More games, Send word; a game may add its own, as Dip's game, The tally, More games, Send word; a game may add its own, as Dip's
Playing here), `TabbedDialog.svelte`, with each game's words in its hall. Playing here), `TabbedDialog.svelte`, with each game's words in its hall.
+41 -12
View File
@@ -75,12 +75,14 @@
const ready = $derived(name.trim().length > 0); const ready = $derived(name.trim().length > 0);
// Each held seat is looked up once, so the ledger can say whose move it // Each held seat is looked up once, so the ledger can say whose move it
// is. A seat the server refuses outright is doubted; a second refusal in // is; until the answer comes, its game is absent and reads "looking". A
// a row forgets it. Silence (a restart, a stale answer) forgets nothing. // seat the server refuses outright is doubted; a second refusal in a row
// forgets it. Silence (a restart, a stale answer) forgets nothing.
const asked = new Set<string>();
$effect(() => { $effect(() => {
for (const held of seats) { for (const held of seats) {
if (held.roomId in games) continue; if (asked.has(held.roomId)) continue;
games[held.roomId] = null; asked.add(held.roomId);
api.view(held.roomId, held.token) api.view(held.roomId, held.token)
.then((v) => { .then((v) => {
trustSeat(held.roomId); trustSeat(held.roomId);
@@ -147,6 +149,10 @@
seats = allSeats(); seats = allSeats();
} }
/** A game counts as finished once its table says so; until then, even while loading, it is in play. */
const finished = $derived(seats.filter((h) => !!games[h.roomId]?.over));
const active = $derived(seats.filter((h) => !games[h.roomId]?.over));
function status(held: { roomId: string; seat: string }): string { function status(held: { roomId: string; seat: string }): string {
const g = games[held.roomId]; const g = games[held.roomId];
if (g === undefined) return 'looking'; if (g === undefined) return 'looking';
@@ -188,7 +194,7 @@
} }
</script> </script>
<section class="lid"> <section class="lid" class:returning={active.length > 0}>
<div class="lid-head"> <div class="lid-head">
<h1>__NAME__</h1> <h1>__NAME__</h1>
<p class="pitch">One line that says what the game is.</p> <p class="pitch">One line that says what the game is.</p>
@@ -241,10 +247,7 @@
<p class="caption">A picture of the game goes here.</p> <p class="caption">A picture of the game goes here.</p>
</div> </div>
{#if seats.length} {#snippet gameRow(held: (typeof seats)[number])}
<div class="ledger">
<div class="ledger-head">your games</div>
{#each seats as held (held.roomId)}
<div class="ledger-row" class:your-move={yourMove(held)}> <div class="ledger-row" class:your-move={yourMove(held)}>
<a class="ledger-resume" href={`/join/${held.roomId}`}> <a class="ledger-resume" href={`/join/${held.roomId}`}>
<span class="ledger-code">{held.roomId}</span> <span class="ledger-code">{held.roomId}</span>
@@ -252,7 +255,22 @@
</a> </a>
<button type="button" class="dismiss" title="forget this game" onclick={() => forget(held.roomId)}>×</button> <button type="button" class="dismiss" title="forget this game" onclick={() => forget(held.roomId)}>×</button>
</div> </div>
{/each} {/snippet}
<!-- The games still in play, first thing for someone coming back; finished ones wait further down. -->
{#if active.length}
<div class="ledger">
<div class="ledger-head">your games</div>
{#each active as held (held.roomId)}{@render gameRow(held)}{/each}
</div>
{/if}
{#if finished.length || reports.length}
<div class="ledger past">
{#if finished.length}
<div class="ledger-head">finished games</div>
{#each finished as held (held.roomId)}{@render gameRow(held)}{/each}
{/if}
{#if reports.length} {#if reports.length}
<div class="ledger-head reports-head">your reports to the keeper</div> <div class="ledger-head reports-head">your reports to the keeper</div>
{#each reports as r (r.id)} {#each reports as r (r.id)}
@@ -347,14 +365,21 @@
padding: clamp(1.4rem, 3vw, 2.2rem) clamp(1.2rem, 3vw, 2.6rem) 1.8rem; padding: clamp(1.4rem, 3vw, 2.2rem) clamp(1.2rem, 3vw, 2.6rem) 1.8rem;
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr); grid-template-columns: minmax(0, 1fr);
grid-template-areas: 'head' 'play' 'look' 'ledger'; grid-template-areas: 'head' 'play' 'look' 'ledger' 'past';
gap: 1.2rem 2.2rem; gap: 1.2rem 2.2rem;
} }
/* Someone coming back sees their games first. */
.lid.returning {
grid-template-areas: 'head' 'ledger' 'play' 'look' 'past';
}
@media (min-width: 901px) { @media (min-width: 901px) {
.lid { .lid {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
grid-template-areas: 'head head' 'play look' 'ledger ledger'; grid-template-areas: 'head head' 'play look' 'ledger ledger' 'past past';
}
.lid.returning {
grid-template-areas: 'head head' 'ledger ledger' 'play look' 'past past';
} }
} }
@@ -474,6 +499,10 @@
padding-top: 0.5rem; padding-top: 0.5rem;
} }
.ledger.past {
grid-area: past;
}
.ledger-head { .ledger-head {
font-style: italic; font-style: italic;
color: var(--bone-dim); color: var(--bone-dim);