{"version":3,"sources":["../src/loading.tsx"],"sourcesContent":["// In-app loading primitives (LOADING_UX_SPEC §9, Surface E; design brief 19).\n//\n// Opt-in, presentational components an app author drops in for IN-APP waits (a\n// React.lazy chunk, a slow fetch, a pending action) so they match the platform's\n// loading language for free. Per product_values §3 this is convenience — a plain\n// app that ignores it still works.\n//\n// TRUST BOUNDARY (read brief 19): these render INSIDE the app's iframe, under the\n// app's principal — presentational only, NO capability, NO host round-trip. They\n// share the token VOCABULARY (radii, the shimmer sweep, soft placeholder fills)\n// with the host skeletons, but they are deliberately NOT the host's trusted chrome:\n// no reserved landmark, no wordmark, no \"platform is loading\" framing. They use\n// ordinary app a11y (`aria-hidden` for decorative skeletons, `role=\"status\"` for an\n// indeterminate indicator) — never the host's reserved landmark/accessible name\n// (that is the trusted-path control, reserved to the host). An in-app loader that\n// mimicked host chrome would be a spoofing vector.\n//\n// Self-contained: the SDK ships no CSS pipeline, so the keyframes are injected once\n// into the document and everything else is inline styles — zero config for the author.\nimport { useEffect, useLayoutEffect, useState, type CSSProperties, type ReactNode } from 'react';\n\n/** Loading timing constants, matching the host (LOADING_UX_SPEC §3, the 2026-06-22\n *  design bundle). Exported so authors who hand-roll still match the platform. */\nexport const LOADING_TIMINGS = {\n  /** Below this, NO spinner appears — a fast wait never flashes one (§6.2 floor). */\n  spinThresholdMs: 150,\n  /** The reveal cross-fade duration the host uses. */\n  fadeMs: 150,\n  /** One slow shimmer sweep across a placeholder. */\n  shimmerMs: 1900,\n} as const;\n\n/** The in-app skeleton archetypes (the same shapes as the host §4.1 catalog). */\nexport type SkeletonArchetype = 'panel.list' | 'panel.tree' | 'panel.editor' | 'panel.conversation' | 'generic';\n\nconst STYLE_ID = 'ir-sdk-loading-styles';\nconst STYLE_TEXT = `\n@keyframes ir-sdk-sweep{0%{transform:translateX(-130%)}60%,100%{transform:translateX(130%)}}\n@keyframes ir-sdk-spin{to{transform:rotate(360deg)}}\n.ir-sdk-shim{position:relative;overflow:hidden}\n.ir-sdk-shim::after{content:\"\";position:absolute;inset:0;background:linear-gradient(100deg,transparent 28%,rgba(127,127,127,.18) 50%,transparent 72%);transform:translateX(-130%);animation:ir-sdk-sweep ${LOADING_TIMINGS.shimmerMs}ms ease-in-out infinite}\n.ir-sdk-spin{animation:ir-sdk-spin .8s linear infinite}\n@media (prefers-reduced-motion:reduce){.ir-sdk-shim::after{display:none}.ir-sdk-spin{animation:none}}\n`;\n\n/** Inject the shimmer/spin keyframes once (idempotent, browser-only). */\nfunction ensureLoadingStyles(): void {\n  if (typeof document === 'undefined') return;\n  if (document.getElementById(STYLE_ID)) return;\n  const el = document.createElement('style');\n  el.id = STYLE_ID;\n  el.textContent = STYLE_TEXT;\n  document.head.appendChild(el);\n}\n\n/** Ensure the loading keyframes exist before the component paints. */\nfunction useLoadingStyles(): void {\n  useLayoutEffect(() => {\n    ensureLoadingStyles();\n  }, []);\n}\n\n// A soft, low-contrast placeholder fill that reads on a light OR dark app surface.\nconst PLACEHOLDER: CSSProperties = {\n  background: 'rgba(127,127,127,0.20)',\n  borderRadius: 6,\n};\n\n/** A single placeholder bar — compose these into a custom skeleton shape. */\nexport function SkeletonRow({\n  width = '100%',\n  height = 12,\n  style,\n}: {\n  width?: number | string;\n  height?: number | string;\n  style?: CSSProperties;\n}) {\n  useLoadingStyles();\n  return <div className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, width, height, ...style }} />;\n}\n\nfunction rows(specs: Array<CSSProperties & { key: number }>): ReactNode {\n  return specs.map(({ key, ...s }) => (\n    <div key={key} className=\"ir-sdk-shim\" aria-hidden=\"true\" style={{ ...PLACEHOLDER, ...s }} />\n  ));\n}\n\n/** A shaped, in-app skeleton matching the host archetypes — for an app's own lazy\n *  region (e.g. `<Suspense fallback={<Skeleton archetype=\"panel.list\" />}>`).\n *  Decorative (`aria-hidden`); pair it with `aria-busy` on the region it stands in. */\nexport function Skeleton({ archetype = 'generic', style }: { archetype?: SkeletonArchetype; style?: CSSProperties }) {\n  useLoadingStyles();\n  const wrap: CSSProperties = {\n    display: 'flex',\n    flexDirection: 'column',\n    gap: 10,\n    padding: 12,\n    width: '100%',\n    boxSizing: 'border-box',\n    ...style,\n  };\n  let body: ReactNode;\n  switch (archetype) {\n    case 'panel.list':\n      body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14 })));\n      break;\n    case 'panel.tree':\n      body = rows([0, 1, 2, 3, 4].map((key) => ({ key, height: 14, marginLeft: (key % 3) * 16 })));\n      break;\n    case 'panel.editor':\n      body = rows([62, 88, 73, 41, 80].map((w, key) => ({ key, height: 11, width: `${w}%` })));\n      break;\n    case 'panel.conversation':\n      body = rows(\n        [0, 1, 2, 3].map((key) => ({\n          key,\n          height: 40,\n          width: '70%',\n          borderRadius: 12,\n          alignSelf: key % 2 ? 'flex-end' : 'flex-start',\n        })),\n      );\n      break;\n    case 'generic':\n    default:\n      body = (\n        <>\n          <div\n            className=\"ir-sdk-shim\"\n            aria-hidden=\"true\"\n            style={{ ...PLACEHOLDER, height: 16, width: '45%', borderRadius: 4 }}\n          />\n          {rows([0, 1].map((key) => ({ key, height: 56, borderRadius: 8 })))}\n        </>\n      );\n  }\n  return (\n    <div aria-hidden=\"true\" style={wrap}>\n      {body}\n    </div>\n  );\n}\n\n/** Become true after `ms`, so a fast wait never flashes an indicator (§6.2 floor). */\nfunction useDelayedFlag(ms: number): boolean {\n  const [on, setOn] = useState(false);\n  useEffect(() => {\n    const t = setTimeout(() => setOn(true), ms);\n    return () => clearTimeout(t);\n  }, [ms]);\n  return on;\n}\n\n/** An indeterminate spinner for waits where a shaped skeleton doesn't fit (a pending\n *  button, a small inline fetch). Wired to the host's ~150 ms-before-spin rule: it\n *  renders nothing until the threshold, so a fast wait shows no flash. Reduced motion\n *  stills the rotation (the ring stays as a static indicator). In-app a11y only. */\nexport function Spinner({\n  size = 18,\n  thresholdMs = LOADING_TIMINGS.spinThresholdMs,\n  label = 'Loading',\n}: {\n  size?: number;\n  thresholdMs?: number;\n  label?: string;\n}) {\n  useLoadingStyles();\n  const show = useDelayedFlag(thresholdMs);\n  if (!show) return null;\n  return (\n    <span\n      className=\"ir-sdk-spin\"\n      role=\"status\"\n      aria-label={label}\n      style={{\n        display: 'inline-block',\n        width: size,\n        height: size,\n        border: '2px solid rgba(127,127,127,0.3)',\n        borderTopColor: 'currentColor',\n        borderRadius: '50%',\n        boxSizing: 'border-box',\n      }}\n    />\n  );\n}\n\n/** Wrap an in-app region whose content is still loading: shows a centered spinner\n *  (past the 150 ms floor) with `aria-busy`, then reveals `children`. For a shaped\n *  wait, pass a `<Skeleton>` as `fallback` instead. App a11y only — not host chrome. */\nexport function LoadingRegion({\n  loading,\n  fallback,\n  label = 'Loading',\n  children,\n}: {\n  loading: boolean;\n  fallback?: ReactNode;\n  label?: string;\n  children?: ReactNode;\n}) {\n  useLoadingStyles();\n  if (!loading) return <>{children}</>;\n  return (\n    <div\n      aria-busy=\"true\"\n      style={{\n        display: 'flex',\n        alignItems: 'center',\n        justifyContent: 'center',\n        width: '100%',\n        height: '100%',\n        minHeight: 48,\n      }}\n    >\n      {fallback ?? <Spinner label={label} />}\n    </div>\n  );\n}\n"],"mappings":"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","names":[]}