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>
This commit is contained in:
Ilia Denisov
2026-05-12 00:13:19 +02:00
parent 070fdc0ee5
commit 2d17760a5e
20 changed files with 1572 additions and 118 deletions
@@ -37,6 +37,12 @@ export interface RenderedReportSource {
* 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,
@@ -46,6 +52,7 @@ export function createRenderedReportSource(
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);
},
};