Files
runicgateway.com/src/pages/modules/uo.astro
wtclaude 56473d4d51
All checks were successful
PR checks / checks (pull_request) Successful in 2m37s
feat(site): phase 17 - per-game module pages, /runicnpc/, and checkTerms over the built site
PLAN.md §16 phase 17, built on the org lead's answers of 2026-10-22,
recorded as D75-D79 (count of record 79).

- D75: core marketing pages describe what a game module adds in core's
  words. "Game intelligence" became "What a game module adds"; each
  game's own list moved to src/data/games/ and renders only on the new
  /modules/uo/ and /modules/rust/. /modules/ is a card per game.
- The page-side capability check is per game (assertGameCoverage), the
  item phase 13 moved here.
- D76: the homepage keeps its game screenshot, labelled. screens.mjs
  entries may carry `game` (D67); Screenshot.astro and the app grid
  print the game's name and mark the figure data-game.
- D77: /app/ describes the Rust screens (released the same day).
- D78: /runicnpc/, the standalone page, is in the header and footer;
  the narrow header was re-measured so five links keep two rows.
- D79: checkTerms.mjs --built reads every built page's visible text
  after the build; game routes and data-game elements allow their
  game's words. Proven against ten plants. Still the twelfth check.
- Re-read fixes: /architecture/'s "two machines is also the maximum",
  Discord "guild" on /integrations/, and collection.mjs against what
  Steam sign-in and the Rust module keep (PLAY_DATA_SAFETY.md
  regenerated; the Play answers are unchanged).

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

166 lines
5.0 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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, screens } from '../../data/games/uo.mjs';
import { assertGameCoverage } from '../../data/games/index.mjs';
/**
* `/modules/uo/` — PLAN.md §16, phase 17.
*
* The Ultima Online module's own page, and so one of the few marketing pages that may use
* that game's words (D63; `GAME_ROUTES` in `checkTerms.mjs`). The feature list is
* `games/uo.mjs`, held to the module's declared capabilities by the call below, and the
* screenshots are the ones `/features/` showed until this phase — every one tagged with its
* game in `screens.mjs` (D67).
*
* Versions are read from `platform.json`, which `checkFacts.mjs` holds to the module's
* release, the installer's bundle and both halves of the bridge.
*/
assertGameCoverage('uo');
const uo = platform.games.uo;
const gitea = `${platform.gitea.base}/${platform.gitea.org}`;
const docs = `${gitea}/docs/src/branch/main`;
const pieces = [
{
role: 'The module',
name: uo.module.repo,
version: platform.releases['Module-uo'],
what: 'Every Ultima Online screen on the site, installed from Admin → Modules.',
},
{
role: 'The sidecar',
name: 'uo-link',
version: uo.bundle.sidecar,
what: 'Beside the shard, and the only part of the bridge that listens on a network.',
},
{
role: 'The bridge plugin',
name: uo.pluginRepo,
version: uo.bundle.overlay,
what: `An overlay compiled by ServUO at boot, on ServUO ${uo.bundle.servuoMin} or newer.`,
},
];
const title = 'The Ultima Online module';
const description =
'What the Ultima Online module adds to a Runic Gateway site: live shard status, the ' +
'player-vendor marketplace, the spawn atlas, guilds, houses and more, from a running ServUO shard.';
---
<Base title={title} description={description}>
<PageHeader eyebrow="Game module" title="Ultima Online">
<p>
The <code>uo</code> module turns a Runic Gateway site into an Ultima Online shard’s
site, fed live from a running ServUO shard. It was the first module, and the one the
seam was cut around: this support was extracted out of the site, and every URL it had
before the move it still has.
</p>
<p>
The shard side is installed with the installer as one bundle —
{' '}<strong>{uo.bundle.tag}</strong>, speaking protocol {uo.protocol} — and the site
side is a paste in the admin panel.
</p>
</PageHeader>
<section class="page section pieces" aria-labelledby="pieces-heading">
<h2 id="pieces-heading">The pieces</h2>
<ul class="pieces__list">
{
pieces.map((piece) => (
<li class="panel">
<p class="pieces__role">{piece.role}</p>
<p class="pieces__name">
<code>{piece.name}</code>
<span class="chip chip--version">{piece.version}</span>
</p>
<p class="pieces__what">{piece.what}</p>
</li>
))
}
</ul>
</section>
<GameFeatures
title="What it adds"
lede="Everything here is published from the live shard, and every public surface passes the visibility settings first — each one is the operator's to switch on."
items={features}
/>
<section class="page section shots" aria-label="Screenshots">
{screens.map((id) => <Screenshot id={id} />)}
</section>
<section class="page section next">
<h2>Setting it up</h2>
<p class="prose">
The documentation’s Ultima Online group starts from connecting a ServUO shard and ends
at the reference for every file the bridge reads.
</p>
<div class="next__links">
<a class="btn btn--primary" href="/docs/ultima-online/">The Ultima Online docs</a>
<a class="btn btn--ghost" href={`${docs}/modules/uo/README.md`} rel="noopener noreferrer">
The module in depth
</a>
<a class="btn btn--ghost" href="/modules/">All modules</a>
</div>
</section>
</Base>
<style>
.pieces h2,
.next h2 {
margin: 0 0 0.75rem;
font-size: clamp(1.5rem, 3vw, 1.95rem);
}
.pieces__list {
display: grid;
gap: 1rem;
margin: 1.25rem 0 0;
padding: 0;
list-style: none;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}
.pieces__role {
margin: 0;
color: var(--dim);
font-size: 0.78rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.pieces__name {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.6rem;
margin: 0.5rem 0 0;
color: var(--gold);
}
.pieces__what {
margin: 0.6rem 0 0;
color: var(--muted);
font-size: 0.94rem;
}
.next p {
margin: 0;
color: var(--muted);
}
.next__links {
display: flex;
flex-wrap: wrap;
gap: 0.7rem;
margin-top: 1.5rem;
}
</style>