The chronicle marks each line with its wizard's color; the turn's owner stands out

Two of Randy's flourishes. Every chronicle line now carries a small
square in the color of the wizard acting — whoever cast, struck,
walked, or countered, else the turn's owner — so one player's doings
can be picked out of the scroll at a glance. And the life-points row
of the wizard whose turn it is wears an accent bar and a tint, since
bold alone was lost in the handwriting.

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-14 19:23:00 -04:00
co-authored by Claude Fable 5.1
parent 19aec73b30
commit 3fc9df85ac
2 changed files with 41 additions and 3 deletions
+20 -1
View File
@@ -2958,7 +2958,7 @@
{:else}
{#each net.log as line, i (i)}
<div class:table-talk={line.text.startsWith("\u{1F4AC}")}>
{line.text}
<span class="log-mark" class:blank={!line.actor} style:background={line.actor ? playerColor(line.actor) : "transparent"}></span>{line.text}
{#if line.notable && line.turn !== null && prefs.instantReplay && !net.spectating}
<button class="moment-eye" title="relive this turn"
onclick={() => net.requestMoment(line.turn!)}>👁</button>
@@ -3848,6 +3848,14 @@
min-height: 1.55rem;
}
.slip.creature-note { background: #e7edda; border-color: #7d8a5a; }
.score-row.active {
font-weight: 700;
background: rgba(58, 47, 31, 0.08);
border-left: 3px solid #3a2f1f;
margin-left: -0.45rem;
padding-left: 0.3rem;
border-radius: 2px;
}
.score-row.active .score-name { font-weight: 700; }
.score-temper {
font-family: "Caveat", cursive;
@@ -3916,6 +3924,17 @@
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.45);
}
.chronicle div + div { margin-top: 0.05rem; }
/* the acting wizard's color, so one player's lines can be picked out at a glance */
.log-mark {
display: inline-block;
width: 0.55em;
height: 0.55em;
margin-right: 0.45em;
border-radius: 2px;
border: 1px solid rgba(58, 47, 31, 0.35);
vertical-align: 0.05em;
}
.log-mark.blank { border-color: transparent; }
.moment-eye {
background: none;
border: none;
+21 -2
View File
@@ -234,6 +234,22 @@ export interface LogLine {
text: string;
turn: number | null;
notable: boolean;
/** The wizard acting, for the color mark beside the line: whoever
* cast, struck, walked, or countered otherwise the turn's owner. */
actor?: string;
}
/** Events whose `player` is the one acting rather than the one acted on. */
const ACTING = new Set([
"moved", "warpStepped", "jumpedPit", "counteractionPlayed", "treasurePickedUp", "treasureDropped",
"numberPlayedForMovement", "cardsDiscarded", "turnStarted", "turnEnded", "doorUnlocked", "madDash",
"climbedFromPit", "spellTrapped",
]);
function actorOf(e: GameEvent, turnOwner: string | null): string | undefined {
const any = e as unknown as Record<string, unknown>;
for (const k of ["caster", "attacker", "by", "owner"]) if (typeof any[k] === "string") return any[k] as string;
if (ACTING.has(e.type) && typeof any.player === "string") return any.player as string;
return turnOwner ?? undefined;
}
/** The boundaries the turn count walks mirrored by the server's
@@ -305,6 +321,8 @@ class Net {
* socket that never fires onclose; the watchdog closes it by hand so
* the reconnect loop (and the masthead banner) actually engage. */
private lastHeard = Date.now();
/** Whose turn the chronicle is in, for the mark on lines with no actor of their own. */
private turnOwner: string | null = null;
private watchdog: ReturnType<typeof setInterval> | null = null;
roomId = $state<string | null>(null);
players = $state<string[]>([]);
@@ -510,6 +528,7 @@ class Net {
this.openingRolls = { rolls: e.dieRolls, first: e.firstPlayer, players: e.players };
}
if (TURN_BOUNDARY.has(e.type)) this.turnCounter++;
if (e.type === "turnStarted") this.turnOwner = e.player;
const line = humanize(e);
if (line) {
// Every turn wears its eye, on the header line that opens
@@ -517,7 +536,7 @@ class Net {
// winning line carries its own besides.
const notable = this.turnCounter >= 0 &&
(e.type === "gameWon" || TURN_BOUNDARY.has(e.type));
this.log = [...this.log, { text: line, turn: this.turnCounter >= 0 ? this.turnCounter : null, notable }];
this.log = [...this.log, { text: line, turn: this.turnCounter >= 0 ? this.turnCounter : null, notable, actor: actorOf(e, this.turnOwner) }];
}
}
if (talk > 0) {
@@ -545,7 +564,7 @@ class Net {
break;
}
case "chat": {
this.log = [...this.log, { text: `\u{1F4AC} ${msg.player}: ${msg.text}`, turn: null, notable: false }];
this.log = [...this.log, { text: `\u{1F4AC} ${msg.player}: ${msg.text}`, turn: null, notable: false, actor: msg.player }];
this.chatCount += 1;
if (this.roomId) this.markChatSeen();
break;