feat(site): phase 18 - the Rust screenshots, web and app
All checks were successful
PR checks / checks (pull_request) Successful in 2m55s

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
This commit is contained in:
2026-10-22 22:05:06 -05:00
parent 4f38e29ad7
commit c47e9c1aa4
21 changed files with 745 additions and 29 deletions

View File

@@ -39,9 +39,10 @@ const shots = screensOf('phone');
<section class="page section shots">
<h2>What it looks like</h2>
<p class="prose shots__lede">
Captured against a real deployment with real content, not mocked up. 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.
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">

View File

@@ -2,10 +2,10 @@
import Screenshot from '../Screenshot.astro';
/**
* The homepage's one screenshot. PLAN.md §13 phase 9, D4 / D44.
* The homepage's screenshots, one per game. PLAN.md §13 phase 9, D4 / D44; §16 phase 18.
*
* -----------------------------------------------------------------------------------------
* WHY ONE, AND WHY THIS ONE
* WHY ONE PER GAME, AND WHY THESE
* -----------------------------------------------------------------------------------------
* `DataPath` above it draws the claim — a private game server, a sidecar, a public site —
* and a diagram of a data path is a promise that the data arrives. This is the page where
@@ -16,11 +16,13 @@ import Screenshot from '../Screenshot.astro';
* tagged `uo` in `screens.mjs`, so it prints "Ultima Online" beside it and may keep that
* game's words in its alt and caption. A core screen was the alternative and was declined:
* the claim here is live game state on a public page, which no core screen shows. Phase 18
* adds a Rust capture beside it, labelled the same way.
* added a Rust capture beside it, labelled the same way: the live map, because every pixel
* of it is the running server's — the Rust demo's players are a demo cast (D81), and the
* map shows none of them.
*
* A gallery here would compete with `Capabilities` further down, which is the part of the
* homepage that enumerates. So: one figure, the signature screen, and the rest of the set
* on each game's page under `/modules/`, where each one sits beside the claim it supports.
* homepage that enumerates. So: one figure per game, its signature screen, and the rest of
* the set on each game's page under `/modules/`, where each sits beside the claim it supports.
*/
---
@@ -28,12 +30,16 @@ import Screenshot from '../Screenshot.astro';
<p class="eyebrow">What it looks like</p>
<h2>The other end of that diagram</h2>
<p class="prose looks__lede">
A demo deployment with a real Ultima Online server behind it. The gold supply, the state
of the link and the player online in Britain are all read from the game server over the
bridge. None of it is typed in, and none of it is a mock-up.
Two demo deployments, each with a real game server behind it. On the first, the gold
supply, the state of the link and the player online are all read from an Ultima Online
server over the bridge. On the second, the map is the running Rust server's own: its
island, its monuments, and the events on it as they happen.
</p>
<Screenshot id="shard-status" />
<div class="looks__pair">
<Screenshot id="shard-status" />
<Screenshot id="rust-map" />
</div>
</section>
<style>
@@ -47,4 +53,11 @@ import Screenshot from '../Screenshot.astro';
max-width: 46rem;
color: var(--muted);
}
/* Side by side where there is room for two screenshots to stay readable, stacked below. */
.looks__pair {
display: grid;
gap: 0 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr));
}
</style>

View File

@@ -4,6 +4,7 @@ description: Admin → Rust permissions — the site holds every server's permis
---
import { Aside } from '@astrojs/starlight/components';
import Screenshot from '../../../../components/Screenshot.astro';
**Admin → Rust permissions** (`/admin/rust`) is where the site holds every permission and group
on each server: what was there before the site, what is made here, and what is changed in the
@@ -66,6 +67,8 @@ new one. Each group has four tabs:
same permissions and players on every server it is on — including servers added later, if you
tick that — and a change made in one game gives that server its own copy.
<Screenshot id="admin-rust-permissions" />
### Chat styles
A group's **Settings** tab has a **Chat style (BetterChat)** section. Give a group a style and

View File

@@ -4,6 +4,7 @@ description: Authoring RunicNPC's profiles and faction table on the site — whi
---
import { Aside } from '@astrojs/starlight/components';
import Screenshot from '../../../../../components/Screenshot.astro';
**Admin → Rust NPC profiles** is where a Runic Gateway site authors RunicNPC's profiles. There is
no in-game profile editor: on a server a site manages, the profiles and the faction table are the
@@ -47,6 +48,8 @@ Each row shows a profile's label and RunicNPC name, the servers it is for, its r
a boss or has its own loot, its health, kits and movement, and how its kills count. **New profile**
and **Edit** open the editor below the list.
<Screenshot id="admin-rust-npcs" />
### Which servers
A profile is for **every server** — including servers added later — or for the ones ticked. Each

View File

