ui/phase-12: order composer skeleton

OrderDraftStore persists per-game command drafts in Cache; the
sidebar Order tab renders the list with a per-row delete control.
The layout passes a `historyMode` prop through Sidebar / BottomTabs
as a constant `false`, so Phase 26 only flips the source.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-08 23:26:58 +02:00
parent e5dab2a43a
commit 460591c159
18 changed files with 1022 additions and 53 deletions
@@ -34,6 +34,10 @@ the next game's snapshot is loaded fresh.
import Order from "$lib/sidebar/order-tab.svelte";
import type { MobileTool } from "$lib/sidebar/types";
import { GameStateStore, GAME_STATE_CONTEXT_KEY } from "$lib/game-state.svelte";
import {
ORDER_DRAFT_CONTEXT_KEY,
OrderDraftStore,
} from "../../../sync/order-draft.svelte";
import { session } from "$lib/session-store.svelte";
import { loadStore } from "../../../platform/store/index";
import { loadCore } from "../../../platform/core/index";
@@ -45,6 +49,9 @@ the next game's snapshot is loaded fresh.
let sidebarOpen = $state(false);
let mobileTool: MobileTool = $state("map");
// Phase 12 ships the prop wiring; Phase 26 replaces this constant
// with the real history-mode signal from `lib/history-mode.ts`.
const historyMode = false;
const gameId = $derived(page.params.id ?? "");
const isOnMap = $derived(/\/games\/[^/]+\/map\/?$/.test(page.url.pathname));
@@ -54,6 +61,8 @@ the next game's snapshot is loaded fresh.
const gameState = new GameStateStore();
setContext(GAME_STATE_CONTEXT_KEY, gameState);
const orderDraft = new OrderDraftStore();
setContext(ORDER_DRAFT_CONTEXT_KEY, orderDraft);
function toggleSidebar(): void {
sidebarOpen = !sidebarOpen;
@@ -85,7 +94,10 @@ the next game's snapshot is loaded fresh.
deviceSessionId,
gatewayResponsePublicKey: GATEWAY_RESPONSE_PUBLIC_KEY,
});
await gameState.init({ client, cache, gameId });
await Promise.all([
gameState.init({ client, cache, gameId }),
orderDraft.init({ cache, gameId }),
]);
} catch (err) {
gameState.failBootstrap(describeBootstrapError(err));
}
@@ -94,6 +106,7 @@ the next game's snapshot is loaded fresh.
onDestroy(() => {
gameState.dispose();
orderDraft.dispose();
});
function describeBootstrapError(err: unknown): string {
@@ -118,12 +131,17 @@ the next game's snapshot is loaded fresh.
{@render children()}
{/if}
</main>
<Sidebar open={sidebarOpen} onClose={() => (sidebarOpen = false)} />
<Sidebar
open={sidebarOpen}
onClose={() => (sidebarOpen = false)}
{historyMode}
/>
</div>
<BottomTabs
{gameId}
activeTool={effectiveTool}
onSelectTool={(tool) => (mobileTool = tool)}
hideOrder={historyMode}
/>
</div>