@liforma/client 0.4.15 → 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/next/index.js +619 -608
- package/dist/next/index.js.map +1 -1
- package/dist/next/react/ExperienceThumbnail.d.ts.map +1 -1
- package/dist/next/thumbnail/avatarPlate.d.ts +11 -0
- package/dist/next/thumbnail/avatarPlate.d.ts.map +1 -0
- package/dist/next/thumbnail/avatarPlate.test.d.ts +2 -0
- package/dist/next/thumbnail/avatarPlate.test.d.ts.map +1 -0
- package/dist/next/thumbnail/expandAssetUrl.d.ts +3 -2
- package/dist/next/thumbnail/expandAssetUrl.d.ts.map +1 -1
- package/dist/next/thumbnail/index.d.ts +2 -0
- package/dist/next/thumbnail/index.d.ts.map +1 -1
- package/dist/next/thumbnail/toGalleryThumbUrl.d.ts +7 -3
- package/dist/next/thumbnail/toGalleryThumbUrl.d.ts.map +1 -1
- package/dist/next/thumbnail.js +211 -200
- package/dist/next/thumbnail.js.map +1 -1
- package/dist/npm/index.js +496 -492
- package/dist/npm/index.js.map +1 -1
- package/dist/npm/thumbnail/avatarPlate.d.ts +11 -0
- package/dist/npm/thumbnail/avatarPlate.d.ts.map +1 -0
- package/dist/npm/thumbnail/expandAssetUrl.d.ts +3 -2
- package/dist/npm/thumbnail/expandAssetUrl.d.ts.map +1 -1
- package/dist/npm/thumbnail/index.d.ts +2 -0
- package/dist/npm/thumbnail/index.d.ts.map +1 -1
- package/dist/npm/thumbnail/index.js +123 -110
- package/dist/npm/thumbnail/index.js.map +1 -1
- package/dist/npm/thumbnail/toGalleryThumbUrl.d.ts +7 -3
- package/dist/npm/thumbnail/toGalleryThumbUrl.d.ts.map +1 -1
- package/dist/npm/v2/liforma-experience-thumbnail.d.ts.map +1 -1
- package/dist/react/index.js +749 -738
- package/dist/react/index.js.map +1 -1
- package/dist/react/react/ExperienceThumbnail.d.ts.map +1 -1
- package/dist/react/thumbnail/avatarPlate.d.ts +11 -0
- package/dist/react/thumbnail/avatarPlate.d.ts.map +1 -0
- package/dist/react/thumbnail/avatarPlate.test.d.ts +2 -0
- package/dist/react/thumbnail/avatarPlate.test.d.ts.map +1 -0
- package/dist/react/thumbnail/expandAssetUrl.d.ts +3 -2
- package/dist/react/thumbnail/expandAssetUrl.d.ts.map +1 -1
- package/dist/react/thumbnail/index.d.ts +2 -0
- package/dist/react/thumbnail/index.d.ts.map +1 -1
- package/dist/react/thumbnail/toGalleryThumbUrl.d.ts +7 -3
- package/dist/react/thumbnail/toGalleryThumbUrl.d.ts.map +1 -1
- package/dist/react/thumbnail.js +211 -200
- package/dist/react/thumbnail.js.map +1 -1
- package/dist/sdk/v2/client.js +1 -1
- package/dist/sdk/v2/client.js.map +1 -1
- package/dist/sdk/v2/thumbnail.js +2 -2
- package/dist/sdk/v2/thumbnail.js.map +1 -1
- package/dist/svelte/ExperienceThumbnail.svelte +3 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thumbnail.js","names":[],"sources":["../../src/thumbnail/types.ts","../../src/thumbnail/locationPlate.ts","../../src/thumbnail/expandAssetUrl.ts","../../src/thumbnail/toGalleryThumbUrl.ts","../../src/shims/localStack.ts","../../src/thumbnail/hostedLaunchUrl.ts","../../src/thumbnail/thumbLoadScheduler.ts","../../src/thumbnail/lazyLoad.ts","../../src/thumbnail/scrollParallax.ts","../../src/thumbnail/styles.ts","../../src/thumbnail/ensureStyles.ts","../../src/thumbnail/plateDelivery.ts","../../src/react/ExperienceThumbnail.tsx"],"sourcesContent":["/**\n * Catalog-shaped layered gallery preview.\n * Values may be absolute URLs or Liforma paths (`/avatars/…`, `/locations/…/scene`).\n */\nexport type GalleryThumb = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n};\n\nexport type ThumbnailInteractionKind = 'anchor' | 'button' | 'presentational';\n\nexport type ResolveThumbnailInteractionInput = {\n\treadonly href?: string | null;\n\treadonly hasClickHandler?: boolean;\n\treadonly experienceId?: string | null;\n};\n\nexport type ThumbnailLayers = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\n/** Shared props across Svelte / React / Next (React uses `onClick`). */\nexport type ExperienceThumbnailBaseProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\t/** Same as `<Experience fit>`: `full` (default), `medium`, or `face`. */\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n\treadonly normalizeUrls?: boolean;\n\t/** CDN origin for catalog paths. Default `https://cdn.liforma.ai`. */\n\treadonly cdnBaseUrl?: string;\n};\n\nexport function thumbnailFitClass(fit?: 'full' | 'face' | 'medium'): string {\n\tif (fit === 'face') return 'liforma-et--fit-face';\n\tif (fit === 'medium') return 'liforma-et--fit-medium';\n\treturn '';\n}\n","/** Location colour plates used by ExperienceThumbnail (no zoom/portrait crops). */\nexport const THUMBNAIL_LOCATION_PLATE_SIZES = ['256x256', '400x225', '800x450', '1672x941'] as const;\nexport type ThumbnailLocationPlateSize = (typeof THUMBNAIL_LOCATION_PLATE_SIZES)[number];\n\nconst LOCATION_SIZE_RE = /(-)(\\d+x\\d+)(\\.(?:webp|avif))(\\?|$)/i;\n\n/**\n * Square / portrait hosts keep the gallery crop. Landscape hosts use a 16:9 plate\n * so cover-fit fills height instead of clipping the top and bottom of a square.\n * Unmeasured boxes stay on `256x256`. Hosts 225px tall or smaller use `400x225`.\n */\nexport function pickThumbnailLocationPlateSize(\n\twidth: number,\n\theight: number\n): ThumbnailLocationPlateSize {\n\tif (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) {\n\t\treturn '256x256';\n\t}\n\tif (width <= height) return '256x256';\n\tif (height <= 225) return '400x225';\n\tif (width > 800 || height > 450) return '1672x941';\n\treturn '800x450';\n}\n\nexport function rewriteThumbnailLocationPlateSize(\n\turl: string,\n\tsize: ThumbnailLocationPlateSize\n): string {\n\tconst trimmed = url.trim();\n\tif (!trimmed) return trimmed;\n\treturn trimmed.replace(\n\t\tLOCATION_SIZE_RE,\n\t\t(_match, dash: string, _current: string, ext: string, end: string) =>\n\t\t\t`${dash}${size}${ext}${end}`\n\t);\n}\n\n/** Observe the thumb host and report CSS box size (for plate picking). */\nexport function attachThumbnailBox(\n\telement: HTMLElement,\n\tonBox: (width: number, height: number) => void\n): () => void {\n\tconst emit = () => {\n\t\tonBox(element.clientWidth, element.clientHeight);\n\t};\n\temit();\n\tif (typeof ResizeObserver === 'undefined') {\n\t\treturn () => {};\n\t}\n\tconst observer = new ResizeObserver(emit);\n\tobserver.observe(element);\n\treturn () => observer.disconnect();\n}\n","import {\n\trewriteThumbnailLocationPlateSize,\n\ttype ThumbnailLocationPlateSize\n} from './locationPlate';\n\n/** Default origin for Liforma gallery plates (catalog paths have no host). */\nexport const DEFAULT_THUMBNAIL_CDN_BASE_URL = 'https://cdn.liforma.ai';\n\nconst LOCATION_SIZE_STEM_RE = /-(256x256|400x225|400x450|800x450|836x971|1672x941|167x94)$/i;\n\nexport function normalizeThumbnailCdnBaseUrl(cdnBaseUrl?: string): string {\n\tconst trimmed = cdnBaseUrl?.trim().replace(/\\/$/, '');\n\treturn trimmed || DEFAULT_THUMBNAIL_CDN_BASE_URL;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\treturn /^https?:\\/\\//i.test(value) || value.startsWith('//');\n}\n\nfunction absoluteHttpUrl(value: string): string {\n\tif (value.startsWith('//')) return `https:${value}`;\n\treturn value;\n}\n\nfunction splitPathAndSearch(value: string): { pathname: string; search: string } {\n\tconst q = value.indexOf('?');\n\tif (q === -1) return { pathname: value, search: '' };\n\treturn { pathname: value.slice(0, q), search: value.slice(q) };\n}\n\nfunction joinCdn(cdnBase: string, pathname: string, search = ''): string {\n\tconst path = pathname.startsWith('/') ? pathname : `/${pathname}`;\n\treturn `${cdnBase}${path}${search}`;\n}\n\n/**\n * Expand a catalog location path (or keep a full URL) to a sized plate URL.\n *\n * `/locations/modern/city/office/interior-desk-daytime` →\n * `{cdn}/locations/modern/city/office/interior-desk-daytime-800x450.webp`\n */\nexport function expandThumbnailLocationUrl(\n\tref: string,\n\tlocationPlateSize: ThumbnailLocationPlateSize,\n\tcdnBaseUrl?: string\n): string {\n\tconst raw = ref.trim();\n\tif (!raw) return raw;\n\tconst cdnBase = normalizeThumbnailCdnBaseUrl(cdnBaseUrl);\n\n\tif (isHttpUrl(raw)) {\n\t\treturn rewriteThumbnailLocationPlateSize(absoluteHttpUrl(raw), locationPlateSize);\n\t}\n\n\tconst { pathname: rawPath, search } = splitPathAndSearch(raw.replace(/\\\\/g, '/'));\n\tlet pathname = rawPath;\n\n\tif (!pathname.startsWith('/')) {\n\t\tpathname = `/locations/${pathname.replace(/^locations\\//i, '')}`;\n\t} else if (/^\\/locations\\//i.test(pathname)) {\n\t\t/* already a location path */\n\t} else if (/^\\/avatars\\//i.test(pathname)) {\n\t\treturn joinCdn(cdnBase, pathname, search);\n\t} else {\n\t\treturn raw;\n\t}\n\n\tif (/\\.webp$/i.test(pathname) && !LOCATION_SIZE_STEM_RE.test(pathname.replace(/\\.webp$/i, ''))) {\n\t\treturn joinCdn(cdnBase, pathname, search);\n\t}\n\n\tlet stem = pathname.replace(/^\\/locations\\//i, '').replace(/\\.webp$/i, '');\n\tstem = stem.replace(LOCATION_SIZE_STEM_RE, '');\n\treturn `${cdnBase}/locations/${stem}-${locationPlateSize}.webp${search}`;\n}\n\n/**\n * Expand a catalog avatar path (or keep a full URL) onto the public CDN.\n * `/avatars/{id}` → `{cdn}/avatars/{id}/256/{id}_neutral.webp`\n */\nexport function expandThumbnailAvatarUrl(ref: string, cdnBaseUrl?: string): string {\n\tconst raw = ref.trim();\n\tif (!raw) return raw;\n\tconst cdnBase = normalizeThumbnailCdnBaseUrl(cdnBaseUrl);\n\n\tif (isHttpUrl(raw)) {\n\t\treturn absoluteHttpUrl(raw);\n\t}\n\n\tconst { pathname: rawPath, search } = splitPathAndSearch(raw.replace(/\\\\/g, '/'));\n\tlet pathname = rawPath;\n\n\tif (!pathname.startsWith('/')) {\n\t\tif (/^avatars\\//i.test(pathname)) {\n\t\t\tpathname = `/${pathname}`;\n\t\t} else if (/^[^/]+$/.test(pathname) && !search) {\n\t\t\treturn `${cdnBase}/avatars/${pathname}/256/${pathname}_neutral.webp`;\n\t\t} else {\n\t\t\treturn raw;\n\t\t}\n\t}\n\n\tif (!/^\\/avatars\\//i.test(pathname)) {\n\t\treturn raw;\n\t}\n\n\tconst rest = pathname.replace(/^\\/avatars\\//i, '').replace(/\\/+$/, '');\n\tif (rest && !rest.includes('/') && !search) {\n\t\treturn `${cdnBase}/avatars/${rest}/256/${rest}_neutral.webp`;\n\t}\n\n\treturn joinCdn(cdnBase, pathname, search);\n}\n","import {\n\texpandThumbnailAvatarUrl,\n\texpandThumbnailLocationUrl,\n\tnormalizeThumbnailCdnBaseUrl\n} from './expandAssetUrl';\nimport type { ThumbnailLocationPlateSize } from './locationPlate';\n\nconst AVATAR_TIER_RE = /(\\/avatars\\/[^/]+\\/)(\\d+)(\\/)/i;\nconst AVATAR_CH_PLATE_RE = /_CH(\\.webp)/i;\n\nexport type { ThumbnailLocationPlateSize };\n\nfunction isLocationRef(raw: string): boolean {\n\tif (/\\/locations\\//i.test(raw) || /^locations\\//i.test(raw)) return true;\n\tif (/^https?:\\/\\//i.test(raw) || raw.startsWith('//')) return false;\n\tif (/^\\/?avatars\\//i.test(raw)) return false;\n\tif (raw.startsWith('/')) return false;\n\treturn raw.includes('/');\n}\n\n/**\n * Rewrite known Liforma CDN URLs to gallery thumb tiers.\n * Avatars: /{tier}/ → /256/; locations: -{WxH}.webp → the requested plate\n * (`256x256` by default, `400x225` / `800x450` / `1672x941` for landscape hosts).\n * Catalog paths (`/locations/…/scene`, `/avatars/{id}`) expand onto the CDN first.\n * Unrecognized URLs are returned unchanged.\n */\nexport function toGalleryThumbUrl(\n\turl: string,\n\tlocationPlateSize: ThumbnailLocationPlateSize = '256x256',\n\tcdnBaseUrl?: string\n): string {\n\tconst raw = url.trim();\n\tif (!raw) return raw;\n\n\tif (isLocationRef(raw)) {\n\t\treturn expandThumbnailLocationUrl(raw, locationPlateSize, cdnBaseUrl);\n\t}\n\n\tlet next = expandThumbnailAvatarUrl(raw, cdnBaseUrl);\n\tnext = next.replace(AVATAR_TIER_RE, (_match, prefix: string, tier: string, slash: string) => {\n\t\tif (tier === '256') return `${prefix}${tier}${slash}`;\n\t\treturn `${prefix}256${slash}`;\n\t});\n\treturn next.replace(AVATAR_CH_PLATE_RE, '_neutral$1');\n}\n\nexport function resolveThumbnailLayers(input: {\n\treadonly galleryThumb?: {\n\t\treadonly avatarImage?: string;\n\t\treadonly backgroundImage?: string;\n\t\treadonly foregroundImage?: string;\n\t};\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n\t/** When false, keep caller absolute URLs (lightbox / preview). Paths still expand. Default true. */\n\treadonly normalize?: boolean;\n\t/** Location plate when expanding or rewriting location assets. Default `256x256`. */\n\treadonly locationPlateSize?: ThumbnailLocationPlateSize;\n\t/** CDN origin for catalog paths. Default `https://cdn.liforma.ai`. */\n\treadonly cdnBaseUrl?: string;\n}): {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n} | null {\n\tconst avatarSource =\n\t\tinput.avatarImage?.trim() ||\n\t\tinput.galleryThumb?.avatarImage?.trim() ||\n\t\t'';\n\tif (!avatarSource) return null;\n\n\tconst backgroundSource =\n\t\tinput.backgroundImage?.trim() || input.galleryThumb?.backgroundImage?.trim() || '';\n\tconst foregroundSource =\n\t\tinput.foregroundImage?.trim() || input.galleryThumb?.foregroundImage?.trim() || '';\n\tconst fallbackSource = input.fallbackImage?.trim() || '';\n\tconst plate = input.locationPlateSize ?? '256x256';\n\tconst cdnBaseUrl = normalizeThumbnailCdnBaseUrl(input.cdnBaseUrl);\n\tconst rewriteKnown = input.normalize !== false;\n\n\tconst rewriteAvatar = (ref: string) => {\n\t\tif (!rewriteKnown && /^https?:\\/\\//i.test(ref.trim())) return ref.trim();\n\t\treturn toGalleryThumbUrl(ref, plate, cdnBaseUrl);\n\t};\n\tconst rewriteLocation = (ref: string) => {\n\t\tif (!rewriteKnown && /^https?:\\/\\//i.test(ref.trim())) return ref.trim();\n\t\treturn expandThumbnailLocationUrl(ref, plate, cdnBaseUrl);\n\t};\n\n\treturn {\n\t\tavatarImage: rewriteAvatar(avatarSource),\n\t\t...(backgroundSource ? { backgroundImage: rewriteLocation(backgroundSource) } : {}),\n\t\t...(foregroundSource ? { foregroundImage: rewriteLocation(foregroundSource) } : {}),\n\t\t...(fallbackSource ? { fallbackImage: rewriteAvatar(fallbackSource) } : {})\n\t};\n}\n","/**\n * Opt-in local monorepo stack (api :3001, player :3002).\n * Never inferred from hostname alone — third-party demos run on localhost against production.\n */\nexport function useExplicitLocalStack(): boolean {\n\tif ((globalThis as { __LIFORMA_STACK?: string }).__LIFORMA_STACK === 'local') {\n\t\treturn true;\n\t}\n\n\tif (typeof location === 'undefined') {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\treturn new URLSearchParams(location.search).get('stack') === 'local';\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { useExplicitLocalStack } from '../shims/localStack.ts';\nimport type { ResolveThumbnailInteractionInput, ThumbnailInteractionKind } from './types';\n\nconst PRODUCTION_PLAYER_ORIGIN = 'https://player.liforma.ai';\nconst LOCAL_PLAYER_ORIGIN = 'http://localhost:3002';\n\n/** Priority: href → click-only → hosted (experienceId) → presentational. */\nexport function resolveThumbnailInteraction(\n\tinput: ResolveThumbnailInteractionInput\n): ThumbnailInteractionKind {\n\tif (input.href?.trim()) return 'anchor';\n\tif (input.hasClickHandler) return 'button';\n\tif (input.experienceId?.trim()) return 'anchor';\n\treturn 'presentational';\n}\n\nexport function resolvePlayerBaseUrl(playerBaseUrl?: string): string {\n\tconst configured = playerBaseUrl?.trim();\n\tif (configured) return configured.replace(/\\/$/, '');\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_ORIGIN;\n\t}\n\n\treturn PRODUCTION_PLAYER_ORIGIN;\n}\n\n/**\n * Build hosted player launch URL: `{player}/{experienceId}?returnUrl=…`\n * When `returnUrl` is omitted, callers should assign on click (SSR-safe) or pass an explicit URL.\n */\nexport function buildHostedLaunchUrl(options: {\n\treadonly experienceId: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n}): string {\n\tconst experienceId = options.experienceId.trim();\n\tconst base = resolvePlayerBaseUrl(options.playerBaseUrl);\n\tconst url = new URL(`${base}/${encodeURIComponent(experienceId)}`);\n\tconst returnUrl = options.returnUrl?.trim();\n\tif (returnUrl) {\n\t\turl.searchParams.set('returnUrl', returnUrl);\n\t}\n\treturn url.toString();\n}\n\n/** Resolve href for anchor mode (app link or hosted launch). */\nexport function resolveThumbnailHref(options: {\n\treadonly href?: string;\n\treadonly experienceId?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\t/** When true and returnUrl omitted, use window.location.href (browser only). */\n\treadonly useCurrentPageAsReturnUrl?: boolean;\n}): string | undefined {\n\tconst href = options.href?.trim();\n\tif (href) return href;\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) return undefined;\n\n\tlet returnUrl = options.returnUrl?.trim();\n\tif (!returnUrl && options.useCurrentPageAsReturnUrl && typeof window !== 'undefined') {\n\t\treturnUrl = window.location.href;\n\t}\n\n\treturn buildHostedLaunchUrl({\n\t\texperienceId,\n\t\t...(returnUrl ? { returnUrl } : {}),\n\t\tplayerBaseUrl: options.playerBaseUrl\n\t});\n}\n","/** Touch / coarse pointers — limit how many thumbs start loading at once. */\nexport const COARSE_THUMB_LOAD_CONCURRENCY = 4;\n/** Delay before a started thumb frees its queue slot for the next card. */\nexport const COARSE_THUMB_LOAD_STAGGER_MS = 160;\n\nexport function prefersCoarsePointer(): boolean {\n\tif (typeof window === 'undefined') return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\ntype QueuedLoad = {\n\treadonly start: () => void;\n\tcancelled: boolean;\n};\n\nlet inflight = 0;\nconst waiting: QueuedLoad[] = [];\n\nfunction maxConcurrent(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_CONCURRENCY : Number.POSITIVE_INFINITY;\n}\n\nfunction staggerMs(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_STAGGER_MS : 0;\n}\n\nfunction pump(): void {\n\tconst limit = maxConcurrent();\n\twhile (inflight < limit && waiting.length > 0) {\n\t\tconst next = waiting.shift();\n\t\tif (!next || next.cancelled) continue;\n\t\tinflight += 1;\n\t\tnext.start();\n\t\tconst delay = staggerMs();\n\t\tif (delay <= 0) {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tcontinue;\n\t\t}\n\t\twindow.setTimeout(() => {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tpump();\n\t\t}, delay);\n\t}\n}\n\n/**\n * On coarse pointers, start thumb image loads in small waves so old devices\n * are not hit with dozens of concurrent decodes. Fine pointers start immediately.\n * Returns a cancel function (no-op after start has run).\n */\nexport function scheduleThumbLoad(start: () => void): () => void {\n\tif (!prefersCoarsePointer()) {\n\t\tstart();\n\t\treturn () => {};\n\t}\n\n\tconst item: QueuedLoad = { start, cancelled: false };\n\twaiting.push(item);\n\tpump();\n\n\treturn () => {\n\t\titem.cancelled = true;\n\t};\n}\n\n/** Test helper — clear queue state between vitest cases. */\nexport function resetThumbLoadSchedulerForTests(): void {\n\tinflight = 0;\n\twaiting.length = 0;\n}\n","import { prefersCoarsePointer, scheduleThumbLoad } from './thumbLoadScheduler';\n\nexport type LazyThumbOptions = {\n\treadonly rootMargin?: string;\n\treadonly onVisibleChange: (visible: boolean) => void;\n};\n\n/** Desktop: prefetch earlier. Coarse/touch: tighter margin to cut burst loads. */\nexport function getDefaultLazyRootMargin(): string {\n\treturn prefersCoarsePointer() ? '96px 0px' : '280px 0px';\n}\n\nfunction parseVerticalRootMarginPx(rootMargin: string): number {\n\tconst first = rootMargin.trim().split(/\\s+/)[0] ?? '0px';\n\tconst value = Number.parseFloat(first);\n\treturn Number.isFinite(value) ? Math.abs(value) : 280;\n}\n\nfunction isNearViewport(element: HTMLElement, marginPx: number): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst rect = element.getBoundingClientRect();\n\treturn rect.bottom >= -marginPx && rect.top <= window.innerHeight + marginPx;\n}\n\n/** Observe the thumb and enable image loads when near the viewport. */\nexport function attachLazyThumb(element: HTMLElement, options: LazyThumbOptions): () => void {\n\tconst { onVisibleChange } = options;\n\tconst rootMargin = options.rootMargin ?? getDefaultLazyRootMargin();\n\tconst marginPx = parseVerticalRootMarginPx(rootMargin);\n\tlet visible = false;\n\tlet loadStarted = false;\n\tlet cancelScheduled: (() => void) | null = null;\n\n\tconst requestLoad = () => {\n\t\tif (loadStarted) return;\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = scheduleThumbLoad(() => {\n\t\t\tcancelScheduled = null;\n\t\t\tloadStarted = true;\n\t\t\tonVisibleChange(true);\n\t\t});\n\t};\n\n\tconst setVisible = (next: boolean) => {\n\t\tif (next === visible) return;\n\t\tvisible = next;\n\t\tif (next) {\n\t\t\trequestLoad();\n\t\t\treturn;\n\t\t}\n\t\t// Keep images once started; only cancel a queued (not-yet-started) load.\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tonVisibleChange(false);\n\t};\n\n\tif (typeof IntersectionObserver === 'undefined') {\n\t\trequestLoad();\n\t\treturn () => {\n\t\t\tcancelScheduled?.();\n\t\t\tcancelScheduled = null;\n\t\t};\n\t}\n\n\tconst observer = new IntersectionObserver(\n\t\t(entries) => {\n\t\t\tsetVisible(entries.some((entry) => entry.isIntersecting));\n\t\t},\n\t\t{ root: null, rootMargin, threshold: 0 }\n\t);\n\n\tobserver.observe(element);\n\n\tif (isNearViewport(element, marginPx)) {\n\t\tsetVisible(true);\n\t}\n\n\treturn () => {\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tobserver.disconnect();\n\t};\n}\n\nexport function attachImageReady(element: HTMLImageElement, onReady: () => void): () => void {\n\tconst notify = () => onReady();\n\tif (element.complete) {\n\t\tqueueMicrotask(notify);\n\t} else {\n\t\telement.addEventListener('load', notify, { once: true });\n\t\telement.addEventListener('error', notify, { once: true });\n\t}\n\treturn () => {\n\t\telement.removeEventListener('load', notify);\n\t\telement.removeEventListener('error', notify);\n\t};\n}\n","/** Touch devices without hover — drive thumbnail parallax from scroll position. */\nexport function prefersScrollLinkedParallax(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tif (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\n/**\n * Map an element's vertical centre in the viewport to horizontal parallax in [-1, 1].\n */\nexport function horizontalParallaxFromViewportY(rect: DOMRect, viewportHeight: number): number {\n\tconst centerY = rect.top + rect.height / 2;\n\tconst t = centerY / Math.max(viewportHeight, 1);\n\treturn (t - 0.5) * 2;\n}\n\nexport type ScrollLinkedParallaxOptions = {\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\ntype ScrollParallaxSubscriber = {\n\treadonly element: HTMLElement;\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\nconst subscribers = new Set<ScrollParallaxSubscriber>();\nlet listening = false;\nlet frame = 0;\n\nfunction syncAll(): void {\n\tframe = 0;\n\tif (subscribers.size === 0 || typeof window === 'undefined') return;\n\tconst viewportHeight = window.innerHeight;\n\tfor (const entry of subscribers) {\n\t\tif (entry.pointerActive?.()) continue;\n\t\tconst rect = entry.element.getBoundingClientRect();\n\t\t// Skip off-screen thumbs — avoids N layout reads + framework updates per scroll.\n\t\tif (rect.bottom < -64 || rect.top > viewportHeight + 64) continue;\n\t\tentry.onParallax(horizontalParallaxFromViewportY(rect, viewportHeight));\n\t}\n}\n\nfunction scheduleSync(): void {\n\tif (frame !== 0) return;\n\tframe = requestAnimationFrame(syncAll);\n}\n\nfunction ensureListening(): void {\n\tif (listening || typeof window === 'undefined') return;\n\tlistening = true;\n\twindow.addEventListener('scroll', scheduleSync, { passive: true });\n\twindow.addEventListener('resize', scheduleSync, { passive: true });\n}\n\nfunction stopListeningIfIdle(): void {\n\tif (subscribers.size > 0 || !listening || typeof window === 'undefined') return;\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n\twindow.removeEventListener('scroll', scheduleSync);\n\twindow.removeEventListener('resize', scheduleSync);\n}\n\n/**\n * Subscribe to scroll/resize and update horizontal parallax from viewport Y.\n * Uses one shared window listener for all thumbs (old iPads with large galleries).\n */\nexport function attachScrollLinkedParallax(\n\telement: HTMLElement,\n\toptions: ScrollLinkedParallaxOptions\n): () => void {\n\tif (!prefersScrollLinkedParallax()) return () => {};\n\n\tconst entry: ScrollParallaxSubscriber = {\n\t\telement,\n\t\tonParallax: options.onParallax,\n\t\tpointerActive: options.pointerActive\n\t};\n\tsubscribers.add(entry);\n\tensureListening();\n\n\tif (!entry.pointerActive?.()) {\n\t\toptions.onParallax(\n\t\t\thorizontalParallaxFromViewportY(element.getBoundingClientRect(), window.innerHeight)\n\t\t);\n\t}\n\n\treturn () => {\n\t\tsubscribers.delete(entry);\n\t\tstopListeningIfIdle();\n\t};\n}\n\n/** Test helper — clear shared scroll state between vitest cases. */\nexport function resetScrollLinkedParallaxForTests(): void {\n\tsubscribers.clear();\n\tif (typeof window !== 'undefined' && listening) {\n\t\twindow.removeEventListener('scroll', scheduleSync);\n\t\twindow.removeEventListener('resize', scheduleSync);\n\t}\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n}\n","/** Shared styles for ExperienceThumbnail (class prefix `liforma-et`). */\nexport const EXPERIENCE_THUMBNAIL_CSS = `\n:host {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n}\n\n.liforma-et {\n\t--liforma-et-parallax-x: 0;\n\t--liforma-et-parallax-y: 0;\n\t--liforma-et-bg-scale: 1;\n\t--liforma-et-fg-scale: 1;\n\t--liforma-et-avatar-scale: 1;\n\t--liforma-et-bg-shift: 0px;\n\t--liforma-et-fg-shift: 0px;\n\t--liforma-et-avatar-shift: 0px;\n\t--liforma-et-reveal-duration: 420ms;\n\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 0;\n\toverflow: hidden;\n\tbackground: #111;\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: inherit;\n\t-webkit-tap-highlight-color: transparent;\n}\n\na.liforma-et,\nbutton.liforma-et {\n\tcursor: pointer;\n}\n\nbutton.liforma-et {\n\tfont: inherit;\n\tappearance: none;\n}\n\n.liforma-et__placeholder {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 0;\n\tbackground:\n\t\tradial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 58%),\n\t\tlinear-gradient(180deg, #1a1a1a 0%, #101010 100%);\n\topacity: 1;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et--revealed .liforma-et__placeholder {\n\topacity: 0;\n}\n\n.liforma-et__media {\n\topacity: 0;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et__media--ready {\n\topacity: 1;\n}\n\n.liforma-et__background {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-bg-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-bg-shift))\n\t\t)\n\t\tscale(var(--liforma-et-bg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et__avatar {\n\tposition: relative;\n\tz-index: 2;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: contain;\n\tobject-position: center bottom;\n\ttransform: scale(var(--liforma-et-avatar-scale))\n\t\ttranslate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-avatar-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-avatar-shift))\n\t\t);\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n/* Gallery plates are typically square in a square tile — contain vs cover is a no-op\n without a zoom. Scale the whole scene from the upper third so Face matches\n player fit (location + avatar crop together). */\n.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.85;\n\t--liforma-et-bg-scale: 1.85;\n\t--liforma-et-fg-scale: 1.85;\n}\n\n.liforma-et--fit-face .liforma-et__avatar,\n.liforma-et--fit-face .liforma-et__background,\n.liforma-et--fit-face .liforma-et__foreground {\n\ttransform-origin: 50% 18%;\n}\n\n.liforma-et--fit-face .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 18%;\n}\n\n/* Bust / medium: less zoom than face so upper chest stays in frame (player 3.75H window). */\n.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.35;\n\t--liforma-et-bg-scale: 1.35;\n\t--liforma-et-fg-scale: 1.35;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar,\n.liforma-et--fit-medium .liforma-et__background,\n.liforma-et--fit-medium .liforma-et__foreground {\n\ttransform-origin: 50% 28%;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 28%;\n}\n\n.liforma-et__foreground {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 3;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\tpointer-events: none;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-fg-shift) * -1),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-fg-shift) * -1)\n\t\t)\n\t\tscale(var(--liforma-et-fg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et--parallax {\n\t--liforma-et-bg-scale: 1.08;\n\t--liforma-et-fg-scale: 1.05;\n\t--liforma-et-avatar-scale: 1.08;\n\t--liforma-et-bg-shift: 10px;\n\t--liforma-et-fg-shift: 4px;\n\t--liforma-et-avatar-shift: 4px;\n}\n\n/* Promote layers only while parallax is active — avoids N compositor surfaces on galleries. */\n.liforma-et--parallax .liforma-et__background,\n.liforma-et--parallax .liforma-et__foreground,\n.liforma-et--parallax .liforma-et__avatar {\n\twill-change: transform;\n}\n\n.liforma-et--parallax.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.95;\n\t--liforma-et-bg-scale: 1.95;\n\t--liforma-et-fg-scale: 1.95;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--parallax.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.42;\n\t--liforma-et-bg-scale: 1.42;\n\t--liforma-et-fg-scale: 1.42;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--scroll-parallax .liforma-et__background,\n.liforma-et--scroll-parallax .liforma-et__foreground,\n.liforma-et--scroll-parallax .liforma-et__avatar {\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-et {\n\t\t--liforma-et-reveal-duration: 0ms;\n\t}\n\n\t.liforma-et--parallax {\n\t\t--liforma-et-bg-scale: 1;\n\t\t--liforma-et-fg-scale: 1;\n\t\t--liforma-et-avatar-scale: 1;\n\t\t--liforma-et-bg-shift: 0px;\n\t\t--liforma-et-fg-shift: 0px;\n\t\t--liforma-et-avatar-shift: 0px;\n\t}\n\n\t.liforma-et--parallax.liforma-et--fit-face {\n\t\t--liforma-et-avatar-scale: 1.85;\n\t\t--liforma-et-bg-scale: 1.85;\n\t\t--liforma-et-fg-scale: 1.85;\n\t}\n\n\t.liforma-et__background,\n\t.liforma-et__foreground,\n\t.liforma-et__avatar {\n\t\ttransition: none;\n\t}\n}\n`.trim();\n","let styleMounted = false;\n\n/** Inject shared thumbnail CSS once per document. */\nexport function ensureExperienceThumbnailStyles(css: string): void {\n\tif (typeof document === 'undefined' || styleMounted) return;\n\tif (document.querySelector('style[data-liforma-et]')) {\n\t\tstyleMounted = true;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.setAttribute('data-liforma-et', '');\n\tstyle.textContent = css;\n\tdocument.head.appendChild(style);\n\tstyleMounted = true;\n}\n","/**\n * CDN colour-plate delivery for thumbnails (avatars + locations).\n * Catalogue identity stays `.webp`; fetch uses `.avif` when the browser can decode it.\n * Depth maps stay WebP. Costume rendered composites have AVIF siblings from the compose worker.\n */\n\nconst AVIF_PROBE_DATA_URI =\n\t'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';\n\nlet avifSupportPromise: Promise<boolean> | null = null;\n\n/** True when this URL is a colour plate WebP that may have an AVIF sibling (not depth). */\nexport function isColourPlateWebpUrl(url: string): boolean {\n\tconst trimmed = url.trim();\n\tif (!trimmed || trimmed.startsWith('blob:') || trimmed.startsWith('data:')) return false;\n\tconst pathOnly = trimmed.split(/[?#]/)[0] ?? trimmed;\n\tconst isAvatar = /\\/avatars\\//i.test(pathOnly);\n\tconst isLocation = /\\/locations\\//i.test(pathOnly);\n\tif (!isAvatar && !isLocation) return false;\n\tif (/\\.depth\\.webp$/i.test(pathOnly)) return false;\n\treturn /\\.webp$/i.test(pathOnly);\n}\n\n/** Rewrite a colour-plate `.webp` path to `.avif`, preserving query/hash. */\nexport function toAvifPlateUrl(url: string): string | null {\n\tif (!isColourPlateWebpUrl(url)) return null;\n\tconst qIndex = url.search(/[?#]/);\n\tif (qIndex < 0) return url.replace(/\\.webp$/i, '.avif');\n\tconst path = url.slice(0, qIndex);\n\tconst suffix = url.slice(qIndex);\n\treturn `${path.replace(/\\.webp$/i, '.avif')}${suffix}`;\n}\n\nexport function supportsAvifDecode(): Promise<boolean> {\n\tif (typeof Image === 'undefined') return Promise.resolve(false);\n\tif (!avifSupportPromise) {\n\t\tavifSupportPromise = new Promise<boolean>((resolve) => {\n\t\t\tconst img = new Image();\n\t\t\timg.onload = () => resolve(img.naturalWidth > 0 && img.naturalHeight > 0);\n\t\t\timg.onerror = () => resolve(false);\n\t\t\timg.src = AVIF_PROBE_DATA_URI;\n\t\t});\n\t}\n\treturn avifSupportPromise;\n}\n\nexport function resetAvifDecodeSupportCacheForTests(): void {\n\tavifSupportPromise = null;\n}\n\n/** WebP catalogue URL → AVIF sibling when `useAvif` is true. */\nexport function thumbnailPlateSrc(url: string, useAvif: boolean): string {\n\tif (!useAvif) return url;\n\treturn toAvifPlateUrl(url) ?? url;\n}\n\n/** Colour-plate `.avif` → catalogue `.webp` (compose worker / missing sibling). */\nexport function thumbnailColourPlateWebpFallbackUrl(url: string): string | null {\n\tconst trimmed = url.trim();\n\tconst qIndex = trimmed.search(/[?#]/);\n\tconst pathOnly = qIndex < 0 ? trimmed : trimmed.slice(0, qIndex);\n\tif (!/\\.avif$/i.test(pathOnly)) return null;\n\tconst webpPath = pathOnly.replace(/\\.avif$/i, '.webp');\n\treturn qIndex < 0 ? webpPath : `${webpPath}${trimmed.slice(qIndex)}`;\n}\n\n/** 400×225 is colour-only; if missing, step up to the published 800×450 sibling. */\nexport function thumbnailLocationPlateFallbackUrl(url: string): string | null {\n\tconst trimmed = url.trim();\n\tif (!/-400x225\\.(webp|avif)(?:$|[?#])/i.test(trimmed)) return null;\n\treturn trimmed.replace(/-400x225\\.(webp|avif)/i, '-800x450.$1');\n}\n","'use client';\n\nimport {\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n\ttype CSSProperties,\n\ttype MouseEvent as ReactMouseEvent,\n\ttype PointerEvent as ReactPointerEvent\n} from 'react';\n\nimport {\n\tattachLazyThumb,\n\tattachScrollLinkedParallax,\n\tattachThumbnailBox,\n\tensureExperienceThumbnailStyles,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\thorizontalParallaxFromViewportY,\n\tpickThumbnailLocationPlateSize,\n\tresolveThumbnailHref,\n\tresolveThumbnailInteraction,\n\tresolveThumbnailLayers,\n\tthumbnailFitClass,\n\tthumbnailColourPlateWebpFallbackUrl,\n\tthumbnailLocationPlateFallbackUrl,\n\tthumbnailPlateSrc,\n\tsupportsAvifDecode,\n\ttype GalleryThumb\n} from '../thumbnail/index';\n\nexport type ExperienceThumbnailProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly onClick?: (event: ReactMouseEvent<HTMLElement>) => void;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n\treadonly normalizeUrls?: boolean;\n\treadonly cdnBaseUrl?: string;\n};\n\nfunction mediaClass(base: string, ready: boolean): string {\n\treturn `${base} liforma-et__media${ready ? ' liforma-et__media--ready' : ''}`;\n}\n\nexport function ExperienceThumbnail(props: ExperienceThumbnailProps) {\n\tconst {\n\t\tgalleryThumb,\n\t\tavatarImage,\n\t\tbackgroundImage,\n\t\tforegroundImage,\n\t\talt,\n\t\texperienceId,\n\t\thref,\n\t\ttarget,\n\t\trel,\n\t\tonClick,\n\t\treturnUrl,\n\t\tplayerBaseUrl,\n\t\tparallax = true,\n\t\tfit = 'full',\n\t\tlazy = true,\n\t\tclassName = '',\n\t\tfallbackImage,\n\t\tnormalizeUrls = true,\n\t\tcdnBaseUrl\n\t} = props;\n\n\tconst rootRef = useRef<HTMLElement | null>(null);\n\tconst pointerDownRef = useRef(false);\n\tconst [boxW, setBoxW] = useState(0);\n\tconst [boxH, setBoxH] = useState(0);\n\n\tconst locationPlateSize = pickThumbnailLocationPlateSize(boxW, boxH);\n\tconst layers = useMemo(\n\t\t() =>\n\t\t\tresolveThumbnailLayers({\n\t\t\t\tgalleryThumb,\n\t\t\t\tavatarImage,\n\t\t\t\tbackgroundImage,\n\t\t\t\tforegroundImage,\n\t\t\t\tfallbackImage,\n\t\t\t\tnormalize: normalizeUrls,\n\t\t\t\tlocationPlateSize,\n\t\t\t\tcdnBaseUrl\n\t\t\t}),\n\t\t[\n\t\t\tgalleryThumb,\n\t\t\tavatarImage,\n\t\t\tbackgroundImage,\n\t\t\tforegroundImage,\n\t\t\tfallbackImage,\n\t\t\tnormalizeUrls,\n\t\t\tlocationPlateSize,\n\t\t\tcdnBaseUrl\n\t\t]\n\t);\n\n\tconst interaction = resolveThumbnailInteraction({\n\t\thref,\n\t\thasClickHandler: typeof onClick === 'function',\n\t\texperienceId\n\t});\n\tconst isHostedLaunch =\n\t\tinteraction === 'anchor' && !href?.trim() && Boolean(experienceId?.trim());\n\tconst hasScene = Boolean(layers?.backgroundImage);\n\tconst parallaxEnabled = parallax !== false;\n\n\tconst [shouldLoad, setShouldLoad] = useState(!lazy);\n\tconst [useAvif, setUseAvif] = useState(false);\n\tconst [plateFormatReady, setPlateFormatReady] = useState(false);\n\tconst [backgroundTriedFallback, setBackgroundTriedFallback] = useState(false);\n\tconst [foregroundTriedFallback, setForegroundTriedFallback] = useState(false);\n\tconst [avatarTriedWebpFallback, setAvatarTriedWebpFallback] = useState(false);\n\tconst [hovering, setHovering] = useState(false);\n\tconst [scrollParallax, setScrollParallax] = useState(false);\n\tconst [parallaxX, setParallaxX] = useState(0);\n\tconst [parallaxY, setParallaxY] = useState(0);\n\tconst [avatarLoadFailed, setAvatarLoadFailed] = useState(false);\n\tconst [backgroundReady, setBackgroundReady] = useState(false);\n\tconst [avatarReady, setAvatarReady] = useState(false);\n\tconst [foregroundReady, setForegroundReady] = useState(false);\n\tconst [resolvedHref, setResolvedHref] = useState<string | undefined>();\n\n\tconst resolvedAvatarImage = layers\n\t\t? avatarLoadFailed && layers.fallbackImage\n\t\t\t? layers.fallbackImage\n\t\t\t: layers.avatarImage\n\t\t: '';\n\n\tuseEffect(() => {\n\t\tensureExperienceThumbnailStyles(EXPERIENCE_THUMBNAIL_CSS);\n\t\tvoid supportsAvifDecode().then((ok) => {\n\t\t\tsetUseAvif(ok);\n\t\t\tsetPlateFormatReady(true);\n\t\t});\n\t}, []);\n\n\tuseEffect(() => {\n\t\tsetResolvedHref(\n\t\t\tresolveThumbnailHref({\n\t\t\t\thref,\n\t\t\t\texperienceId,\n\t\t\t\treturnUrl,\n\t\t\t\tplayerBaseUrl,\n\t\t\t\tuseCurrentPageAsReturnUrl: isHostedLaunch && !returnUrl?.trim()\n\t\t\t})\n\t\t);\n\t}, [href, experienceId, returnUrl, playerBaseUrl, isHostedLaunch]);\n\n\tuseEffect(() => {\n\t\tsetBackgroundReady(false);\n\t\tsetAvatarReady(false);\n\t\tsetForegroundReady(false);\n\t\tsetAvatarLoadFailed(false);\n\t\tsetBackgroundTriedFallback(false);\n\t\tsetForegroundTriedFallback(false);\n\t\tsetAvatarTriedWebpFallback(false);\n\t}, [layers?.backgroundImage, layers?.foregroundImage, resolvedAvatarImage]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el) return;\n\t\treturn attachThumbnailBox(el, (width, height) => {\n\t\t\tsetBoxW(width);\n\t\t\tsetBoxH(height);\n\t\t});\n\t}, [interaction, resolvedHref]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el) return;\n\t\tif (!lazy) {\n\t\t\tsetShouldLoad(true);\n\t\t\treturn;\n\t\t}\n\t\treturn attachLazyThumb(el, {\n\t\t\tonVisibleChange: (visible) => {\n\t\t\t\tif (visible) setShouldLoad(true);\n\t\t\t}\n\t\t});\n\t}, [lazy, layers, interaction, resolvedHref]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el || !parallaxEnabled) return;\n\t\treturn attachScrollLinkedParallax(el, {\n\t\t\tonParallax: (x) => {\n\t\t\t\tsetScrollParallax(true);\n\t\t\t\tif (!pointerDownRef.current) {\n\t\t\t\t\tsetParallaxX(x);\n\t\t\t\t\tsetParallaxY(0);\n\t\t\t\t}\n\t\t\t},\n\t\t\tpointerActive: () => pointerDownRef.current\n\t\t});\n\t}, [parallaxEnabled, layers, interaction, resolvedHref]);\n\n\tconst onPointerMove = (event: ReactPointerEvent<HTMLElement>) => {\n\t\tif (!parallaxEnabled || scrollParallax) return;\n\t\tconst rect = event.currentTarget.getBoundingClientRect();\n\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\tsetParallaxX(((event.clientX - rect.left) / rect.width - 0.5) * 2);\n\t\tsetParallaxY(((event.clientY - rect.top) / rect.height - 0.5) * 2);\n\t};\n\n\tconst clearPointer = (thumb: HTMLElement) => {\n\t\tconst wasDown = pointerDownRef.current;\n\t\tpointerDownRef.current = false;\n\t\tsetHovering(false);\n\t\tif (scrollParallax && wasDown) {\n\t\t\tsetParallaxX(\n\t\t\t\thorizontalParallaxFromViewportY(thumb.getBoundingClientRect(), window.innerHeight)\n\t\t\t);\n\t\t\tsetParallaxY(0);\n\t\t} else if (!scrollParallax) {\n\t\t\tsetParallaxX(0);\n\t\t\tsetParallaxY(0);\n\t\t}\n\t};\n\n\tconst onPointerDown = (event: ReactPointerEvent<HTMLElement>) => {\n\t\tif (!parallaxEnabled) return;\n\t\tpointerDownRef.current = true;\n\t\tif (event.pointerType === 'mouse') setHovering(true);\n\t\tconst thumb = event.currentTarget;\n\t\tconst onGlobalEnd = () => {\n\t\t\tclearPointer(thumb);\n\t\t\twindow.removeEventListener('pointerup', onGlobalEnd);\n\t\t\twindow.removeEventListener('pointercancel', onGlobalEnd);\n\t\t};\n\t\twindow.addEventListener('pointerup', onGlobalEnd);\n\t\twindow.addEventListener('pointercancel', onGlobalEnd);\n\t};\n\n\tconst handleClick = (event: ReactMouseEvent<HTMLElement>) => {\n\t\tonClick?.(event);\n\t\tif (event.defaultPrevented) return;\n\t\tif (isHostedLaunch && !returnUrl?.trim()) {\n\t\t\tconst next = resolveThumbnailHref({\n\t\t\t\texperienceId,\n\t\t\t\tplayerBaseUrl,\n\t\t\t\tuseCurrentPageAsReturnUrl: true\n\t\t\t});\n\t\t\tif (\n\t\t\t\tnext &&\n\t\t\t\tevent.button === 0 &&\n\t\t\t\t!event.metaKey &&\n\t\t\t\t!event.ctrlKey &&\n\t\t\t\t!event.shiftKey\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\twindow.location.assign(next);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (!layers) return null;\n\n\tconst parallaxLayersActive = parallaxEnabled && (hovering || scrollParallax);\n\t/** Fade placeholder as soon as any layer can paint — don't wait for the slowest plate. */\n\tconst revealThumb =\n\t\tshouldLoad && plateFormatReady && (avatarReady || backgroundReady || foregroundReady);\n\n\tconst rootClass = [\n\t\t'liforma-et',\n\t\thasScene ? 'liforma-et--scene' : '',\n\t\tthumbnailFitClass(fit),\n\t\tparallaxLayersActive ? 'liforma-et--parallax' : '',\n\t\tscrollParallax ? 'liforma-et--scroll-parallax' : '',\n\t\tshouldLoad && !revealThumb ? 'liforma-et--loading' : '',\n\t\trevealThumb ? 'liforma-et--revealed' : '',\n\t\tclassName\n\t]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst style = {\n\t\t['--liforma-et-parallax-x' as string]: parallaxX,\n\t\t['--liforma-et-parallax-y' as string]: parallaxY\n\t} as CSSProperties;\n\n\tconst markReady = (img: HTMLImageElement, setReady: (v: boolean) => void) => {\n\t\tif (img.complete) setReady(true);\n\t};\n\n\tconst media = (\n\t\t<>\n\t\t\t<span className=\"liforma-et__placeholder\" aria-hidden=\"true\" />\n\t\t\t{shouldLoad && plateFormatReady && layers.backgroundImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__background', backgroundReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(layers.backgroundImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setBackgroundReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setBackgroundReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst fallback = thumbnailLocationPlateFallbackUrl(img.src);\n\t\t\t\t\t\tif (fallback && !backgroundTriedFallback) {\n\t\t\t\t\t\t\tsetBackgroundTriedFallback(true);\n\t\t\t\t\t\t\timg.src = fallback;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetBackgroundReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{shouldLoad && plateFormatReady && resolvedAvatarImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__avatar', avatarReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(resolvedAvatarImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setAvatarReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setAvatarReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst webp = thumbnailColourPlateWebpFallbackUrl(img.src);\n\t\t\t\t\t\tif (webp && !avatarTriedWebpFallback) {\n\t\t\t\t\t\t\tsetAvatarTriedWebpFallback(true);\n\t\t\t\t\t\t\timg.src = webp;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (layers.fallbackImage) setAvatarLoadFailed(true);\n\t\t\t\t\t\telse setAvatarReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{shouldLoad && plateFormatReady && layers.foregroundImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__foreground', foregroundReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(layers.foregroundImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setForegroundReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setForegroundReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst fallback = thumbnailLocationPlateFallbackUrl(img.src);\n\t\t\t\t\t\tif (fallback && !foregroundTriedFallback) {\n\t\t\t\t\t\t\tsetForegroundTriedFallback(true);\n\t\t\t\t\t\t\timg.src = fallback;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetForegroundReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t</>\n\t);\n\n\tconst pointerProps = {\n\t\tonPointerDown,\n\t\tonPointerUp: (event: ReactPointerEvent<HTMLElement>) => clearPointer(event.currentTarget),\n\t\tonPointerCancel: (event: ReactPointerEvent<HTMLElement>) =>\n\t\t\tclearPointer(event.currentTarget),\n\t\tonPointerEnter: (event: ReactPointerEvent<HTMLElement>) => {\n\t\t\tif (parallaxEnabled && event.pointerType === 'mouse') setHovering(true);\n\t\t},\n\t\tonPointerMove,\n\t\tonPointerLeave: () => {\n\t\t\tif (!pointerDownRef.current) {\n\t\t\t\tsetHovering(false);\n\t\t\t\tif (!scrollParallax) {\n\t\t\t\t\tsetParallaxX(0);\n\t\t\t\t\tsetParallaxY(0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tconst setRootRef = (node: HTMLElement | null) => {\n\t\trootRef.current = node;\n\t};\n\n\tif (interaction === 'anchor' && resolvedHref) {\n\t\treturn (\n\t\t\t<a\n\t\t\t\tref={setRootRef}\n\t\t\t\tclassName={rootClass}\n\t\t\t\tstyle={style}\n\t\t\t\thref={resolvedHref}\n\t\t\t\ttarget={target}\n\t\t\t\trel={rel}\n\t\t\t\taria-label={alt}\n\t\t\t\tonClick={handleClick}\n\t\t\t\t{...pointerProps}\n\t\t\t>\n\t\t\t\t{media}\n\t\t\t</a>\n\t\t);\n\t}\n\n\tif (interaction === 'button') {\n\t\treturn (\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tref={setRootRef}\n\t\t\t\tclassName={rootClass}\n\t\t\t\tstyle={style}\n\t\t\t\taria-label={alt}\n\t\t\t\tonClick={handleClick}\n\t\t\t\t{...pointerProps}\n\t\t\t>\n\t\t\t\t{media}\n\t\t\t</button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tref={setRootRef}\n\t\t\tclassName={rootClass}\n\t\t\tstyle={style}\n\t\t\trole={alt ? 'img' : undefined}\n\t\t\taria-label={alt || undefined}\n\t\t\taria-hidden={alt ? undefined : true}\n\t\t\t{...pointerProps}\n\t\t>\n\t\t\t{media}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"thumbnail.js","names":[],"sources":["../../src/thumbnail/types.ts","../../src/thumbnail/avatarPlate.ts","../../src/thumbnail/locationPlate.ts","../../src/thumbnail/expandAssetUrl.ts","../../src/thumbnail/toGalleryThumbUrl.ts","../../src/shims/localStack.ts","../../src/thumbnail/hostedLaunchUrl.ts","../../src/thumbnail/thumbLoadScheduler.ts","../../src/thumbnail/lazyLoad.ts","../../src/thumbnail/scrollParallax.ts","../../src/thumbnail/styles.ts","../../src/thumbnail/ensureStyles.ts","../../src/thumbnail/plateDelivery.ts","../../src/react/ExperienceThumbnail.tsx"],"sourcesContent":["/**\n * Catalog-shaped layered gallery preview.\n * Values may be absolute URLs or Liforma paths (`/avatars/…`, `/locations/…/scene`).\n */\nexport type GalleryThumb = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n};\n\nexport type ThumbnailInteractionKind = 'anchor' | 'button' | 'presentational';\n\nexport type ResolveThumbnailInteractionInput = {\n\treadonly href?: string | null;\n\treadonly hasClickHandler?: boolean;\n\treadonly experienceId?: string | null;\n};\n\nexport type ThumbnailLayers = {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n};\n\n/** Shared props across Svelte / React / Next (React uses `onClick`). */\nexport type ExperienceThumbnailBaseProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\t/** Same as `<Experience fit>`: `full` (default), `medium`, or `face`. */\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n\treadonly normalizeUrls?: boolean;\n\t/** CDN origin for catalog paths. Default `https://cdn.liforma.ai`. */\n\treadonly cdnBaseUrl?: string;\n};\n\nexport function thumbnailFitClass(fit?: 'full' | 'face' | 'medium'): string {\n\tif (fit === 'face') return 'liforma-et--fit-face';\n\tif (fit === 'medium') return 'liforma-et--fit-medium';\n\treturn '';\n}\n","/** Avatar colour-plate tiers published on the CDN (`/avatars/{id}/{tier}/…`). */\nexport const THUMBNAIL_AVATAR_PLATE_SIZES = ['256', '512', '1024'] as const;\nexport type ThumbnailAvatarPlateSize = (typeof THUMBNAIL_AVATAR_PLATE_SIZES)[number];\n\nconst AVATAR_TIER_RE = /(\\/avatars\\/[^/]+\\/)(\\d+)(\\/)/i;\n\n/**\n * Pick avatar CDN tier from the host CSS box.\n * Unmeasured boxes stay on `256` (gallery default). Hosts larger than a 256\n * plate use `512`; spotlight-scale hosts (long edge above 512) use `1024`.\n */\nexport function pickThumbnailAvatarPlateSize(\n\twidth: number,\n\theight: number\n): ThumbnailAvatarPlateSize {\n\tif (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) {\n\t\treturn '256';\n\t}\n\tconst edge = Math.max(width, height);\n\tif (edge > 512) return '1024';\n\tif (edge > 280) return '512';\n\treturn '256';\n}\n\nexport function rewriteThumbnailAvatarPlateSize(\n\turl: string,\n\tsize: ThumbnailAvatarPlateSize\n): string {\n\tconst trimmed = url.trim();\n\tif (!trimmed) return trimmed;\n\treturn trimmed.replace(\n\t\tAVATAR_TIER_RE,\n\t\t(_match, prefix: string, _tier: string, slash: string) => `${prefix}${size}${slash}`\n\t);\n}\n","/** Location colour plates used by ExperienceThumbnail (no zoom/portrait crops). */\nexport const THUMBNAIL_LOCATION_PLATE_SIZES = ['256x256', '400x225', '800x450', '1672x941'] as const;\nexport type ThumbnailLocationPlateSize = (typeof THUMBNAIL_LOCATION_PLATE_SIZES)[number];\n\nconst LOCATION_SIZE_RE = /(-)(\\d+x\\d+)(\\.(?:webp|avif))(\\?|$)/i;\n\n/**\n * Square / portrait hosts keep the gallery crop. Landscape hosts use a 16:9 plate\n * so cover-fit fills height instead of clipping the top and bottom of a square.\n * Unmeasured boxes stay on `256x256`. Hosts 225px tall or smaller use `400x225`.\n */\nexport function pickThumbnailLocationPlateSize(\n\twidth: number,\n\theight: number\n): ThumbnailLocationPlateSize {\n\tif (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) {\n\t\treturn '256x256';\n\t}\n\tif (width <= height) return '256x256';\n\tif (height <= 225) return '400x225';\n\tif (width > 800 || height > 450) return '1672x941';\n\treturn '800x450';\n}\n\nexport function rewriteThumbnailLocationPlateSize(\n\turl: string,\n\tsize: ThumbnailLocationPlateSize\n): string {\n\tconst trimmed = url.trim();\n\tif (!trimmed) return trimmed;\n\treturn trimmed.replace(\n\t\tLOCATION_SIZE_RE,\n\t\t(_match, dash: string, _current: string, ext: string, end: string) =>\n\t\t\t`${dash}${size}${ext}${end}`\n\t);\n}\n\n/** Observe the thumb host and report CSS box size (for plate picking). */\nexport function attachThumbnailBox(\n\telement: HTMLElement,\n\tonBox: (width: number, height: number) => void\n): () => void {\n\tconst emit = () => {\n\t\tonBox(element.clientWidth, element.clientHeight);\n\t};\n\temit();\n\tif (typeof ResizeObserver === 'undefined') {\n\t\treturn () => {};\n\t}\n\tconst observer = new ResizeObserver(emit);\n\tobserver.observe(element);\n\treturn () => observer.disconnect();\n}\n","import {\n\trewriteThumbnailAvatarPlateSize,\n\ttype ThumbnailAvatarPlateSize\n} from './avatarPlate';\nimport {\n\trewriteThumbnailLocationPlateSize,\n\ttype ThumbnailLocationPlateSize\n} from './locationPlate';\n\n/** Default origin for Liforma gallery plates (catalog paths have no host). */\nexport const DEFAULT_THUMBNAIL_CDN_BASE_URL = 'https://cdn.liforma.ai';\n\nconst LOCATION_SIZE_STEM_RE = /-(256x256|400x225|400x450|800x450|836x971|1672x941|167x94)$/i;\n\nexport function normalizeThumbnailCdnBaseUrl(cdnBaseUrl?: string): string {\n\tconst trimmed = cdnBaseUrl?.trim().replace(/\\/$/, '');\n\treturn trimmed || DEFAULT_THUMBNAIL_CDN_BASE_URL;\n}\n\nfunction isHttpUrl(value: string): boolean {\n\treturn /^https?:\\/\\//i.test(value) || value.startsWith('//');\n}\n\nfunction absoluteHttpUrl(value: string): string {\n\tif (value.startsWith('//')) return `https:${value}`;\n\treturn value;\n}\n\nfunction splitPathAndSearch(value: string): { pathname: string; search: string } {\n\tconst q = value.indexOf('?');\n\tif (q === -1) return { pathname: value, search: '' };\n\treturn { pathname: value.slice(0, q), search: value.slice(q) };\n}\n\nfunction joinCdn(cdnBase: string, pathname: string, search = ''): string {\n\tconst path = pathname.startsWith('/') ? pathname : `/${pathname}`;\n\treturn `${cdnBase}${path}${search}`;\n}\n\n/**\n * Expand a catalog location path (or keep a full URL) to a sized plate URL.\n *\n * `/locations/modern/city/office/interior-desk-daytime` →\n * `{cdn}/locations/modern/city/office/interior-desk-daytime-800x450.webp`\n */\nexport function expandThumbnailLocationUrl(\n\tref: string,\n\tlocationPlateSize: ThumbnailLocationPlateSize,\n\tcdnBaseUrl?: string\n): string {\n\tconst raw = ref.trim();\n\tif (!raw) return raw;\n\tconst cdnBase = normalizeThumbnailCdnBaseUrl(cdnBaseUrl);\n\n\tif (isHttpUrl(raw)) {\n\t\treturn rewriteThumbnailLocationPlateSize(absoluteHttpUrl(raw), locationPlateSize);\n\t}\n\n\tconst { pathname: rawPath, search } = splitPathAndSearch(raw.replace(/\\\\/g, '/'));\n\tlet pathname = rawPath;\n\n\tif (!pathname.startsWith('/')) {\n\t\tpathname = `/locations/${pathname.replace(/^locations\\//i, '')}`;\n\t} else if (/^\\/locations\\//i.test(pathname)) {\n\t\t/* already a location path */\n\t} else if (/^\\/avatars\\//i.test(pathname)) {\n\t\treturn joinCdn(cdnBase, pathname, search);\n\t} else {\n\t\treturn raw;\n\t}\n\n\tif (/\\.webp$/i.test(pathname) && !LOCATION_SIZE_STEM_RE.test(pathname.replace(/\\.webp$/i, ''))) {\n\t\treturn joinCdn(cdnBase, pathname, search);\n\t}\n\n\tlet stem = pathname.replace(/^\\/locations\\//i, '').replace(/\\.webp$/i, '');\n\tstem = stem.replace(LOCATION_SIZE_STEM_RE, '');\n\treturn `${cdnBase}/locations/${stem}-${locationPlateSize}.webp${search}`;\n}\n\n/**\n * Expand a catalog avatar path (or keep a full URL) onto the public CDN.\n * `/avatars/{id}` → `{cdn}/avatars/{id}/{tier}/{id}_neutral.webp`\n */\nexport function expandThumbnailAvatarUrl(\n\tref: string,\n\tcdnBaseUrl?: string,\n\tavatarPlateSize: ThumbnailAvatarPlateSize = '256'\n): string {\n\tconst raw = ref.trim();\n\tif (!raw) return raw;\n\tconst cdnBase = normalizeThumbnailCdnBaseUrl(cdnBaseUrl);\n\tconst tier = avatarPlateSize;\n\n\tif (isHttpUrl(raw)) {\n\t\treturn rewriteThumbnailAvatarPlateSize(absoluteHttpUrl(raw), tier);\n\t}\n\n\tconst { pathname: rawPath, search } = splitPathAndSearch(raw.replace(/\\\\/g, '/'));\n\tlet pathname = rawPath;\n\n\tif (!pathname.startsWith('/')) {\n\t\tif (/^avatars\\//i.test(pathname)) {\n\t\t\tpathname = `/${pathname}`;\n\t\t} else if (/^[^/]+$/.test(pathname) && !search) {\n\t\t\treturn `${cdnBase}/avatars/${pathname}/${tier}/${pathname}_neutral.webp`;\n\t\t} else {\n\t\t\treturn raw;\n\t\t}\n\t}\n\n\tif (!/^\\/avatars\\//i.test(pathname)) {\n\t\treturn raw;\n\t}\n\n\tconst rest = pathname.replace(/^\\/avatars\\//i, '').replace(/\\/+$/, '');\n\tif (rest && !rest.includes('/') && !search) {\n\t\treturn `${cdnBase}/avatars/${rest}/${tier}/${rest}_neutral.webp`;\n\t}\n\n\treturn rewriteThumbnailAvatarPlateSize(joinCdn(cdnBase, pathname, search), tier);\n}\n","import type { ThumbnailAvatarPlateSize } from './avatarPlate';\nimport {\n\texpandThumbnailAvatarUrl,\n\texpandThumbnailLocationUrl,\n\tnormalizeThumbnailCdnBaseUrl\n} from './expandAssetUrl';\nimport type { ThumbnailLocationPlateSize } from './locationPlate';\n\nconst AVATAR_CH_PLATE_RE = /_CH(\\.webp)/i;\n\nexport type { ThumbnailAvatarPlateSize, ThumbnailLocationPlateSize };\n\nfunction isLocationRef(raw: string): boolean {\n\tif (/\\/locations\\//i.test(raw) || /^locations\\//i.test(raw)) return true;\n\tif (/^https?:\\/\\//i.test(raw) || raw.startsWith('//')) return false;\n\tif (/^\\/?avatars\\//i.test(raw)) return false;\n\tif (raw.startsWith('/')) return false;\n\treturn raw.includes('/');\n}\n\n/**\n * Rewrite known Liforma CDN URLs to gallery thumb tiers.\n * Avatars: /{tier}/ → the requested plate (`256` by default, `512` / `1024` for larger hosts);\n * locations: -{WxH}.webp → the requested plate\n * (`256x256` by default, `400x225` / `800x450` / `1672x941` for landscape hosts).\n * Catalog paths (`/locations/…/scene`, `/avatars/{id}`) expand onto the CDN first.\n * Unrecognized URLs are returned unchanged.\n */\nexport function toGalleryThumbUrl(\n\turl: string,\n\tlocationPlateSize: ThumbnailLocationPlateSize = '256x256',\n\tcdnBaseUrl?: string,\n\tavatarPlateSize: ThumbnailAvatarPlateSize = '256'\n): string {\n\tconst raw = url.trim();\n\tif (!raw) return raw;\n\n\tif (isLocationRef(raw)) {\n\t\treturn expandThumbnailLocationUrl(raw, locationPlateSize, cdnBaseUrl);\n\t}\n\n\tconst next = expandThumbnailAvatarUrl(raw, cdnBaseUrl, avatarPlateSize);\n\treturn next.replace(AVATAR_CH_PLATE_RE, '_neutral$1');\n}\n\nexport function resolveThumbnailLayers(input: {\n\treadonly galleryThumb?: {\n\t\treadonly avatarImage?: string;\n\t\treadonly backgroundImage?: string;\n\t\treadonly foregroundImage?: string;\n\t};\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n\t/** When false, keep caller absolute URLs (lightbox / preview). Paths still expand. Default true. */\n\treadonly normalize?: boolean;\n\t/** Location plate when expanding or rewriting location assets. Default `256x256`. */\n\treadonly locationPlateSize?: ThumbnailLocationPlateSize;\n\t/** Avatar plate when expanding or rewriting avatar assets. Default `256`. */\n\treadonly avatarPlateSize?: ThumbnailAvatarPlateSize;\n\t/** CDN origin for catalog paths. Default `https://cdn.liforma.ai`. */\n\treadonly cdnBaseUrl?: string;\n}): {\n\treadonly avatarImage: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly fallbackImage?: string;\n} | null {\n\tconst avatarSource =\n\t\tinput.avatarImage?.trim() ||\n\t\tinput.galleryThumb?.avatarImage?.trim() ||\n\t\t'';\n\tif (!avatarSource) return null;\n\n\tconst backgroundSource =\n\t\tinput.backgroundImage?.trim() || input.galleryThumb?.backgroundImage?.trim() || '';\n\tconst foregroundSource =\n\t\tinput.foregroundImage?.trim() || input.galleryThumb?.foregroundImage?.trim() || '';\n\tconst fallbackSource = input.fallbackImage?.trim() || '';\n\tconst plate = input.locationPlateSize ?? '256x256';\n\tconst avatarPlate = input.avatarPlateSize ?? '256';\n\tconst cdnBaseUrl = normalizeThumbnailCdnBaseUrl(input.cdnBaseUrl);\n\tconst rewriteKnown = input.normalize !== false;\n\n\tconst rewriteAvatar = (ref: string) => {\n\t\tif (!rewriteKnown && /^https?:\\/\\//i.test(ref.trim())) return ref.trim();\n\t\treturn toGalleryThumbUrl(ref, plate, cdnBaseUrl, avatarPlate);\n\t};\n\tconst rewriteLocation = (ref: string) => {\n\t\tif (!rewriteKnown && /^https?:\\/\\//i.test(ref.trim())) return ref.trim();\n\t\treturn expandThumbnailLocationUrl(ref, plate, cdnBaseUrl);\n\t};\n\n\treturn {\n\t\tavatarImage: rewriteAvatar(avatarSource),\n\t\t...(backgroundSource ? { backgroundImage: rewriteLocation(backgroundSource) } : {}),\n\t\t...(foregroundSource ? { foregroundImage: rewriteLocation(foregroundSource) } : {}),\n\t\t...(fallbackSource ? { fallbackImage: rewriteAvatar(fallbackSource) } : {})\n\t};\n}\n","/**\n * Opt-in local monorepo stack (api :3001, player :3002).\n * Never inferred from hostname alone — third-party demos run on localhost against production.\n */\nexport function useExplicitLocalStack(): boolean {\n\tif ((globalThis as { __LIFORMA_STACK?: string }).__LIFORMA_STACK === 'local') {\n\t\treturn true;\n\t}\n\n\tif (typeof location === 'undefined') {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\treturn new URLSearchParams(location.search).get('stack') === 'local';\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import { useExplicitLocalStack } from '../shims/localStack.ts';\nimport type { ResolveThumbnailInteractionInput, ThumbnailInteractionKind } from './types';\n\nconst PRODUCTION_PLAYER_ORIGIN = 'https://player.liforma.ai';\nconst LOCAL_PLAYER_ORIGIN = 'http://localhost:3002';\n\n/** Priority: href → click-only → hosted (experienceId) → presentational. */\nexport function resolveThumbnailInteraction(\n\tinput: ResolveThumbnailInteractionInput\n): ThumbnailInteractionKind {\n\tif (input.href?.trim()) return 'anchor';\n\tif (input.hasClickHandler) return 'button';\n\tif (input.experienceId?.trim()) return 'anchor';\n\treturn 'presentational';\n}\n\nexport function resolvePlayerBaseUrl(playerBaseUrl?: string): string {\n\tconst configured = playerBaseUrl?.trim();\n\tif (configured) return configured.replace(/\\/$/, '');\n\n\tif (useExplicitLocalStack()) {\n\t\treturn LOCAL_PLAYER_ORIGIN;\n\t}\n\n\treturn PRODUCTION_PLAYER_ORIGIN;\n}\n\n/**\n * Build hosted player launch URL: `{player}/{experienceId}?returnUrl=…`\n * When `returnUrl` is omitted, callers should assign on click (SSR-safe) or pass an explicit URL.\n */\nexport function buildHostedLaunchUrl(options: {\n\treadonly experienceId: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n}): string {\n\tconst experienceId = options.experienceId.trim();\n\tconst base = resolvePlayerBaseUrl(options.playerBaseUrl);\n\tconst url = new URL(`${base}/${encodeURIComponent(experienceId)}`);\n\tconst returnUrl = options.returnUrl?.trim();\n\tif (returnUrl) {\n\t\turl.searchParams.set('returnUrl', returnUrl);\n\t}\n\treturn url.toString();\n}\n\n/** Resolve href for anchor mode (app link or hosted launch). */\nexport function resolveThumbnailHref(options: {\n\treadonly href?: string;\n\treadonly experienceId?: string;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\t/** When true and returnUrl omitted, use window.location.href (browser only). */\n\treadonly useCurrentPageAsReturnUrl?: boolean;\n}): string | undefined {\n\tconst href = options.href?.trim();\n\tif (href) return href;\n\n\tconst experienceId = options.experienceId?.trim();\n\tif (!experienceId) return undefined;\n\n\tlet returnUrl = options.returnUrl?.trim();\n\tif (!returnUrl && options.useCurrentPageAsReturnUrl && typeof window !== 'undefined') {\n\t\treturnUrl = window.location.href;\n\t}\n\n\treturn buildHostedLaunchUrl({\n\t\texperienceId,\n\t\t...(returnUrl ? { returnUrl } : {}),\n\t\tplayerBaseUrl: options.playerBaseUrl\n\t});\n}\n","/** Touch / coarse pointers — limit how many thumbs start loading at once. */\nexport const COARSE_THUMB_LOAD_CONCURRENCY = 4;\n/** Delay before a started thumb frees its queue slot for the next card. */\nexport const COARSE_THUMB_LOAD_STAGGER_MS = 160;\n\nexport function prefersCoarsePointer(): boolean {\n\tif (typeof window === 'undefined') return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\ntype QueuedLoad = {\n\treadonly start: () => void;\n\tcancelled: boolean;\n};\n\nlet inflight = 0;\nconst waiting: QueuedLoad[] = [];\n\nfunction maxConcurrent(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_CONCURRENCY : Number.POSITIVE_INFINITY;\n}\n\nfunction staggerMs(): number {\n\treturn prefersCoarsePointer() ? COARSE_THUMB_LOAD_STAGGER_MS : 0;\n}\n\nfunction pump(): void {\n\tconst limit = maxConcurrent();\n\twhile (inflight < limit && waiting.length > 0) {\n\t\tconst next = waiting.shift();\n\t\tif (!next || next.cancelled) continue;\n\t\tinflight += 1;\n\t\tnext.start();\n\t\tconst delay = staggerMs();\n\t\tif (delay <= 0) {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tcontinue;\n\t\t}\n\t\twindow.setTimeout(() => {\n\t\t\tinflight = Math.max(0, inflight - 1);\n\t\t\tpump();\n\t\t}, delay);\n\t}\n}\n\n/**\n * On coarse pointers, start thumb image loads in small waves so old devices\n * are not hit with dozens of concurrent decodes. Fine pointers start immediately.\n * Returns a cancel function (no-op after start has run).\n */\nexport function scheduleThumbLoad(start: () => void): () => void {\n\tif (!prefersCoarsePointer()) {\n\t\tstart();\n\t\treturn () => {};\n\t}\n\n\tconst item: QueuedLoad = { start, cancelled: false };\n\twaiting.push(item);\n\tpump();\n\n\treturn () => {\n\t\titem.cancelled = true;\n\t};\n}\n\n/** Test helper — clear queue state between vitest cases. */\nexport function resetThumbLoadSchedulerForTests(): void {\n\tinflight = 0;\n\twaiting.length = 0;\n}\n","import { prefersCoarsePointer, scheduleThumbLoad } from './thumbLoadScheduler';\n\nexport type LazyThumbOptions = {\n\treadonly rootMargin?: string;\n\treadonly onVisibleChange: (visible: boolean) => void;\n};\n\n/** Desktop: prefetch earlier. Coarse/touch: tighter margin to cut burst loads. */\nexport function getDefaultLazyRootMargin(): string {\n\treturn prefersCoarsePointer() ? '96px 0px' : '280px 0px';\n}\n\nfunction parseVerticalRootMarginPx(rootMargin: string): number {\n\tconst first = rootMargin.trim().split(/\\s+/)[0] ?? '0px';\n\tconst value = Number.parseFloat(first);\n\treturn Number.isFinite(value) ? Math.abs(value) : 280;\n}\n\nfunction isNearViewport(element: HTMLElement, marginPx: number): boolean {\n\tif (typeof window === 'undefined') return false;\n\tconst rect = element.getBoundingClientRect();\n\treturn rect.bottom >= -marginPx && rect.top <= window.innerHeight + marginPx;\n}\n\n/** Observe the thumb and enable image loads when near the viewport. */\nexport function attachLazyThumb(element: HTMLElement, options: LazyThumbOptions): () => void {\n\tconst { onVisibleChange } = options;\n\tconst rootMargin = options.rootMargin ?? getDefaultLazyRootMargin();\n\tconst marginPx = parseVerticalRootMarginPx(rootMargin);\n\tlet visible = false;\n\tlet loadStarted = false;\n\tlet cancelScheduled: (() => void) | null = null;\n\n\tconst requestLoad = () => {\n\t\tif (loadStarted) return;\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = scheduleThumbLoad(() => {\n\t\t\tcancelScheduled = null;\n\t\t\tloadStarted = true;\n\t\t\tonVisibleChange(true);\n\t\t});\n\t};\n\n\tconst setVisible = (next: boolean) => {\n\t\tif (next === visible) return;\n\t\tvisible = next;\n\t\tif (next) {\n\t\t\trequestLoad();\n\t\t\treturn;\n\t\t}\n\t\t// Keep images once started; only cancel a queued (not-yet-started) load.\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tonVisibleChange(false);\n\t};\n\n\tif (typeof IntersectionObserver === 'undefined') {\n\t\trequestLoad();\n\t\treturn () => {\n\t\t\tcancelScheduled?.();\n\t\t\tcancelScheduled = null;\n\t\t};\n\t}\n\n\tconst observer = new IntersectionObserver(\n\t\t(entries) => {\n\t\t\tsetVisible(entries.some((entry) => entry.isIntersecting));\n\t\t},\n\t\t{ root: null, rootMargin, threshold: 0 }\n\t);\n\n\tobserver.observe(element);\n\n\tif (isNearViewport(element, marginPx)) {\n\t\tsetVisible(true);\n\t}\n\n\treturn () => {\n\t\tcancelScheduled?.();\n\t\tcancelScheduled = null;\n\t\tobserver.disconnect();\n\t};\n}\n\nexport function attachImageReady(element: HTMLImageElement, onReady: () => void): () => void {\n\tconst notify = () => onReady();\n\tif (element.complete) {\n\t\tqueueMicrotask(notify);\n\t} else {\n\t\telement.addEventListener('load', notify, { once: true });\n\t\telement.addEventListener('error', notify, { once: true });\n\t}\n\treturn () => {\n\t\telement.removeEventListener('load', notify);\n\t\telement.removeEventListener('error', notify);\n\t};\n}\n","/** Touch devices without hover — drive thumbnail parallax from scroll position. */\nexport function prefersScrollLinkedParallax(): boolean {\n\tif (typeof window === 'undefined') return false;\n\tif (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return false;\n\treturn window.matchMedia('(hover: none), (pointer: coarse)').matches;\n}\n\n/**\n * Map an element's vertical centre in the viewport to horizontal parallax in [-1, 1].\n */\nexport function horizontalParallaxFromViewportY(rect: DOMRect, viewportHeight: number): number {\n\tconst centerY = rect.top + rect.height / 2;\n\tconst t = centerY / Math.max(viewportHeight, 1);\n\treturn (t - 0.5) * 2;\n}\n\nexport type ScrollLinkedParallaxOptions = {\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\ntype ScrollParallaxSubscriber = {\n\treadonly element: HTMLElement;\n\treadonly onParallax: (x: number) => void;\n\treadonly pointerActive?: () => boolean;\n};\n\nconst subscribers = new Set<ScrollParallaxSubscriber>();\nlet listening = false;\nlet frame = 0;\n\nfunction syncAll(): void {\n\tframe = 0;\n\tif (subscribers.size === 0 || typeof window === 'undefined') return;\n\tconst viewportHeight = window.innerHeight;\n\tfor (const entry of subscribers) {\n\t\tif (entry.pointerActive?.()) continue;\n\t\tconst rect = entry.element.getBoundingClientRect();\n\t\t// Skip off-screen thumbs — avoids N layout reads + framework updates per scroll.\n\t\tif (rect.bottom < -64 || rect.top > viewportHeight + 64) continue;\n\t\tentry.onParallax(horizontalParallaxFromViewportY(rect, viewportHeight));\n\t}\n}\n\nfunction scheduleSync(): void {\n\tif (frame !== 0) return;\n\tframe = requestAnimationFrame(syncAll);\n}\n\nfunction ensureListening(): void {\n\tif (listening || typeof window === 'undefined') return;\n\tlistening = true;\n\twindow.addEventListener('scroll', scheduleSync, { passive: true });\n\twindow.addEventListener('resize', scheduleSync, { passive: true });\n}\n\nfunction stopListeningIfIdle(): void {\n\tif (subscribers.size > 0 || !listening || typeof window === 'undefined') return;\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n\twindow.removeEventListener('scroll', scheduleSync);\n\twindow.removeEventListener('resize', scheduleSync);\n}\n\n/**\n * Subscribe to scroll/resize and update horizontal parallax from viewport Y.\n * Uses one shared window listener for all thumbs (old iPads with large galleries).\n */\nexport function attachScrollLinkedParallax(\n\telement: HTMLElement,\n\toptions: ScrollLinkedParallaxOptions\n): () => void {\n\tif (!prefersScrollLinkedParallax()) return () => {};\n\n\tconst entry: ScrollParallaxSubscriber = {\n\t\telement,\n\t\tonParallax: options.onParallax,\n\t\tpointerActive: options.pointerActive\n\t};\n\tsubscribers.add(entry);\n\tensureListening();\n\n\tif (!entry.pointerActive?.()) {\n\t\toptions.onParallax(\n\t\t\thorizontalParallaxFromViewportY(element.getBoundingClientRect(), window.innerHeight)\n\t\t);\n\t}\n\n\treturn () => {\n\t\tsubscribers.delete(entry);\n\t\tstopListeningIfIdle();\n\t};\n}\n\n/** Test helper — clear shared scroll state between vitest cases. */\nexport function resetScrollLinkedParallaxForTests(): void {\n\tsubscribers.clear();\n\tif (typeof window !== 'undefined' && listening) {\n\t\twindow.removeEventListener('scroll', scheduleSync);\n\t\twindow.removeEventListener('resize', scheduleSync);\n\t}\n\tlistening = false;\n\tif (frame !== 0) {\n\t\tcancelAnimationFrame(frame);\n\t\tframe = 0;\n\t}\n}\n","/** Shared styles for ExperienceThumbnail (class prefix `liforma-et`). */\nexport const EXPERIENCE_THUMBNAIL_CSS = `\n:host {\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n}\n\n.liforma-et {\n\t--liforma-et-parallax-x: 0;\n\t--liforma-et-parallax-y: 0;\n\t--liforma-et-bg-scale: 1;\n\t--liforma-et-fg-scale: 1;\n\t--liforma-et-avatar-scale: 1;\n\t--liforma-et-bg-shift: 0px;\n\t--liforma-et-fg-shift: 0px;\n\t--liforma-et-avatar-shift: 0px;\n\t--liforma-et-reveal-duration: 420ms;\n\n\tbox-sizing: border-box;\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 0;\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tborder-radius: 0;\n\toverflow: hidden;\n\tbackground: #111;\n\tcolor: inherit;\n\ttext-decoration: none;\n\tcursor: inherit;\n\t-webkit-tap-highlight-color: transparent;\n}\n\na.liforma-et,\nbutton.liforma-et {\n\tcursor: pointer;\n}\n\nbutton.liforma-et {\n\tfont: inherit;\n\tappearance: none;\n}\n\n.liforma-et__placeholder {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 0;\n\tbackground:\n\t\tradial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 58%),\n\t\tlinear-gradient(180deg, #1a1a1a 0%, #101010 100%);\n\topacity: 1;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et--revealed .liforma-et__placeholder {\n\topacity: 0;\n}\n\n.liforma-et__media {\n\topacity: 0;\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n.liforma-et__media--ready {\n\topacity: 1;\n}\n\n.liforma-et__background {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 1;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-bg-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-bg-shift))\n\t\t)\n\t\tscale(var(--liforma-et-bg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et__avatar {\n\tposition: relative;\n\tz-index: 2;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: contain;\n\tobject-position: center bottom;\n\ttransform: scale(var(--liforma-et-avatar-scale))\n\t\ttranslate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-avatar-shift)),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-avatar-shift))\n\t\t);\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n/* Gallery plates are typically square in a square tile — contain vs cover is a no-op\n without a zoom. Scale the whole scene from the upper third so Face matches\n player fit (location + avatar crop together). */\n.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.85;\n\t--liforma-et-bg-scale: 1.85;\n\t--liforma-et-fg-scale: 1.85;\n}\n\n.liforma-et--fit-face .liforma-et__avatar,\n.liforma-et--fit-face .liforma-et__background,\n.liforma-et--fit-face .liforma-et__foreground {\n\ttransform-origin: 50% 18%;\n}\n\n.liforma-et--fit-face .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 18%;\n}\n\n/* Bust / medium: less zoom than face so upper chest stays in frame (player 3.75H window). */\n.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.35;\n\t--liforma-et-bg-scale: 1.35;\n\t--liforma-et-fg-scale: 1.35;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar,\n.liforma-et--fit-medium .liforma-et__background,\n.liforma-et--fit-medium .liforma-et__foreground {\n\ttransform-origin: 50% 28%;\n}\n\n.liforma-et--fit-medium .liforma-et__avatar {\n\tobject-fit: cover;\n\tobject-position: 50% 28%;\n}\n\n.liforma-et__foreground {\n\tposition: absolute;\n\tinset: 0;\n\tz-index: 3;\n\twidth: 100%;\n\theight: 100%;\n\tobject-fit: cover;\n\tobject-position: center center;\n\tpointer-events: none;\n\ttransform: translate(\n\t\t\tcalc(var(--liforma-et-parallax-x) * var(--liforma-et-fg-shift) * -1),\n\t\t\tcalc(var(--liforma-et-parallax-y) * var(--liforma-et-fg-shift) * -1)\n\t\t)\n\t\tscale(var(--liforma-et-fg-scale));\n\ttransform-origin: center center;\n\ttransition:\n\t\topacity var(--liforma-et-reveal-duration) ease,\n\t\ttransform 160ms ease;\n}\n\n.liforma-et--parallax {\n\t--liforma-et-bg-scale: 1.08;\n\t--liforma-et-fg-scale: 1.05;\n\t--liforma-et-avatar-scale: 1.08;\n\t--liforma-et-bg-shift: 10px;\n\t--liforma-et-fg-shift: 4px;\n\t--liforma-et-avatar-shift: 4px;\n}\n\n/* Promote layers only while parallax is active — avoids N compositor surfaces on galleries. */\n.liforma-et--parallax .liforma-et__background,\n.liforma-et--parallax .liforma-et__foreground,\n.liforma-et--parallax .liforma-et__avatar {\n\twill-change: transform;\n}\n\n.liforma-et--parallax.liforma-et--fit-face {\n\t--liforma-et-avatar-scale: 1.95;\n\t--liforma-et-bg-scale: 1.95;\n\t--liforma-et-fg-scale: 1.95;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--parallax.liforma-et--fit-medium {\n\t--liforma-et-avatar-scale: 1.42;\n\t--liforma-et-bg-scale: 1.42;\n\t--liforma-et-fg-scale: 1.42;\n\t--liforma-et-avatar-shift: 0px;\n}\n\n.liforma-et--scroll-parallax .liforma-et__background,\n.liforma-et--scroll-parallax .liforma-et__foreground,\n.liforma-et--scroll-parallax .liforma-et__avatar {\n\ttransition: opacity var(--liforma-et-reveal-duration) ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.liforma-et {\n\t\t--liforma-et-reveal-duration: 0ms;\n\t}\n\n\t.liforma-et--parallax {\n\t\t--liforma-et-bg-scale: 1;\n\t\t--liforma-et-fg-scale: 1;\n\t\t--liforma-et-avatar-scale: 1;\n\t\t--liforma-et-bg-shift: 0px;\n\t\t--liforma-et-fg-shift: 0px;\n\t\t--liforma-et-avatar-shift: 0px;\n\t}\n\n\t.liforma-et--parallax.liforma-et--fit-face {\n\t\t--liforma-et-avatar-scale: 1.85;\n\t\t--liforma-et-bg-scale: 1.85;\n\t\t--liforma-et-fg-scale: 1.85;\n\t}\n\n\t.liforma-et__background,\n\t.liforma-et__foreground,\n\t.liforma-et__avatar {\n\t\ttransition: none;\n\t}\n}\n`.trim();\n","let styleMounted = false;\n\n/** Inject shared thumbnail CSS once per document. */\nexport function ensureExperienceThumbnailStyles(css: string): void {\n\tif (typeof document === 'undefined' || styleMounted) return;\n\tif (document.querySelector('style[data-liforma-et]')) {\n\t\tstyleMounted = true;\n\t\treturn;\n\t}\n\tconst style = document.createElement('style');\n\tstyle.setAttribute('data-liforma-et', '');\n\tstyle.textContent = css;\n\tdocument.head.appendChild(style);\n\tstyleMounted = true;\n}\n","/**\n * CDN colour-plate delivery for thumbnails (avatars + locations).\n * Catalogue identity stays `.webp`; fetch uses `.avif` when the browser can decode it.\n * Depth maps stay WebP. Costume rendered composites have AVIF siblings from the compose worker.\n */\n\nconst AVIF_PROBE_DATA_URI =\n\t'data:image/avif;base64,AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAEAAAABAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';\n\nlet avifSupportPromise: Promise<boolean> | null = null;\n\n/** True when this URL is a colour plate WebP that may have an AVIF sibling (not depth). */\nexport function isColourPlateWebpUrl(url: string): boolean {\n\tconst trimmed = url.trim();\n\tif (!trimmed || trimmed.startsWith('blob:') || trimmed.startsWith('data:')) return false;\n\tconst pathOnly = trimmed.split(/[?#]/)[0] ?? trimmed;\n\tconst isAvatar = /\\/avatars\\//i.test(pathOnly);\n\tconst isLocation = /\\/locations\\//i.test(pathOnly);\n\tif (!isAvatar && !isLocation) return false;\n\tif (/\\.depth\\.webp$/i.test(pathOnly)) return false;\n\treturn /\\.webp$/i.test(pathOnly);\n}\n\n/** Rewrite a colour-plate `.webp` path to `.avif`, preserving query/hash. */\nexport function toAvifPlateUrl(url: string): string | null {\n\tif (!isColourPlateWebpUrl(url)) return null;\n\tconst qIndex = url.search(/[?#]/);\n\tif (qIndex < 0) return url.replace(/\\.webp$/i, '.avif');\n\tconst path = url.slice(0, qIndex);\n\tconst suffix = url.slice(qIndex);\n\treturn `${path.replace(/\\.webp$/i, '.avif')}${suffix}`;\n}\n\nexport function supportsAvifDecode(): Promise<boolean> {\n\tif (typeof Image === 'undefined') return Promise.resolve(false);\n\tif (!avifSupportPromise) {\n\t\tavifSupportPromise = new Promise<boolean>((resolve) => {\n\t\t\tconst img = new Image();\n\t\t\timg.onload = () => resolve(img.naturalWidth > 0 && img.naturalHeight > 0);\n\t\t\timg.onerror = () => resolve(false);\n\t\t\timg.src = AVIF_PROBE_DATA_URI;\n\t\t});\n\t}\n\treturn avifSupportPromise;\n}\n\nexport function resetAvifDecodeSupportCacheForTests(): void {\n\tavifSupportPromise = null;\n}\n\n/** WebP catalogue URL → AVIF sibling when `useAvif` is true. */\nexport function thumbnailPlateSrc(url: string, useAvif: boolean): string {\n\tif (!useAvif) return url;\n\treturn toAvifPlateUrl(url) ?? url;\n}\n\n/** Colour-plate `.avif` → catalogue `.webp` (compose worker / missing sibling). */\nexport function thumbnailColourPlateWebpFallbackUrl(url: string): string | null {\n\tconst trimmed = url.trim();\n\tconst qIndex = trimmed.search(/[?#]/);\n\tconst pathOnly = qIndex < 0 ? trimmed : trimmed.slice(0, qIndex);\n\tif (!/\\.avif$/i.test(pathOnly)) return null;\n\tconst webpPath = pathOnly.replace(/\\.avif$/i, '.webp');\n\treturn qIndex < 0 ? webpPath : `${webpPath}${trimmed.slice(qIndex)}`;\n}\n\n/** 400×225 is colour-only; if missing, step up to the published 800×450 sibling. */\nexport function thumbnailLocationPlateFallbackUrl(url: string): string | null {\n\tconst trimmed = url.trim();\n\tif (!/-400x225\\.(webp|avif)(?:$|[?#])/i.test(trimmed)) return null;\n\treturn trimmed.replace(/-400x225\\.(webp|avif)/i, '-800x450.$1');\n}\n","'use client';\n\nimport {\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n\ttype CSSProperties,\n\ttype MouseEvent as ReactMouseEvent,\n\ttype PointerEvent as ReactPointerEvent\n} from 'react';\n\nimport {\n\tattachLazyThumb,\n\tattachScrollLinkedParallax,\n\tattachThumbnailBox,\n\tensureExperienceThumbnailStyles,\n\tEXPERIENCE_THUMBNAIL_CSS,\n\thorizontalParallaxFromViewportY,\n\tpickThumbnailAvatarPlateSize,\n\tpickThumbnailLocationPlateSize,\n\tresolveThumbnailHref,\n\tresolveThumbnailInteraction,\n\tresolveThumbnailLayers,\n\tthumbnailFitClass,\n\tthumbnailColourPlateWebpFallbackUrl,\n\tthumbnailLocationPlateFallbackUrl,\n\tthumbnailPlateSrc,\n\tsupportsAvifDecode,\n\ttype GalleryThumb\n} from '../thumbnail/index';\n\nexport type ExperienceThumbnailProps = {\n\treadonly galleryThumb?: GalleryThumb;\n\treadonly avatarImage?: string;\n\treadonly backgroundImage?: string;\n\treadonly foregroundImage?: string;\n\treadonly alt: string;\n\treadonly experienceId?: string;\n\treadonly href?: string;\n\treadonly target?: string;\n\treadonly rel?: string;\n\treadonly onClick?: (event: ReactMouseEvent<HTMLElement>) => void;\n\treadonly returnUrl?: string;\n\treadonly playerBaseUrl?: string;\n\treadonly parallax?: boolean;\n\treadonly fit?: 'full' | 'face' | 'medium';\n\treadonly lazy?: boolean;\n\treadonly className?: string;\n\treadonly fallbackImage?: string;\n\treadonly normalizeUrls?: boolean;\n\treadonly cdnBaseUrl?: string;\n};\n\nfunction mediaClass(base: string, ready: boolean): string {\n\treturn `${base} liforma-et__media${ready ? ' liforma-et__media--ready' : ''}`;\n}\n\nexport function ExperienceThumbnail(props: ExperienceThumbnailProps) {\n\tconst {\n\t\tgalleryThumb,\n\t\tavatarImage,\n\t\tbackgroundImage,\n\t\tforegroundImage,\n\t\talt,\n\t\texperienceId,\n\t\thref,\n\t\ttarget,\n\t\trel,\n\t\tonClick,\n\t\treturnUrl,\n\t\tplayerBaseUrl,\n\t\tparallax = true,\n\t\tfit = 'full',\n\t\tlazy = true,\n\t\tclassName = '',\n\t\tfallbackImage,\n\t\tnormalizeUrls = true,\n\t\tcdnBaseUrl\n\t} = props;\n\n\tconst rootRef = useRef<HTMLElement | null>(null);\n\tconst pointerDownRef = useRef(false);\n\tconst [boxW, setBoxW] = useState(0);\n\tconst [boxH, setBoxH] = useState(0);\n\n\tconst locationPlateSize = pickThumbnailLocationPlateSize(boxW, boxH);\n\tconst avatarPlateSize = pickThumbnailAvatarPlateSize(boxW, boxH);\n\tconst layers = useMemo(\n\t\t() =>\n\t\t\tresolveThumbnailLayers({\n\t\t\t\tgalleryThumb,\n\t\t\t\tavatarImage,\n\t\t\t\tbackgroundImage,\n\t\t\t\tforegroundImage,\n\t\t\t\tfallbackImage,\n\t\t\t\tnormalize: normalizeUrls,\n\t\t\t\tlocationPlateSize,\n\t\t\t\tavatarPlateSize,\n\t\t\t\tcdnBaseUrl\n\t\t\t}),\n\t\t[\n\t\t\tgalleryThumb,\n\t\t\tavatarImage,\n\t\t\tbackgroundImage,\n\t\t\tforegroundImage,\n\t\t\tfallbackImage,\n\t\t\tnormalizeUrls,\n\t\t\tlocationPlateSize,\n\t\t\tavatarPlateSize,\n\t\t\tcdnBaseUrl\n\t\t]\n\t);\n\n\tconst interaction = resolveThumbnailInteraction({\n\t\thref,\n\t\thasClickHandler: typeof onClick === 'function',\n\t\texperienceId\n\t});\n\tconst isHostedLaunch =\n\t\tinteraction === 'anchor' && !href?.trim() && Boolean(experienceId?.trim());\n\tconst hasScene = Boolean(layers?.backgroundImage);\n\tconst parallaxEnabled = parallax !== false;\n\n\tconst [shouldLoad, setShouldLoad] = useState(!lazy);\n\tconst [useAvif, setUseAvif] = useState(false);\n\tconst [plateFormatReady, setPlateFormatReady] = useState(false);\n\tconst [backgroundTriedFallback, setBackgroundTriedFallback] = useState(false);\n\tconst [foregroundTriedFallback, setForegroundTriedFallback] = useState(false);\n\tconst [avatarTriedWebpFallback, setAvatarTriedWebpFallback] = useState(false);\n\tconst [hovering, setHovering] = useState(false);\n\tconst [scrollParallax, setScrollParallax] = useState(false);\n\tconst [parallaxX, setParallaxX] = useState(0);\n\tconst [parallaxY, setParallaxY] = useState(0);\n\tconst [avatarLoadFailed, setAvatarLoadFailed] = useState(false);\n\tconst [backgroundReady, setBackgroundReady] = useState(false);\n\tconst [avatarReady, setAvatarReady] = useState(false);\n\tconst [foregroundReady, setForegroundReady] = useState(false);\n\tconst [resolvedHref, setResolvedHref] = useState<string | undefined>();\n\n\tconst resolvedAvatarImage = layers\n\t\t? avatarLoadFailed && layers.fallbackImage\n\t\t\t? layers.fallbackImage\n\t\t\t: layers.avatarImage\n\t\t: '';\n\n\tuseEffect(() => {\n\t\tensureExperienceThumbnailStyles(EXPERIENCE_THUMBNAIL_CSS);\n\t\tvoid supportsAvifDecode().then((ok) => {\n\t\t\tsetUseAvif(ok);\n\t\t\tsetPlateFormatReady(true);\n\t\t});\n\t}, []);\n\n\tuseEffect(() => {\n\t\tsetResolvedHref(\n\t\t\tresolveThumbnailHref({\n\t\t\t\thref,\n\t\t\t\texperienceId,\n\t\t\t\treturnUrl,\n\t\t\t\tplayerBaseUrl,\n\t\t\t\tuseCurrentPageAsReturnUrl: isHostedLaunch && !returnUrl?.trim()\n\t\t\t})\n\t\t);\n\t}, [href, experienceId, returnUrl, playerBaseUrl, isHostedLaunch]);\n\n\tuseEffect(() => {\n\t\tsetBackgroundReady(false);\n\t\tsetAvatarReady(false);\n\t\tsetForegroundReady(false);\n\t\tsetAvatarLoadFailed(false);\n\t\tsetBackgroundTriedFallback(false);\n\t\tsetForegroundTriedFallback(false);\n\t\tsetAvatarTriedWebpFallback(false);\n\t}, [layers?.backgroundImage, layers?.foregroundImage, resolvedAvatarImage]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el) return;\n\t\treturn attachThumbnailBox(el, (width, height) => {\n\t\t\tsetBoxW(width);\n\t\t\tsetBoxH(height);\n\t\t});\n\t}, [interaction, resolvedHref]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el) return;\n\t\tif (!lazy) {\n\t\t\tsetShouldLoad(true);\n\t\t\treturn;\n\t\t}\n\t\treturn attachLazyThumb(el, {\n\t\t\tonVisibleChange: (visible) => {\n\t\t\t\tif (visible) setShouldLoad(true);\n\t\t\t}\n\t\t});\n\t}, [lazy, layers, interaction, resolvedHref]);\n\n\tuseEffect(() => {\n\t\tconst el = rootRef.current;\n\t\tif (!el || !parallaxEnabled) return;\n\t\treturn attachScrollLinkedParallax(el, {\n\t\t\tonParallax: (x) => {\n\t\t\t\tsetScrollParallax(true);\n\t\t\t\tif (!pointerDownRef.current) {\n\t\t\t\t\tsetParallaxX(x);\n\t\t\t\t\tsetParallaxY(0);\n\t\t\t\t}\n\t\t\t},\n\t\t\tpointerActive: () => pointerDownRef.current\n\t\t});\n\t}, [parallaxEnabled, layers, interaction, resolvedHref]);\n\n\tconst onPointerMove = (event: ReactPointerEvent<HTMLElement>) => {\n\t\tif (!parallaxEnabled || scrollParallax) return;\n\t\tconst rect = event.currentTarget.getBoundingClientRect();\n\t\tif (rect.width <= 0 || rect.height <= 0) return;\n\t\tsetParallaxX(((event.clientX - rect.left) / rect.width - 0.5) * 2);\n\t\tsetParallaxY(((event.clientY - rect.top) / rect.height - 0.5) * 2);\n\t};\n\n\tconst clearPointer = (thumb: HTMLElement) => {\n\t\tconst wasDown = pointerDownRef.current;\n\t\tpointerDownRef.current = false;\n\t\tsetHovering(false);\n\t\tif (scrollParallax && wasDown) {\n\t\t\tsetParallaxX(\n\t\t\t\thorizontalParallaxFromViewportY(thumb.getBoundingClientRect(), window.innerHeight)\n\t\t\t);\n\t\t\tsetParallaxY(0);\n\t\t} else if (!scrollParallax) {\n\t\t\tsetParallaxX(0);\n\t\t\tsetParallaxY(0);\n\t\t}\n\t};\n\n\tconst onPointerDown = (event: ReactPointerEvent<HTMLElement>) => {\n\t\tif (!parallaxEnabled) return;\n\t\tpointerDownRef.current = true;\n\t\tif (event.pointerType === 'mouse') setHovering(true);\n\t\tconst thumb = event.currentTarget;\n\t\tconst onGlobalEnd = () => {\n\t\t\tclearPointer(thumb);\n\t\t\twindow.removeEventListener('pointerup', onGlobalEnd);\n\t\t\twindow.removeEventListener('pointercancel', onGlobalEnd);\n\t\t};\n\t\twindow.addEventListener('pointerup', onGlobalEnd);\n\t\twindow.addEventListener('pointercancel', onGlobalEnd);\n\t};\n\n\tconst handleClick = (event: ReactMouseEvent<HTMLElement>) => {\n\t\tonClick?.(event);\n\t\tif (event.defaultPrevented) return;\n\t\tif (isHostedLaunch && !returnUrl?.trim()) {\n\t\t\tconst next = resolveThumbnailHref({\n\t\t\t\texperienceId,\n\t\t\t\tplayerBaseUrl,\n\t\t\t\tuseCurrentPageAsReturnUrl: true\n\t\t\t});\n\t\t\tif (\n\t\t\t\tnext &&\n\t\t\t\tevent.button === 0 &&\n\t\t\t\t!event.metaKey &&\n\t\t\t\t!event.ctrlKey &&\n\t\t\t\t!event.shiftKey\n\t\t\t) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\twindow.location.assign(next);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (!layers) return null;\n\n\tconst parallaxLayersActive = parallaxEnabled && (hovering || scrollParallax);\n\t/** Fade placeholder as soon as any layer can paint — don't wait for the slowest plate. */\n\tconst revealThumb =\n\t\tshouldLoad && plateFormatReady && (avatarReady || backgroundReady || foregroundReady);\n\n\tconst rootClass = [\n\t\t'liforma-et',\n\t\thasScene ? 'liforma-et--scene' : '',\n\t\tthumbnailFitClass(fit),\n\t\tparallaxLayersActive ? 'liforma-et--parallax' : '',\n\t\tscrollParallax ? 'liforma-et--scroll-parallax' : '',\n\t\tshouldLoad && !revealThumb ? 'liforma-et--loading' : '',\n\t\trevealThumb ? 'liforma-et--revealed' : '',\n\t\tclassName\n\t]\n\t\t.filter(Boolean)\n\t\t.join(' ');\n\n\tconst style = {\n\t\t['--liforma-et-parallax-x' as string]: parallaxX,\n\t\t['--liforma-et-parallax-y' as string]: parallaxY\n\t} as CSSProperties;\n\n\tconst markReady = (img: HTMLImageElement, setReady: (v: boolean) => void) => {\n\t\tif (img.complete) setReady(true);\n\t};\n\n\tconst media = (\n\t\t<>\n\t\t\t<span className=\"liforma-et__placeholder\" aria-hidden=\"true\" />\n\t\t\t{shouldLoad && plateFormatReady && layers.backgroundImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__background', backgroundReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(layers.backgroundImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setBackgroundReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setBackgroundReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst fallback = thumbnailLocationPlateFallbackUrl(img.src);\n\t\t\t\t\t\tif (fallback && !backgroundTriedFallback) {\n\t\t\t\t\t\t\tsetBackgroundTriedFallback(true);\n\t\t\t\t\t\t\timg.src = fallback;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetBackgroundReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{shouldLoad && plateFormatReady && resolvedAvatarImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__avatar', avatarReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(resolvedAvatarImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setAvatarReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setAvatarReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst webp = thumbnailColourPlateWebpFallbackUrl(img.src);\n\t\t\t\t\t\tif (webp && !avatarTriedWebpFallback) {\n\t\t\t\t\t\t\tsetAvatarTriedWebpFallback(true);\n\t\t\t\t\t\t\timg.src = webp;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (layers.fallbackImage) setAvatarLoadFailed(true);\n\t\t\t\t\t\telse setAvatarReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t\t{shouldLoad && plateFormatReady && layers.foregroundImage ? (\n\t\t\t\t<img\n\t\t\t\t\tclassName={mediaClass('liforma-et__foreground', foregroundReady)}\n\t\t\t\t\tsrc={thumbnailPlateSrc(layers.foregroundImage, useAvif)}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tdecoding=\"async\"\n\t\t\t\t\tref={(node) => {\n\t\t\t\t\t\tif (node) markReady(node, setForegroundReady);\n\t\t\t\t\t}}\n\t\t\t\t\tonLoad={() => setForegroundReady(true)}\n\t\t\t\t\tonError={(event) => {\n\t\t\t\t\t\tconst img = event.currentTarget;\n\t\t\t\t\t\tconst fallback = thumbnailLocationPlateFallbackUrl(img.src);\n\t\t\t\t\t\tif (fallback && !foregroundTriedFallback) {\n\t\t\t\t\t\t\tsetForegroundTriedFallback(true);\n\t\t\t\t\t\t\timg.src = fallback;\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetForegroundReady(true);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t) : null}\n\t\t</>\n\t);\n\n\tconst pointerProps = {\n\t\tonPointerDown,\n\t\tonPointerUp: (event: ReactPointerEvent<HTMLElement>) => clearPointer(event.currentTarget),\n\t\tonPointerCancel: (event: ReactPointerEvent<HTMLElement>) =>\n\t\t\tclearPointer(event.currentTarget),\n\t\tonPointerEnter: (event: ReactPointerEvent<HTMLElement>) => {\n\t\t\tif (parallaxEnabled && event.pointerType === 'mouse') setHovering(true);\n\t\t},\n\t\tonPointerMove,\n\t\tonPointerLeave: () => {\n\t\t\tif (!pointerDownRef.current) {\n\t\t\t\tsetHovering(false);\n\t\t\t\tif (!scrollParallax) {\n\t\t\t\t\tsetParallaxX(0);\n\t\t\t\t\tsetParallaxY(0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tconst setRootRef = (node: HTMLElement | null) => {\n\t\trootRef.current = node;\n\t};\n\n\tif (interaction === 'anchor' && resolvedHref) {\n\t\treturn (\n\t\t\t<a\n\t\t\t\tref={setRootRef}\n\t\t\t\tclassName={rootClass}\n\t\t\t\tstyle={style}\n\t\t\t\thref={resolvedHref}\n\t\t\t\ttarget={target}\n\t\t\t\trel={rel}\n\t\t\t\taria-label={alt}\n\t\t\t\tonClick={handleClick}\n\t\t\t\t{...pointerProps}\n\t\t\t>\n\t\t\t\t{media}\n\t\t\t</a>\n\t\t);\n\t}\n\n\tif (interaction === 'button') {\n\t\treturn (\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tref={setRootRef}\n\t\t\t\tclassName={rootClass}\n\t\t\t\tstyle={style}\n\t\t\t\taria-label={alt}\n\t\t\t\tonClick={handleClick}\n\t\t\t\t{...pointerProps}\n\t\t\t>\n\t\t\t\t{media}\n\t\t\t</button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tref={setRootRef}\n\t\t\tclassName={rootClass}\n\t\t\tstyle={style}\n\t\t\trole={alt ? 'img' : undefined}\n\t\t\taria-label={alt || undefined}\n\t\t\taria-hidden={alt ? undefined : true}\n\t\t\t{...pointerProps}\n\t\t>\n\t\t\t{media}\n\t\t</div>\n\t);\n}\n"],"mappings":"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"}
|
package/dist/sdk/v2/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Liforma=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(){if(globalThis.__LIFORMA_STACK===`local`)return!0;if(typeof location>`u`)return!1;try{return new URLSearchParams(location.search).get(`stack`)===`local`}catch{return!1}}var n=`https://api.liforma.ai`,r=`http://localhost:3001`;function i(){let e=globalThis.__LIFORMA_API_BASE_URL;if(e?.trim())return e.trim().replace(/\/$/,``);if(typeof document>`u`)return null;let t=(document.querySelector(`script[data-liforma-sdk][src*="client.js"]`)??document.querySelector(`script[src*="/sdk/v2/client.js"]`))?.getAttribute(`data-api-base-url`)?.trim();return t?t.replace(/\/$/,``):null}function a(){return i()||(t()?r:n)}var o=class extends Error{code;requestId;status;details;constructor(e,t){super(e,t?.cause===void 0?void 0:{cause:t.cause}),this.name=`LiformaError`,this.code=t?.code?.trim()||`UNKNOWN`,this.requestId=t?.requestId?.trim()||void 0,this.status=t?.status,this.details=t?.details}};function s(e){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e}function c(e,t,n){let r=n?.get(`Liforma-Request-Id`)?.trim()||void 0,i;try{i=JSON.parse(t)}catch{}let a=i?.error&&typeof i.error==`object`?i.error:void 0,c=a?.code??i?.code,l=a?.message??i?.message,u=a?.requestId??i?.requestId??r,d=typeof c==`string`&&c.trim()?c.trim():`HTTP_ERROR`,f=typeof l==`string`&&l.trim()?l.trim():t.trim().slice(0,500)||`Request failed (${e})`,p=typeof u==`string`&&u.trim()?u.trim():r,{code:m,message:h,requestId:g,..._}=s(a)??s(i)??{};return new o(f,{code:d,requestId:p,status:e,details:Object.keys(_).length>0?_:void 0})}var l=128,u=/[\u0000-\u001f\u007f]/g;function d(e){if(!e)return;let t=e.replace(u,``).trim();if(t)return t.slice(0,l)}function f(e){let t=new URLSearchParams((e??``).startsWith(`?`)?(e??``).slice(1):e??``);return{...d(t.get(`utm_source`))?{utmSource:d(t.get(`utm_source`))}:{},...d(t.get(`utm_medium`))?{utmMedium:d(t.get(`utm_medium`))}:{},...d(t.get(`utm_campaign`))?{utmCampaign:d(t.get(`utm_campaign`))}:{},...d(t.get(`utm_content`))?{utmContent:d(t.get(`utm_content`))}:{},...d(t.get(`utm_term`))?{utmTerm:d(t.get(`utm_term`))}:{}}}function p(e){if(e===!1)return;if(e&&typeof e==`object`){let t={...d(e.utmSource)?{utmSource:d(e.utmSource)}:{},...d(e.utmMedium)?{utmMedium:d(e.utmMedium)}:{},...d(e.utmCampaign)?{utmCampaign:d(e.utmCampaign)}:{},...d(e.utmContent)?{utmContent:d(e.utmContent)}:{},...d(e.utmTerm)?{utmTerm:d(e.utmTerm)}:{},...d(e.referrer)?{referrer:d(e.referrer)}:{}};return Object.keys(t).length?t:void 0}if(typeof window>`u`)return;let t=f(window.location?.search),n=d(document.referrer||void 0),r={...t,...n?{referrer:n}:{}};return Object.keys(r).length?r:void 0}function m(e){let t=e.locale?.trim();if(t)return t;let n=e.language?.trim();if(n)return n;if(typeof navigator<`u`&&navigator.language?.trim())return navigator.language.trim()}function h(e){let t=e.secondaryLocale?.trim(),n=e.learningLocale?.trim();if(t&&n&&t!==n)throw Error(`secondaryLocale and learningLocale disagree; pass only one or make them equal.`);return t||n||void 0}function g(e,t){let n=e.trim();if(!n)throw Error(`sessionEndpoint must not be empty.`);if(typeof location>`u`)throw Error(`sessionEndpoint is only available in a browser.`);let r=new URL(n,location.href);if(r.origin!==location.origin)throw Error(`sessionEndpoint must be same-origin.`);let i=t.experienceId.trim();if(!i)throw Error(`sessionEndpoint requires experienceId.`);let a=m(t),o=h(t),s=t.idempotencyKey?.trim();return{url:r.toString(),init:{method:`POST`,credentials:`same-origin`,headers:{Accept:`application/json`,"Content-Type":`application/json`,...s?{"Idempotency-Key":s}:{}},body:JSON.stringify({experienceId:i,...a?{locale:a}:{},...o?{secondaryLocale:o}:{},...t.mode?{mode:t.mode}:{},...t.speechInputMode?{speechInputMode:t.speechInputMode}:{},...t.speechOnly===void 0?{}:{speechOnly:t.speechOnly},...(()=>{let e=p(t.attribution);return e?{attribution:e}:{}})(),...t.alreadyConverted?.length?{alreadyConverted:[...t.alreadyConverted]}:{}})}}}function _(e,t){return t===void 0?{}:{[e]:t}}function v(e){return!!e&&typeof e==`object`&&!Array.isArray(e)}function y(e){return typeof e==`boolean`}function b(e){if(!v(e))return null;let t=e.speech,n=e.conversation,r=e.avatar,i=e.tools;return!v(t)||!v(n)||!v(r)||!v(i)||!y(t.input)||!y(t.output)||!y(t.externalAudio)||!y(n.managed)||!y(r.enabled)||!y(i.enabled)?null:{speech:{input:t.input,output:t.output,externalAudio:t.externalAudio},conversation:{managed:n.managed},avatar:{enabled:r.enabled},tools:{enabled:i.enabled}}}function x(e){return!v(e)||!y(e.avatar)||!y(e.speechInput)||!y(e.speechOutput)||!y(e.tools)||!y(e.externalAudio)?null:{speech:{input:e.speechInput,output:e.speechOutput,externalAudio:e.externalAudio},conversation:{managed:!0},avatar:{enabled:e.avatar},tools:{enabled:e.tools}}}function S(e){return b(e)??x(e)}function C(e){if(!v(e))throw Error(`Session Launch response is missing session.`);let t=typeof e.id==`string`?e.id.trim():``,n=typeof e.experienceId==`string`?e.experienceId.trim():``,r=typeof e.experienceRevisionId==`string`?e.experienceRevisionId.trim():``,i=typeof e.expiresAt==`string`?e.expiresAt.trim():``,a=typeof e.locale==`string`?e.locale.trim():``,o=typeof e.secondaryLocale==`string`&&e.secondaryLocale.trim()?e.secondaryLocale.trim():void 0,s=e.mode===`conversation`||e.mode===`presenter`?e.mode:null,c=S(e.capabilities),l=ee(e.localeVoiceFallbacks),u=e.status===`active`||e.status===`ended`?e.status:void 0,d=typeof e.createdAt==`string`&&e.createdAt.trim()?e.createdAt.trim():void 0;if(!t||!n||!r||!i||!a||!s||!c)throw Error(`Session Launch response has an invalid session object.`);return{id:t,experienceId:n,experienceRevisionId:r,expiresAt:i,locale:a,...o?{secondaryLocale:o}:{},mode:s,capabilities:c,...l?{localeVoiceFallbacks:l}:{},...u?{status:u}:{},...d?{createdAt:d}:{}}}function ee(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e){if(!v(n))continue;let e=n.axis===`locale`||n.axis===`secondaryLocale`?n.axis:null,r=typeof n.requested==`string`?n.requested.trim():``,i=typeof n.resolved==`string`?n.resolved.trim():``;!e||!r||!i||n.reason!==`voice_unsupported`||t.push({axis:e,requested:r,resolved:i,reason:`voice_unsupported`})}return t.length>0?t:void 0}function w(e){let t=e.localeVoiceFallbacks;if(t?.length)for(let e of t){let t=e.axis===`secondaryLocale`?`secondaryLocale`:`locale`;console.warn(`[liforma] Session ${t} fell back to ${e.resolved} because the voice does not support ${e.requested}.`)}}function T(e){if(!v(e))throw Error(`Session Launch response must be a JSON object.`);let t=typeof e.launch==`string`?e.launch.trim():``;if(!t)throw Error(`Session Launch response is missing launch.`);let n=C(e.session);return w(n),{session:n,launch:t}}function te(){try{if(typeof globalThis.location?.origin==`string`&&globalThis.location.origin)return globalThis.location.origin}catch{}}function ne(e){return e?e.trim().toUpperCase().replace(/-/g,`_`):``}function re(e,t){let n=te(),r=ne(t.code),i=t.message.trim(),a=t.details??{};if(r===`ORIGIN_NOT_ALLOWED`||/^Origin not allowed/i.test(i)&&n){if(i.includes(e)&&i.includes(`allowed origin`))return t;let r=typeof a.origin==`string`&&a.origin||n||`(unknown origin)`;return new o(`Your origin ${r} is not an allowed origin for experience ${e}. If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), open Workspace → Settings → Origins, add ${r} to the list of allowed origins, and ensure “Allow public client-only embeds” is enabled.`,{code:`ORIGIN_NOT_ALLOWED`,requestId:t.requestId,status:t.status,details:a})}return r===`BROWSER_EMBEDS_DISABLED`||/^Browser embeds are not enabled/i.test(i)?i.includes(`Allow public client-only embeds`)?t:new o(`Browser embeds are not enabled for experience ${e}. If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), open Workspace → Settings → Origins, and enable “Allow public client-only embeds”.`,{code:`BROWSER_EMBEDS_DISABLED`,requestId:t.requestId,status:t.status,details:a}):r===`EXPERIENCE_UNAVAILABLE`||/^Experience is not available\.?$/i.test(i)||/^Unknown experience\.?$/i.test(i)?i.includes(`No playable experience found`)?t:new o(`No playable experience found for “${e}”. Check that the experience id is correct (it should look like exp_…), and that the experience exists and is published in the Liforma developer portal (app.liforma.ai). Deleted, archived, and unpublished experiences cannot be started from a browser embed.`,{code:`EXPERIENCE_UNAVAILABLE`,requestId:t.requestId,status:t.status,details:a}):t}function ie(e){let t=e?.locale?.trim();if(t)return t;let n=e?.language?.trim();if(n)return n;if(typeof navigator<`u`&&navigator.language?.trim())return navigator.language.trim()}function ae(e){let t=e?.secondaryLocale?.trim(),n=e?.learningLocale?.trim();if(t&&n&&t!==n)throw new o(`secondaryLocale and learningLocale disagree; pass only one or make them equal.`,{code:`INVALID_BODY`});return t||n||void 0}function oe(e,t){let n={experienceId:e},r=ie(t);r&&(n.locale=r);let i=ae(t);i&&(n.secondaryLocale=i),t?.mode&&(n.mode=t.mode),t?.speechInputMode&&(n.speechInputMode=t.speechInputMode),t?.speechOnly!==void 0&&(n.speechOnly=t.speechOnly);let a=p(t?.attribution);return a&&(n.attribution=a),t?.alreadyConverted?.length&&(n.alreadyConverted=[...t.alreadyConverted]),n}async function se(e,t){let n=a(),r=t?.idempotencyKey?.trim(),i=await fetch(`${n}/v1/browser-sessions`,{method:`POST`,headers:{"Content-Type":`application/json`,...r?{"Idempotency-Key":r}:{}},body:JSON.stringify(oe(e,t))});if(!i.ok){let t=await i.text();throw re(e,c(i.status,t,i.headers))}let s;try{s=await i.json()}catch{throw new o(`Browser session mint returned invalid JSON.`,{code:`INVALID_RESPONSE`,status:i.status,requestId:i.headers.get(`Liforma-Request-Id`)??void 0})}return T(s)}async function ce(e,t){let n=g(e,t),r=new AbortController,i=()=>r.abort(t.signal?.reason);t.signal?.aborted?i():t.signal?.addEventListener(`abort`,i,{once:!0});let a=globalThis.setTimeout(()=>r.abort(new DOMException(`Session endpoint timed out.`,`TimeoutError`)),t.timeoutMs??3e4),s;try{s=await fetch(n.url,{...n.init,signal:r.signal})}finally{globalThis.clearTimeout(a),t.signal?.removeEventListener(`abort`,i)}if(!s.ok){let e=await s.text();throw c(s.status,e,s.headers)}if(!s.headers.get(`content-type`)?.toLowerCase().includes(`application/json`))throw new o(`Session endpoint must return a JSON Session Launch response.`,{code:`INVALID_RESPONSE`,status:s.status,requestId:s.headers.get(`Liforma-Request-Id`)??void 0});let l;try{l=await s.json()}catch{throw new o(`Session endpoint returned invalid JSON.`,{code:`INVALID_RESPONSE`,status:s.status,requestId:s.headers.get(`Liforma-Request-Id`)??void 0})}return T(l)}function E(e){return typeof e!=`object`||!e?!1:typeof e[Symbol.asyncIterator]==`function`}function le(e){if(typeof e==`string`){let t=e.trim();if(!t)throw Error(`conversationProcessor returned empty text.`);return{text:t}}let t=e.text.trim();if(!t)throw Error(`conversationProcessor returned empty text.`);return{text:t,...e.characterId?{characterId:e.characterId}:{},...e.behavior?{behavior:e.behavior}:{}}}async function ue(e,t){let n=await e(t);return E(n)?{kind:`stream`,stream:n}:{kind:`complete`,speech:le(n)}}var de=`3002`;function D(e){let t=new URL(e);return t.hostname===`localhost`||t.hostname===`127.0.0.1`?`Cannot connect to the Liforma player at ${e} — nothing is listening on port ${t.port||de}. Start the player dev server in player.liforma.ai (npm run dev).`:`Cannot connect to the Liforma player at ${e}. The embed did not load; check your network connection and player URL configuration.`}async function fe(e,t){try{await fetch(e,{method:`GET`,mode:`no-cors`,signal:AbortSignal.timeout(8e3)})}catch{throw Error(D(t))}}function O(e,t,n,r){try{r&&r.length>0?e.postMessage(t,n,r):e.postMessage(t,n)}catch(e){throw e instanceof DOMException&&e.name===`SecurityError`?Error(D(n),{cause:e}):e}}var pe=new Set([1,2]);function me(e){return typeof e==`number`&&pe.has(e)}var he=new Set(`close.stateUpdate.commandResult.userTranscript.characterSpeechStarted.characterSpeechEnded.characterFocusChanged.conversationUpdate.listeningState.userSpeechStarted.userSpeechEnded.modeChange.activityChange.feedback.feedbackFailed.message.started.embedReady.widgetReady.widgetExpand.widgetCollapse.widgetAudioSettings.widgetDragStart.widgetDragMove.widgetDragEnd.fullWindow.theatre.interactionChange`.split(`.`));function ge(e){if(!e||typeof e!=`object`)return null;let t=e;if(!me(t.schemaVersion)||!t.type||!he.has(t.type))return null;if(t.type===`embedReady`||t.type===`widgetReady`||t.type===`widgetExpand`||t.type===`widgetCollapse`)return t;if(t.type===`fullWindow`||t.type===`theatre`){let e=t.payload;return!e||typeof e!=`object`||typeof e.active!=`boolean`?null:t}if(t.type===`widgetAudioSettings`){let e=t.payload;return!e||typeof e!=`object`||typeof e.open!=`boolean`?null:t}if(t.type===`widgetDragStart`||t.type===`widgetDragEnd`){let e=t.payload;return!e||typeof e!=`object`||typeof e.pointerId!=`number`||!Number.isFinite(e.pointerId)?null:t}if(t.type===`widgetDragMove`){let e=t.payload;if(!e||typeof e!=`object`)return null;let{pointerId:n,dx:r,dy:i}=e;return typeof n!=`number`||!Number.isFinite(n)||typeof r!=`number`||!Number.isFinite(r)||typeof i!=`number`||!Number.isFinite(i)?null:t}let n=t.payload;return!n||typeof n!=`object`?null:t}function k(e,t){return t===void 0?{}:{[e]:t}}function _e(e,t,n){let r=e.trim();if(!r)throw Error(`createLaunchMessage requires a non-empty launch string.`);let i=n?.startButton,a=n?.theme,o=n?.showFeedback,s=n?.interaction,c=n?.theatre;return{type:`launch`,schemaVersion:2,payload:{launch:r,...t===void 0?{}:{session:t},...i?{startButton:i}:{},...a?{theme:a}:{},...o===void 0?{}:{showFeedback:o},...s?{interaction:s}:{},...k(`settingsButton`,n?.settingsButton),...k(`theatreButton`,n?.theatreButton),...k(`fullscreenButton`,n?.fullscreenButton),...c===void 0?{}:{theatre:c}}}}function ve(){return{type:`parentHello`,schemaVersion:2}}function ye(e){return{type:`widgetSetChrome`,schemaVersion:2,payload:{mode:e}}}function be(){return{type:`widgetToggleAudioSettings`,schemaVersion:2,payload:{}}}function xe(e){return{type:`setTheatre`,schemaVersion:2,payload:{active:e}}}var Se=`2147483000`;function A(e){let{style:t}=e;return{position:t.position,top:t.top,right:t.right,bottom:t.bottom,left:t.left,inset:t.inset,width:t.width,height:t.height,zIndex:t.zIndex,margin:t.margin,borderRadius:t.borderRadius,maxWidth:t.maxWidth,maxHeight:t.maxHeight}}function j(e){let{style:t}=e;t.position=`fixed`,t.top=`0`,t.right=`0`,t.bottom=`0`,t.left=`0`,t.inset=`0`,t.width=`100vw`,t.width=`100dvw`,t.height=`100vh`,t.height=`100dvh`,t.zIndex=Se,t.margin=`0`,t.borderRadius=`0`,t.maxWidth=`none`,t.maxHeight=`none`,e.dataset.liformaFullWindow=`1`}function Ce(e,t){if(delete e.dataset.liformaFullWindow,!t){e.style.position=``,e.style.top=``,e.style.right=``,e.style.bottom=``,e.style.left=``,e.style.inset=``,e.style.width=``,e.style.height=``,e.style.zIndex=``,e.style.margin=``,e.style.borderRadius=``,e.style.maxWidth=``,e.style.maxHeight=``;return}let{style:n}=e;n.position=t.position,n.top=t.top,n.right=t.right,n.bottom=t.bottom,n.left=t.left,n.inset=t.inset,n.width=t.width,n.height=t.height,n.zIndex=t.zIndex,n.margin=t.margin,n.borderRadius=t.borderRadius,n.maxWidth=t.maxWidth,n.maxHeight=t.maxHeight}function we(e){return e}function M(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?`cmd_${crypto.randomUUID().replace(/-/g,``).slice(0,12)}`:`cmd_${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`}var Te=3e4;function Ee(e){let t=e.container;getComputedStyle(t).position===`static`&&(t.style.position=`relative`);let n=document.createElement(`iframe`);n.src=e.src,n.title=`Liforma experience`,n.setAttribute(`allow`,`microphone; autoplay; clipboard-read; clipboard-write; fullscreen`),n.setAttribute(`allowfullscreen`,``),n.style.cssText=e.transparent?`display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:transparent;`:`display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:#030508;`,e.transparent&&n.setAttribute(`allowtransparency`,`true`),n.setAttribute(`fetchpriority`,`high`),`fetchPriority`in n&&(n.fetchPriority=`high`);let r=!1,i=!1,a=null,o=null,s=r=>{i!==r&&(i=r,r?(a=A(n),o=document.body.style.overflow,document.body.append(n),j(n),document.body.style.overflow=`hidden`):(t.append(n),Ce(n,a),a=null,o!==null&&(document.body.style.overflow=o,o=null)),e.onFullWindowChange?.(r))},c=new Map,l=null,u=0,d=0,f=[],p=null,m=null,h=new Promise((e,t)=>{p=e,m=t}),g=()=>{for(let e of f)window.clearTimeout(e);f.length=0},_=t=>{if(r)return;let i=Date.now();if(d>0&&i-d<250)return;d=i,g();let a=_e(t.launch,t.session,t.chrome),o=()=>{if(r)return;let t=n.contentWindow;t&&O(t,a,e.playerOrigin)};o();for(let e=1;e<=12;e+=1)f.push(window.setTimeout(o,e*75))},v=()=>{r||!l||_(l)},y=()=>{if(u+=1,u===1){fe(e.src,e.playerOrigin).then(()=>p?.(),e=>m?.(e instanceof Error?e:Error(String(e))));return}d=0,v()},b=()=>{u>0||m?.(Error(D(e.playerOrigin)))};n.addEventListener(`load`,y),n.addEventListener(`error`,b);let x=t=>{if(r||t.source!==n.contentWindow||t.origin!==e.playerOrigin)return;let i=ge(t.data);if(i)switch(i.type){case`close`:e.onClose?.(i.payload);break;case`stateUpdate`:e.onPlayerStatusChange?.(i.payload.state),e.onStateUpdate?.(i.payload.state);break;case`commandResult`:{let e=c.get(i.payload.correlationId);if(!e)return;c.delete(i.payload.correlationId),i.payload.ok?e.resolve(i.payload.result):e.reject(Error(i.payload.error??`Command failed.`));break}case`userTranscript`:e.onUserTranscript?.(i.payload);break;case`characterSpeechStarted`:e.onCharacterSpeechStarted?.(i.payload);break;case`characterSpeechEnded`:e.onCharacterSpeechEnded?.(i.payload);break;case`characterFocusChanged`:e.onCharacterFocusChanged?.(i.payload);break;case`conversationUpdate`:e.onConversationUpdate?.(i.payload.conversation);break;case`listeningState`:e.onListeningState?.(i.payload.listening);break;case`userSpeechStarted`:e.onUserSpeechStarted?.(i.payload);break;case`userSpeechEnded`:e.onUserSpeechEnded?.(i.payload);break;case`modeChange`:e.onActivityChange?.(we(i.payload.mode));break;case`activityChange`:e.onActivityChange?.(i.payload.activity);break;case`feedback`:e.onFeedback?.(i.payload);break;case`feedbackFailed`:e.onFeedbackFailed?.(i.payload);break;case`message`:e.onMessage?.(i.payload);break;case`started`:e.onStarted?.(i.payload);break;case`embedReady`:v();break;case`widgetReady`:e.onWidgetReady?.();break;case`widgetExpand`:e.onWidgetExpand?.();break;case`widgetCollapse`:e.onWidgetCollapse?.();break;case`widgetAudioSettings`:e.onWidgetAudioSettings?.(i.payload.open);break;case`widgetDragStart`:e.onWidgetDragStart?.(i.payload);break;case`widgetDragMove`:e.onWidgetDragMove?.(i.payload);break;case`widgetDragEnd`:e.onWidgetDragEnd?.(i.payload);break;case`fullWindow`:s(i.payload.active);break;case`theatre`:e.onTheatreChange?.(i.payload.active);break;case`interactionChange`:e.onInteractionChange?.(i.payload.interaction);break}};if(window.addEventListener(`message`,x),t.replaceChildren(n),t.clientHeight<2&&!t.style.minHeight){let e=getComputedStyle(t).minHeight;(e===`0px`||e===`auto`)&&(t.style.minHeight=`480px`)}let S=(t,i)=>{if(r)return;let a=n.contentWindow;if(!a)throw Error(D(e.playerOrigin));O(a,t,e.playerOrigin,i)},C=(e,t=12e4,n)=>{let r=e.type!==`parentHello`&&e.payload&&`correlationId`in e.payload?e.payload.correlationId:M();return e.type!==`parentHello`&&e.payload&&`correlationId`in e.payload||e.type!==`parentHello`&&e.payload&&(e.payload.correlationId=r),new Promise((i,a)=>{c.set(r,{resolve:e=>i(e),reject:a}),S(e,n),setTimeout(()=>{c.has(r)&&(c.delete(r),a(Error(`Timed out waiting for player command result.`)))},t)})};return{waitForLoad(){return h},postParentHello(){S(ve())},postLaunch(e,t,n){r||(l={launch:e,session:t,chrome:n},d=0,_(l))},postManifest(e){if(typeof e==`string`){this.postLaunch(e);return}if(e&&typeof e==`object`&&typeof e.launch==`string`){let t=String(e.launch),n=e.session;this.postLaunch(t,n);return}throw Error(`postManifest is deprecated; use postLaunch with an opaque launch string from SessionLaunchResponse.`)},postWidgetSetChrome(e){S(ye(e))},postWidgetToggleAudioSettings(){S(be())},postSetTheatre(e){S(xe(e))},speak(e){return C({type:`speak`,schemaVersion:2,payload:{correlationId:M(),text:e.text,...e.characterId?{characterId:e.characterId}:{},...e.behavior?{behavior:e.behavior}:{}}})},startListening(){return C({type:`startListening`,schemaVersion:2,payload:{correlationId:M()}})},stopListening(){return C({type:`stopListening`,schemaVersion:2,payload:{correlationId:M()}})},listenOnce(e){return C({type:`listenOnce`,schemaVersion:2,payload:{correlationId:M(),...e?.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}}})},async speakProcessorStream(e,t){let n=M(),r=M(),i=(await C({type:`speakStreamStart`,schemaVersion:2,payload:{correlationId:n,...t?.characterId?{characterId:t.characterId}:{},...t?.behavior?{behavior:t.behavior}:{}}})).streamId;try{for await(let n of e){if(t?.signal?.aborted)throw new DOMException(`Processor stream aborted.`,`AbortError`);n&&S({type:`speakStreamChunk`,schemaVersion:2,payload:{streamId:i,text:n}})}if(t?.signal?.aborted)throw new DOMException(`Processor stream aborted.`,`AbortError`);return await C({type:`speakStreamEnd`,schemaVersion:2,payload:{correlationId:r,streamId:i}})}catch(e){try{await C({type:`speakStreamEnd`,schemaVersion:2,payload:{correlationId:r,streamId:i}})}catch{}throw e}},speechPlay(e){let t=M(),n=[];return(e.source.kind===`pcm`||e.source.kind===`encoded`)&&n.push(e.source.data),C({type:`speechPlay`,schemaVersion:2,payload:{correlationId:t,source:e.source,...e.characterId?{characterId:e.characterId}:{},...e.transcript===void 0?{}:{transcript:e.transcript},...e.history?{history:e.history}:{},...e.queue?{queue:e.queue}:{}}},12e4,n)},speechUtteranceBegin(e){return C({type:`speechUtteranceBegin`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,format:e.format,queue:e.queue,...e.characterId?{characterId:e.characterId}:{},...e.transcript===void 0?{}:{transcript:e.transcript}}})},speechUtteranceWrite(e){return C({type:`speechUtteranceWrite`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,audio:e.audio,byteLength:e.byteLength}},Te,[e.audio])},speechUtteranceSetTranscript(e){return C({type:`speechUtteranceSetTranscript`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,transcript:e.transcript}})},speechUtteranceClose(e){return C({type:`speechUtteranceClose`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,...e.transcript===void 0?{}:{transcript:e.transcript},...e.history?{history:e.history}:{}}})},speechUtteranceCancel(e){return C({type:`speechUtteranceCancel`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId}})},speechInterrupt(e){return C({type:`speechInterrupt`,schemaVersion:2,payload:{correlationId:M(),...e}})},getConversation(){return C({type:`getConversation`,schemaVersion:2,payload:{correlationId:M()}})},focusCharacter(e){return C({type:`focusCharacter`,schemaVersion:2,payload:{correlationId:M(),characterId:e}})},setFit(e){return C({type:`setFit`,schemaVersion:2,payload:{correlationId:M(),fit:e}})},setStage(e){return C({type:`setStage`,schemaVersion:2,payload:{correlationId:M(),stage:e}})},setPipBackground(e){return C({type:`setPipBackground`,schemaVersion:2,payload:{correlationId:M(),pipBackground:e}})},setPipShadow(e){return C({type:`setPipShadow`,schemaVersion:2,payload:{correlationId:M(),pipShadow:e}})},setInteraction(e){return C({type:`setInteraction`,schemaVersion:2,payload:{correlationId:M(),interaction:e}})},getInteraction(){return C({type:`getInteraction`,schemaVersion:2,payload:{correlationId:M()}})},conversion(e){return C({type:`conversion`,schemaVersion:2,payload:{correlationId:M(),key:e}})},engage(){return C({type:`engage`,schemaVersion:2,payload:{correlationId:M()}})},reportSdkError(e){return C({type:`reportSdkError`,schemaVersion:2,payload:{correlationId:M(),errorCode:e.errorCode,message:e.message,stack:e.stack,severity:e.severity,operation:e.operation,release:e.release}})},destroy(){if(!r){r=!0,i&&s(!1);for(let e of c.values())e.reject(Error(`Iframe host destroyed.`));c.clear(),g(),l=null,n.removeEventListener(`load`,y),n.removeEventListener(`error`,b),window.removeEventListener(`message`,x),n.remove()}}}}function De(e){let t,n=!1,r=!1,i=()=>{r||!n||t===void 0||(r=!0,e(t))};return{setValue(e){t=e,i()},markReady(){n=!0,i()}}}var N=`sdk@${{name:`@liforma/client`,version:`0.4.15`,type:`module`,description:`Liforma browser SDK — Experience sessions, speech API, and Svelte embed component.`,license:`MIT`,homepage:`https://docs.liforma.ai`,repository:{type:`git`,url:`git+https://github.com/LiformaLtd/cdn.liforma.ai.git`},bugs:{url:`https://github.com/LiformaLtd/cdn.liforma.ai/issues`},publishConfig:{access:`public`},sideEffects:!1,exports:{".":{types:`./dist/npm/v2/npm.d.ts`,import:`./dist/npm/index.js`},"./svelte":{types:`./dist/svelte/index.d.ts`,import:`./dist/svelte/index.js`,svelte:`./dist/svelte/index.js`},"./svelte/thumbnail":{types:`./dist/svelte/thumbnail.d.ts`,import:`./dist/svelte/thumbnail.js`,svelte:`./dist/svelte/thumbnail.js`},"./thumbnail":{types:`./dist/npm/thumbnail/index.d.ts`,import:`./dist/npm/thumbnail/index.js`},"./react":{types:`./dist/react/react/index.d.ts`,import:`./dist/react/index.js`},"./react/thumbnail":{types:`./dist/react/react/thumbnail.d.ts`,import:`./dist/react/thumbnail.js`},"./next":{types:`./dist/next/next/index.d.ts`,import:`./dist/next/index.js`},"./next/thumbnail":{types:`./dist/next/next/thumbnail.d.ts`,import:`./dist/next/thumbnail.js`},"./byo":{types:`./dist/byo/index.d.ts`,import:`./dist/byo/index.js`},"./elevenlabs":{types:`./dist/elevenlabs/index.d.ts`,import:`./dist/elevenlabs/index.js`},"./openai":{types:`./dist/openai/index.d.ts`,import:`./dist/openai/index.js`},"./deepgram":{types:`./dist/deepgram/index.d.ts`,import:`./dist/deepgram/index.js`},"./google":{types:`./dist/google/index.d.ts`,import:`./dist/google/index.js`},"./livekit":{types:`./dist/livekit/index.d.ts`,import:`./dist/livekit/index.js`}},files:[`dist/npm`,`dist/svelte`,`dist/react`,`dist/next`,`dist/byo`,`dist/elevenlabs`,`dist/openai`,`dist/deepgram`,`dist/google`,`dist/livekit`,`dist/sdk`,`README.md`,`CHANGELOG.md`,`LICENSE`],peerDependencies:{"@elevenlabs/client":`>=1.0.0`,"livekit-client":`>=2.0.0`,react:`^18.0.0 || ^19.0.0`,"react-dom":`^18.0.0 || ^19.0.0`,svelte:`^5.0.0`},peerDependenciesMeta:{svelte:{optional:!0},react:{optional:!0},"react-dom":{optional:!0},"@elevenlabs/client":{optional:!0},"livekit-client":{optional:!0}},scripts:{build:`npm run build:v2 && npm run build:npm && npm run build:svelte && npm run build:react && npm run build:next && npm run build:byo && npm run build:elevenlabs && npm run build:openai && npm run build:deepgram && npm run build:google && npm run build:livekit && npm run copy:preview`,"build:react":`vite build --config vite.config.react.ts && vite build --config vite.config.react-thumbnail.ts && tsc -p tsconfig.react.json`,"build:next":`vite build --config vite.config.next.ts && vite build --config vite.config.next-thumbnail.ts && tsc -p tsconfig.next.json`,"build:byo":`vite build --config vite.config.byo.ts && tsc -p tsconfig.byo.json`,"build:elevenlabs":`vite build --config vite.config.elevenlabs.ts && tsc -p tsconfig.elevenlabs.json`,"build:openai":`vite build --config vite.config.openai.ts && tsc -p tsconfig.openai.json`,"build:deepgram":`vite build --config vite.config.deepgram.ts && tsc -p tsconfig.deepgram.json`,"build:google":`vite build --config vite.config.google.ts && tsc -p tsconfig.google.json`,"build:livekit":`vite build --config vite.config.livekit.ts && tsc -p tsconfig.livekit.json`,"copy:preview":`mkdir -p dist/preview && cp preview/v2-embed-test.html dist/preview/ && cp preview/v2-embed-test-redirect.html dist/preview/`,"build:v2":`vite build --config vite.config.v2.ts && vite build --config vite.config.v2-thumbnail.ts`,"build:npm":`vite build --config vite.config.npm.ts && vite build --config vite.config.thumbnail.ts && tsc -p tsconfig.npm.json`,"build:svelte":`svelte-package -i src/svelte -o dist/svelte && vite build --config vite.config.svelte.ts`,"check:consumer":`svelte-check --workspace test/consumer --tsconfig ./tsconfig.json`,test:`vitest run`,preview:`node scripts/preview-local.mjs`,"dev:sdk":`npm run build:v2 && npm run copy:preview && npm run preview`,dev:`npm run build && npm run preview`,"publish:sdk":`node scripts/publish-sdk.mjs`,"publish:npm":`node scripts/publish-npm.mjs`,"publish:avatar":`node scripts/publish-avatar.mjs`,"publish:avatars-dist":`node scripts/publish-avatars.mjs`,"publish:avatars":`node scripts/republish-avatars.mjs`,"build:avatars":`node scripts/build-avatars.mjs`,"republish:avatars":`node scripts/republish-avatars.mjs`,"catalogue:avatars":`node ../labs.liforma.ai/scripts/generate-avatar-index.mjs`,"verify:avatars-dist":`node scripts/verify-avatars-dist.mjs`,"verify:avatar-pack":`node scripts/verify-avatar-pack.mjs`,"repair:render-plate-metadata":`node scripts/repair-render-plate-metadata.mjs`,"verify:render-plate-crop":`node scripts/verify-render-plate-crop.mjs`,"migrate:avatar-render-prep":`npm run migrate:avatar-render-prep --prefix ../labs.liforma.ai --`,"convert:avatar-teeth":`node scripts/convert-avatar-teeth-webp.mjs`,"warm:avatar-composites":`node scripts/warm-avatar-composites.mjs`,"test:compose-costume":`node scripts/test-compose-costume-layers.mjs`,"test:compose-costume-wasm":`node scripts/test-compose-costume-wasm.mjs`,"test:parse-composite-filename":`node scripts/test-parse-composite-filename.mjs`,"test:avatar-composite-rendered-path":`node scripts/test-avatar-composite-rendered-path.mjs`,"test:avatar-compose-worker-cors":`node scripts/test-avatar-compose-worker-cors.mjs`,"test:experience-thumb-url":`node scripts/test-experience-thumb-url.mjs`,"test:compose-experience-thumb":`node scripts/test-compose-experience-thumb.mjs`,"test:location-depth-paths":`node scripts/test-location-depth-paths.mjs`,"test:location-binary-assets":`node scripts/test-location-binary-assets.mjs`,"test:location-da3-depth":`node scripts/test-location-da3-depth.mjs`,"test:location-interactions":`node scripts/test-location-interactions.mjs`,"test:render-plate-chroma-key":`node scripts/test-render-plate-chroma-key.mjs`,"test:sparse-plate":`node scripts/test-sparse-plate.mjs`,"test:face-meshes-publish-depth":`node scripts/test-face-meshes-publish-depth.mjs`,"test:avatar-avif-sibling":`node scripts/test-avatar-avif-sibling.mjs`,"test:screen-chroma-key":`node scripts/test-screen-chroma-key.mjs`,"pull:sources":`node scripts/sync-authoring-sources.mjs`,"push:sources":`node scripts/sync-authoring-sources.mjs`,"catalogue:locations":`node scripts/catalogue-locations.mjs`,"generate:location-interactions":`node scripts/generate-location-interactions.mjs`,"generate:location-depth":`node scripts/generate-location-depth.mjs`,"generate:location-depth16-da3":`node scripts/generate-location-depth16-da3.mjs`,"generate:location-discontinuities":`node scripts/generate-location-discontinuities.mjs`,"build:locations":`node scripts/build-locations.mjs`,"publish:locations":`node scripts/publish-locations.mjs`,"publish:location-400x225":`node scripts/build-location-400x225.mjs`,"test:location-400x225":`node scripts/test-location-400x225.mjs`,"verify:locations":`node scripts/verify-location-sources.mjs`,"publish:models":`node scripts/publish-models.mjs`,"publish:models:krea-int8":`node scripts/_upload-model-prefix.mjs text-to-image/lf-t2i-turbo-int8`,"publish:models:ltx-int8":`node scripts/_upload-model-prefix.mjs text-to-video/lf-t2v-distill-int8`,"publish:models:video-omni":`node scripts/_upload-model-prefix.mjs text-to-video/lf-video-omni-v1`,"fetch:depth-anything-v2":`node scripts/fetch-depth-anything-v2.mjs`,"fetch:video-depth-anything":`node scripts/fetch-video-depth-anything.mjs`,"fetch:depth-pro":`node scripts/fetch-depth-pro.mjs`,"fetch:face-landmarker":`node scripts/fetch-face-landmarker.mjs`,"fetch:anime-face-detector":`node scripts/fetch-anime-face-detector.mjs`,"fetch:face-parsing":`node scripts/fetch-face-parsing.mjs`,"fetch:vitmatte":`node scripts/fetch-vitmatte.mjs`,"fetch:liveportrait-mlx":`node scripts/fetch-liveportrait-mlx.mjs`,"fetch:wav2vec-phoneme-ctc":`node scripts/fetch-wav2vec-phoneme-ctc.mjs`,"fetch:lf-sta":`node scripts/fetch-wav2vec-phoneme-ctc.mjs`,"fetch:supertonic-3":`node scripts/fetch-supertonic-3.mjs`,"fetch:lf-tts":`node scripts/fetch-supertonic-3.mjs`,"fetch:lf-t2i-turbo":`python3 ../tools/krea-2-salad/scripts/fetch_cdn_snapshot.py`,"fetch:models":`npm run fetch:depth-anything-v2 && npm run fetch:video-depth-anything && npm run fetch:depth-pro && npm run fetch:face-landmarker && npm run fetch:anime-face-detector && npm run fetch:face-parsing && npm run fetch:vitmatte && npm run fetch:lf-sta && npm run fetch:lf-tts`,locations:`npm run catalogue:locations && npm run build:locations`,"configure:cors":`node scripts/configure-r2-cors.mjs`,"verify:cors":`node scripts/verify-cdn-cors.mjs`,"deploy:worker:avatar-compose":`node scripts/deploy-avatar-compose-worker.mjs`,"deploy:worker:experience-thumb":`node scripts/deploy-experience-thumb-worker.mjs`,deploy:`npm run build && npm run publish:sdk`,verify:`npm run build && node scripts/check-sdk-size.mjs && node scripts/verify-thumbnail-light.mjs && node scripts/test-embed-protocol.mjs && node scripts/test-manifest-embed-url.mjs && node scripts/test-player-reachability.mjs && node scripts/test-session-endpoint.mjs && node scripts/verify-package.mjs`,"preview:test":`npm run build && npm run preview`},dependencies:{"@jsquash/avif":`^1.3.0`,"@jsquash/webp":`^1.5.0`,zod:`^4.4.3`},devDependencies:{"@aws-sdk/client-s3":`^3.1000.0`,"@aws-sdk/lib-storage":`^3.1110.0`,"@elevenlabs/client":`^1.17.0`,"@sveltejs/package":`^2.5.8`,"@sveltejs/vite-plugin-svelte":`^7.0.0`,"@testing-library/react":`^16.3.0`,"@testing-library/svelte":`^5.4.2`,"@types/node":`^25.9.3`,"@types/react":`^19.1.12`,"@types/react-dom":`^19.1.9`,"@vitejs/plugin-react":`^5.0.2`,"@webgpu/types":`^0.1.70`,dotenv:`^17.2.3`,jsdom:`^30.0.1`,"livekit-client":`^2.21.0`,react:`^19.1.1`,"react-dom":`^19.1.1`,sharp:`^0.34.2`,svelte:`^5.55.2`,"svelte-check":`^4.7.4`,typescript:`^6.0.3`,vite:`^8.0.16`,vitest:`^4.1.10`,wrangler:`^4.101.0`}}.version}`,Oe=`https://player.liforma.ai/embed`,ke=`http://localhost:3002/embed`;function Ae(){let e=globalThis.__LIFORMA_PLAYER_EMBED_URL;if(e?.trim())return e.trim().replace(/\/$/,``);if(typeof document>`u`)return null;let t=(document.querySelector(`script[data-liforma-sdk][src*="client.js"]`)??document.querySelector(`script[src*="/sdk/v2/client.js"]`))?.getAttribute(`data-player-embed-url`)?.trim();return t?t.replace(/\/$/,``):null}function P(){let e=Ae();return e?e.endsWith(`/embed`)?e:`${e}/embed`:t()?ke:Oe}function je(e,t){return e?.trim()||t?.trim()||void 0}function Me(e,t,n,r,i,a,o,s,c,l,u,d,f){let p=(e??P()).replace(/\/$/,``),m=p.endsWith(`/embed`)?p:`${p}/embed`,h=new URL(m),g=n?.trim();g&&h.searchParams.set(`backdropId`,g);let _=r?.trim();_&&h.searchParams.set(`avatarId`,_);let v=d?.trim();v&&h.searchParams.set(`previewBg`,v);let y=f?.trim();return y&&h.searchParams.set(`previewFg`,y),(i===`face`||i===`medium`)&&h.searchParams.set(`fit`,i),c===`pip`&&h.searchParams.set(`stage`,`pip`),l===`set`&&h.searchParams.set(`pipBackground`,`set`),u===!1&&h.searchParams.set(`pipShadow`,`0`),a===`widget`&&h.searchParams.set(`ui`,`widget`),o&&h.searchParams.set(`close`,`1`),t&&h.searchParams.set(`debug`,`true`),(s===2||s===3||s===4)&&h.searchParams.set(`cast`,String(s)),Ne(h.toString())}function Ne(e){if(typeof location>`u`)return e;let t=new URL(e);return t.searchParams.set(`parentOrigin`,location.origin),t.toString()}function Pe(e){return new URL(e).origin}function Fe(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?`evt_${crypto.randomUUID().replace(/-/g,``).slice(0,12)}`:`evt_${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`}function Ie(e,t,n){return{id:Fe(),type:e,sessionId:t,timestamp:new Date().toISOString(),data:n}}function Le(){return{ready:new Set,started:new Set,sessionMinted:new Set,userTranscript:new Set,userSpeechStarted:new Set,userSpeechEnded:new Set,characterSpeechStarted:new Set,characterSpeechEnded:new Set,characterFocusChanged:new Set,conversationUpdate:new Set,message:new Set,listeningState:new Set,activityChange:new Set,interactionChange:new Set,feedback:new Set,feedbackFailed:new Set,conversationProcessorError:new Set}}function Re(e,t,n){return e[t].add(n),()=>e[t].delete(n)}function ze(e,t,n){for(let r of e[t])r(n)}function Be(e){for(let t of Object.keys(e))e[t].clear()}function F(e){return`data`in e&&`format`in e&&e.format!=null&&!(`track`in e)}function I(e){return`url`in e&&typeof e.url==`string`}function L(e){if(!(`track`in e))return!1;let t=e.track;return typeof t!=`object`||!t?!1:typeof MediaStreamTrack<`u`&&t instanceof MediaStreamTrack?!0:`kind`in t&&`readyState`in t}function R(e){return!F(e)&&!I(e)&&!L(e)&&`encoding`in e&&typeof e.encoding==`string`}var Ve=[8e3,16e3,22050,24e3,44100,48e3],z={MAX_CHUNK_BYTES:64*1024,RECOMMENDED_CHUNK_MS_MIN:20,RECOMMENDED_CHUNK_MS_MAX:200,MAX_BUFFERED_AUDIO_MS:8e3,PLAYBACK_JITTER_BUFFER_MS:150,PLAYBACK_JITTER_BUFFER_MS_MIN:80,PLAYBACK_JITTER_BUFFER_MS_MAX:250,MAX_UTTERANCE_DURATION_MS:9e4,MAX_PENDING_WRITES:8,DEFAULT_WRITE_TIMEOUT_MS:5e3},B=class extends Error{operation;requiredCapability;name=`UnsupportedSpeechOperationError`;constructor(e,t){super(`Speech operation "${e}" requires capability "${t}".`),this.operation=e,this.requiredCapability=t}},V=class extends Error{name=`SpeechUtteranceClosedError`;constructor(e=`Cannot write to a closed or cancelled speech utterance.`){super(e)}},H=class extends Error{name=`SpeechRuntimeError`;constructor(e,t){super(e,t)}},U=class extends Error{name=`SpeechBufferTimeoutError`;constructor(e=`Timed out waiting for speech buffer capacity.`){super(e)}};function W(e=`none`){return{mode:e,trackedDurationMs:0,fallbackDurationMs:0}}function G(e){return Ve.includes(e)}function K(e){if(e.encoding!==`pcm_s16le`)throw new H(`Unsupported audio encoding: ${e.encoding}`);if(e.channels!==1)throw new H(`Unsupported channel count: ${e.channels}`);if(!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`)}function He(e){if(F(e)){K(e.format);return}if(I(e)){if(!e.url.trim())throw new H(`Audio URL must be non-empty.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}if(L(e)){if(e.track.kind!==`audio`)throw new H(`speech.play track must be an audio MediaStreamTrack.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}if(R(e)){let t=e.encoding.trim().toLowerCase();if(!t)throw new H(`Encoded audio MIME type is required.`);if(t===`pcm_s16le`)throw new H(`Use format: { encoding: "pcm_s16le", … } for raw PCM.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}throw new H(`Unsupported speech.play audio shape.`)}function Ue(e){if(e.format)return K(e.format),e.format;if(e.track){let t={encoding:`pcm_s16le`,sampleRate:e.sampleRate??24e3,channels:1};return K(t),t}throw new H(`createUtterance requires format or track.`)}function q(e){return e instanceof Uint8Array?e:new Uint8Array(e)}function We(e){return e===`replace-active`||e===`replace-all`?`interrupt`:`enqueue`}function Ge(e){let t=new Uint8Array(e.length*2),n=new DataView(t.buffer);for(let t=0;t<e.length;t++){let r=Math.max(-1,Math.min(1,e[t]));n.setInt16(t*2,r<0?r*32768:r*32767,!0)}return t}function Ke(e,t,n,r,i){let a=-.5*e+1.5*t-1.5*n+.5*r,o=e-2.5*t+2*n-.5*r,s=-.5*e+.5*n,c=t;return((a*i+o)*i+s)*i+c}function qe(e,t){if(!(e>0)||!(t>0))throw Error(`Resampler rates must be positive`);if(e===t)return{fromRate:e,toRate:t,push(e){return e},flush(){return new Float32Array}};let n=e/t,r=new Float32Array,i=0;return{fromRate:e,toRate:t,push(e){if(e.length===0)return new Float32Array;let t=new Float32Array(r.length+e.length);if(t.set(r),t.set(e,r.length),r=t,r.length<4)return new Float32Array;let a=[];for(;i+2<r.length-1;){let e=Math.floor(i),t=i-e,o=Math.max(0,e-1),s=Math.min(r.length-1,e+1),c=Math.min(r.length-1,e+2);a.push(Ke(r[o],r[e],r[s],r[c],t)),i+=n}let o=Math.max(0,Math.floor(i)-1);return o>0&&(r=r.subarray(o),i-=o),a.length?Float32Array.from(a):new Float32Array},flush(){return r.length===0?new Float32Array:(r=new Float32Array,i=0,new Float32Array)}}}var J=`liforma-pcm-capture`,Je=`
|
|
1
|
+
var Liforma=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(){if(globalThis.__LIFORMA_STACK===`local`)return!0;if(typeof location>`u`)return!1;try{return new URLSearchParams(location.search).get(`stack`)===`local`}catch{return!1}}var n=`https://api.liforma.ai`,r=`http://localhost:3001`;function i(){let e=globalThis.__LIFORMA_API_BASE_URL;if(e?.trim())return e.trim().replace(/\/$/,``);if(typeof document>`u`)return null;let t=(document.querySelector(`script[data-liforma-sdk][src*="client.js"]`)??document.querySelector(`script[src*="/sdk/v2/client.js"]`))?.getAttribute(`data-api-base-url`)?.trim();return t?t.replace(/\/$/,``):null}function a(){return i()||(t()?r:n)}var o=class extends Error{code;requestId;status;details;constructor(e,t){super(e,t?.cause===void 0?void 0:{cause:t.cause}),this.name=`LiformaError`,this.code=t?.code?.trim()||`UNKNOWN`,this.requestId=t?.requestId?.trim()||void 0,this.status=t?.status,this.details=t?.details}};function s(e){if(!(!e||typeof e!=`object`||Array.isArray(e)))return e}function c(e,t,n){let r=n?.get(`Liforma-Request-Id`)?.trim()||void 0,i;try{i=JSON.parse(t)}catch{}let a=i?.error&&typeof i.error==`object`?i.error:void 0,c=a?.code??i?.code,l=a?.message??i?.message,u=a?.requestId??i?.requestId??r,d=typeof c==`string`&&c.trim()?c.trim():`HTTP_ERROR`,f=typeof l==`string`&&l.trim()?l.trim():t.trim().slice(0,500)||`Request failed (${e})`,p=typeof u==`string`&&u.trim()?u.trim():r,{code:m,message:h,requestId:g,..._}=s(a)??s(i)??{};return new o(f,{code:d,requestId:p,status:e,details:Object.keys(_).length>0?_:void 0})}var l=128,u=/[\u0000-\u001f\u007f]/g;function d(e){if(!e)return;let t=e.replace(u,``).trim();if(t)return t.slice(0,l)}function f(e){let t=new URLSearchParams((e??``).startsWith(`?`)?(e??``).slice(1):e??``);return{...d(t.get(`utm_source`))?{utmSource:d(t.get(`utm_source`))}:{},...d(t.get(`utm_medium`))?{utmMedium:d(t.get(`utm_medium`))}:{},...d(t.get(`utm_campaign`))?{utmCampaign:d(t.get(`utm_campaign`))}:{},...d(t.get(`utm_content`))?{utmContent:d(t.get(`utm_content`))}:{},...d(t.get(`utm_term`))?{utmTerm:d(t.get(`utm_term`))}:{}}}function p(e){if(e===!1)return;if(e&&typeof e==`object`){let t={...d(e.utmSource)?{utmSource:d(e.utmSource)}:{},...d(e.utmMedium)?{utmMedium:d(e.utmMedium)}:{},...d(e.utmCampaign)?{utmCampaign:d(e.utmCampaign)}:{},...d(e.utmContent)?{utmContent:d(e.utmContent)}:{},...d(e.utmTerm)?{utmTerm:d(e.utmTerm)}:{},...d(e.referrer)?{referrer:d(e.referrer)}:{}};return Object.keys(t).length?t:void 0}if(typeof window>`u`)return;let t=f(window.location?.search),n=d(document.referrer||void 0),r={...t,...n?{referrer:n}:{}};return Object.keys(r).length?r:void 0}function m(e){let t=e.locale?.trim();if(t)return t;let n=e.language?.trim();if(n)return n;if(typeof navigator<`u`&&navigator.language?.trim())return navigator.language.trim()}function h(e){let t=e.secondaryLocale?.trim(),n=e.learningLocale?.trim();if(t&&n&&t!==n)throw Error(`secondaryLocale and learningLocale disagree; pass only one or make them equal.`);return t||n||void 0}function g(e,t){let n=e.trim();if(!n)throw Error(`sessionEndpoint must not be empty.`);if(typeof location>`u`)throw Error(`sessionEndpoint is only available in a browser.`);let r=new URL(n,location.href);if(r.origin!==location.origin)throw Error(`sessionEndpoint must be same-origin.`);let i=t.experienceId.trim();if(!i)throw Error(`sessionEndpoint requires experienceId.`);let a=m(t),o=h(t),s=t.idempotencyKey?.trim();return{url:r.toString(),init:{method:`POST`,credentials:`same-origin`,headers:{Accept:`application/json`,"Content-Type":`application/json`,...s?{"Idempotency-Key":s}:{}},body:JSON.stringify({experienceId:i,...a?{locale:a}:{},...o?{secondaryLocale:o}:{},...t.mode?{mode:t.mode}:{},...t.speechInputMode?{speechInputMode:t.speechInputMode}:{},...t.speechOnly===void 0?{}:{speechOnly:t.speechOnly},...(()=>{let e=p(t.attribution);return e?{attribution:e}:{}})(),...t.alreadyConverted?.length?{alreadyConverted:[...t.alreadyConverted]}:{}})}}}function _(e,t){return t===void 0?{}:{[e]:t}}function v(e){return!!e&&typeof e==`object`&&!Array.isArray(e)}function y(e){return typeof e==`boolean`}function b(e){if(!v(e))return null;let t=e.speech,n=e.conversation,r=e.avatar,i=e.tools;return!v(t)||!v(n)||!v(r)||!v(i)||!y(t.input)||!y(t.output)||!y(t.externalAudio)||!y(n.managed)||!y(r.enabled)||!y(i.enabled)?null:{speech:{input:t.input,output:t.output,externalAudio:t.externalAudio},conversation:{managed:n.managed},avatar:{enabled:r.enabled},tools:{enabled:i.enabled}}}function x(e){return!v(e)||!y(e.avatar)||!y(e.speechInput)||!y(e.speechOutput)||!y(e.tools)||!y(e.externalAudio)?null:{speech:{input:e.speechInput,output:e.speechOutput,externalAudio:e.externalAudio},conversation:{managed:!0},avatar:{enabled:e.avatar},tools:{enabled:e.tools}}}function S(e){return b(e)??x(e)}function C(e){if(!v(e))throw Error(`Session Launch response is missing session.`);let t=typeof e.id==`string`?e.id.trim():``,n=typeof e.experienceId==`string`?e.experienceId.trim():``,r=typeof e.experienceRevisionId==`string`?e.experienceRevisionId.trim():``,i=typeof e.expiresAt==`string`?e.expiresAt.trim():``,a=typeof e.locale==`string`?e.locale.trim():``,o=typeof e.secondaryLocale==`string`&&e.secondaryLocale.trim()?e.secondaryLocale.trim():void 0,s=e.mode===`conversation`||e.mode===`presenter`?e.mode:null,c=S(e.capabilities),l=ee(e.localeVoiceFallbacks),u=e.status===`active`||e.status===`ended`?e.status:void 0,d=typeof e.createdAt==`string`&&e.createdAt.trim()?e.createdAt.trim():void 0;if(!t||!n||!r||!i||!a||!s||!c)throw Error(`Session Launch response has an invalid session object.`);return{id:t,experienceId:n,experienceRevisionId:r,expiresAt:i,locale:a,...o?{secondaryLocale:o}:{},mode:s,capabilities:c,...l?{localeVoiceFallbacks:l}:{},...u?{status:u}:{},...d?{createdAt:d}:{}}}function ee(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e){if(!v(n))continue;let e=n.axis===`locale`||n.axis===`secondaryLocale`?n.axis:null,r=typeof n.requested==`string`?n.requested.trim():``,i=typeof n.resolved==`string`?n.resolved.trim():``;!e||!r||!i||n.reason!==`voice_unsupported`||t.push({axis:e,requested:r,resolved:i,reason:`voice_unsupported`})}return t.length>0?t:void 0}function w(e){let t=e.localeVoiceFallbacks;if(t?.length)for(let e of t){let t=e.axis===`secondaryLocale`?`secondaryLocale`:`locale`;console.warn(`[liforma] Session ${t} fell back to ${e.resolved} because the voice does not support ${e.requested}.`)}}function T(e){if(!v(e))throw Error(`Session Launch response must be a JSON object.`);let t=typeof e.launch==`string`?e.launch.trim():``;if(!t)throw Error(`Session Launch response is missing launch.`);let n=C(e.session);return w(n),{session:n,launch:t}}function te(){try{if(typeof globalThis.location?.origin==`string`&&globalThis.location.origin)return globalThis.location.origin}catch{}}function ne(e){return e?e.trim().toUpperCase().replace(/-/g,`_`):``}function re(e,t){let n=te(),r=ne(t.code),i=t.message.trim(),a=t.details??{};if(r===`ORIGIN_NOT_ALLOWED`||/^Origin not allowed/i.test(i)&&n){if(i.includes(e)&&i.includes(`allowed origin`))return t;let r=typeof a.origin==`string`&&a.origin||n||`(unknown origin)`;return new o(`Your origin ${r} is not an allowed origin for experience ${e}. If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), open Workspace → Settings → Origins, add ${r} to the list of allowed origins, and ensure “Allow public client-only embeds” is enabled.`,{code:`ORIGIN_NOT_ALLOWED`,requestId:t.requestId,status:t.status,details:a})}return r===`BROWSER_EMBEDS_DISABLED`||/^Browser embeds are not enabled/i.test(i)?i.includes(`Allow public client-only embeds`)?t:new o(`Browser embeds are not enabled for experience ${e}. If you own this experience, sign in to the Liforma developer portal (app.liforma.ai), open Workspace → Settings → Origins, and enable “Allow public client-only embeds”.`,{code:`BROWSER_EMBEDS_DISABLED`,requestId:t.requestId,status:t.status,details:a}):r===`EXPERIENCE_UNAVAILABLE`||/^Experience is not available\.?$/i.test(i)||/^Unknown experience\.?$/i.test(i)?i.includes(`No playable experience found`)?t:new o(`No playable experience found for “${e}”. Check that the experience id is correct (it should look like exp_…), and that the experience exists and is published in the Liforma developer portal (app.liforma.ai). Deleted, archived, and unpublished experiences cannot be started from a browser embed.`,{code:`EXPERIENCE_UNAVAILABLE`,requestId:t.requestId,status:t.status,details:a}):t}function ie(e){let t=e?.locale?.trim();if(t)return t;let n=e?.language?.trim();if(n)return n;if(typeof navigator<`u`&&navigator.language?.trim())return navigator.language.trim()}function ae(e){let t=e?.secondaryLocale?.trim(),n=e?.learningLocale?.trim();if(t&&n&&t!==n)throw new o(`secondaryLocale and learningLocale disagree; pass only one or make them equal.`,{code:`INVALID_BODY`});return t||n||void 0}function oe(e,t){let n={experienceId:e},r=ie(t);r&&(n.locale=r);let i=ae(t);i&&(n.secondaryLocale=i),t?.mode&&(n.mode=t.mode),t?.speechInputMode&&(n.speechInputMode=t.speechInputMode),t?.speechOnly!==void 0&&(n.speechOnly=t.speechOnly);let a=p(t?.attribution);return a&&(n.attribution=a),t?.alreadyConverted?.length&&(n.alreadyConverted=[...t.alreadyConverted]),n}async function se(e,t){let n=a(),r=t?.idempotencyKey?.trim(),i=await fetch(`${n}/v1/browser-sessions`,{method:`POST`,headers:{"Content-Type":`application/json`,...r?{"Idempotency-Key":r}:{}},body:JSON.stringify(oe(e,t))});if(!i.ok){let t=await i.text();throw re(e,c(i.status,t,i.headers))}let s;try{s=await i.json()}catch{throw new o(`Browser session mint returned invalid JSON.`,{code:`INVALID_RESPONSE`,status:i.status,requestId:i.headers.get(`Liforma-Request-Id`)??void 0})}return T(s)}async function ce(e,t){let n=g(e,t),r=new AbortController,i=()=>r.abort(t.signal?.reason);t.signal?.aborted?i():t.signal?.addEventListener(`abort`,i,{once:!0});let a=globalThis.setTimeout(()=>r.abort(new DOMException(`Session endpoint timed out.`,`TimeoutError`)),t.timeoutMs??3e4),s;try{s=await fetch(n.url,{...n.init,signal:r.signal})}finally{globalThis.clearTimeout(a),t.signal?.removeEventListener(`abort`,i)}if(!s.ok){let e=await s.text();throw c(s.status,e,s.headers)}if(!s.headers.get(`content-type`)?.toLowerCase().includes(`application/json`))throw new o(`Session endpoint must return a JSON Session Launch response.`,{code:`INVALID_RESPONSE`,status:s.status,requestId:s.headers.get(`Liforma-Request-Id`)??void 0});let l;try{l=await s.json()}catch{throw new o(`Session endpoint returned invalid JSON.`,{code:`INVALID_RESPONSE`,status:s.status,requestId:s.headers.get(`Liforma-Request-Id`)??void 0})}return T(l)}function E(e){return typeof e!=`object`||!e?!1:typeof e[Symbol.asyncIterator]==`function`}function le(e){if(typeof e==`string`){let t=e.trim();if(!t)throw Error(`conversationProcessor returned empty text.`);return{text:t}}let t=e.text.trim();if(!t)throw Error(`conversationProcessor returned empty text.`);return{text:t,...e.characterId?{characterId:e.characterId}:{},...e.behavior?{behavior:e.behavior}:{}}}async function ue(e,t){let n=await e(t);return E(n)?{kind:`stream`,stream:n}:{kind:`complete`,speech:le(n)}}var de=`3002`;function D(e){let t=new URL(e);return t.hostname===`localhost`||t.hostname===`127.0.0.1`?`Cannot connect to the Liforma player at ${e} — nothing is listening on port ${t.port||de}. Start the player dev server in player.liforma.ai (npm run dev).`:`Cannot connect to the Liforma player at ${e}. The embed did not load; check your network connection and player URL configuration.`}async function fe(e,t){try{await fetch(e,{method:`GET`,mode:`no-cors`,signal:AbortSignal.timeout(8e3)})}catch{throw Error(D(t))}}function O(e,t,n,r){try{r&&r.length>0?e.postMessage(t,n,r):e.postMessage(t,n)}catch(e){throw e instanceof DOMException&&e.name===`SecurityError`?Error(D(n),{cause:e}):e}}var pe=new Set([1,2]);function me(e){return typeof e==`number`&&pe.has(e)}var he=new Set(`close.stateUpdate.commandResult.userTranscript.characterSpeechStarted.characterSpeechEnded.characterFocusChanged.conversationUpdate.listeningState.userSpeechStarted.userSpeechEnded.modeChange.activityChange.feedback.feedbackFailed.message.started.embedReady.widgetReady.widgetExpand.widgetCollapse.widgetAudioSettings.widgetDragStart.widgetDragMove.widgetDragEnd.fullWindow.theatre.interactionChange`.split(`.`));function ge(e){if(!e||typeof e!=`object`)return null;let t=e;if(!me(t.schemaVersion)||!t.type||!he.has(t.type))return null;if(t.type===`embedReady`||t.type===`widgetReady`||t.type===`widgetExpand`||t.type===`widgetCollapse`)return t;if(t.type===`fullWindow`||t.type===`theatre`){let e=t.payload;return!e||typeof e!=`object`||typeof e.active!=`boolean`?null:t}if(t.type===`widgetAudioSettings`){let e=t.payload;return!e||typeof e!=`object`||typeof e.open!=`boolean`?null:t}if(t.type===`widgetDragStart`||t.type===`widgetDragEnd`){let e=t.payload;return!e||typeof e!=`object`||typeof e.pointerId!=`number`||!Number.isFinite(e.pointerId)?null:t}if(t.type===`widgetDragMove`){let e=t.payload;if(!e||typeof e!=`object`)return null;let{pointerId:n,dx:r,dy:i}=e;return typeof n!=`number`||!Number.isFinite(n)||typeof r!=`number`||!Number.isFinite(r)||typeof i!=`number`||!Number.isFinite(i)?null:t}let n=t.payload;return!n||typeof n!=`object`?null:t}function k(e,t){return t===void 0?{}:{[e]:t}}function _e(e,t,n){let r=e.trim();if(!r)throw Error(`createLaunchMessage requires a non-empty launch string.`);let i=n?.startButton,a=n?.theme,o=n?.showFeedback,s=n?.interaction,c=n?.theatre;return{type:`launch`,schemaVersion:2,payload:{launch:r,...t===void 0?{}:{session:t},...i?{startButton:i}:{},...a?{theme:a}:{},...o===void 0?{}:{showFeedback:o},...s?{interaction:s}:{},...k(`settingsButton`,n?.settingsButton),...k(`theatreButton`,n?.theatreButton),...k(`fullscreenButton`,n?.fullscreenButton),...c===void 0?{}:{theatre:c}}}}function ve(){return{type:`parentHello`,schemaVersion:2}}function ye(e){return{type:`widgetSetChrome`,schemaVersion:2,payload:{mode:e}}}function be(){return{type:`widgetToggleAudioSettings`,schemaVersion:2,payload:{}}}function xe(e){return{type:`setTheatre`,schemaVersion:2,payload:{active:e}}}var Se=`2147483000`;function A(e){let{style:t}=e;return{position:t.position,top:t.top,right:t.right,bottom:t.bottom,left:t.left,inset:t.inset,width:t.width,height:t.height,zIndex:t.zIndex,margin:t.margin,borderRadius:t.borderRadius,maxWidth:t.maxWidth,maxHeight:t.maxHeight}}function j(e){let{style:t}=e;t.position=`fixed`,t.top=`0`,t.right=`0`,t.bottom=`0`,t.left=`0`,t.inset=`0`,t.width=`100vw`,t.width=`100dvw`,t.height=`100vh`,t.height=`100dvh`,t.zIndex=Se,t.margin=`0`,t.borderRadius=`0`,t.maxWidth=`none`,t.maxHeight=`none`,e.dataset.liformaFullWindow=`1`}function Ce(e,t){if(delete e.dataset.liformaFullWindow,!t){e.style.position=``,e.style.top=``,e.style.right=``,e.style.bottom=``,e.style.left=``,e.style.inset=``,e.style.width=``,e.style.height=``,e.style.zIndex=``,e.style.margin=``,e.style.borderRadius=``,e.style.maxWidth=``,e.style.maxHeight=``;return}let{style:n}=e;n.position=t.position,n.top=t.top,n.right=t.right,n.bottom=t.bottom,n.left=t.left,n.inset=t.inset,n.width=t.width,n.height=t.height,n.zIndex=t.zIndex,n.margin=t.margin,n.borderRadius=t.borderRadius,n.maxWidth=t.maxWidth,n.maxHeight=t.maxHeight}function we(e){return e}function M(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?`cmd_${crypto.randomUUID().replace(/-/g,``).slice(0,12)}`:`cmd_${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`}var Te=3e4;function Ee(e){let t=e.container;getComputedStyle(t).position===`static`&&(t.style.position=`relative`);let n=document.createElement(`iframe`);n.src=e.src,n.title=`Liforma experience`,n.setAttribute(`allow`,`microphone; autoplay; clipboard-read; clipboard-write; fullscreen`),n.setAttribute(`allowfullscreen`,``),n.style.cssText=e.transparent?`display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:transparent;`:`display:block;position:absolute;inset:0;width:100%;height:100%;border:0;background:#030508;`,e.transparent&&n.setAttribute(`allowtransparency`,`true`),n.setAttribute(`fetchpriority`,`high`),`fetchPriority`in n&&(n.fetchPriority=`high`);let r=!1,i=!1,a=null,o=null,s=r=>{i!==r&&(i=r,r?(a=A(n),o=document.body.style.overflow,document.body.append(n),j(n),document.body.style.overflow=`hidden`):(t.append(n),Ce(n,a),a=null,o!==null&&(document.body.style.overflow=o,o=null)),e.onFullWindowChange?.(r))},c=new Map,l=null,u=0,d=0,f=[],p=null,m=null,h=new Promise((e,t)=>{p=e,m=t}),g=()=>{for(let e of f)window.clearTimeout(e);f.length=0},_=t=>{if(r)return;let i=Date.now();if(d>0&&i-d<250)return;d=i,g();let a=_e(t.launch,t.session,t.chrome),o=()=>{if(r)return;let t=n.contentWindow;t&&O(t,a,e.playerOrigin)};o();for(let e=1;e<=12;e+=1)f.push(window.setTimeout(o,e*75))},v=()=>{r||!l||_(l)},y=()=>{if(u+=1,u===1){fe(e.src,e.playerOrigin).then(()=>p?.(),e=>m?.(e instanceof Error?e:Error(String(e))));return}d=0,v()},b=()=>{u>0||m?.(Error(D(e.playerOrigin)))};n.addEventListener(`load`,y),n.addEventListener(`error`,b);let x=t=>{if(r||t.source!==n.contentWindow||t.origin!==e.playerOrigin)return;let i=ge(t.data);if(i)switch(i.type){case`close`:e.onClose?.(i.payload);break;case`stateUpdate`:e.onPlayerStatusChange?.(i.payload.state),e.onStateUpdate?.(i.payload.state);break;case`commandResult`:{let e=c.get(i.payload.correlationId);if(!e)return;c.delete(i.payload.correlationId),i.payload.ok?e.resolve(i.payload.result):e.reject(Error(i.payload.error??`Command failed.`));break}case`userTranscript`:e.onUserTranscript?.(i.payload);break;case`characterSpeechStarted`:e.onCharacterSpeechStarted?.(i.payload);break;case`characterSpeechEnded`:e.onCharacterSpeechEnded?.(i.payload);break;case`characterFocusChanged`:e.onCharacterFocusChanged?.(i.payload);break;case`conversationUpdate`:e.onConversationUpdate?.(i.payload.conversation);break;case`listeningState`:e.onListeningState?.(i.payload.listening);break;case`userSpeechStarted`:e.onUserSpeechStarted?.(i.payload);break;case`userSpeechEnded`:e.onUserSpeechEnded?.(i.payload);break;case`modeChange`:e.onActivityChange?.(we(i.payload.mode));break;case`activityChange`:e.onActivityChange?.(i.payload.activity);break;case`feedback`:e.onFeedback?.(i.payload);break;case`feedbackFailed`:e.onFeedbackFailed?.(i.payload);break;case`message`:e.onMessage?.(i.payload);break;case`started`:e.onStarted?.(i.payload);break;case`embedReady`:v();break;case`widgetReady`:e.onWidgetReady?.();break;case`widgetExpand`:e.onWidgetExpand?.();break;case`widgetCollapse`:e.onWidgetCollapse?.();break;case`widgetAudioSettings`:e.onWidgetAudioSettings?.(i.payload.open);break;case`widgetDragStart`:e.onWidgetDragStart?.(i.payload);break;case`widgetDragMove`:e.onWidgetDragMove?.(i.payload);break;case`widgetDragEnd`:e.onWidgetDragEnd?.(i.payload);break;case`fullWindow`:s(i.payload.active);break;case`theatre`:e.onTheatreChange?.(i.payload.active);break;case`interactionChange`:e.onInteractionChange?.(i.payload.interaction);break}};if(window.addEventListener(`message`,x),t.replaceChildren(n),t.clientHeight<2&&!t.style.minHeight){let e=getComputedStyle(t).minHeight;(e===`0px`||e===`auto`)&&(t.style.minHeight=`480px`)}let S=(t,i)=>{if(r)return;let a=n.contentWindow;if(!a)throw Error(D(e.playerOrigin));O(a,t,e.playerOrigin,i)},C=(e,t=12e4,n)=>{let r=e.type!==`parentHello`&&e.payload&&`correlationId`in e.payload?e.payload.correlationId:M();return e.type!==`parentHello`&&e.payload&&`correlationId`in e.payload||e.type!==`parentHello`&&e.payload&&(e.payload.correlationId=r),new Promise((i,a)=>{c.set(r,{resolve:e=>i(e),reject:a}),S(e,n),setTimeout(()=>{c.has(r)&&(c.delete(r),a(Error(`Timed out waiting for player command result.`)))},t)})};return{waitForLoad(){return h},postParentHello(){S(ve())},postLaunch(e,t,n){r||(l={launch:e,session:t,chrome:n},d=0,_(l))},postManifest(e){if(typeof e==`string`){this.postLaunch(e);return}if(e&&typeof e==`object`&&typeof e.launch==`string`){let t=String(e.launch),n=e.session;this.postLaunch(t,n);return}throw Error(`postManifest is deprecated; use postLaunch with an opaque launch string from SessionLaunchResponse.`)},postWidgetSetChrome(e){S(ye(e))},postWidgetToggleAudioSettings(){S(be())},postSetTheatre(e){S(xe(e))},speak(e){return C({type:`speak`,schemaVersion:2,payload:{correlationId:M(),text:e.text,...e.characterId?{characterId:e.characterId}:{},...e.behavior?{behavior:e.behavior}:{}}})},startListening(){return C({type:`startListening`,schemaVersion:2,payload:{correlationId:M()}})},stopListening(){return C({type:`stopListening`,schemaVersion:2,payload:{correlationId:M()}})},listenOnce(e){return C({type:`listenOnce`,schemaVersion:2,payload:{correlationId:M(),...e?.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}}})},async speakProcessorStream(e,t){let n=M(),r=M(),i=(await C({type:`speakStreamStart`,schemaVersion:2,payload:{correlationId:n,...t?.characterId?{characterId:t.characterId}:{},...t?.behavior?{behavior:t.behavior}:{}}})).streamId;try{for await(let n of e){if(t?.signal?.aborted)throw new DOMException(`Processor stream aborted.`,`AbortError`);n&&S({type:`speakStreamChunk`,schemaVersion:2,payload:{streamId:i,text:n}})}if(t?.signal?.aborted)throw new DOMException(`Processor stream aborted.`,`AbortError`);return await C({type:`speakStreamEnd`,schemaVersion:2,payload:{correlationId:r,streamId:i}})}catch(e){try{await C({type:`speakStreamEnd`,schemaVersion:2,payload:{correlationId:r,streamId:i}})}catch{}throw e}},speechPlay(e){let t=M(),n=[];return(e.source.kind===`pcm`||e.source.kind===`encoded`)&&n.push(e.source.data),C({type:`speechPlay`,schemaVersion:2,payload:{correlationId:t,source:e.source,...e.characterId?{characterId:e.characterId}:{},...e.transcript===void 0?{}:{transcript:e.transcript},...e.history?{history:e.history}:{},...e.queue?{queue:e.queue}:{}}},12e4,n)},speechUtteranceBegin(e){return C({type:`speechUtteranceBegin`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,format:e.format,queue:e.queue,...e.characterId?{characterId:e.characterId}:{},...e.transcript===void 0?{}:{transcript:e.transcript}}})},speechUtteranceWrite(e){return C({type:`speechUtteranceWrite`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,audio:e.audio,byteLength:e.byteLength}},Te,[e.audio])},speechUtteranceSetTranscript(e){return C({type:`speechUtteranceSetTranscript`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,transcript:e.transcript}})},speechUtteranceClose(e){return C({type:`speechUtteranceClose`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId,...e.transcript===void 0?{}:{transcript:e.transcript},...e.history?{history:e.history}:{}}})},speechUtteranceCancel(e){return C({type:`speechUtteranceCancel`,schemaVersion:2,payload:{correlationId:M(),utteranceId:e.utteranceId}})},speechInterrupt(e){return C({type:`speechInterrupt`,schemaVersion:2,payload:{correlationId:M(),...e}})},getConversation(){return C({type:`getConversation`,schemaVersion:2,payload:{correlationId:M()}})},focusCharacter(e){return C({type:`focusCharacter`,schemaVersion:2,payload:{correlationId:M(),characterId:e}})},setFit(e){return C({type:`setFit`,schemaVersion:2,payload:{correlationId:M(),fit:e}})},setStage(e){return C({type:`setStage`,schemaVersion:2,payload:{correlationId:M(),stage:e}})},setPipBackground(e){return C({type:`setPipBackground`,schemaVersion:2,payload:{correlationId:M(),pipBackground:e}})},setPipShadow(e){return C({type:`setPipShadow`,schemaVersion:2,payload:{correlationId:M(),pipShadow:e}})},setInteraction(e){return C({type:`setInteraction`,schemaVersion:2,payload:{correlationId:M(),interaction:e}})},getInteraction(){return C({type:`getInteraction`,schemaVersion:2,payload:{correlationId:M()}})},conversion(e){return C({type:`conversion`,schemaVersion:2,payload:{correlationId:M(),key:e}})},engage(){return C({type:`engage`,schemaVersion:2,payload:{correlationId:M()}})},reportSdkError(e){return C({type:`reportSdkError`,schemaVersion:2,payload:{correlationId:M(),errorCode:e.errorCode,message:e.message,stack:e.stack,severity:e.severity,operation:e.operation,release:e.release}})},destroy(){if(!r){r=!0,i&&s(!1);for(let e of c.values())e.reject(Error(`Iframe host destroyed.`));c.clear(),g(),l=null,n.removeEventListener(`load`,y),n.removeEventListener(`error`,b),window.removeEventListener(`message`,x),n.remove()}}}}function De(e){let t,n=!1,r=!1,i=()=>{r||!n||t===void 0||(r=!0,e(t))};return{setValue(e){t=e,i()},markReady(){n=!0,i()}}}var N=`sdk@${{name:`@liforma/client`,version:`0.4.16`,type:`module`,description:`Liforma browser SDK — Experience sessions, speech API, and Svelte embed component.`,license:`MIT`,homepage:`https://docs.liforma.ai`,repository:{type:`git`,url:`git+https://github.com/LiformaLtd/cdn.liforma.ai.git`},bugs:{url:`https://github.com/LiformaLtd/cdn.liforma.ai/issues`},publishConfig:{access:`public`},sideEffects:!1,exports:{".":{types:`./dist/npm/v2/npm.d.ts`,import:`./dist/npm/index.js`},"./svelte":{types:`./dist/svelte/index.d.ts`,import:`./dist/svelte/index.js`,svelte:`./dist/svelte/index.js`},"./svelte/thumbnail":{types:`./dist/svelte/thumbnail.d.ts`,import:`./dist/svelte/thumbnail.js`,svelte:`./dist/svelte/thumbnail.js`},"./thumbnail":{types:`./dist/npm/thumbnail/index.d.ts`,import:`./dist/npm/thumbnail/index.js`},"./react":{types:`./dist/react/react/index.d.ts`,import:`./dist/react/index.js`},"./react/thumbnail":{types:`./dist/react/react/thumbnail.d.ts`,import:`./dist/react/thumbnail.js`},"./next":{types:`./dist/next/next/index.d.ts`,import:`./dist/next/index.js`},"./next/thumbnail":{types:`./dist/next/next/thumbnail.d.ts`,import:`./dist/next/thumbnail.js`},"./byo":{types:`./dist/byo/index.d.ts`,import:`./dist/byo/index.js`},"./elevenlabs":{types:`./dist/elevenlabs/index.d.ts`,import:`./dist/elevenlabs/index.js`},"./openai":{types:`./dist/openai/index.d.ts`,import:`./dist/openai/index.js`},"./deepgram":{types:`./dist/deepgram/index.d.ts`,import:`./dist/deepgram/index.js`},"./google":{types:`./dist/google/index.d.ts`,import:`./dist/google/index.js`},"./livekit":{types:`./dist/livekit/index.d.ts`,import:`./dist/livekit/index.js`}},files:[`dist/npm`,`dist/svelte`,`dist/react`,`dist/next`,`dist/byo`,`dist/elevenlabs`,`dist/openai`,`dist/deepgram`,`dist/google`,`dist/livekit`,`dist/sdk`,`README.md`,`CHANGELOG.md`,`LICENSE`],peerDependencies:{"@elevenlabs/client":`>=1.0.0`,"livekit-client":`>=2.0.0`,react:`^18.0.0 || ^19.0.0`,"react-dom":`^18.0.0 || ^19.0.0`,svelte:`^5.0.0`},peerDependenciesMeta:{svelte:{optional:!0},react:{optional:!0},"react-dom":{optional:!0},"@elevenlabs/client":{optional:!0},"livekit-client":{optional:!0}},scripts:{build:`npm run build:v2 && npm run build:npm && npm run build:svelte && npm run build:react && npm run build:next && npm run build:byo && npm run build:elevenlabs && npm run build:openai && npm run build:deepgram && npm run build:google && npm run build:livekit && npm run copy:preview`,"build:react":`vite build --config vite.config.react.ts && vite build --config vite.config.react-thumbnail.ts && tsc -p tsconfig.react.json`,"build:next":`vite build --config vite.config.next.ts && vite build --config vite.config.next-thumbnail.ts && tsc -p tsconfig.next.json`,"build:byo":`vite build --config vite.config.byo.ts && tsc -p tsconfig.byo.json`,"build:elevenlabs":`vite build --config vite.config.elevenlabs.ts && tsc -p tsconfig.elevenlabs.json`,"build:openai":`vite build --config vite.config.openai.ts && tsc -p tsconfig.openai.json`,"build:deepgram":`vite build --config vite.config.deepgram.ts && tsc -p tsconfig.deepgram.json`,"build:google":`vite build --config vite.config.google.ts && tsc -p tsconfig.google.json`,"build:livekit":`vite build --config vite.config.livekit.ts && tsc -p tsconfig.livekit.json`,"copy:preview":`mkdir -p dist/preview && cp preview/v2-embed-test.html dist/preview/ && cp preview/v2-embed-test-redirect.html dist/preview/`,"build:v2":`vite build --config vite.config.v2.ts && vite build --config vite.config.v2-thumbnail.ts`,"build:npm":`vite build --config vite.config.npm.ts && vite build --config vite.config.thumbnail.ts && tsc -p tsconfig.npm.json`,"build:svelte":`svelte-package -i src/svelte -o dist/svelte && vite build --config vite.config.svelte.ts`,"check:consumer":`svelte-check --workspace test/consumer --tsconfig ./tsconfig.json`,test:`vitest run`,preview:`node scripts/preview-local.mjs`,"dev:sdk":`npm run build:v2 && npm run copy:preview && npm run preview`,dev:`npm run build && npm run preview`,"publish:sdk":`node scripts/publish-sdk.mjs`,"publish:npm":`node scripts/publish-npm.mjs`,"publish:avatar":`node scripts/publish-avatar.mjs`,"publish:avatars-dist":`node scripts/publish-avatars.mjs`,"publish:avatars":`node scripts/republish-avatars.mjs`,"build:avatars":`node scripts/build-avatars.mjs`,"republish:avatars":`node scripts/republish-avatars.mjs`,"catalogue:avatars":`node ../labs.liforma.ai/scripts/generate-avatar-index.mjs`,"verify:avatars-dist":`node scripts/verify-avatars-dist.mjs`,"verify:avatar-pack":`node scripts/verify-avatar-pack.mjs`,"repair:render-plate-metadata":`node scripts/repair-render-plate-metadata.mjs`,"verify:render-plate-crop":`node scripts/verify-render-plate-crop.mjs`,"migrate:avatar-render-prep":`npm run migrate:avatar-render-prep --prefix ../labs.liforma.ai --`,"convert:avatar-teeth":`node scripts/convert-avatar-teeth-webp.mjs`,"warm:avatar-composites":`node scripts/warm-avatar-composites.mjs`,"test:compose-costume":`node scripts/test-compose-costume-layers.mjs`,"test:compose-costume-wasm":`node scripts/test-compose-costume-wasm.mjs`,"test:parse-composite-filename":`node scripts/test-parse-composite-filename.mjs`,"test:avatar-composite-rendered-path":`node scripts/test-avatar-composite-rendered-path.mjs`,"test:avatar-compose-worker-cors":`node scripts/test-avatar-compose-worker-cors.mjs`,"test:experience-thumb-url":`node scripts/test-experience-thumb-url.mjs`,"test:compose-experience-thumb":`node scripts/test-compose-experience-thumb.mjs`,"test:location-depth-paths":`node scripts/test-location-depth-paths.mjs`,"test:location-binary-assets":`node scripts/test-location-binary-assets.mjs`,"test:location-da3-depth":`node scripts/test-location-da3-depth.mjs`,"test:location-interactions":`node scripts/test-location-interactions.mjs`,"test:render-plate-chroma-key":`node scripts/test-render-plate-chroma-key.mjs`,"test:sparse-plate":`node scripts/test-sparse-plate.mjs`,"test:face-meshes-publish-depth":`node scripts/test-face-meshes-publish-depth.mjs`,"test:avatar-avif-sibling":`node scripts/test-avatar-avif-sibling.mjs`,"test:screen-chroma-key":`node scripts/test-screen-chroma-key.mjs`,"pull:sources":`node scripts/sync-authoring-sources.mjs`,"push:sources":`node scripts/sync-authoring-sources.mjs`,"catalogue:locations":`node scripts/catalogue-locations.mjs`,"generate:location-interactions":`node scripts/generate-location-interactions.mjs`,"generate:location-depth":`node scripts/generate-location-depth.mjs`,"generate:location-depth16-da3":`node scripts/generate-location-depth16-da3.mjs`,"generate:location-discontinuities":`node scripts/generate-location-discontinuities.mjs`,"build:locations":`node scripts/build-locations.mjs`,"publish:locations":`node scripts/publish-locations.mjs`,"publish:location-400x225":`node scripts/build-location-400x225.mjs`,"test:location-400x225":`node scripts/test-location-400x225.mjs`,"verify:locations":`node scripts/verify-location-sources.mjs`,"publish:models":`node scripts/publish-models.mjs`,"publish:models:krea-int8":`node scripts/_upload-model-prefix.mjs text-to-image/lf-t2i-turbo-int8`,"publish:models:ltx-int8":`node scripts/_upload-model-prefix.mjs text-to-video/lf-t2v-distill-int8`,"publish:models:video-omni":`node scripts/_upload-model-prefix.mjs text-to-video/lf-video-omni-v1`,"fetch:depth-anything-v2":`node scripts/fetch-depth-anything-v2.mjs`,"fetch:video-depth-anything":`node scripts/fetch-video-depth-anything.mjs`,"fetch:depth-pro":`node scripts/fetch-depth-pro.mjs`,"fetch:face-landmarker":`node scripts/fetch-face-landmarker.mjs`,"fetch:anime-face-detector":`node scripts/fetch-anime-face-detector.mjs`,"fetch:face-parsing":`node scripts/fetch-face-parsing.mjs`,"fetch:vitmatte":`node scripts/fetch-vitmatte.mjs`,"fetch:liveportrait-mlx":`node scripts/fetch-liveportrait-mlx.mjs`,"fetch:wav2vec-phoneme-ctc":`node scripts/fetch-wav2vec-phoneme-ctc.mjs`,"fetch:lf-sta":`node scripts/fetch-wav2vec-phoneme-ctc.mjs`,"fetch:supertonic-3":`node scripts/fetch-supertonic-3.mjs`,"fetch:lf-tts":`node scripts/fetch-supertonic-3.mjs`,"fetch:lf-t2i-turbo":`python3 ../tools/krea-2-salad/scripts/fetch_cdn_snapshot.py`,"fetch:models":`npm run fetch:depth-anything-v2 && npm run fetch:video-depth-anything && npm run fetch:depth-pro && npm run fetch:face-landmarker && npm run fetch:anime-face-detector && npm run fetch:face-parsing && npm run fetch:vitmatte && npm run fetch:lf-sta && npm run fetch:lf-tts`,locations:`npm run catalogue:locations && npm run build:locations`,"configure:cors":`node scripts/configure-r2-cors.mjs`,"verify:cors":`node scripts/verify-cdn-cors.mjs`,"deploy:worker:avatar-compose":`node scripts/deploy-avatar-compose-worker.mjs`,"deploy:worker:experience-thumb":`node scripts/deploy-experience-thumb-worker.mjs`,deploy:`npm run build && npm run publish:sdk`,verify:`npm run build && node scripts/check-sdk-size.mjs && node scripts/verify-thumbnail-light.mjs && node scripts/test-embed-protocol.mjs && node scripts/test-manifest-embed-url.mjs && node scripts/test-player-reachability.mjs && node scripts/test-session-endpoint.mjs && node scripts/verify-package.mjs`,"preview:test":`npm run build && npm run preview`},dependencies:{"@jsquash/avif":`^1.3.0`,"@jsquash/webp":`^1.5.0`,zod:`^4.4.3`},devDependencies:{"@aws-sdk/client-s3":`^3.1000.0`,"@aws-sdk/lib-storage":`^3.1110.0`,"@elevenlabs/client":`^1.17.0`,"@sveltejs/package":`^2.5.8`,"@sveltejs/vite-plugin-svelte":`^7.0.0`,"@testing-library/react":`^16.3.0`,"@testing-library/svelte":`^5.4.2`,"@types/node":`^25.9.3`,"@types/react":`^19.1.12`,"@types/react-dom":`^19.1.9`,"@vitejs/plugin-react":`^5.0.2`,"@webgpu/types":`^0.1.70`,dotenv:`^17.2.3`,jsdom:`^30.0.1`,"livekit-client":`^2.21.0`,react:`^19.1.1`,"react-dom":`^19.1.1`,sharp:`^0.34.2`,svelte:`^5.55.2`,"svelte-check":`^4.7.4`,typescript:`^6.0.3`,vite:`^8.0.16`,vitest:`^4.1.10`,wrangler:`^4.101.0`}}.version}`,Oe=`https://player.liforma.ai/embed`,ke=`http://localhost:3002/embed`;function Ae(){let e=globalThis.__LIFORMA_PLAYER_EMBED_URL;if(e?.trim())return e.trim().replace(/\/$/,``);if(typeof document>`u`)return null;let t=(document.querySelector(`script[data-liforma-sdk][src*="client.js"]`)??document.querySelector(`script[src*="/sdk/v2/client.js"]`))?.getAttribute(`data-player-embed-url`)?.trim();return t?t.replace(/\/$/,``):null}function P(){let e=Ae();return e?e.endsWith(`/embed`)?e:`${e}/embed`:t()?ke:Oe}function je(e,t){return e?.trim()||t?.trim()||void 0}function Me(e,t,n,r,i,a,o,s,c,l,u,d,f){let p=(e??P()).replace(/\/$/,``),m=p.endsWith(`/embed`)?p:`${p}/embed`,h=new URL(m),g=n?.trim();g&&h.searchParams.set(`backdropId`,g);let _=r?.trim();_&&h.searchParams.set(`avatarId`,_);let v=d?.trim();v&&h.searchParams.set(`previewBg`,v);let y=f?.trim();return y&&h.searchParams.set(`previewFg`,y),(i===`face`||i===`medium`)&&h.searchParams.set(`fit`,i),c===`pip`&&h.searchParams.set(`stage`,`pip`),l===`set`&&h.searchParams.set(`pipBackground`,`set`),u===!1&&h.searchParams.set(`pipShadow`,`0`),a===`widget`&&h.searchParams.set(`ui`,`widget`),o&&h.searchParams.set(`close`,`1`),t&&h.searchParams.set(`debug`,`true`),(s===2||s===3||s===4)&&h.searchParams.set(`cast`,String(s)),Ne(h.toString())}function Ne(e){if(typeof location>`u`)return e;let t=new URL(e);return t.searchParams.set(`parentOrigin`,location.origin),t.toString()}function Pe(e){return new URL(e).origin}function Fe(){return typeof crypto<`u`&&typeof crypto.randomUUID==`function`?`evt_${crypto.randomUUID().replace(/-/g,``).slice(0,12)}`:`evt_${Date.now().toString(36)}${Math.random().toString(36).slice(2,8)}`}function Ie(e,t,n){return{id:Fe(),type:e,sessionId:t,timestamp:new Date().toISOString(),data:n}}function Le(){return{ready:new Set,started:new Set,sessionMinted:new Set,userTranscript:new Set,userSpeechStarted:new Set,userSpeechEnded:new Set,characterSpeechStarted:new Set,characterSpeechEnded:new Set,characterFocusChanged:new Set,conversationUpdate:new Set,message:new Set,listeningState:new Set,activityChange:new Set,interactionChange:new Set,feedback:new Set,feedbackFailed:new Set,conversationProcessorError:new Set}}function Re(e,t,n){return e[t].add(n),()=>e[t].delete(n)}function ze(e,t,n){for(let r of e[t])r(n)}function Be(e){for(let t of Object.keys(e))e[t].clear()}function F(e){return`data`in e&&`format`in e&&e.format!=null&&!(`track`in e)}function I(e){return`url`in e&&typeof e.url==`string`}function L(e){if(!(`track`in e))return!1;let t=e.track;return typeof t!=`object`||!t?!1:typeof MediaStreamTrack<`u`&&t instanceof MediaStreamTrack?!0:`kind`in t&&`readyState`in t}function R(e){return!F(e)&&!I(e)&&!L(e)&&`encoding`in e&&typeof e.encoding==`string`}var Ve=[8e3,16e3,22050,24e3,44100,48e3],z={MAX_CHUNK_BYTES:64*1024,RECOMMENDED_CHUNK_MS_MIN:20,RECOMMENDED_CHUNK_MS_MAX:200,MAX_BUFFERED_AUDIO_MS:8e3,PLAYBACK_JITTER_BUFFER_MS:150,PLAYBACK_JITTER_BUFFER_MS_MIN:80,PLAYBACK_JITTER_BUFFER_MS_MAX:250,MAX_UTTERANCE_DURATION_MS:9e4,MAX_PENDING_WRITES:8,DEFAULT_WRITE_TIMEOUT_MS:5e3},B=class extends Error{operation;requiredCapability;name=`UnsupportedSpeechOperationError`;constructor(e,t){super(`Speech operation "${e}" requires capability "${t}".`),this.operation=e,this.requiredCapability=t}},V=class extends Error{name=`SpeechUtteranceClosedError`;constructor(e=`Cannot write to a closed or cancelled speech utterance.`){super(e)}},H=class extends Error{name=`SpeechRuntimeError`;constructor(e,t){super(e,t)}},U=class extends Error{name=`SpeechBufferTimeoutError`;constructor(e=`Timed out waiting for speech buffer capacity.`){super(e)}};function W(e=`none`){return{mode:e,trackedDurationMs:0,fallbackDurationMs:0}}function G(e){return Ve.includes(e)}function K(e){if(e.encoding!==`pcm_s16le`)throw new H(`Unsupported audio encoding: ${e.encoding}`);if(e.channels!==1)throw new H(`Unsupported channel count: ${e.channels}`);if(!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`)}function He(e){if(F(e)){K(e.format);return}if(I(e)){if(!e.url.trim())throw new H(`Audio URL must be non-empty.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}if(L(e)){if(e.track.kind!==`audio`)throw new H(`speech.play track must be an audio MediaStreamTrack.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}if(R(e)){let t=e.encoding.trim().toLowerCase();if(!t)throw new H(`Encoded audio MIME type is required.`);if(t===`pcm_s16le`)throw new H(`Use format: { encoding: "pcm_s16le", … } for raw PCM.`);if(e.sampleRate!==void 0&&!G(e.sampleRate))throw new H(`Unsupported sample rate: ${e.sampleRate}`);return}throw new H(`Unsupported speech.play audio shape.`)}function Ue(e){if(e.format)return K(e.format),e.format;if(e.track){let t={encoding:`pcm_s16le`,sampleRate:e.sampleRate??24e3,channels:1};return K(t),t}throw new H(`createUtterance requires format or track.`)}function q(e){return e instanceof Uint8Array?e:new Uint8Array(e)}function We(e){return e===`replace-active`||e===`replace-all`?`interrupt`:`enqueue`}function Ge(e){let t=new Uint8Array(e.length*2),n=new DataView(t.buffer);for(let t=0;t<e.length;t++){let r=Math.max(-1,Math.min(1,e[t]));n.setInt16(t*2,r<0?r*32768:r*32767,!0)}return t}function Ke(e,t,n,r,i){let a=-.5*e+1.5*t-1.5*n+.5*r,o=e-2.5*t+2*n-.5*r,s=-.5*e+.5*n,c=t;return((a*i+o)*i+s)*i+c}function qe(e,t){if(!(e>0)||!(t>0))throw Error(`Resampler rates must be positive`);if(e===t)return{fromRate:e,toRate:t,push(e){return e},flush(){return new Float32Array}};let n=e/t,r=new Float32Array,i=0;return{fromRate:e,toRate:t,push(e){if(e.length===0)return new Float32Array;let t=new Float32Array(r.length+e.length);if(t.set(r),t.set(e,r.length),r=t,r.length<4)return new Float32Array;let a=[];for(;i+2<r.length-1;){let e=Math.floor(i),t=i-e,o=Math.max(0,e-1),s=Math.min(r.length-1,e+1),c=Math.min(r.length-1,e+2);a.push(Ke(r[o],r[e],r[s],r[c],t)),i+=n}let o=Math.max(0,Math.floor(i)-1);return o>0&&(r=r.subarray(o),i-=o),a.length?Float32Array.from(a):new Float32Array},flush(){return r.length===0?new Float32Array:(r=new Float32Array,i=0,new Float32Array)}}}var J=`liforma-pcm-capture`,Je=`
|
|
2
2
|
class LiformaPcmCaptureProcessor extends AudioWorkletProcessor {
|
|
3
3
|
process(inputs) {
|
|
4
4
|
const ch = inputs[0] && inputs[0][0];
|