Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"schemaVersion": 1,
"id": "hegemony-worker-record-art-v1",
"recordedAt": "2026-07-19",
"purpose": "Transparent decorative Worker art for the Worker inspection panel",
"projectAuthorization": {
"authorizedBy": "Warpkeep project owner",
"instructionDate": "2026-07-19",
"scope": "Use the supplied transparent Worker illustration in the Warpkeep Worker UI slice stacked on the stable Realm PR. This authorizes repository/runtime integration only after review; it does not authorize merge, deployment, production activation, worker seeding, or generic worker authority.",
"notGranted": [
"proof of underlying copyright ownership",
"a public open-content licence",
"general third-party derivative or redistribution permission",
"trademark or canonical-identity rights",
"worker ownership, status, route, cargo, reward, or settlement authority"
]
},
"sourceInputs": [
{
"role": "owner-supplied transparent Worker illustration",
"repositoryRetained": false,
"originalName": "codex-clipboard-1312042e-175b-466f-adda-9bef7b12c1f0.png",
"bytes": 478174,
"sha256": "f6ae700affb5ce981074c7952bc81f90b60e2dab947b94867c5394d3e23b4d6d",
"image": {
"format": "png",
"width": 1024,
"height": 1024,
"channels": 4,
"alpha": true
}
}
],
"processing": {
"tool": "sharp 0.35.3",
"operation": "lossy WebP derivative with alpha preserved",
"outputEncoding": "quality 92, alphaQuality 100, effort 6, smartSubsample true",
"runtimeAsset": {
"path": "public/images/realm/hegemony-worker-record.webp",
"format": "webp",
"width": 1024,
"height": 1024,
"bytes": 86984,
"sha256": "ff758ecbf520b05ccf0a2fa490bcafa6c564514de5ee56ef5a720fd6da24193e",
"decodedRgbaSha256": "2e77492f76801576adcc9cfe660fa15494123bc43fcd767e005cd5ad95d8b047",
"alpha": {
"transparentPixels": 858605,
"partiallyTransparentPixels": 39551,
"opaquePixels": 150420
},
"visibleBoundsAlpha16": {
"minX": 256,
"minY": 50,
"maxX": 863,
"maxY": 934
}
}
},
"presentationBoundary": {
"component": "src/components/realm/WorkerInspectionPanel.tsx",
"runtimeUse": "same-origin decorative hero art in a focus-safe Worker inspector",
"forbiddenClaims": [
"private FID",
"private cargo or account balance",
"browser-invented worker ownership",
"browser-derived dispatch or recall authority",
"reward or Marks linkage"
]
},
"licence": {
"spdx": "LicenseRef-Warpkeep-Provenance-Required",
"policy": "The exact project instruction authorizes this Warpkeep runtime use but does not establish a public relicensing grant. See ASSETS-LICENSE.md."
},
"visualQa": {
"transparentSourceVerified": true,
"visibleBoundsRecorded": true,
"runtimeDerivativeViewedOnTransparentViewer": true,
"externalRuntimeUrl": false
}
}
Binary file added public/images/realm/hegemony-worker-record.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions src/components/WarpkeepExperience.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,15 @@ export function WarpkeepExperience() {
onClaimStoneExpedition={backend.state.stoneExpedition === undefined
? undefined
: backend.claimStoneExpedition}
workerProjection={backend.state.workerProjection}
workerRoster={backend.state.workerRoster}
workerResourceState={backend.state.workerResourceState}
onRecallWorker={backend.state.workerProjection?.mode === 'active'
? backend.recallWorker
: undefined}
onRecallAllWorkers={backend.state.workerProjection?.mode === 'active'
? backend.recallAllWorkers
: undefined}
graphicsPreference={graphicsPreference}
resolvedGraphicsQuality={resolvedGraphicsQuality}
audioMuted={audioMuted}
Expand Down
78 changes: 69 additions & 9 deletions src/components/realm/RealmHud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ import {
type RealmEconomicResourceKey
} from './realmResourcePresentation';
import './RealmPlayerChrome.css';
import { WorkerCommandCenter } from './WorkerCommandCenter';
import { WorkerInspectionPanel } from './WorkerInspectionPanel';
import type {
ReadyWorkerProjection,
ReadyWorkerResourceState,
WorkerRosterPresentation,
RealmWorkerPublicPresentation
} from './realmWorkerPresentation';

type RealmHudProps = Readonly<{
identity: RealmIdentity;
Expand All @@ -57,6 +65,11 @@ type RealmHudProps = Readonly<{
onRequestExplore?: () => void;
activeWagons?: readonly RealmActiveWagonMenuItem[];
onOpenActiveWagon?: (wagon: RealmActiveWagonMenuItem) => void;
workerProjection?: ReadyWorkerProjection;
workerRoster?: WorkerRosterPresentation;
workerResourceState?: ReadyWorkerResourceState;
onRecallWorker?: (workerId: string) => Promise<void>;
onRecallAllWorkers?: () => Promise<void>;
onRecenterKeep: () => void;
onRequestReturn: () => void;
}>;
Expand Down Expand Up @@ -142,8 +155,9 @@ function assignRef<T>(ref: Ref<T> | undefined, value: T | null) {
}

function RealmResourceRail({
resources
}: Readonly<{ resources: ReadyRealmResourcePresentation }>) {
resources,
workerResourceState
}: Readonly<{ resources: ReadyRealmResourcePresentation; workerResourceState?: ReadyWorkerResourceState }>) {
const tooltipIdPrefix = `realm-resource-tooltip-${useId().replace(/:/g, '')}`;
const railRef = useRef<HTMLElement>(null);
const [activeTooltip, setActiveTooltip] = useState<RealmResourceTooltipKey | null>(null);
Expand Down Expand Up @@ -185,7 +199,9 @@ function RealmResourceRail({
}
return {
label: RESOURCE_LABELS[resource],
status: `${formatExactRealmResourceQuantity(resources.balances[resource]) ?? '0'} stored · ${formatExactRealmResourceQuantity(resources.pendingBalances[resource]) ?? '0'} ready to collect`
status: workerResourceState
? `${formatExactRealmResourceQuantity(workerResourceState.available[resource]) ?? '0'} available`
: `${formatExactRealmResourceQuantity(resources.balances[resource]) ?? '0'} stored · ${formatExactRealmResourceQuantity(resources.pendingBalances[resource]) ?? '0'} ready to collect`
};
};

Expand Down Expand Up @@ -219,14 +235,17 @@ function RealmResourceRail({
>
<ul>
{REALM_ECONOMIC_RESOURCE_ORDER.map((resource) => {
const compact = formatCompactRealmResourceQuantity(resources.balances[resource])!;
const exact = formatExactRealmResourceQuantity(resources.balances[resource])!;
const railValue = workerResourceState?.available[resource] ?? resources.balances[resource];
const compact = formatCompactRealmResourceQuantity(railValue)!;
const exact = formatExactRealmResourceQuantity(railValue)!;
const pending = formatExactRealmResourceQuantity(resources.pendingBalances[resource])!;
return (
<li key={resource}>
<button
aria-describedby={tooltipId(resource)}
aria-label={`${RESOURCE_LABELS[resource]}: ${exact} stored; ${pending} ready to collect. Show resource details.`}
aria-label={workerResourceState
? `${RESOURCE_LABELS[resource]}: ${exact} available. Show resource details.`
: `${RESOURCE_LABELS[resource]}: ${exact} stored; ${pending} ready to collect. Show resource details.`}
className="realm-resource-rail__trigger"
type="button"
{...triggerEvents(resource)}
Expand Down Expand Up @@ -436,10 +455,17 @@ export function RealmHud({
onRequestExplore,
activeWagons = [],
onOpenActiveWagon,
workerProjection,
workerRoster,
workerResourceState,
onRecallWorker,
onRecallAllWorkers,
onRecenterKeep,
onRequestReturn
}: RealmHudProps) {
const [surface, setSurface] = useState<RealmMenuSurface>('closed');
const [workerSurface, setWorkerSurface] = useState<'closed' | 'center' | 'inspection'>('closed');
const [selectedWorker, setSelectedWorker] = useState<RealmWorkerPublicPresentation | undefined>(undefined);
const [collecting, setCollecting] = useState(false);
const triggerRef = useRef<HTMLButtonElement>(null);
const wasOpenRef = useRef(false);
Expand Down Expand Up @@ -477,6 +503,8 @@ export function RealmHud({
const pendingYield = resources !== undefined && REALM_ECONOMIC_RESOURCE_ORDER.some(
(resource) => resources.pendingBalances[resource] > 0n
);
const genericWorkersActive = workerProjection?.mode === 'active';
const availableWorkers = workerProjection?.workers.filter((worker) => worker.status === 'idle').length ?? 0;

if (selectionAnnouncementRef.current.key !== selectionAnnouncementKey) {
selectionAnnouncementRef.current = {
Expand Down Expand Up @@ -528,7 +556,19 @@ export function RealmHud({
<CastleProfileAvatar profile={playerProfile} />
</button>

{resources ? <RealmResourceRail resources={resources} /> : null}
{genericWorkersActive ? (
<button
aria-label={`Open workers, ${availableWorkers} of 4 available`}
aria-expanded={workerSurface !== 'closed'}
className="realm-worker-control"
onClick={() => setWorkerSurface((current) => current === 'closed' ? 'center' : 'closed')}
type="button"
>
<span aria-hidden="true">W</span><strong>{availableWorkers}/4</strong>
</button>
) : null}

{resources ? <RealmResourceRail resources={resources} workerResourceState={genericWorkersActive ? workerResourceState : undefined} /> : null}

<p
aria-atomic="true"
Expand All @@ -545,8 +585,8 @@ export function RealmHud({
canOpenSettings={onGraphicsPreferenceChange !== undefined}
collecting={collecting}
pendingYield={pendingYield}
canCollect={onCollectResources !== undefined}
activeWagons={activeWagons}
canCollect={!genericWorkersActive && onCollectResources !== undefined}
activeWagons={genericWorkersActive ? [] : activeWagons}
onClose={() => setSurface('closed')}
onCollect={() => void collect()}
onExplore={() => closeThen(() => onRequestExplore?.())}
Expand All @@ -559,6 +599,26 @@ export function RealmHud({
/>
) : null}

{genericWorkersActive && workerProjection && workerSurface === 'center' ? (
<WorkerCommandCenter
id="realm-worker-command-center"
onClose={() => setWorkerSurface('closed')}
onRecallAllWorkers={onRecallAllWorkers}
onRecallWorker={onRecallWorker}
onSelectWorker={(worker) => { setSelectedWorker(worker); setWorkerSurface('inspection'); }}
roster={workerRoster}
workers={workerProjection.workers}
/>
) : null}
{genericWorkersActive && selectedWorker && workerSurface === 'inspection' ? (
<WorkerInspectionPanel
id="realm-worker-inspection"
onRecallWorker={onRecallWorker}
onRequestClose={() => { setSelectedWorker(undefined); setWorkerSurface('center'); }}
worker={selectedWorker}
/>
) : null}

{surface === 'settings' && onGraphicsPreferenceChange ? (
<SettingsPanel
audioMuted={audioMuted}
Expand Down
16 changes: 16 additions & 0 deletions src/components/realm/RealmMapScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ import {
} from './realmRendererRecovery';
import './RealmMapScreen.css';
import './RealmCastlePresentation.css';
import type { ReadyWorkerProjection, ReadyWorkerResourceState, WorkerRosterPresentation } from './realmWorkerPresentation';

export {
BLOCKED_SHARED_FOREST_PROJECTION_SIGNATURE,
Expand Down Expand Up @@ -192,6 +193,11 @@ type RealmMapScreenProps = Readonly<{
onDispatchStoneExpedition?: (siteId: string) => Promise<void>;
/** Guarded owner-only Stone settlement reducer; never supplied to observers. */
onClaimStoneExpedition?: () => Promise<void>;
workerProjection?: ReadyWorkerProjection;
workerRoster?: WorkerRosterPresentation;
workerResourceState?: ReadyWorkerResourceState;
onRecallWorker?: (workerId: string) => Promise<void>;
onRecallAllWorkers?: () => Promise<void>;
graphicsPreference?: GraphicsPreference;
resolvedGraphicsQuality?: GraphicsQualityTier;
audioMuted?: boolean;
Expand Down Expand Up @@ -282,6 +288,11 @@ function CanonicalRealmMapScreen({
stoneExpedition,
onDispatchStoneExpedition,
onClaimStoneExpedition,
workerProjection,
workerRoster,
workerResourceState,
onRecallWorker,
onRecallAllWorkers,
graphicsPreference,
resolvedGraphicsQuality,
audioMuted,
Expand Down Expand Up @@ -1955,6 +1966,11 @@ function CanonicalRealmMapScreen({
onRequestExplore={() => dispatchInteraction({ type: 'open-navigator' })}
activeWagons={activeWagons}
onOpenActiveWagon={openActiveWagon}
workerProjection={observerMode ? undefined : workerProjection}
workerRoster={observerMode ? undefined : workerRoster}
workerResourceState={observerMode ? undefined : workerResourceState}
onRecallWorker={observerMode ? undefined : onRecallWorker}
onRecallAllWorkers={observerMode ? undefined : onRecallAllWorkers}
keepCoord={keepCoord}
selectedCell={selectedCell}
selectedTerrainKind={selectedTerrainKind}
Expand Down
1 change: 1 addition & 0 deletions src/components/realm/RealmPlayerChrome.css
Original file line number Diff line number Diff line change
Expand Up @@ -436,3 +436,4 @@
transition: none;
}
}
.realm-worker-control{position:fixed;top:clamp(.75rem,2.2vw,1.5rem);right:clamp(4.8rem,9vw,7rem);z-index:8;display:flex;align-items:center;gap:.3rem;min-width:3.15rem;min-height:2.8rem;padding:.35rem .55rem;border:1px solid rgb(240 207 130 / 58%);border-radius:999px;background:linear-gradient(180deg,rgb(49 34 63 / 94%),rgb(16 16 24 / 96%));color:#f7dda0;box-shadow:0 .35rem 1rem rgb(0 0 0 / 28%);font-size:.7rem;cursor:pointer}.realm-worker-control span{display:grid;width:1.35rem;height:1.35rem;place-items:center;border:1px solid rgb(205 158 240 / 56%);border-radius:50%;color:#d7b7ee;font-size:.63rem;font-weight:900}.realm-worker-control strong{font-size:.68rem;letter-spacing:.04em}.realm-worker-control:focus-visible{outline:2px solid #f5d994;outline-offset:2px}
1 change: 1 addition & 0 deletions src/components/realm/WorkerCommandCenter.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading