metagame/components
PURPOSE — Shared React components used across the metagame screens (hub, shop, ships, profile, collection, overlays, modals). The screen layer composes these into pages; this layer owns the reusable chrome, the medical-UI panel/modal primitives, the rarity-card visual kit, the live nebula backdrop, and the cross-screen overlays (challenges, planet track, reward popup, update banner, feedback modal, welcome / claim-account modals).
OWNS
- The shared metagame chrome (top bar with account pill, build-version stamp, currency display; bottom nav with SHOP / PLAY / SHIPS tabs; an overlay-mode bottom bar with BACK + optional right-aligned tabs).
- The medical-UI primitives: card / flat / doors panel variants, the fullscreen scrim modal wrapper, the size-aware door-open audio cue, the one-time module-level haptic listener that pulses on primary-button presses.
- The rarity-card visual kit: rarity badge, trimmed ship sprite (uniform alpha-bbox crop drawn to a DPR-correct canvas with a sprite memo cache), collectible card (sprite + hull name + star/XP bar + locked overlay + selected-state pulse keyframes injected once at module scope).
- The live WebGL nebula backdrop wrapper around the nebula engine singleton (RAF loop, resize observer, post-FX subscription, blackout-overlay fade).
- The decorative red-orange sun layer with drifting glow lobes (currently hidden).
- The challenge popover (tier KPI claim row + per-planet challenge card list grouped by category and sorted by rarity, with live progress from challenge-store raw state).
- The planet progress sticker on the hub (per-level XP fill + level badge + level-up gold flash).
- The planet track viewer (fullscreen scrollable reward track with per-segment progress bar, glass-orb level badge, rarity-colored reward card, locked-card variant, claim handler, reward-reveal popup with auto-dismiss).
- The leaderboard table wrapper (rank / name / kills / weapon icons / highest tier, self-row highlight).
- The reward popup queue renderer (auto-dismiss timer, tap-to-skip, inline fade/pop keyframes).
- The update-detection module (version.txt polling, shared subscriber list, hub-mounted slide-up banner, route-gated fullscreen overlay).
- The feedback modal (canvas screenshot during gameplay or html2canvas DOM capture on menus, textarea, screenshot preview well, mission pause/resume bridge via a module-level active-mission ref).
- The welcome modal (callsign entry after magic-link claim) and claim-account modal (email entry for magic link).
- The onboarding shell (chrome-free centered layout for the prologue).
READS FROM
metagame/stores(player profile, wallet, display overrides, reward queue, challenge completions + per-planet stats, planet progress + claims, tier highest + next-milestone + can-claim, inventory star/XP).metagame/services(supabaseclient for direct inserts,invokeRpcfor claim / leaderboard RPCs,profileServicefor display-name updates,anchor-registryfor fly-to anchor refs).engine/core/configfor the build-version stamp polled againstversion.txt.engine/rendering/nebula-enginefor the WebGL nebula singleton lifecycle.engine/rendering/post-fx-storefor the live post-FX uniform bundle.engine/rendering/ships-v4-loaderfor ship sprite paths.engine/rendering/card-themefor rarity gradients, badge letters/fills, accent colors, effect tiers, card shadow.engine/vfx/juicefor the panel-open audio cue.engine/bridgefor theMissionHandletype used by the feedback pause bridge.data/shipsfor ship defs, rarity colors, display-name helper.data/ship-progressionfor XP-to-next-star math.data/challengesfor per-planet challenge defs, rarity ordering, and the rarity/category enums.data/planet-configfor planet metadata and the planet-id type.data/planet-progressionfor the planet track, per-level XP math, and reward-rarity colors.data/weapon-iconsfor the leaderboard weapon-icon paths and emoji fallback.- React Router (
useNavigate,useLocation) for nav-tab active state and account-pill routing. window.location/version.txtfor the update-poll loop.html2canvasfor menu-screen DOM screenshots.
PUSHES TO
metagame/stores(wallet snapshot replace on tier-milestone claim, local claim records on tier-milestone + planet-reward claims, welcome-dismiss, reward-queue dismiss).metagame/services(Supabaseplayer_feedbackinsert; RPC dispatches forclaim_tier_milestone,claim_planet_reward,get_tier_leaderboard; magic-link send viaclaimWithEmail; display-name update).engine/rendering/nebula-engine(init, resize, render, destroy across mount lifecycle).engine/vfx/juice(size-awarepanel_opencue on door-variant panel mount).- React Router (programmatic navigation from the nav tabs and account pill).
navigator.vibrate(15ms pulse on every.med-btn-primarypointer-down).window.location.reload(forced refresh when an update is available).- Window events (
ss:open-feedbacklistener,ss:mission-changeddispatch on bridge writes).
DOES NOT
- Define screens, routes, or page-level orchestration — screens compose these components and own per-page state and side effects.
- Mutate game-engine state directly. The feedback bridge calls
pause/resumeon a passed mission handle; nothing else reaches into the engine. - Compute progression curves, rarity rolls, claim eligibility, or reward contents — those live in
data/*and the stores; components only render the resolved values and dispatch claim RPCs. - Render canvas gameplay, HUD, or in-game popups — only metagame-side React.
- Persist user data locally beyond the stores it writes to; no direct
localStoragewrites from this layer. - Own audio routing, signal buses, or telemetry — talks to
Juice.firefor the panel cue and to Supabase for feedback, nothing else. - Resolve auth, session, or OAuth flows beyond invoking the player-store and profile-service methods that handle them.
Signals fired / Signals watched
- Listens for window event
ss:open-feedback(canvas ”?” tap → open feedback modal). - Dispatches window event
ss:mission-changedwhenever the active-mission bridge ref is set or cleared. - Subscribes to the post-FX store for live uniform updates on the nebula loop.
- No engine-signal traffic; everything else is React props, store selectors, and direct method calls.
Entry points
BottomNav— main-menu bottom tabs (SHOP / PLAY / SHIPS) with active-pill highlight from the current route.OverlayBottomBar— overlay-mode bottom bar with a wide BACK button and optional right-aligned tab buttons.V32Shell— shared metagame chrome (top bar + scrollable main stage + bottom nav), with a chrome-hide mode for fullscreen overlays.OnboardingShell— chrome-free centered layout used during the prologue beats.MedPanel— medical-UI panel primitive in card / flat / doors variants; door variant plays a size-aware open cue.MedPanelModal— fullscreen scrim wrapper for centered modal content with optional scrim-dismiss.RarityBadge— circular letter badge that dangles off the corner of a rarity card.TrimmedShipImage— canvas-rendered ship sprite with a shared uniform alpha-bbox crop and an optional zoom factor.CollectibleCard— rarity-frame ship card with sprite, hull name, star/XP bar, locked overlay, and selected-state white pulse.NebulaBackground— live WebGL nebula backdrop with archetype prop and a blackout-overlay opacity prop.SunEffect— red-orange sun with drifting glow lobes (currently rendered hidden).ChallengePopover— fullscreen popover with a tier-milestone claim row plus per-planet challenge card list.PlanetProgressBar— compact hub-screen XP sticker with per-level fill, level badge, level-up flash, and an unclaimed-reward indicator.PlanetTrackViewer— fullscreen scrollable reward track with claim flow and reward-reveal popup.LeaderboardWrapper— table of top players by highest tier, with self-row highlight and weapon-icon row.RewardPopup— front-of-queue reward overlay with auto-dismiss and tap-to-skip.useUpdateAvailable— hook returning true once a newer build is published.HubUpdateBanner— slide-up bottom banner on the hub that reloads on tap.UpdateBanner— fullscreen update overlay shown only on safe menu routes.FeedbackFAB— feedback modal triggered by the canvas ”?” event, with pause/resume bridge.setActiveMission/clearActiveMission— bridge setters used by the game screen so the feedback modal can pause/resume gameplay and grab the canvas for a screenshot.WelcomeModal— callsign-entry modal shown after a successful magic-link claim.ClaimAccountModal— email-entry modal that sends the magic link to upgrade a guest profile.
Pattern notes
- Cross-cutting concerns ride on module-level state rather than props or context: the active-mission bridge ref (set by the game screen, read by the feedback modal), the update-poll subscriber list (one polling loop shared across mounts), and the haptic delegation listener (single capture-phase pointer-down listener installed once at module load).
- The selected-state card pulse and the reward-popup fade/pop animations are injected as
<style>tags on first module evaluation rather than living in a stylesheet; idempotent via an id check. - Ship sprites are loaded once per URL and cached in module-level maps (one for resolved images, one for in-flight promises); the canvas reads them through a
ResizeObserveron the parent so card layouts repaint without a React re-render. - The medical-UI variants enforce a consistent shape: a
<MedPanelModal>scrim wrapping a<MedPanel variant="doors">is the shared pattern for the welcome / claim-account / feedback / update modals. - Optimistic claim flows write to the local store before awaiting the RPC and rely on bootstrap to reconcile any RPC failure; the reward-reveal popup fires regardless of RPC outcome.
- Selectors that consume store maps read raw state (e.g.
s.completions.has(id),s.planetStats[planetId]) and compute derived values inline, to avoid the new-object-per-call infinite loop that occurs when methods are invoked inside Zustand selectors. - The overlay-bottom-bar variant shares the same height, background, and hairline-border-top as the main bottom-nav so swapping into overlay mode does not shift content above it.