From 06bf2f17b114c1faa2c33a9855c41d045731d314 Mon Sep 17 00:00:00 2001 From: fireshaper Date: Wed, 4 Nov 2026 18:05:00 -0500 Subject: [PATCH] Step 29.3: the map shell and panel host Co-Authored-By: Claude Opus 5.5 --- apps/web/src/App.tsx | 12 +- apps/web/src/layout/Layout.tsx | 101 +++++++--------- apps/web/src/layout/MapBackdrop.tsx | 47 ++++++++ apps/web/src/layout/PanelHost.tsx | 152 ++++++++++++++++++++++++ apps/web/src/map/MapCanvas.tsx | 58 +++++++-- apps/web/src/map/camera.ts | 15 +++ apps/web/src/map/selection.ts | 11 ++ apps/web/src/screens/Dashboard.tsx | 10 +- apps/web/src/screens/Fleet.tsx | 4 +- apps/web/src/screens/HQs.tsx | 4 +- apps/web/src/screens/LocationBoards.tsx | 2 +- apps/web/src/screens/Locations.tsx | 4 +- apps/web/src/screens/MapScreen.tsx | 34 ++---- apps/web/src/screens/Market.tsx | 4 +- apps/web/src/screens/Pilots.tsx | 4 +- apps/web/src/screens/TutorialBoard.tsx | 2 +- apps/web/src/ui/Screen.tsx | 4 +- apps/web/test/map-canvas-double.tsx | 4 + 18 files changed, 355 insertions(+), 117 deletions(-) create mode 100644 apps/web/src/layout/MapBackdrop.tsx create mode 100644 apps/web/src/layout/PanelHost.tsx create mode 100644 apps/web/src/map/selection.ts diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 193f2e5..0e62789 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,4 +1,4 @@ -import { lazy, Suspense, useEffect, type ReactNode } from "react"; +import { useEffect, type ReactNode } from "react"; import { Route, Routes } from "react-router"; import { useApi } from "./api/context"; import { startClockTicker, syncClock } from "./clock/clock"; @@ -17,6 +17,7 @@ import { Fleet } from "./screens/Fleet"; import { Dashboard } from "./screens/Dashboard"; import { HQs } from "./screens/HQs"; import { Locations } from "./screens/Locations"; +import { MapScreen } from "./screens/MapScreen"; import { Market } from "./screens/Market"; import { Pilots } from "./screens/Pilots"; import { Logbook } from "./screens/Logbook"; @@ -27,16 +28,9 @@ import { usePlayerState, useRefetchOnArrival } from "./state/player"; import { RadioCallSheet, useRefetchOnRadio } from "./components/RadioCalls"; import { Button } from "./ui"; -// The map pulls in PixiJS; load it only when the Map tab is opened. -const MapScreen = lazy(() => import("./screens/MapScreen").then((m) => ({ default: m.MapScreen }))); - const SCREEN_COMPONENTS: Record<(typeof SCREENS)[number]["path"], ReactNode> = { "/": , - "/map": ( - Loading map…

}> - -
- ), + "/map": , "/contracts": , "/fleet": , "/pilots": , diff --git a/apps/web/src/layout/Layout.tsx b/apps/web/src/layout/Layout.tsx index ff7810e..3f463a3 100644 --- a/apps/web/src/layout/Layout.tsx +++ b/apps/web/src/layout/Layout.tsx @@ -1,9 +1,11 @@ -import type { ReactNode } from "react"; -import { Link, NavLink, Outlet } from "react-router"; +import type { CSSProperties, ReactNode } from "react"; +import { Link, NavLink, Outlet, useLocation } from "react-router"; import { GameClock } from "../clock/GameClock"; import { SCREENS, type ScreenPath } from "../routes"; import { Icon } from "../ui"; import { useTrackScreen } from "./lastScreen"; +import { MapBackdrop } from "./MapBackdrop"; +import { panelIsWide, PanelHost } from "./PanelHost"; export interface LayoutProps { /** Shown at the top right. Credits arrive with the player state (Step 8.5). */ @@ -16,9 +18,9 @@ export interface LayoutProps { } /** - * HUD header with credits and game clock, the current screen, and icon - * navigation: a bottom bar on phones, a left rail from `md` (CR-005). - * `--header-h` and `--nav-h` let full-height screens (the map) fit exactly. + * The shell (CR-029): the map behind everything, a floating glass header with + * credits and game clock, the current screen in a panel, and icon navigation: + * a bottom bar on phones, a floating rail from `md`. */ export function Layout({ credits = "—", @@ -27,53 +29,52 @@ export function Layout({ attention = {}, }: LayoutProps) { useTrackScreen(); + const { pathname } = useLocation(); + const panelWidth = panelIsWide(pathname) ? "45rem" : "26.25rem"; return ( -
-
+
+ +
{/* The wordmark only fits beside the chips from 640 px; on phones the mark alone. */}
- DEADHEAD - - Space Freight - + DEADHEAD
-
-
- - - {credits} - -
-
- - +
+ + {clock} -
+
+ + {credits} +
- {banner} + {banner && ( +
+ {banner} +
+ )} -
+ -
+