feat(ui): open the move history at the newest move; scale the landscape rack glyphs
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m18s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 2m6s

The move table fills top to bottom, oldest first, so it used to open on the
opening moves — the least interesting end of a long game. Pin it to its last
row when it is shown (the portrait drawer and the landscape dock alike), once
per showing rather than on every arriving move, so a player reading back
through the journal is not yanked to the bottom by an opponent's move.

The landscape tray shares the narrow left panel, so its tiles — and with them
the cqw-sized glyphs — come out markedly smaller than the full-width portrait
tray. Scale the letter and Erudit's blank star up a quarter there; the point
value keeps its size (it reads fine small, and growing it would crowd the
letter on a tile that size).
This commit is contained in:
Ilia Denisov
2026-07-27 21:11:34 +02:00
parent f383442430
commit 92700d0646
5 changed files with 96 additions and 2 deletions
+24 -1
View File
@@ -1358,6 +1358,29 @@
}
});
// The move table's scroller (the grid between the sticky header and the pinned bag footer).
let hgridEl = $state<HTMLDivElement>();
// Whether the table has already been pinned to its newest row for the current showing.
// A plain flag (not $state): the effect below writes it, and tracking it would re-run the
// effect from its own write.
let historyPinned = false;
// The history opens showing the LATEST moves: the grid fills top to bottom (oldest first), so
// an untouched scroller would open on the opening moves — the least interesting end. It is
// pinned once per showing, not on every arriving move, so a player who has scrolled back
// through a long game is not yanked to the bottom by an opponent's move. In landscape the
// table is docked open from the start, so the moves usually arrive after it mounts — hence
// the wait for a non-empty journal rather than a plain "on open".
$effect(() => {
const el = hgridEl;
if (!historyShown || !el) {
historyPinned = false;
return;
}
if (historyPinned || moves.length === 0) return;
historyPinned = true;
el.scrollTop = el.scrollHeight;
});
// Friend state for the in-game "add friend" affordance (the 🤝 in each opponent's score
// card while the history is open), derived from the server so it is correct across reloads
// and live-updates when a request is answered: `friends` are the caller's accepted friends;
@@ -1657,7 +1680,7 @@
</button>
{/if}
</div>
<div class="hgridwrap">
<div class="hgridwrap" bind:this={hgridEl}>
<div class="hgrid" style="grid-template-columns: repeat({view.game.seats.length}, 1fr)">
{#each historyGrid(moves, view.game.seats.length, thinkingSeat) as row}
{#each row as cell (cell.player)}
+15
View File
@@ -159,4 +159,19 @@
font-size: 7.6vmin; /* cqw fallback for Chrome < 105 */
font-size: 7.6cqw;
}
/* Landscape docks the rack in the narrow left panel (Game.svelte's .leftpane), so its tiles —
and with them the cqw-sized glyphs — come out markedly smaller than the full-width portrait
tray. Scale the tile face up by a quarter there so the letters stay comfortably legible; the
point value keeps its size (it reads fine small, and growing it would crowd the letter on a
tile this size). The media query matches the one that selects the landscape layout. */
@media (orientation: landscape) {
.letter {
font-size: 7.5vmin; /* cqw fallback for Chrome < 105 */
font-size: 7.5cqw;
}
.star {
font-size: 9.5vmin; /* cqw fallback for Chrome < 105 */
font-size: 9.5cqw;
}
}
</style>