The chronicle remembers everything, and holds still while you read it

The game log's sixty-line window was a render cap, not a limit of what
the client holds — a rejoin replays the whole game into memory. The
chronicle now renders every line back to the deal, and its auto-scroll
grows manners: it follows the newest line only while the reader sits at
the bottom, and holds its place once they scroll up into history.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-23 18:35:15 -04:00
co-authored by Claude Fable 5
parent 1e79f2ffb2
commit 6788de2f6e
+13 -4
View File
@@ -1192,10 +1192,18 @@
); );
let chronicleEl = $state<HTMLElement | null>(null); let chronicleEl = $state<HTMLElement | null>(null);
/** The chronicle follows the newest line only while the reader is at
* the bottom; scrolled back into history, it holds still. */
let chronicleStick = true;
function onChronicleScroll() {
if (!chronicleEl) return;
chronicleStick =
chronicleEl.scrollHeight - chronicleEl.scrollTop - chronicleEl.clientHeight < 60;
}
$effect(() => { $effect(() => {
void net.log.length; void net.log.length;
void local.log.length; void local.log.length;
if (chronicleEl) chronicleEl.scrollTop = chronicleEl.scrollHeight; if (chronicleEl && chronicleStick) chronicleEl.scrollTop = chronicleEl.scrollHeight;
}); });
// Tab title + favicon carry the turn signal even from another tab. // Tab title + favicon carry the turn signal even from another tab.
@@ -2044,13 +2052,14 @@
</div> </div>
</div> </div>
<div class="chronicle" aria-label="game log" bind:this={chronicleEl}> <div class="chronicle" aria-label="game log" bind:this={chronicleEl}
onscroll={onChronicleScroll}>
{#if local.active} {#if local.active}
{#each local.log.slice(-60) as line, i (i)} {#each local.log as line, i (i)}
<div class:table-talk={line.startsWith("\u{1F4AC}")}>{line}</div> <div class:table-talk={line.startsWith("\u{1F4AC}")}>{line}</div>
{/each} {/each}
{:else} {:else}
{#each net.log.slice(-60) as line, i (i)} {#each net.log as line, i (i)}
<div class:table-talk={line.text.startsWith("\u{1F4AC}")}> <div class:table-talk={line.text.startsWith("\u{1F4AC}")}>
{line.text} {line.text}
{#if line.notable && line.turn !== null && prefs.instantReplay && !net.spectating} {#if line.notable && line.turn !== null && prefs.instantReplay && !net.spectating}