import { renderEveSurface, type EveCommandIntent, type EveEmbeddedDocumentRequest, type EveSurfaceComponent, } from "../node_modules/@gamecult/eve-browser-lowering/dist/index.js"; import type { AetheriaRtsApi, AetheriaMenuSurfaceDocument, AetheriaMenuSurfaceComponent, AuthorityStatusDocument, AetheriaRuntimeViewportFeedSnapshot, BodyView, DaemonHealthDocument, GravityInfluence, InventoryItem, InventoryDocument, SelectedObjectDocument, StarbridgeSessionDocument, ViewObject, Viewport, ViewportResponse, } from "./aetheria-rts-contract.js"; export {}; declare global { interface Window { aetheriaRts: AetheriaRtsApi; } } const canvas = requiredElement("#map"); const statusEl = requiredElement("#status"); const zoomLabel = requiredElement("#zoom-label"); const selectionTitle = requiredElement("#selection-title"); const selectionDetails = requiredElement("#selection-details"); const frameDetails = requiredElement("#frame-details"); const starbridgeDetails = requiredElement("#starbridge-details"); const runtimeSurfaceDetails = requiredElement("#runtime-surface-details"); const controlledList = requiredElement("#controlled-list"); const debugSurfaceHost = requiredElement("#debug-surface-host"); const ctx = requiredContext(canvas); let zoom = 1; let centerX = 0; let centerY = 0; let selectedEntityIndex = -1; let latest: ViewportResponse | null = null; let latestSelected: SelectedObjectDocument | null = null; let latestInventory: InventoryDocument | null = null; let latestDaemonHealth: DaemonHealthDocument | null = null; let latestAuthority: AuthorityStatusDocument | null = null; let latestStarbridge: StarbridgeSessionDocument | null = null; let latestSurfaceIndex: Awaited> | null = null; let latestOperationReceipt: string | null = null; let latestReceivedAt = 0; let latestPollMs = 0; let refreshInFlight = false; let viewportFeedUnsubscribe: (() => void) | null = null; let eveSurfacePoll: number | null = null; let latestEveSurfaceKey = ""; let activeMainMenuSurfaceId = "aetheria.main_menu.root"; let renderDaemonEveSurfaceNow: (() => Promise) | null = null; const mainMenuMode = window.location.hash === "#main-menu"; const legacyViewportMode = window.location.hash === "#legacy-viewport"; function requiredElement(selector: string): TElement { const element = document.querySelector(selector); if (!element) { throw new Error(`Aetheria RTS shell is missing ${selector}.`); } return element; } function requiredContext(target: HTMLCanvasElement): CanvasRenderingContext2D { const context = target.getContext("2d"); if (!context) { throw new Error("Canvas 2D context is unavailable."); } return context; } const worldSpan = () => 2200 / zoom; function currentViewport(): Viewport { const span = worldSpan(); const aspect = Math.max(0.25, canvas.clientWidth / Math.max(1, canvas.clientHeight)); const width = span * aspect; const height = span; return { minX: centerX - width * 0.5, minY: centerY - height * 0.5, maxX: centerX + width * 0.5, maxY: centerY + height * 0.5 }; } function setStatus(text: string): void { statusEl.textContent = text; } async function showDaemonEveSurface(): Promise { document.body.classList.remove("main-menu-mode"); document.body.classList.add("eve-game-mode"); viewportFeedUnsubscribe?.(); let host = document.querySelector("#main-menu-host"); if (!host) { host = document.createElement("section"); host.id = "main-menu-host"; host.className = "main-menu-host"; document.body.append(host); } host.setAttribute("aria-label", "Aetheria daemon Eve surface"); const renderLatest = async () => { try { const surface = await window.aetheriaRts.eveSurface({ recordKey: "eve:surface:aetheria.daemon.game", }); const loweredSurface = withActiveMainMenuPanel(surface, activeMainMenuSurfaceId); const surfaceKey = `${surface.providerId}:${surface.surface.id}:${surface.version}:${surface.updatedAtUtc}:${activeMainMenuSurfaceId}`; if (surfaceKey !== latestEveSurfaceKey) { latestEveSurfaceKey = surfaceKey; renderEveSurface(loweredSurface, host, { body: document.body, assetUrlResolver: resolveAetheriaAssetUrl, clientId: "aetheria.rts.electron", commandSink: intent => submitEveCommand(intent), documentResolver: resolveEveDocument, source: "Aetheria Daemon", statusElement: statusEl, }); wireWindowControls(); } } catch (error) { setStatus(error instanceof Error ? error.message : "Aetheria daemon Eve surface unavailable."); } }; renderDaemonEveSurfaceNow = renderLatest; await renderLatest(); if (eveSurfacePoll == null) { eveSurfacePoll = window.setInterval(() => { void renderLatest(); }, 250); } } async function showMainMenuPanel(surfaceId: string): Promise { activeMainMenuSurfaceId = normalizeMainMenuSurfaceId(surfaceId); latestEveSurfaceKey = ""; if (renderDaemonEveSurfaceNow) { await renderDaemonEveSurfaceNow(); return; } await showDaemonEveSurface(); } function normalizeMainMenuSurfaceId(surfaceId: string): string { switch (surfaceId) { case "aetheria.main_menu.settings": case "aetheria.main_menu.player_settings": case "aetheria.main_menu.verse_settings": case "aetheria.main_menu.input_settings": return surfaceId; default: return "aetheria.main_menu.root"; } } function withActiveMainMenuPanel( document: AetheriaMenuSurfaceDocument, panelSurfaceId: string, ): AetheriaMenuSurfaceDocument { return { ...document, surface: { ...document.surface, root: rewriteMainMenuPanelSlot(document.surface.root, panelSurfaceId), }, }; } type MutableEveComponent = AetheriaMenuSurfaceComponent & { layout?: Record; style?: Record; }; function rewriteMainMenuPanelSlot( component: AetheriaMenuSurfaceComponent, panelSurfaceId: string, ): AetheriaMenuSurfaceComponent { const source = component as MutableEveComponent; const props = source.props ?? {}; const embeddedDocuments = source.embeddedDocuments ?? []; const isMainMenuSlot = props.slotId === "mainMenuPanel" || embeddedDocuments.some(slot => slot.slotId === "mainMenuPanel"); return { ...source, props: isMainMenuSlot ? { ...props, documentId: panelSurfaceId } : props, embeddedDocuments: isMainMenuSlot ? embeddedDocuments.map(slot => slot.slotId === "mainMenuPanel" ? { ...slot, documentId: panelSurfaceId } : slot) : source.embeddedDocuments, children: source.children.map(child => rewriteMainMenuPanelSlot(child, panelSurfaceId)), }; } async function resolveEveDocument( request: EveEmbeddedDocumentRequest, component: EveSurfaceComponent, ): Promise<{ document?: unknown; documentId: string; schemaId: string; surface?: AetheriaMenuSurfaceDocument["surface"] } | undefined> { if (request.schemaId === "gamecult.eve.surface.v1" || request.slotId === "mainMenuPanel") { const surface = await window.aetheriaRts.mainMenuSurface({ surfaceId: request.documentId, inGame: false, canOpenRuntimeInputScreen: false, panelOnly: true, }); return { documentId: request.documentId, schemaId: "gamecult.eve.surface.v1", surface: surface.surface, }; } const viewport = viewportFromComponent(component); if (request.schemaId === "gamecult.aetheria.render_splats_viewport.v1" || request.slotId === "renderSplats") { return { document: await window.aetheriaRts.renderSplatsViewport(viewport), documentId: request.documentId, schemaId: "gamecult.aetheria.render_splats_viewport.v1", }; } if (request.schemaId === "gamecult.aetheria.gravity_viewport.v1" || request.slotId === "gravity") { return { document: await window.aetheriaRts.gravityViewport(viewport), documentId: request.documentId, schemaId: "gamecult.aetheria.gravity_viewport.v1", }; } if (request.schemaId === "gamecult.aetheria.objects_viewport.v1" || request.slotId === "objects") { return { document: await window.aetheriaRts.objectsViewport(viewport), documentId: request.documentId, schemaId: "gamecult.aetheria.objects_viewport.v1", }; } return undefined; } function viewportFromComponent(component: EveSurfaceComponent): Viewport { const props = component.props ?? {}; return { minX: numberProp(props.minX, -1500), minY: numberProp(props.minY, -1000), maxX: numberProp(props.maxX, 1500), maxY: numberProp(props.maxY, 1000), }; } function numberProp(value: unknown, fallback: number): number { if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string") { const parsed = Number.parseFloat(value); if (Number.isFinite(parsed)) return parsed; } return fallback; } function resolveAetheriaAssetUrl(uri: string): string { if (!uri.startsWith("resources://")) return uri; const resourcePath = uri.slice("resources://".length).replace(/^\/+/, ""); const filePath = resourcePath.match(/\.[a-z0-9]+$/i) ? resourcePath : `${resourcePath}.png`; return new URL(`../../Assets/Resources/${filePath}`, document.baseURI).href; } async function startDebugSurface(): Promise { try { renderDebugSurface(await window.aetheriaRts.debugSurface()); window.aetheriaRts.watchDebugSurface(renderDebugSurface); } catch (error) { debugSurfaceHost.textContent = error instanceof Error ? error.message : "CultUI debug surface unavailable."; } } function renderDebugSurface(surface: AetheriaMenuSurfaceDocument): void { renderEveSurface(surface, debugSurfaceHost, { body: document.body, assetUrlResolver: resolveAetheriaAssetUrl, clientId: "aetheria.rts.electron", commandSink: intent => handleMenuCommand(intent.command, surface), source: "CultUI Debug", statusElement: statusEl, }); } function wireWindowControls(): void { document.querySelectorAll("[data-window-control]").forEach(button => { if (button.dataset.windowControlWired === "true") return; button.dataset.windowControlWired = "true"; button.addEventListener("click", () => { const action = button.dataset.windowControl as "minimize" | "maximize" | "close" | undefined; if (action) void window.aetheriaRts.windowControl(action); }); }); } function handleMenuCommand(commandOrIntent: string | EveCommandIntent, surface?: AetheriaMenuSurfaceDocument): void { const command = typeof commandOrIntent === "string" ? commandOrIntent : commandOrIntent.command; switch (command) { case "aetheria.main_menu.root.show_settings": void showMainMenuPanel("aetheria.main_menu.settings"); return; case "aetheria.main_menu.settings.show_player_settings": void showMainMenuPanel("aetheria.main_menu.player_settings"); return; case "aetheria.main_menu.settings.show_verse_settings": void showMainMenuPanel("aetheria.main_menu.verse_settings"); return; case "aetheria.main_menu.settings.show_input_settings": void showMainMenuPanel("aetheria.main_menu.input_settings"); return; case "aetheria.main_menu.settings.back_to_settings": void showMainMenuPanel("aetheria.main_menu.settings"); return; case "aetheria.main_menu.settings.back_to_main": void showMainMenuPanel("aetheria.main_menu.root"); return; default: setStatus(`${surface?.title ?? "Aetheria Starbridge"}: ${command || "no command"}`); return; } } async function submitEveCommand(intent: EveCommandIntent): Promise { if (intent.command.startsWith("aetheria.main_menu.")) { handleMenuCommand(intent.command); return; } try { const receipt = await window.aetheriaRts.submitEveCommand({ providerId: intent.providerId, surfaceId: intent.surfaceId, command: intent.command, clientId: intent.clientId, issuedAtUtc: intent.issuedAt, payload: intent.payload, }); setStatus(`${intent.command}: ${receipt.accepted ? "submitted" : "rejected"}`); } catch (error) { setStatus(error instanceof Error ? error.message : `Failed to submit ${intent.command}.`); } } function stopEveSurfacePoll(): void { if (eveSurfacePoll == null) return; window.clearInterval(eveSurfacePoll); eveSurfacePoll = null; } function startViewportFeed(): void { const viewport = currentViewport(); viewportFeedUnsubscribe?.(); viewportFeedUnsubscribe = window.aetheriaRts.watchViewportFeed({ viewport, selectedEntityIndex }, applyViewportSnapshot); } function applyViewportSnapshot(snapshot: AetheriaRuntimeViewportFeedSnapshot): void { latestPollMs = snapshot.sampleMs; latestReceivedAt = Date.parse(snapshot.receivedAtUtc) || Date.now(); latest = snapshot.viewport; latestSelected = snapshot.selectedObject; latestInventory = snapshot.inventory; latestDaemonHealth = snapshot.daemonHealth; latestAuthority = snapshot.authorityStatus; latestStarbridge = snapshot.starbridgeSession; const controlled = snapshot.viewport.objects.find(object => object.controlled); if (controlled && selectedEntityIndex < 0) { selectedEntityIndex = controlled.entityIndex; centerX = controlled.x; centerY = controlled.y; startViewportFeed(); return; } setStatus(`${snapshot.viewport.zoneName || `Zone ${snapshot.viewport.zoneIndex}`} frame ${snapshot.viewport.frameId}`); render(); renderPanels(); } async function refreshRuntimeSurfaces(): Promise { try { latestSurfaceIndex = await window.aetheriaRts.surfaceCatalogIndex(); renderPanels(); } catch (error) { runtimeSurfaceDetails.innerHTML = details([ ["State", error instanceof Error ? error.message : "unavailable"] ]); } } function resizeCanvas(): void { const ratio = Math.max(1, window.devicePixelRatio || 1); const width = Math.max(1, Math.floor(canvas.clientWidth * ratio)); const height = Math.max(1, Math.floor(canvas.clientHeight * ratio)); if (canvas.width !== width || canvas.height !== height) { canvas.width = width; canvas.height = height; } ctx.setTransform(ratio, 0, 0, ratio, 0, 0); } function worldToScreen(x: number, y: number): { x: number; y: number } { const viewport = currentViewport(); const sx = (x - viewport.minX) / (viewport.maxX - viewport.minX) * canvas.clientWidth; const sy = canvas.clientHeight - (y - viewport.minY) / (viewport.maxY - viewport.minY) * canvas.clientHeight; return { x: sx, y: sy }; } function screenToWorld(x: number, y: number): { x: number; y: number } { const viewport = currentViewport(); return { x: viewport.minX + x / Math.max(1, canvas.clientWidth) * (viewport.maxX - viewport.minX), y: viewport.minY + (1 - y / Math.max(1, canvas.clientHeight)) * (viewport.maxY - viewport.minY) }; } function worldRadiusToScreen(radius: number): number { const viewport = currentViewport(); return radius / (viewport.maxX - viewport.minX) * canvas.clientWidth; } function render(): void { resizeCanvas(); ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); drawGrid(); if (!latest) { return; } for (const influence of latest.gravityInfluences) { drawGravity(influence); } for (const body of latest.bodies) { drawBody(body); } for (const object of latest.objects) { drawObject(object); } } function drawGrid(): void { const viewport = currentViewport(); const spacing = chooseGridSpacing((viewport.maxX - viewport.minX) / 8); const startX = Math.floor(viewport.minX / spacing) * spacing; const startY = Math.floor(viewport.minY / spacing) * spacing; ctx.lineWidth = 1; ctx.strokeStyle = "#1b242a"; ctx.beginPath(); for (let x = startX; x <= viewport.maxX; x += spacing) { const screen = worldToScreen(x, viewport.minY); ctx.moveTo(screen.x, 0); ctx.lineTo(screen.x, canvas.clientHeight); } for (let y = startY; y <= viewport.maxY; y += spacing) { const screen = worldToScreen(viewport.minX, y); ctx.moveTo(0, screen.y); ctx.lineTo(canvas.clientWidth, screen.y); } ctx.stroke(); } function chooseGridSpacing(target: number): number { const power = 10 ** Math.floor(Math.log10(Math.max(1, target))); const scaled = target / power; if (scaled > 5) return power * 10; if (scaled > 2) return power * 5; return power * 2; } function drawGravity(influence: GravityInfluence): void { const screen = worldToScreen(influence.x, influence.y); const radius = worldRadiusToScreen(influence.radius); ctx.lineWidth = 1; ctx.strokeStyle = influence.kind === "Sun" ? "rgba(246, 192, 82, 0.28)" : "rgba(97, 174, 197, 0.24)"; ctx.fillStyle = influence.kind === "Sun" ? "rgba(246, 192, 82, 0.05)" : "rgba(97, 174, 197, 0.04)"; ctx.beginPath(); ctx.arc(screen.x, screen.y, Math.max(1, radius), 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } function drawBody(body: BodyView): void { const screen = worldToScreen(body.x, body.y); const radius = Math.max(3, Math.min(28, worldRadiusToScreen(body.radius) * 0.025)); ctx.fillStyle = body.kind === "sun" ? "#f4bc5d" : body.isAsteroidBelt ? "#9d9080" : "#71a8b7"; ctx.beginPath(); ctx.arc(screen.x, screen.y, radius, 0, Math.PI * 2); ctx.fill(); } function drawObject(object: ViewObject): void { const screen = worldToScreen(object.x, object.y); const selected = object.entityIndex === selectedEntityIndex; const station = object.kind.toLowerCase().includes("station"); const size = selected ? 9 : object.controlled ? 7 : 5; ctx.save(); ctx.translate(screen.x, screen.y); ctx.rotate(Math.atan2(object.directionY, object.directionX)); ctx.fillStyle = object.isActive ? selected ? "#ffffff" : object.controlled ? "#54d18a" : object.factionKey === "raider" ? "#e36b4b" : "#aeb7bd" : "#56616a"; ctx.strokeStyle = "#0d1013"; ctx.lineWidth = 2; ctx.beginPath(); if (station) { ctx.rect(-size, -size, size * 2, size * 2); } else { ctx.moveTo(size + 4, 0); ctx.lineTo(-size, size * 0.7); ctx.lineTo(-size * 0.45, 0); ctx.lineTo(-size, -size * 0.7); } ctx.closePath(); ctx.stroke(); ctx.fill(); ctx.restore(); if (object.targetEntityIndex >= 0) { const target = latest?.objects.find(candidate => candidate.entityIndex === object.targetEntityIndex); if (target) { const targetScreen = worldToScreen(target.x, target.y); ctx.strokeStyle = object.controlled ? "rgba(84, 209, 138, 0.42)" : "rgba(227, 107, 75, 0.36)"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(screen.x, screen.y); ctx.lineTo(targetScreen.x, targetScreen.y); ctx.stroke(); } } if (selected) { ctx.strokeStyle = "#ffffff"; ctx.lineWidth = 1; ctx.beginPath(); ctx.arc(screen.x, screen.y, 14, 0, Math.PI * 2); ctx.stroke(); } } function renderPanels(): void { if (!latest) { return; } const selected = latestSelected?.selected ?? null; selectionTitle.textContent = selected?.displayName || "No Selection"; selectionDetails.innerHTML = selected ? details([ ["Entity", selected.entityIndex.toString()], ["Key", selected.entityKey], ["Kind", selected.kind || "unknown"], ["Faction", selected.factionKey || "unknown"], ["State", selected.isActive ? "active" : "disabled"], ["Hull", selected.status.hull.toFixed(1)], ["Shield", selected.status.shield.toFixed(1)], ["Heat", selected.status.heat.toFixed(1)], ["Position", `${selected.x.toFixed(1)}, ${selected.y.toFixed(1)}, z ${selected.z.toFixed(1)}`], ["Velocity", `${selected.velocityX.toFixed(2)}, ${selected.velocityY.toFixed(2)}`], ["Target", selected.targetEntityIndex >= 0 ? `#${selected.targetEntityIndex}` : "none"], ["Controlled", selected.controlled ? "yes" : "no"], ["Equipment", inventoryText(latestInventory?.equipment ?? [])], ["Cargo", inventoryText(latestInventory?.cargo ?? [])] ]) : `
State
Click a visible object on the map.
`; frameDetails.innerHTML = details([ ["Run", latest.runId], ["Zone", `${latest.zoneIndex}`], ["Frame", latest.frameId.toString()], ["Sim Time", `${latest.simulationTimeSeconds.toFixed(2)}s`], ["Frame Age", `${Math.max(0, Date.now() - latestReceivedAt)}ms`], ["Viewport Poll", `${latestPollMs.toFixed(1)}ms`], ["Visible", latest.objects.length.toString()], ["Daemon", latestDaemonHealth ? `${latestDaemonHealth.status} / ${latestDaemonHealth.transport}` : "unknown"], ["Authority", latestAuthority ? latestAuthority.policyId : "unknown"], ["Mode", latestAuthority ? latestAuthority.defaultMode : "unknown"], ["Last Op", latestOperationReceipt ?? "none"] ]); runtimeSurfaceDetails.innerHTML = latestSurfaceIndex ? details([ ["Catalog", latestSurfaceIndex.catalogId], ["Queries", surfaceListText(latestSurfaceIndex.queries)], ["Live Feeds", surfaceListText(latestSurfaceIndex.liveFeeds)], ["Operations", surfaceListText(latestSurfaceIndex.operations)], ["Pointers", surfaceListText(latestSurfaceIndex.statePointers)], ["Native Views", surfaceListText(latestSurfaceIndex.nativeSliceViews)] ]) : `
State
Discovering runtime surfaces.
`; starbridgeDetails.innerHTML = latestStarbridge ? details([ ["Scenario", latestStarbridge.scenarioName || latestStarbridge.scenarioId], ["Phase", latestStarbridge.phase], ["Wave", latestStarbridge.currentWaveIndex.toString()], ["Base", latestStarbridge.baseStatus.displayName || "unknown"], ["Hull", latestStarbridge.baseStatus.hull.toFixed(1)], ["Shield", latestStarbridge.baseStatus.shield.toFixed(1)], ["Heat", latestStarbridge.baseStatus.heat.toFixed(1)], ["Next", waveText(latestStarbridge)], ["Stock", stockText(latestStarbridge)], ["Roles", roleText(latestStarbridge)] ]) : `
State
No Starbridge session.
`; controlledList.innerHTML = ""; const controlledObjects = latest.objects.filter(object => object.controlled); if (controlledObjects.length === 0) { controlledList.innerHTML = `No controlled units in viewport.`; return; } for (const object of controlledObjects) { const row = document.createElement("button"); row.type = "button"; row.className = "controlled-row"; row.innerHTML = `${escapeHtml(object.displayName || object.entityKey)}#${object.entityIndex}`; row.addEventListener("click", () => { selectedEntityIndex = object.entityIndex; centerX = object.x; centerY = object.y; void refresh(); }); controlledList.append(row); } } function inventoryText(items: InventoryItem[]): string { if (items.length === 0) { return "empty"; } return items .map(item => `${item.itemKey} x${item.quantity}`) .join(", "); } function details(rows: Array<[string, string]>): string { return rows .map(([key, value]) => `
${escapeHtml(key)}
${escapeHtml(value)}
`) .join(""); } function escapeHtml(value: string): string { return value.replace(/[&<>"']/g, character => { switch (character) { case "&": return "&"; case "<": return "<"; case ">": return ">"; case "\"": return """; case "'": return "'"; default: return character; } }); } canvas.addEventListener("click", event => { if (!latest) { return; } const rect = canvas.getBoundingClientRect(); const click = screenToWorld(event.clientX - rect.left, event.clientY - rect.top); const nearest = nearestObject(click.x, click.y); if (nearest && worldRadiusToScreen(nearest.distance) < 24) { selectedEntityIndex = nearest.object.entityIndex; void refresh(); } }); canvas.addEventListener("contextmenu", event => { event.preventDefault(); if (!latest) { return; } const actor = latest.objects.find(object => object.entityIndex === selectedEntityIndex && object.controlled); if (!actor) { setStatus("Select an owned pawn or station first."); return; } const rect = canvas.getBoundingClientRect(); const click = screenToWorld(event.clientX - rect.left, event.clientY - rect.top); const nearest = nearestObject(click.x, click.y); if (nearest && nearest.object.entityIndex !== actor.entityIndex && worldRadiusToScreen(nearest.distance) < 28) { void issueTargetCommand(actor, nearest.object); return; } void issueMoveCommand(actor, click.x, click.y); }); function nearestObject(x: number, y: number): { object: ViewObject; distance: number } | null { if (!latest) { return null; } let nearest: ViewObject | null = null; let nearestDistance = Number.POSITIVE_INFINITY; for (const object of latest.objects) { const dx = object.x - x; const dy = object.y - y; const distance = Math.hypot(dx, dy); if (distance < nearestDistance) { nearest = object; nearestDistance = distance; } } return nearest ? { object: nearest, distance: nearestDistance } : null; } async function issueTargetCommand(actor: ViewObject, target: ViewObject): Promise { const receipt = await window.aetheriaRts.setTarget({ actorEntityKey: actor.entityKey, targetEntityKey: target.entityKey, observedFrameId: latest?.frameId }); latestOperationReceipt = formatOperationReceipt("setTarget", receipt, `${actor.entityKey} -> ${target.entityKey}`); setStatus(latestOperationReceipt); await refresh(); } async function issueMoveCommand(actor: ViewObject, x: number, y: number): Promise { const dx = x - actor.x; const dy = y - actor.y; const length = Math.max(0.001, Math.hypot(dx, dy)); const receipt = await window.aetheriaRts.setMoveVector({ actorEntityKey: actor.entityKey, directionX: dx / length, directionY: dy / length, scalar: 1, observedFrameId: latest?.frameId }); latestOperationReceipt = formatOperationReceipt("setMoveVector", receipt, `${actor.entityKey} ${dx.toFixed(1)},${dy.toFixed(1)}`); setStatus(latestOperationReceipt); await refresh(); } function formatOperationReceipt( label: string, receipt: Awaited>, detail: string ): string { const accepted = receipt.accepted ? "accepted" : "rejected"; return `${label} ${receipt.operationId} ${accepted} via ${receipt.route.kind} ${receipt.commandId} ${detail} @ frame ${latest?.frameId ?? "?"}`; } document.querySelector("#zoom-in")?.addEventListener("click", () => { zoom = Math.min(12, zoom * 1.4); updateZoomLabel(); void refresh(); }); document.querySelector("#zoom-out")?.addEventListener("click", () => { zoom = Math.max(0.2, zoom / 1.4); updateZoomLabel(); void refresh(); }); document.querySelector("#refresh")?.addEventListener("click", () => { void refresh(); }); window.addEventListener("resize", () => { render(); }); function updateZoomLabel(): void { zoomLabel.value = `${zoom.toFixed(1)}x`; } async function refresh(): Promise { if (refreshInFlight) { return; } refreshInFlight = true; try { startViewportFeed(); } catch (error) { setStatus(error instanceof Error ? error.message : "Unable to fetch viewport"); } finally { refreshInFlight = false; } } function surfaceListText(surfaces: ReadonlyArray<{ readonly surfaceId: string; readonly routeHint: { readonly kind: string; readonly description?: string }; readonly sources: ReadonlyArray<{ readonly sourceId: string; readonly schemaId?: string }>; }>): string { if (surfaces.length === 0) { return "none"; } return surfaces .map(surface => { const route = surface.routeHint.description ? `${surface.routeHint.kind} ${surface.routeHint.description}` : surface.routeHint.kind; const sources = surface.sources.length === 0 ? "no sources" : surface.sources .map(source => source.schemaId ? `${source.sourceId} (${source.schemaId})` : source.sourceId) .join(" + "); return `${surface.surfaceId} -> ${route}; ${sources}`; }) .join(", "); } function stockText(session: StarbridgeSessionDocument): string { if (session.stationStock.length === 0) { return "empty"; } return session.stationStock .slice(0, 4) .map(item => `${item.itemKey} x${item.quantity}`) .join(", "); } function waveText(session: StarbridgeSessionDocument): string { const wave = session.waveForecast[0]; if (!wave) { return "none"; } const attackers = wave.attackerKeys.length > 0 ? ` / ${wave.attackerKeys.join(", ")}` : ""; return `${wave.displayName || `Wave ${wave.waveIndex}`}${attackers}`; } function roleText(session: StarbridgeSessionDocument): string { if (session.runtimeRoles.length === 0) { return "none"; } return session.runtimeRoles .map(role => `${role.role}:${role.runtimeId}`) .join(", "); } if (mainMenuMode) { void showDaemonEveSurface(); } else if (legacyViewportMode) { document.body.classList.remove("main-menu-mode", "eve-game-mode"); updateZoomLabel(); void startDebugSurface(); void refreshRuntimeSurfaces(); void refresh(); } else { void showDaemonEveSurface(); }