// Provides a derived view of the server `GameReport` overlaid with // the player's local order draft. Every consumer that needs to // render the player's current intent (inspector, map, mobile sheet) // subscribes through this context instead of reading `gameState.report` // directly. // // Lifetime matches the in-game shell layout: one source per game, // rebuilt on layout remount. The source itself is a thin reactive // wrapper — the actual overlay computation lives in // `applyOrderOverlay` (api/game-state.ts) and runs lazily on every // access through the `report` getter. import { applyOrderOverlay, type GameReport, } from "../api/game-state"; import type { GameStateStore } from "./game-state.svelte"; import type { OrderDraftStore } from "../sync/order-draft.svelte"; /** * RENDERED_REPORT_CONTEXT_KEY is the Svelte context key the in-game * shell layout uses to expose a `RenderedReportSource` instance to * descendants. Consumers read the latest overlay through `source.report` * (a reactive getter) and re-render when the underlying stores * change. */ export const RENDERED_REPORT_CONTEXT_KEY = Symbol("rendered-report"); export interface RenderedReportSource { readonly report: GameReport | null; } /** * createRenderedReportSource binds the live `GameStateStore` and * `OrderDraftStore` to a getter that returns the overlay-applied * report on every read. The getter is reactive: Svelte tracks the * underlying `$state` accesses inside `applyOrderOverlay`, so any * change to the report or the draft re-runs every dependent * `$derived` block. * * Phase 26: the order draft is composed against the *current* turn, * so projecting it onto a historical snapshot would render fictional * intent on a past report. In history mode the getter returns the * raw server snapshot untouched — the order tab is hidden anyway and * mutations are gated at the store, so nothing else needs to know. */ export function createRenderedReportSource( gameState: GameStateStore, orderDraft: OrderDraftStore, ): RenderedReportSource { return { get report(): GameReport | null { const raw = gameState.report; if (raw === null) return null; if (gameState.historyMode) return raw; return applyOrderOverlay(raw, orderDraft.commands, orderDraft.statuses); }, }; }