@@ -4,11 +4,14 @@ description: The Map tab on each server's page — where the picture comes from,
---
import { Aside } from '@astrojs/starlight/components';
import Screenshot from '../../../../components/Screenshot.astro';
Each server's public page has a **Map** tab: this wipe's map, with the game's own grid, its
monuments, and layers for everything that moves. It is drawn in the browser from two things the
bridge answers live — the picture, and the positions.
<Screenshot id="rust-map" />
## The picture
**From Rust+, when it is on.** A server with `app.port` set renders its map at boot and keeps the

View File

@@ -120,3 +120,11 @@ export const staffFeatures = [
'titles, map layers, event steps. None is required.',
},
];
/**
* The screenshots `/modules/rust/` shows, by id from `screens.mjs`: what players see under that
* list, and the staff screens under theirs. Each is tagged `game: 'rust'` there (D67). Captured
* in phase 18 (D65) from the Oxide rig, with stand-in players named by their role (D80).
*/
export const screens = ['rust-servers', 'rust-feed', 'rust-leaderboard', 'rust-map'];
export const staffScreens = ['admin-rust-permissions', 'admin-rust-npcs'];

View File

@@ -39,7 +39,15 @@
* deployment is branded "Runic Gateway Demo" rather than a real community's name (D43) —
* the screenshots show the platform, not somebody's private shard.
*
* Nothing here is a mock-up, and nothing here was drawn.
* None of those is a mock-up, and nothing here was drawn.
*
* The Rust captures (phase 18) are a second demo deployment, since one deployment runs one
* game module, wired to the Oxide test server over a real rust-link sidecar. The server, its
* status, its world map and markers, the permission groups its plugins hold and RunicNPC's
* sync state are that server's. Its players are not: the rig has none, so the feed, the
* leaderboard and the linked Steam account were written into the demo's database by
* `scripts/mockRustDemo.mjs`, in the module's own row shapes (D81), with every name a role
* (D80). The captions say what the screen does, never that a player did it.
*
* ---------------------------------------------------------------------------------------
* A SCREENSHOT BELONGS TO ITS GAME (D67)
@@ -69,6 +77,8 @@ export const PHONE = { width: 1440, height: 3088 };
* @property {string} route Route on the demo deployment. The capture script's only input.
* @property {boolean} admin Capture signed in as an administrator rather than anonymously.
* @property {number} [scrollY] Pixels to scroll before the shot, when the subject is below the fold.
* @property {string[]} [click] Visible text of what to click, in order, before the shot — for a
* subject behind a tab or a button the route cannot reach.
* @property {string} alt What the screen shows, for somebody who cannot see it.
* @property {string} caption The sentence printed under the figure.
* @property {'web'|'phone'} family Which geometry the file follows.
@@ -131,6 +141,57 @@ export const screens = [
family: 'web',
},
// ── Rust's public pages (PLAN.md §16 phase 18, D65) ─────────────────────────────────
//
// The second demo deployment (see WHAT IS IN THEM above): a real server, demo players.
// The demo makes who is online public, which a real community decides for itself; the
// players it would name are the demo cast. The server list's player count is the cast's
// too, written just before the shot, since the live server reports its own every 30 s.
{
id: 'rust-servers',
game: 'rust',
route: '/rust',
admin: false,
alt: 'The Rust servers page of a Runic Gateway site, with one server card: its map, world size, when it last wiped, its next wipe a week away, and six of forty players online.',
caption:
'Every server the community runs, as each last reported itself — and the next wipe, worked out from the schedule the operator set.',
family: 'web',
},
{
id: 'rust-feed',
game: 'rust',
route: '/rust/servers/rust-oxide',
admin: false,
scrollY: 268,
alt: "A Rust server's feed: players killing each other with the weapon, distance and map square, chat lines, a cargo ship docking, a raid base cleared, a guarded crate won, a Bradley destroyed and players connecting and leaving, newest first.",
caption:
'The feed: kills, chat, comings and goings and the world’s own events, as the bridge reports them. Who sees players named in it is the operator’s choice.',
family: 'web',
},
{
id: 'rust-leaderboard',
game: 'rust',
route: '/rust/servers/rust-oxide?tab=leaderboard',
admin: false,
scrollY: 520,
alt: "A Rust server's leaderboard for the current wipe, ranking eight players by kills, with deaths, NPC kills, structures built, time played, crates hacked, drops looted and treasures won.",
caption:
'The wipe’s leaderboard. Any column sorts it, and a column for an optional plugin appears only where that plugin is installed.',
family: 'web',
},
{
id: 'rust-map',
game: 'rust',
route: '/rust/servers/rust-oxide?tab=map',
admin: false,
scrollY: 470,
click: ['+'],
alt: "A Rust server's live map: the island from the game's own render under a grid, with monuments, airdrops and a PvP zone around the cargo ship marked, above the switches for each layer.",
caption:
'The live map, rendered from the running server and refreshed every ten seconds while the tab is open. Each layer has its own audience.',
family: 'web',
},
{
id: 'news',
route: '/site/news',
@@ -196,6 +257,30 @@ export const screens = [
family: 'web',
},
{
id: 'admin-rust-permissions',
game: 'rust',
route: '/admin/rust',
admin: true,
click: ['Groups', 'vip'],
alt: "The Rust permissions screen: the server's groups listed down the left, the VIP group open with its permissions filed under the plugin that registers each one, and Granted or Revoked beside every permission.",
caption:
'Every permission and group on the server, kept by the site — what was there before it, what is made here, and what is changed in the game — and pushed back after a wipe.',
family: 'web',
},
{
id: 'admin-rust-npcs',
game: 'rust',
route: '/admin/rust/npcs',
admin: true,
click: ['Edit'],
scrollY: 900,
alt: 'The RunicNPC profile editor for a guard: its names, its Kits kit, prefab and role, how far it notices players before it sleeps, and its health, damage, aim and vision.',
caption:
'A RunicNPC profile, edited on the site and pushed to every server it is for. RunicNPC checks each field the same way here, in the console and through its API.',
family: 'web',
},
// ── The Android app, against the same deployment on the same day (D26) ──────────────
//
// Captured from an emulator pointed at the demo stack through `adb reverse`, signed in as
@@ -256,6 +341,37 @@ export const screens = [
"The drawer is the deployment's own navigation, not a fixed menu — a site that renames or reorders its pages renames and reorders them here.",
family: 'phone',
},
// The app's Rust screens (phase 18), against the Rust demo, signed in as `player` (D80).
// From an `Android-app` `edge` debug build: they were not in a release when captured.
{
id: 'app-rust-server',
game: 'rust',
route: '/rust/servers/rust-oxide',
admin: false,
alt: "The app's Rust server screen: the server's map, size, last and next wipe and six players online, tabs for the feed, leaderboard, online list, map and wipes, and the feed of kills, chat and players connecting.",
caption: 'Each server in the app: the same feed as the site, with the same filters.',
family: 'phone',
},
{
id: 'app-rust-map',
game: 'rust',
route: '/rust/servers/rust-oxide?tab=map',
admin: false,
alt: "The app's live map of a Rust server: the island under a grid with monuments and world events marked, and switches for the grid, monuments and world events, site events and players.",
caption: 'The live map on a phone, refreshed every ten seconds while it is open.',
family: 'phone',
},
{
id: 'app-rust-account',
game: 'rust',
route: '/account/rust',
admin: false,
alt: "The app's Rust account screen for a signed-in player: a box for the link code the game gives, the linked Steam account, the VIP group it has on the server, its coins, and a button to open its backpack.",
caption:
'A player’s own side: link a Steam account with a code from the game, then see what it can do, its coins and its backpack on every server.',
family: 'phone',
},
];
/** One entry by id, or `undefined`. */

