feat(live-state): use Steam avatarfull (184x184), downscale in CSS

64x64 avatarmedium looked soft on high-DPI screens. Switching the
GetPlayerSummaries field to avatarfull (184x184) and constraining
display size to 64px via .live-state .avatar gives sharp rendering on
retina/4k panels at the cost of a slightly larger CDN fetch (still
hot-linked, so no proxying cost).

Also adds the previously-missing CSS for the live-state player grid:
avatar+name+meta arranged in a tight 2-column grid per card, link
spans the avatar+name so the meta stays non-interactive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-12 23:17:51 +02:00
parent 6cbe7dc9f2
commit 096d18ac64
No known key found for this signature in database
3 changed files with 63 additions and 5 deletions

View file

@ -44,7 +44,7 @@ def _session_get(url: str, params: dict, timeout: float = REQUEST_TIMEOUT_SECOND
class SteamProfile:
steam_id_64: str
persona_name: str
avatar_url: str
avatar_url: str # avatarfull (184x184); CSS downscales so high-DPI stays crisp
def fetch_profiles_batch(
@ -70,7 +70,7 @@ def fetch_profiles_batch(
SteamProfile(
steam_id_64=str(p["steamid"]),
persona_name=str(p.get("personaname", "")),
avatar_url=str(p.get("avatarmedium", "")),
avatar_url=str(p.get("avatarfull", "")),
)
)
return out

View file

@ -854,3 +854,61 @@ dialog.modal.modal-wide {
font-size: 0.8em;
color: var(--color-muted);
}
/* Live-state panel current + recent players on the server detail page.
Avatars are fetched as the 184x184 Steam "full" size and downscaled in
CSS so high-DPI screens render crisply. */
.live-state .player-grid {
list-style: none;
padding: 0;
margin: var(--space-m) 0 0;
display: flex;
flex-wrap: wrap;
gap: var(--space-m);
}
.live-state .player-card {
display: grid;
grid-template-columns: auto 1fr;
column-gap: var(--space-s);
align-items: center;
}
.live-state .player-link {
display: contents;
color: inherit;
text-decoration: none;
}
.live-state .player-link:hover .name {
text-decoration: underline;
}
.live-state .avatar {
width: 64px;
height: 64px;
border-radius: var(--radius-s);
object-fit: cover;
grid-row: 1 / span 2;
}
.live-state .avatar.placeholder {
display: inline-block;
background: color-mix(in srgb, var(--color-muted) 30%, transparent);
}
.live-state .name {
grid-column: 2;
font-weight: 600;
}
.live-state .meta {
grid-column: 2;
color: var(--color-muted);
font-size: 0.85em;
}
.live-state .server-live-summary {
font-size: 1.05em;
}

View file

@ -32,7 +32,7 @@ def test_fetch_profiles_batch_builds_correct_request(monkeypatch: pytest.MonkeyP
captured: list = []
body = {"response": {"players": [
{"steamid": "76561197960828710", "personaname": "Alice",
"avatarmedium": "https://avatars.../alice_medium.jpg"},
"avatarfull": "https://avatars.../alice_full.jpg"},
]}}
_patched_get(monkeypatch, body, captured)
@ -47,7 +47,7 @@ def test_fetch_profiles_batch_builds_correct_request(monkeypatch: pytest.MonkeyP
p = profiles[0]
assert p.steam_id_64 == "76561197960828710"
assert p.persona_name == "Alice"
assert p.avatar_url.endswith("alice_medium.jpg")
assert p.avatar_url.endswith("alice_full.jpg")
def test_fetch_profiles_batch_skips_private_or_missing(monkeypatch: pytest.MonkeyPatch) -> None:
@ -55,7 +55,7 @@ def test_fetch_profiles_batch_skips_private_or_missing(monkeypatch: pytest.Monke
# should accept that and return only what's there.
body = {"response": {"players": [
{"steamid": "76561197960828710", "personaname": "Alice",
"avatarmedium": "https://avatars.../alice_medium.jpg"},
"avatarfull": "https://avatars.../alice_full.jpg"},
]}}
_patched_get(monkeypatch, body, [])
profiles = steam_users.fetch_profiles_batch(