{"version":3,"file":"layout.cjs","sources":["../../src/debug/layout.ts"],"sourcesContent":["/**\n * Viewport signals for the debug overlay.\n *\n * Two ORTHOGONAL signals, deliberately kept apart:\n *\n * - `compact` drives **layout** — which shape a surface takes (logo-anchored drawer\n *   vs corner dropdown, full-screen modal vs floating one). Narrow OR short viewport.\n * - `coarse` drives **hit targets** — how big a tappable thing must be. True on\n *   any touch device, including large tablets.\n *\n * They cross: a tablet is coarse-but-not-compact (desktop layout, big targets);\n * a narrow desktop window is compact-but-not-coarse (mobile layout, normal\n * targets). Collapsing them into one \"isMobile\" boolean gets both cases wrong.\n *\n * This module imports nothing from the overlay — keep it a leaf so `core.ts`,\n * `panel.ts` and `predict.ts` can all depend on it without cycles.\n */\n\nexport type LayoutState = {\n    compact: boolean;\n    coarse: boolean;\n};\n\n// Narrow OR short. Catches every phone in both orientations (portrait 390w hits\n// the width query; landscape 844x390 hits the height query) while leaving\n// tablets (>=744 portrait, >=768 landscape height) on the desktop layout.\nconst COMPACT_QUERY = \"(max-width: 640px), (max-height: 480px)\";\n\nconst COARSE_QUERY = \"(pointer: coarse)\";\n\nlet compactQuery: MediaQueryList | null = null;\nlet coarseQuery: MediaQueryList | null = null;\n\nlet state: LayoutState = { compact: false, coarse: false };\n\nconst listeners = new Set<(state: LayoutState) => void>();\n\nlet pending = false;\n\nfunction read(): LayoutState {\n    return {\n        compact: !!compactQuery && compactQuery.matches,\n        coarse: !!coarseQuery && coarseQuery.matches,\n    };\n}\n\n// Rotation fires resize + orientationchange + two matchMedia changes back to back,\n// so coalesce the burst into a single relayout. rAF does that whenever frames are\n// being produced; the timeout is a safety net for when they are not (hidden tab,\n// occluded window, headless). Whichever fires first wins — `pending` makes `run`\n// idempotent, so a frame that never arrives cannot wedge the bus.\nfunction schedule(): void {\n    if (pending) { return; }\n    pending = true;\n    const run = function() {\n        if (!pending) { return; }\n        pending = false;\n        state = read();\n        reflow();\n    };\n    if (typeof requestAnimationFrame === \"function\") { requestAnimationFrame(run); }\n    setTimeout(run, 50);\n}\n\n/** Re-run every subscriber. Call directly after changing a preference that affects layout. */\nexport function reflow(): void {\n    listeners.forEach(function(fn) { fn(state); });\n}\n\n/** Subscribe to viewport/layout changes. Returns an unsubscribe function. */\nexport function onReflow(fn: (state: LayoutState) => void): () => void {\n    listeners.add(fn);\n    return function() { listeners.delete(fn); };\n}\n\n/** True when the viewport is narrow or short — the overlay switches shape. */\nexport function isCompact(): boolean { return state.compact; }\n\n/** True on touch/stylus input — hit targets grow to 44px. Independent of {@link isCompact}. */\nexport function isCoarsePointer(): boolean { return state.coarse; }\n\n/** Install the one listener set that drives every relayout. Idempotent. */\nexport function initLayout(): void {\n    if (compactQuery || typeof window === \"undefined\" || !window.matchMedia) { return; }\n\n    compactQuery = window.matchMedia(COMPACT_QUERY);\n    coarseQuery = window.matchMedia(COARSE_QUERY);\n    state = read();\n\n    // matchMedia fires only on breakpoint crossings; resize covers in-breakpoint\n    // changes (mobile URL bar collapsing, desktop window drag, split view).\n    compactQuery.addEventListener(\"change\", schedule);\n    coarseQuery.addEventListener(\"change\", schedule);\n    window.addEventListener(\"resize\", schedule, { passive: true });\n    window.addEventListener(\"orientationchange\", schedule, { passive: true });\n}\n"],"names":[],"mappings":";;;;;;;;AAAA;;;;;;;;;;;;;;;;AAgBG;AAOH;AACA;AACA;AACA,MAAM,aAAa,GAAG,yCAAyC;AAE/D,MAAM,YAAY,GAAG,mBAAmB;AAExC,IAAI,YAAY,GAA0B,IAAI;AAC9C,IAAI,WAAW,GAA0B,IAAI;AAE7C,IAAI,KAAK,GAAgB,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE;AAE1D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC;AAEzD,IAAI,OAAO,GAAG,KAAK;AAEnB,SAAS,IAAI,GAAA;IACT,OAAO;AACH,QAAA,OAAO,EAAE,CAAC,CAAC,YAAY,IAAI,YAAY,CAAC,OAAO;AAC/C,QAAA,MAAM,EAAE,CAAC,CAAC,WAAW,IAAI,WAAW,CAAC,OAAO;KAC/C;AACL;AAEA;AACA;AACA;AACA;AACA;AACA,SAAS,QAAQ,GAAA;IACb,IAAI,OAAO,EAAE;QAAE;IAAQ;IACvB,OAAO,GAAG,IAAI;AACd,IAAA,MAAM,GAAG,GAAG,YAAA;QACR,IAAI,CAAC,OAAO,EAAE;YAAE;QAAQ;QACxB,OAAO,GAAG,KAAK;QACf,KAAK,GAAG,IAAI,EAAE;AACd,QAAA,MAAM,EAAE;AACZ,IAAA,CAAC;AACD,IAAA,IAAI,OAAO,qBAAqB,KAAK,UAAU,EAAE;QAAE,qBAAqB,CAAC,GAAG,CAAC;IAAE;AAC/E,IAAA,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC;AACvB;AAEA;SACgB,MAAM,GAAA;AAClB,IAAA,SAAS,CAAC,OAAO,CAAC,UAAS,EAAE,EAAA,EAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD;AAEA;AACM,SAAU,QAAQ,CAAC,EAAgC,EAAA;AACrD,IAAA,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;IACjB,OAAO,YAAA,EAAa,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C;AAEA;AACM,SAAU,SAAS,GAAA,EAAc,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC;AAE7D;AACM,SAAU,eAAe,GAAA,EAAc,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;AAElE;SACgB,UAAU,GAAA;AACtB,IAAA,IAAI,YAAY,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE;QAAE;IAAQ;AAEnF,IAAA,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC;AAC/C,IAAA,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC;IAC7C,KAAK,GAAG,IAAI,EAAE;;;AAId,IAAA,YAAY,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC;AACjD,IAAA,WAAW,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC;AAChD,IAAA,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9D,IAAA,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7E;;;;;;;;"}