View File

@@ -2,9 +2,10 @@
import Base from '../../layouts/Base.astro';
import PageHeader from '../../components/PageHeader.astro';
import GameFeatures from '../../components/GameFeatures.astro';
import Screenshot from '../../components/Screenshot.astro';
import platform from '../../data/platform.json';
import { features, staffFeatures } from '../../data/games/rust.mjs';
import { features, staffFeatures, screens, staffScreens } from '../../data/games/rust.mjs';
import { assertGameCoverage } from '../../data/games/index.mjs';
/**
@@ -17,8 +18,8 @@ import { assertGameCoverage } from '../../data/games/index.mjs';
*
* It carries the RunicNPC card D61 asked for, pointing at the standalone `/runicnpc/` page.
*
* No screenshots yet, and no slots for them: D65 brings the Rust captures in phase 18, from
* the rigs, and D4 rules out a placeholder in the meantime.
* The screenshots came in phase 18 (D65), from the Oxide rig: the public ones under what
* players see, the staff screens under what staff run. Each is tagged `rust` in `screens.mjs`.
*/
assertGameCoverage('rust');
@@ -96,12 +97,25 @@ const description =
items={features}
/>
<section class="page section shots" aria-label="Screenshots of the public pages">
<p class="prose shots__note">
Captured from a demo site connected to a real Rust test server. The server, its status
and its map are that server's own; the players in the feed and the leaderboard are a
demo cast, named for their roles.
</p>
{screens.map((id) => <Screenshot id={id} />)}
</section>
<GameFeatures
title="What staff run from the site"
lede="Seven admin screens, and a Rust section on each user's page."
items={staffFeatures}
/>
<section class="page section shots" aria-label="Screenshots of the staff screens">
{staffScreens.map((id) => <Screenshot id={id} />)}
</section>
<section class="page section npc">
<div class="panel npc__card">
<p class="eyebrow">Also from this project</p>
@@ -134,6 +148,11 @@ const description =
</Base>
<style>
.shots__note {
margin: 0;
color: var(--muted);
}
.pieces h2,
.next h2 {
margin: 0 0 0.75rem;