Files
runicgateway.com/src/components/app/Screenshots.astro
wtclaude c47e9c1aa4
All checks were successful
PR checks / checks (pull_request) Successful in 2m55s
feat(site): phase 18 - the Rust screenshots, web and app
Nine captures tagged rust (D65): the server list, feed, leaderboard,
live map, permissions and the RunicNPC profile editor on the web, and a
server's feed, its map and the player's Rust account in the app, from
an Android-app edge debug build.

D80: every name in a capture is a role (admin, moderator, player...),
from this phase on. D81: the Rust demo's players are mock data written
by scripts/mockRustDemo.mjs; the server, its status, map, permission
groups and RunicNPC sync state are the real Oxide test server's. The
captions and /modules/rust/ and /app/ say which part is a demo cast.

seedDemo.mjs gains --game rust (server row with a weekly wipe, public
presence for the demo, two NPC profiles, a VIP group). captureScreens.mjs
gains a click list per screen. The homepage shows the Rust live map beside
the UO capture (D76); three Rust docs pages carry their screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-22 22:05:06 -05:00

118 lines
4.0 KiB
Plaintext

---
import { screensOf, PHONE } from '../../data/screens.mjs';
import platform from '../../data/platform.json';
/**
* The app's screenshot strip. Reserved in phase 5 (D26), filled in phase 9.
*
* ---------------------------------------------------------------------------------------
* WHY THIS COMPONENT EXISTED FOR A PHASE WITH NOTHING IN IT
* ---------------------------------------------------------------------------------------
* PLAN.md §10 said `/app/` shows "the 14 existing screenshots". They exist —
* `docs/android/screenshots/` on the `docs` repository — and they are the wrong fourteen: a
* trusted-device and recovery-code smoke test from 2026-07-22, captured against a
* development instance with no seeded content, before the theming work that changed how
* every screen looks. Five of them are two-factor prompts. The home shot is an empty page.
*
* Shipping them would have broken D4 and §1 at once, so D26 reserved the slot for the phase
* that stands up the review stack anyway. The shape was defined then and the data arrived
* now, which is exactly what it was for: filling it was a data change.
*
* ---------------------------------------------------------------------------------------
* WHY IT READS screens.mjs RATHER THAN HOLDING ITS OWN LIST
* ---------------------------------------------------------------------------------------
* The draft carried its own `shots` array, written before there was anywhere else to put
* one. There is now: `src/data/screens.mjs` holds every capture the site ships, web and
* phone alike, and `scripts/checkScreens.mjs` proves each one exists at the size the markup
* claims. A second list here would be the one nothing checks.
*
* The phone captures come from an emulator pointed at the same seeded deployment the web
* screenshots were taken from, on the same day — which is the property D26 was really
* after, since the app takes its colours, type and navigation from the site it connects to.
*/
const shots = screensOf('phone');
---
{
shots.length > 0 && (
<section class="page section shots">
<h2>What it looks like</h2>
<p class="prose shots__lede">
Captured from the app against two demo deployments: one with a real Ultima Online
server behind it, and one with a real Rust server whose players are a demo cast. The
app takes its colours, type and navigation from the site it is connected to, so these
show one community's app rather than a neutral one.
</p>
<ul class="shots__grid">
{shots.map((shot) => (
<li class="shots__item" data-game={shot.game}>
<img
src={`/screens/${shot.id}.webp`}
alt={shot.alt}
width={PHONE.width}
height={PHONE.height}
loading="lazy"
decoding="async"
/>
<p class="shots__caption">
{shot.game && <span class="shots__game">{platform.games[shot.game].name}</span>}
{shot.caption}
</p>
</li>
))}
</ul>
</section>
)
}
<style>
.shots h2 {
margin: 0 0 0.75rem;
font-size: clamp(1.6rem, 3.2vw, 2.1rem);
}
.shots__lede {
margin: 0 0 2.25rem;
color: var(--muted);
}
.shots__grid {
display: grid;
gap: 1.5rem;
margin: 0;
padding: 0;
list-style: none;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.shots__item img {
display: block;
width: 100%;
height: auto;
border: 1px solid var(--line);
border-radius: var(--radius-card);
box-shadow: var(--shadow-card);
}
/* The game's name on a game's screen (D67), as Screenshot.astro prints it. */
.shots__game {
display: inline-block;
margin-right: 0.5rem;
padding: 0.05rem 0.55rem;
border: 1px solid var(--line);
border-radius: var(--radius-pill);
color: var(--muted);
font-size: 0.74rem;
letter-spacing: 0.04em;
white-space: nowrap;
}
.shots__caption {
margin: 0.85rem 0 0;
color: var(--dim);
font-size: 0.88rem;
}
</style>