2d17760a5e
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>
60 lines
2.3 KiB
TypeScript
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);
|
|
},
|
|
};
|
|
}
|