Files
galaxy-game/ui/frontend/src/lib/rendered-report.svelte.ts
T
Ilia Denisov 2d17760a5e ui/phase-26: history mode (turn navigator + read-only banner)
Split GameStateStore into currentTurn (server's latest) and viewedTurn
(displayed snapshot) so history excursions don't corrupt the resume
bookmark or the live-turn bound. Add viewTurn / returnToCurrent /
historyMode rune, plus a game-history cache namespace that stores
past-turn reports for fast re-entry. OrderDraftStore.bindClient takes
a getHistoryMode getter and short-circuits add / remove / move while
the user is viewing a past turn; RenderedReportSource skips the order
overlay in the same case. Header replaces the static "turn N" with a
clickable triplet (TurnNavigator), the layout mounts HistoryBanner
under the header, and visibility-refresh is a no-op while history is
active.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 00:13:19 +02:00

60 lines
2.3 KiB
TypeScript

// 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);
},
};
}