From a1d913f852aa1a7ed1e5c719207919231cc6640a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 24 Sep 2026 11:05:32 +0000 Subject: [PATCH] Render rows of glyphs in sidebar score tables A sidebar score entry from gameslib may now be a list of status values (text or glyphs) rather than a single value, so a score section can show each player a row of coloured pieces. resolveSidebarScores resolves such lists entry by entry, and GameStatus (game and Lab) draws them with the same glyph rendering the status table uses, so custom palettes apply. Mega-Volcano and Tintas use this for their new spoiler-tagged colour aids, and other games can use it too. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XB8BRhXCX8rrWsJKVx8nA8 --- src/components/GameMove/GameStatus.js | 62 +++++++++++++++++++-------- src/components/Lab/GameStatus.js | 62 +++++++++++++++++++-------- src/lib/resolveSidebarStatus.js | 19 +++++--- src/lib/resolveSidebarStatus.test.js | 25 +++++++++++ 4 files changed, 125 insertions(+), 43 deletions(-) diff --git a/src/components/GameMove/GameStatus.js b/src/components/GameMove/GameStatus.js index 040392da..4091781b 100644 --- a/src/components/GameMove/GameStatus.js +++ b/src/components/GameMove/GameStatus.js @@ -32,6 +32,29 @@ function renderGlyph( return renderglyph(glyph, player, options); } +// A status value is plain text or a glyph (`{ glyph, colour }`) +function renderStatusValue(value, id, globalMe, colourContext, game) { + if (typeof value === "string") { + return value; + } + return ( + {"color + ); +} + function GameStatus({ status, settings, @@ -104,23 +127,12 @@ function GameStatus({ {status.value.map((v, i) => ( - {typeof v === "string" ? ( - v - ) : ( - {"color + {renderStatusValue( + v, + "genericStatus-" + ind + "-" + i, + globalMe, + colourContext, + game )} ))} @@ -161,7 +173,21 @@ function GameStatus({ allUsers )} - {score} + + {Array.isArray(score) + ? score.map((v, j) => ( + + {renderStatusValue( + v, + "score-" + i + "-" + index + "-" + j, + globalMe, + colourContext, + game + )} + + )) + : score} + ))} diff --git a/src/components/Lab/GameStatus.js b/src/components/Lab/GameStatus.js index d33ce8ef..2a84e29f 100644 --- a/src/components/Lab/GameStatus.js +++ b/src/components/Lab/GameStatus.js @@ -32,6 +32,29 @@ function renderGlyph( return renderglyph(glyph, player, options); } +// A status value is plain text or a glyph (`{ glyph, colour }`) +function renderStatusValue(value, id, globalMe, colourContext, game) { + if (typeof value === "string") { + return value; + } + return ( + {"color + ); +} + function GameStatus({ status, settings, @@ -104,23 +127,12 @@ function GameStatus({ {status.value.map((v, i) => ( - {typeof v === "string" ? ( - v - ) : ( - {"color + {renderStatusValue( + v, + "genericStatus-" + ind + "-" + i, + globalMe, + colourContext, + game )} ))} @@ -161,7 +173,21 @@ function GameStatus({ allUsers )} - {score} + + {Array.isArray(score) + ? score.map((v, j) => ( + + {renderStatusValue( + v, + "score-" + i + "-" + index + "-" + j, + globalMe, + colourContext, + game + )} + + )) + : score} + ))} diff --git a/src/lib/resolveSidebarStatus.js b/src/lib/resolveSidebarStatus.js index fbae90fe..7214d8cc 100644 --- a/src/lib/resolveSidebarStatus.js +++ b/src/lib/resolveSidebarStatus.js @@ -21,6 +21,13 @@ function resolveStatusField(label, playerNames, t) { return resolveRenderLabel(label, playerNames, t); } +function resolveStatusValue(entry, playerNames, t) { + if (isGlyph(entry)) { + return entry; + } + return resolveStatusField(entry, playerNames, t); +} + export function resolveSidebarStatuses( statuses, players, @@ -34,12 +41,7 @@ export function resolveSidebarStatuses( return statuses.map((row) => ({ ...row, key: resolveStatusField(row.key, playerNames, t), - value: row.value.map((entry) => { - if (isGlyph(entry)) { - return entry; - } - return resolveStatusField(entry, playerNames, t); - }), + value: row.value.map((entry) => resolveStatusValue(entry, playerNames, t)), })); } @@ -56,8 +58,11 @@ export function resolveSidebarScores( return scores.map((block) => ({ ...block, name: resolveStatusField(block.name, playerNames, t), + // A player's score may be a row of status values (text or glyphs) scores: block.scores.map((entry) => - resolveStatusField(entry, playerNames, t) + Array.isArray(entry) + ? entry.map((value) => resolveStatusValue(value, playerNames, t)) + : resolveStatusField(entry, playerNames, t) ), })); } diff --git a/src/lib/resolveSidebarStatus.test.js b/src/lib/resolveSidebarStatus.test.js index a82405da..37894bf2 100644 --- a/src/lib/resolveSidebarStatus.test.js +++ b/src/lib/resolveSidebarStatus.test.js @@ -88,4 +88,29 @@ describe("resolveSidebarStatus", () => { expect(resolved[0].name).to.equal("Scores"); expect(resolved[0].scores).to.deep.equal([10, 20]); }); + + it("resolves list score cells entry by entry, leaving glyphs unchanged", () => { + const glyph = { glyph: "piece", colour: 3 }; + const scores = [ + { + name: "Colours", + scores: [ + [ + glyph, + { + textKey: "apgames:status._player", + actor: { kind: "seat", seat: 2 }, + }, + ], + [], + ], + spoiler: true, + }, + ]; + const resolved = resolveSidebarScores(scores, players, users, t); + expect(resolved[0].scores[0][0]).to.equal(glyph); + expect(resolved[0].scores[0][1]).to.equal("Bob"); + expect(resolved[0].scores[1]).to.deep.equal([]); + expect(resolved[0].spoiler).to.equal(true); + }); });