{"version":3,"sources":["../src/formFactor.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { FORM_FACTOR, REQUEST_FORM_FACTOR } from './generated/protocol';\n\n/**\n * The form factor of the surface your app is rendered into, mirrored from the\n * immediately.run host (UI_AS_APPS_SPEC §5.4.1). Read this to lay out\n * responsively — a narrow chrome panel, a full preview, or a mobile carousel\n * pane all report their box here. The host is the source of truth (it owns the\n * region); you cannot reliably measure your own viewport across the sandbox\n * boundary.\n *\n * Baseline capability `formFactor:read` — every app may read it.\n */\nexport type FormFactorClass = 'mobile' | 'tablet' | 'desktop';\n/** Whether the rendered surface is taller than wide (`portrait`) or wider (`landscape`). */\nexport type Orientation = 'portrait' | 'landscape';\n\n/** The host-reported size class, orientation, and pixel box of your app's surface. */\nexport interface FormFactor {\n  class: FormFactorClass;\n  orientation: Orientation;\n  width: number;\n  height: number;\n}\n\n/** Assumed before the host reports — a reasonable desktop default. */\nconst DEFAULT_FORM_FACTOR: FormFactor = {\n  class: 'desktop',\n  orientation: 'landscape',\n  width: 1280,\n  height: 800,\n};\n\nconst isFormFactor = (v: unknown): v is FormFactor => {\n  const f = v as Partial<FormFactor> | null;\n  return (\n    !!f &&\n    (f.class === 'mobile' || f.class === 'tablet' || f.class === 'desktop') &&\n    (f.orientation === 'portrait' || f.orientation === 'landscape') &&\n    typeof f.width === 'number' &&\n    typeof f.height === 'number'\n  );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `form-factor`\n// and answers `request-form-factor` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<FormFactor>({\n  pushType: FORM_FACTOR,\n  requestType: REQUEST_FORM_FACTOR,\n  initial: DEFAULT_FORM_FACTOR,\n  parse: (msg) => (isFormFactor(msg.formFactor) ? (msg.formFactor as FormFactor) : undefined),\n});\n\n/** Returns the current form factor. Poll for a one-off read.\n *\n *  Off-host (plain `vite dev` — no host to report the region's box) this stays at\n *  the default forever: `{ class: 'desktop', orientation: 'landscape', width: 1280,\n *  height: 800 }`. Don't mistake it for a measurement — locally, size your layout\n *  from the DOM if you need the real viewport. */\nexport const getFormFactor = (): FormFactor => channel.get();\n\n/**\n * Subscribe to form-factor changes. The listener is invoked immediately with\n * the current value, then again on every change. Returns an unsubscribe fn.\n */\nexport const onFormFactorChange = (listener: (formFactor: FormFactor) => void): (() => void) =>\n  channel.onChange(listener);\n\n/** React hook returning the current form factor, re-rendering on change.\n *\n *  Off-host (plain `vite dev`) it returns the desktop default (`desktop`,\n *  `landscape`, 1280×800) forever — the host never reports, so no re-render ever\n *  arrives. See {@link getFormFactor}. */\nexport const useFormFactor = (): FormFactor => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAAiD;AAyBjD,MAAM,sBAAkC;AAAA,EACtC,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,MAAM,eAAe,CAAC,MAAgC;AACpD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,UAAU,YAAY,EAAE,UAAU,YAAY,EAAE,UAAU,eAC5D,EAAE,gBAAgB,cAAc,EAAE,gBAAgB,gBACnD,OAAO,EAAE,UAAU,YACnB,OAAO,EAAE,WAAW;AAExB;AAIA,MAAM,cAAU,sCAA8B;AAAA,EAC5C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QAAS,aAAa,IAAI,UAAU,IAAK,IAAI,aAA4B;AACnF,CAAC;AAQM,MAAM,gBAAgB,MAAkB,QAAQ,IAAI;AAMpD,MAAM,qBAAqB,CAAC,aACjC,QAAQ,SAAS,QAAQ;AAOpB,MAAM,gBAAgB,MAAkB,QAAQ,IAAI;","names":[]}