The 35 files behind twelve public pages, seven admin views, two player views and three core-page extensions, ported onto `window.__rg`. Every one of them imports exactly the seven kit members plus `lib/format.js`, which is the finding §2.7.1 predicted and this confirms. `client/src/core.js` is the port mechanism, and unlike the server's it is a plain read: `window.__rg` is published before any module chunk evaluates, so there is no gap to defer around and a ported component keeps its ordinary import shape. `client/src/api.js` rebuilds the UO namespaces over the request primitive — same URLs, because §1.2 freezes the API surface. SPA paths changed and API paths did not. `/site/shard` is `/uo/shard`, and the admin paths lost their now-redundant `shard-` prefixes (`/admin/uo/ops`), a clean break being the only moment that is free. `shim/rg.js` becomes the single reader of the global, so the "core did not publish its dependencies" message is reachable from whichever module the bundler happens to touch first rather than from whichever one is imported first — a guarantee that used to last until someone sorted the imports. Co-Authored-By: Claude <noreply@anthropic.com>
59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
import GameAccounts from '../../components/GameAccounts.jsx'
|
|
import VendorSales from '../../components/VendorSales.jsx'
|
|
import api from '../../api.js'
|
|
import { useAsync } from '../../core.js'
|
|
|
|
// The logged-in player's characters. Shows the link prompt when no game account
|
|
// is linked, otherwise their characters grouped by account (shared component),
|
|
// plus their own home status and recent vendor sales.
|
|
|
|
const DECAY_TONE = {
|
|
LikeNew: '#7fd0a4', Ageless: '#7fd0a4', Slightly: '#a9cf8a', Somewhat: '#d7c56a',
|
|
Fairly: '#e0a95f', Greatly: '#d9736f', IDOC: '#e05a5a', Collapsed: '#8c96a5',
|
|
}
|
|
|
|
// The caller's own houses (home status). Only their own — never anyone else's.
|
|
function MyHouses() {
|
|
const { data } = useAsync(() => api.player.shard.houses(), [])
|
|
if (!data || data.length === 0) return null
|
|
return (
|
|
<section style={{ marginTop: 30 }}>
|
|
<div className="field-label" style={{ marginBottom: 12 }}>My houses</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
{data.map((h) => {
|
|
const label = h.isIdoc ? 'IDOC' : (h.decay || h.stage)
|
|
const tone = h.isIdoc ? '#e05a5a' : (DECAY_TONE[label] || 'var(--muted)')
|
|
return (
|
|
<div key={h.serial} className="panel" style={{ padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 14 }}>
|
|
<div style={{ minWidth: 0, flex: 1 }}>
|
|
<div className="display" style={{ fontSize: '1rem', color: 'var(--head)' }}>{h.name || 'An unnamed house'}</div>
|
|
<div className="sans dim" style={{ fontSize: '0.76rem', marginTop: 2 }}>
|
|
{h.region || h.map || '—'}{h.x != null ? ` · ${h.x}, ${h.y}` : ''}
|
|
</div>
|
|
</div>
|
|
{label && (
|
|
<span className="sans" style={{ flex: 'none', fontSize: '0.68rem', color: tone, border: `1px solid ${tone}66`, borderRadius: 999, padding: '2px 9px' }}>
|
|
{label}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
<p className="sans dim" style={{ margin: '10px 0 0', fontSize: '0.76rem' }}>
|
|
Keep an eye on the decay status — refresh a house in game before it reaches IDOC.
|
|
</p>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
export default function PlayerCharacters() {
|
|
return (
|
|
<div>
|
|
<GameAccounts scope={api.player.shard} charTo={(serial) => `/player/uo/characters/${serial}`} />
|
|
<MyHouses />
|
|
<VendorSales fetchSales={api.player.shard.sales} />
|
|
</div>
|
|
)
|
|
}
|