/** * Shared mobile-composition geometry. Every UI subsystem (HUD, touch controls, * system chrome, screens) registers the physical rectangle it occupies; the * engine allocates the viewport once and detects when two subsystems collide, * instead of each independently claiming an edge. Pure math — no DOM, no React. */ import type { LayoutOrientation } from "./orientation.js"; /** The explicit composition mode a game renders for — not a scaled desktop layout. */ export type GameLayoutMode = "desktop-wide" | "desktop-compact" | "mobile-landscape" | "mobile-portrait"; /** Edge insets in CSS pixels (safe areas, reservations). */ export interface Insets { top: number; right: number; bottom: number; left: number; } /** All-zero insets. */ export declare const ZERO_INSETS: Insets; /** Axis-aligned rectangle in CSS pixels (origin top-left). Structurally compatible with a `DOMRect`'s edge fields. */ export interface LayoutRect { left: number; top: number; right: number; bottom: number; } /** Width of a rect (clamped to ≥0). * @internal */ export declare function rectWidth(rect: LayoutRect): number; /** Height of a rect (clamped to ≥0). * @internal */ export declare function rectHeight(rect: LayoutRect): number; /** Area of a rect in px². * @internal */ export declare function rectArea(rect: LayoutRect): number; /** Do the two rectangles share any interior area? Touching edges do not count. * @internal */ export declare function intersects(a: LayoutRect, b: LayoutRect): boolean; /** Overlapping area in px²; 0 when the rectangles don't intersect. * @internal */ export declare function overlapArea(a: LayoutRect, b: LayoutRect): number; /** Portrait when taller than wide, else landscape. * @internal */ export declare function orientationOf(width: number, height: number): LayoutOrientation; /** Inputs to `resolveLayoutMode`. */ export interface LayoutModeInput { /** Live visible viewport dimensions (prefer `visualViewport`). */ width: number; height: number; /** Touchscreen is the primary input (`(pointer: coarse)`). */ coarsePointer: boolean; /** Does the game support phones at all (`platforms` includes `"mobile"`)? Default true. */ mobileSupported?: boolean; /** Desktop width below which the layout is "compact". Default 900. */ compactWidth?: number; } /** * Resolve the explicit composition mode. A mobile layout is not a scaled * desktop layout: a coarse-pointer device on a game that supports mobile is * always a `mobile-*` mode, split by live orientation; everything else is a * desktop mode split by width. * @internal */ export declare function resolveLayoutMode(input: LayoutModeInput): GameLayoutMode; /** Whether a mode is one of the phone modes. * @internal */ export declare function isMobileMode(mode: GameLayoutMode): boolean; /** What kind of UI a registered region belongs to. */ export type LayoutRegionKind = "hud" | "control" | "system" | "screen"; /** How a region participates in collision reporting. */ export type LayoutCollisionPolicy = "forbid" | "allow" | "warn"; /** Gameplay-importance tier of a HUD element. */ export type HudPriority = "critical" | "secondary" | "tertiary"; /** How a HUD element adapts on phones. */ export type MobileHudBehavior = "persistent" | "compact" | "icon" | "transient" | "hidden" | "sheet" | "modal"; /** A physical rectangle a UI subsystem occupies, published to the shared registry. */ export interface LayoutRegion { id: string; kind: LayoutRegionKind; rect: LayoutRect; /** `forbid` reports any overlap, `warn` reports softly, `allow` opts this region out of collision reporting entirely. */ collisionPolicy: LayoutCollisionPolicy; priority?: HudPriority; /** Region ids this one may overlap without a report. */ allowOverlapWith?: readonly string[]; /** Regions sharing a non-empty group never report against each other. */ collisionGroup?: string; } /** One detected forbidden/warned overlap between two regions. */ export interface LayoutCollision { a: string; b: string; kindA: LayoutRegionKind; kindB: LayoutRegionKind; /** Overlapping area in px². */ area: number; severity: "forbid" | "warn"; } /** Options for `detectLayoutCollisions`. */ export interface DetectCollisionsOptions { /** Ignore overlaps smaller than this many px² — avoids meaningless sub-pixel warnings. Default 64. */ minArea?: number; } /** * Every forbidden or warned rectangle intersection across the registered * regions. A pair is skipped when either side's policy is `allow`, when either * opts into the other via `allowOverlapWith`, when they share a * `collisionGroup`, or when the overlap is below `minArea`. * @internal */ export declare function detectLayoutCollisions(regions: readonly LayoutRegion[], options?: DetectCollisionsOptions): LayoutCollision[]; /** Human-readable diagnostic block for a set of collisions (empty string when there are none). * @internal */ export declare function formatLayoutCollisions(collisions: readonly LayoutCollision[]): string; /** * The usable gameplay rectangle after reserving physical zones for touch * controls and system UI. Starts from the safe-area-inset viewport and carves * each reserved zone from whichever edge it hugs. * @internal */ export declare function computeGameplayRect(viewport: LayoutRect, safeArea: Insets, reserved: readonly LayoutRect[]): LayoutRect; /** The shared live geometry the engine allocates once and every UI subsystem reads. */ export interface GameViewportLayout { /** The layout (CSS-pixel) viewport. */ viewport: LayoutRect; /** The live visible viewport (`window.visualViewport`), which excludes mobile browser chrome. */ visualViewport: LayoutRect; safeArea: Insets; mode: GameLayoutMode; orientation: LayoutOrientation; /** All registered regions with their live rects. */ regions: readonly LayoutRegion[]; /** Rects reserved by touch controls and system UI. */ controlZones: readonly LayoutRect[]; /** Usable world/gameplay area after reservations. */ gameplayRect: LayoutRect; }