@liforma/client 0.2.1

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.
Files changed (274) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +103 -0
  4. package/dist/next/index.js +5186 -0
  5. package/dist/next/index.js.map +1 -0
  6. package/dist/next/next/createLiformaSessionRouteHandler.d.ts +45 -0
  7. package/dist/next/next/createLiformaSessionRouteHandler.d.ts.map +1 -0
  8. package/dist/next/next/index.d.ts +8 -0
  9. package/dist/next/next/index.d.ts.map +1 -0
  10. package/dist/next/react/ExperienceThumbnail.d.ts +22 -0
  11. package/dist/next/react/ExperienceThumbnail.d.ts.map +1 -0
  12. package/dist/next/react/ExperienceWidget.d.ts +35 -0
  13. package/dist/next/react/ExperienceWidget.d.ts.map +1 -0
  14. package/dist/next/shims/apiBaseUrl.d.ts +14 -0
  15. package/dist/next/shims/apiBaseUrl.d.ts.map +1 -0
  16. package/dist/next/shims/localStack.d.ts +6 -0
  17. package/dist/next/shims/localStack.d.ts.map +1 -0
  18. package/dist/next/thumbnail/attachThumbnailParallax.d.ts +7 -0
  19. package/dist/next/thumbnail/attachThumbnailParallax.d.ts.map +1 -0
  20. package/dist/next/thumbnail/ensureStyles.d.ts +3 -0
  21. package/dist/next/thumbnail/ensureStyles.d.ts.map +1 -0
  22. package/dist/next/thumbnail/hostedLaunchUrl.d.ts +23 -0
  23. package/dist/next/thumbnail/hostedLaunchUrl.d.ts.map +1 -0
  24. package/dist/next/thumbnail/index.d.ts +9 -0
  25. package/dist/next/thumbnail/index.d.ts.map +1 -0
  26. package/dist/next/thumbnail/lazyLoad.d.ts +8 -0
  27. package/dist/next/thumbnail/lazyLoad.d.ts.map +1 -0
  28. package/dist/next/thumbnail/scrollParallax.d.ts +13 -0
  29. package/dist/next/thumbnail/scrollParallax.d.ts.map +1 -0
  30. package/dist/next/thumbnail/styles.d.ts +3 -0
  31. package/dist/next/thumbnail/styles.d.ts.map +1 -0
  32. package/dist/next/thumbnail/toGalleryThumbUrl.d.ts +23 -0
  33. package/dist/next/thumbnail/toGalleryThumbUrl.d.ts.map +1 -0
  34. package/dist/next/thumbnail/toGalleryThumbUrl.test.d.ts +2 -0
  35. package/dist/next/thumbnail/toGalleryThumbUrl.test.d.ts.map +1 -0
  36. package/dist/next/thumbnail/types.d.ts +37 -0
  37. package/dist/next/thumbnail/types.d.ts.map +1 -0
  38. package/dist/next/v2/api/fetchExperiencePreview.d.ts +15 -0
  39. package/dist/next/v2/api/fetchExperiencePreview.d.ts.map +1 -0
  40. package/dist/next/v2/api/mintPublicSession.d.ts +25 -0
  41. package/dist/next/v2/api/mintPublicSession.d.ts.map +1 -0
  42. package/dist/next/v2/api/sessionEndpointRequest.d.ts +16 -0
  43. package/dist/next/v2/api/sessionEndpointRequest.d.ts.map +1 -0
  44. package/dist/next/v2/conversationProcessor.d.ts +6 -0
  45. package/dist/next/v2/conversationProcessor.d.ts.map +1 -0
  46. package/dist/next/v2/embed/iframeHost.d.ts +70 -0
  47. package/dist/next/v2/embed/iframeHost.d.ts.map +1 -0
  48. package/dist/next/v2/embed/playerReachability.d.ts +4 -0
  49. package/dist/next/v2/embed/playerReachability.d.ts.map +1 -0
  50. package/dist/next/v2/embed/protocol.d.ts +181 -0
  51. package/dist/next/v2/embed/protocol.d.ts.map +1 -0
  52. package/dist/next/v2/embed/readyGate.d.ts +7 -0
  53. package/dist/next/v2/embed/readyGate.d.ts.map +1 -0
  54. package/dist/next/v2/experience.d.ts +133 -0
  55. package/dist/next/v2/experience.d.ts.map +1 -0
  56. package/dist/next/v2/experienceEvents.d.ts +27 -0
  57. package/dist/next/v2/experienceEvents.d.ts.map +1 -0
  58. package/dist/next/v2/shims/embedBaseUrl.d.ts +19 -0
  59. package/dist/next/v2/shims/embedBaseUrl.d.ts.map +1 -0
  60. package/dist/next/v2/speechOnlyManifest.d.ts +7 -0
  61. package/dist/next/v2/speechOnlyManifest.d.ts.map +1 -0
  62. package/dist/next/v2/types/sessionInteraction.d.ts +120 -0
  63. package/dist/next/v2/types/sessionInteraction.d.ts.map +1 -0
  64. package/dist/next/v2/types/sessionManifest.d.ts +93 -0
  65. package/dist/next/v2/types/sessionManifest.d.ts.map +1 -0
  66. package/dist/next/v2/types/sessionManifestSchema.d.ts +183 -0
  67. package/dist/next/v2/types/sessionManifestSchema.d.ts.map +1 -0
  68. package/dist/next/v2/types/sessionPipeline.d.ts +23 -0
  69. package/dist/next/v2/types/sessionPipeline.d.ts.map +1 -0
  70. package/dist/next/widget/dockGeometry.d.ts +26 -0
  71. package/dist/next/widget/dockGeometry.d.ts.map +1 -0
  72. package/dist/next/widget/index.d.ts +5 -0
  73. package/dist/next/widget/index.d.ts.map +1 -0
  74. package/dist/next/widget/mountExperienceWidget.d.ts +13 -0
  75. package/dist/next/widget/mountExperienceWidget.d.ts.map +1 -0
  76. package/dist/next/widget/styles.d.ts +4 -0
  77. package/dist/next/widget/styles.d.ts.map +1 -0
  78. package/dist/next/widget/types.d.ts +62 -0
  79. package/dist/next/widget/types.d.ts.map +1 -0
  80. package/dist/npm/index.js +4859 -0
  81. package/dist/npm/index.js.map +1 -0
  82. package/dist/npm/shims/apiBaseUrl.d.ts +14 -0
  83. package/dist/npm/shims/apiBaseUrl.d.ts.map +1 -0
  84. package/dist/npm/shims/localStack.d.ts +6 -0
  85. package/dist/npm/shims/localStack.d.ts.map +1 -0
  86. package/dist/npm/svelte/experienceLoadError.d.ts +16 -0
  87. package/dist/npm/svelte/experienceLoadError.d.ts.map +1 -0
  88. package/dist/npm/svelte/experienceLoadErrorOverlay.d.ts +8 -0
  89. package/dist/npm/svelte/experienceLoadErrorOverlay.d.ts.map +1 -0
  90. package/dist/npm/svelte/experienceLoadErrorStyles.d.ts +5 -0
  91. package/dist/npm/svelte/experienceLoadErrorStyles.d.ts.map +1 -0
  92. package/dist/npm/thumbnail/attachThumbnailParallax.d.ts +7 -0
  93. package/dist/npm/thumbnail/attachThumbnailParallax.d.ts.map +1 -0
  94. package/dist/npm/thumbnail/ensureStyles.d.ts +3 -0
  95. package/dist/npm/thumbnail/ensureStyles.d.ts.map +1 -0
  96. package/dist/npm/thumbnail/hostedLaunchUrl.d.ts +23 -0
  97. package/dist/npm/thumbnail/hostedLaunchUrl.d.ts.map +1 -0
  98. package/dist/npm/thumbnail/index.d.ts +9 -0
  99. package/dist/npm/thumbnail/index.d.ts.map +1 -0
  100. package/dist/npm/thumbnail/lazyLoad.d.ts +8 -0
  101. package/dist/npm/thumbnail/lazyLoad.d.ts.map +1 -0
  102. package/dist/npm/thumbnail/scrollParallax.d.ts +13 -0
  103. package/dist/npm/thumbnail/scrollParallax.d.ts.map +1 -0
  104. package/dist/npm/thumbnail/styles.d.ts +3 -0
  105. package/dist/npm/thumbnail/styles.d.ts.map +1 -0
  106. package/dist/npm/thumbnail/toGalleryThumbUrl.d.ts +23 -0
  107. package/dist/npm/thumbnail/toGalleryThumbUrl.d.ts.map +1 -0
  108. package/dist/npm/thumbnail/types.d.ts +37 -0
  109. package/dist/npm/thumbnail/types.d.ts.map +1 -0
  110. package/dist/npm/v2/api/fetchExperiencePreview.d.ts +15 -0
  111. package/dist/npm/v2/api/fetchExperiencePreview.d.ts.map +1 -0
  112. package/dist/npm/v2/api/mintPublicSession.d.ts +25 -0
  113. package/dist/npm/v2/api/mintPublicSession.d.ts.map +1 -0
  114. package/dist/npm/v2/api/sessionEndpointRequest.d.ts +16 -0
  115. package/dist/npm/v2/api/sessionEndpointRequest.d.ts.map +1 -0
  116. package/dist/npm/v2/conversationProcessor.d.ts +6 -0
  117. package/dist/npm/v2/conversationProcessor.d.ts.map +1 -0
  118. package/dist/npm/v2/embed/iframeHost.d.ts +70 -0
  119. package/dist/npm/v2/embed/iframeHost.d.ts.map +1 -0
  120. package/dist/npm/v2/embed/playerReachability.d.ts +4 -0
  121. package/dist/npm/v2/embed/playerReachability.d.ts.map +1 -0
  122. package/dist/npm/v2/embed/protocol.d.ts +181 -0
  123. package/dist/npm/v2/embed/protocol.d.ts.map +1 -0
  124. package/dist/npm/v2/embed/readyGate.d.ts +7 -0
  125. package/dist/npm/v2/embed/readyGate.d.ts.map +1 -0
  126. package/dist/npm/v2/embed/returnNavigation.d.ts +4 -0
  127. package/dist/npm/v2/embed/returnNavigation.d.ts.map +1 -0
  128. package/dist/npm/v2/experience.d.ts +133 -0
  129. package/dist/npm/v2/experience.d.ts.map +1 -0
  130. package/dist/npm/v2/experienceEvents.d.ts +27 -0
  131. package/dist/npm/v2/experienceEvents.d.ts.map +1 -0
  132. package/dist/npm/v2/index.d.ts +21 -0
  133. package/dist/npm/v2/index.d.ts.map +1 -0
  134. package/dist/npm/v2/liforma-experience-thumbnail.d.ts +9 -0
  135. package/dist/npm/v2/liforma-experience-thumbnail.d.ts.map +1 -0
  136. package/dist/npm/v2/liforma-experience-widget.d.ts +8 -0
  137. package/dist/npm/v2/liforma-experience-widget.d.ts.map +1 -0
  138. package/dist/npm/v2/liforma-experience.d.ts +15 -0
  139. package/dist/npm/v2/liforma-experience.d.ts.map +1 -0
  140. package/dist/npm/v2/npm.d.ts +10 -0
  141. package/dist/npm/v2/npm.d.ts.map +1 -0
  142. package/dist/npm/v2/shims/embedBaseUrl.d.ts +19 -0
  143. package/dist/npm/v2/shims/embedBaseUrl.d.ts.map +1 -0
  144. package/dist/npm/v2/speechOnlyManifest.d.ts +7 -0
  145. package/dist/npm/v2/speechOnlyManifest.d.ts.map +1 -0
  146. package/dist/npm/v2/types/index.d.ts +3 -0
  147. package/dist/npm/v2/types/index.d.ts.map +1 -0
  148. package/dist/npm/v2/types/sessionInteraction.d.ts +120 -0
  149. package/dist/npm/v2/types/sessionInteraction.d.ts.map +1 -0
  150. package/dist/npm/v2/types/sessionManifest.d.ts +93 -0
  151. package/dist/npm/v2/types/sessionManifest.d.ts.map +1 -0
  152. package/dist/npm/v2/types/sessionManifestSchema.d.ts +183 -0
  153. package/dist/npm/v2/types/sessionManifestSchema.d.ts.map +1 -0
  154. package/dist/npm/v2/types/sessionPipeline.d.ts +23 -0
  155. package/dist/npm/v2/types/sessionPipeline.d.ts.map +1 -0
  156. package/dist/npm/widget/dockGeometry.d.ts +26 -0
  157. package/dist/npm/widget/dockGeometry.d.ts.map +1 -0
  158. package/dist/npm/widget/index.d.ts +5 -0
  159. package/dist/npm/widget/index.d.ts.map +1 -0
  160. package/dist/npm/widget/mountExperienceWidget.d.ts +13 -0
  161. package/dist/npm/widget/mountExperienceWidget.d.ts.map +1 -0
  162. package/dist/npm/widget/styles.d.ts +4 -0
  163. package/dist/npm/widget/styles.d.ts.map +1 -0
  164. package/dist/npm/widget/types.d.ts +62 -0
  165. package/dist/npm/widget/types.d.ts.map +1 -0
  166. package/dist/react/component/experienceComponentLaunch.d.ts +7 -0
  167. package/dist/react/component/experienceComponentLaunch.d.ts.map +1 -0
  168. package/dist/react/component/experienceComponentRuntime.d.ts +55 -0
  169. package/dist/react/component/experienceComponentRuntime.d.ts.map +1 -0
  170. package/dist/react/component/experienceComponentTypes.d.ts +60 -0
  171. package/dist/react/component/experienceComponentTypes.d.ts.map +1 -0
  172. package/dist/react/index.js +5499 -0
  173. package/dist/react/index.js.map +1 -0
  174. package/dist/react/react/Experience.d.ts +4 -0
  175. package/dist/react/react/Experience.d.ts.map +1 -0
  176. package/dist/react/react/ExperienceThumbnail.d.ts +22 -0
  177. package/dist/react/react/ExperienceThumbnail.d.ts.map +1 -0
  178. package/dist/react/react/ExperienceWidget.d.ts +35 -0
  179. package/dist/react/react/ExperienceWidget.d.ts.map +1 -0
  180. package/dist/react/react/index.d.ts +8 -0
  181. package/dist/react/react/index.d.ts.map +1 -0
  182. package/dist/react/shims/apiBaseUrl.d.ts +14 -0
  183. package/dist/react/shims/apiBaseUrl.d.ts.map +1 -0
  184. package/dist/react/shims/localStack.d.ts +6 -0
  185. package/dist/react/shims/localStack.d.ts.map +1 -0
  186. package/dist/react/svelte/experienceLoadError.d.ts +16 -0
  187. package/dist/react/svelte/experienceLoadError.d.ts.map +1 -0
  188. package/dist/react/svelte/experienceLoadErrorOverlay.d.ts +8 -0
  189. package/dist/react/svelte/experienceLoadErrorOverlay.d.ts.map +1 -0
  190. package/dist/react/svelte/experienceLoadErrorStyles.d.ts +5 -0
  191. package/dist/react/svelte/experienceLoadErrorStyles.d.ts.map +1 -0
  192. package/dist/react/thumbnail/attachThumbnailParallax.d.ts +7 -0
  193. package/dist/react/thumbnail/attachThumbnailParallax.d.ts.map +1 -0
  194. package/dist/react/thumbnail/ensureStyles.d.ts +3 -0
  195. package/dist/react/thumbnail/ensureStyles.d.ts.map +1 -0
  196. package/dist/react/thumbnail/hostedLaunchUrl.d.ts +23 -0
  197. package/dist/react/thumbnail/hostedLaunchUrl.d.ts.map +1 -0
  198. package/dist/react/thumbnail/index.d.ts +9 -0
  199. package/dist/react/thumbnail/index.d.ts.map +1 -0
  200. package/dist/react/thumbnail/lazyLoad.d.ts +8 -0
  201. package/dist/react/thumbnail/lazyLoad.d.ts.map +1 -0
  202. package/dist/react/thumbnail/scrollParallax.d.ts +13 -0
  203. package/dist/react/thumbnail/scrollParallax.d.ts.map +1 -0
  204. package/dist/react/thumbnail/styles.d.ts +3 -0
  205. package/dist/react/thumbnail/styles.d.ts.map +1 -0
  206. package/dist/react/thumbnail/toGalleryThumbUrl.d.ts +23 -0
  207. package/dist/react/thumbnail/toGalleryThumbUrl.d.ts.map +1 -0
  208. package/dist/react/thumbnail/toGalleryThumbUrl.test.d.ts +2 -0
  209. package/dist/react/thumbnail/toGalleryThumbUrl.test.d.ts.map +1 -0
  210. package/dist/react/thumbnail/types.d.ts +37 -0
  211. package/dist/react/thumbnail/types.d.ts.map +1 -0
  212. package/dist/react/v2/api/fetchExperiencePreview.d.ts +15 -0
  213. package/dist/react/v2/api/fetchExperiencePreview.d.ts.map +1 -0
  214. package/dist/react/v2/api/mintPublicSession.d.ts +25 -0
  215. package/dist/react/v2/api/mintPublicSession.d.ts.map +1 -0
  216. package/dist/react/v2/api/sessionEndpointRequest.d.ts +16 -0
  217. package/dist/react/v2/api/sessionEndpointRequest.d.ts.map +1 -0
  218. package/dist/react/v2/conversationProcessor.d.ts +6 -0
  219. package/dist/react/v2/conversationProcessor.d.ts.map +1 -0
  220. package/dist/react/v2/embed/iframeHost.d.ts +70 -0
  221. package/dist/react/v2/embed/iframeHost.d.ts.map +1 -0
  222. package/dist/react/v2/embed/playerReachability.d.ts +4 -0
  223. package/dist/react/v2/embed/playerReachability.d.ts.map +1 -0
  224. package/dist/react/v2/embed/protocol.d.ts +181 -0
  225. package/dist/react/v2/embed/protocol.d.ts.map +1 -0
  226. package/dist/react/v2/embed/readyGate.d.ts +7 -0
  227. package/dist/react/v2/embed/readyGate.d.ts.map +1 -0
  228. package/dist/react/v2/experience.d.ts +133 -0
  229. package/dist/react/v2/experience.d.ts.map +1 -0
  230. package/dist/react/v2/experienceEvents.d.ts +27 -0
  231. package/dist/react/v2/experienceEvents.d.ts.map +1 -0
  232. package/dist/react/v2/shims/embedBaseUrl.d.ts +19 -0
  233. package/dist/react/v2/shims/embedBaseUrl.d.ts.map +1 -0
  234. package/dist/react/v2/speechOnlyManifest.d.ts +7 -0
  235. package/dist/react/v2/speechOnlyManifest.d.ts.map +1 -0
  236. package/dist/react/v2/types/sessionInteraction.d.ts +120 -0
  237. package/dist/react/v2/types/sessionInteraction.d.ts.map +1 -0
  238. package/dist/react/v2/types/sessionManifest.d.ts +93 -0
  239. package/dist/react/v2/types/sessionManifest.d.ts.map +1 -0
  240. package/dist/react/v2/types/sessionManifestSchema.d.ts +183 -0
  241. package/dist/react/v2/types/sessionManifestSchema.d.ts.map +1 -0
  242. package/dist/react/v2/types/sessionPipeline.d.ts +23 -0
  243. package/dist/react/v2/types/sessionPipeline.d.ts.map +1 -0
  244. package/dist/react/widget/dockGeometry.d.ts +26 -0
  245. package/dist/react/widget/dockGeometry.d.ts.map +1 -0
  246. package/dist/react/widget/index.d.ts +5 -0
  247. package/dist/react/widget/index.d.ts.map +1 -0
  248. package/dist/react/widget/mountExperienceWidget.d.ts +13 -0
  249. package/dist/react/widget/mountExperienceWidget.d.ts.map +1 -0
  250. package/dist/react/widget/styles.d.ts +4 -0
  251. package/dist/react/widget/styles.d.ts.map +1 -0
  252. package/dist/react/widget/types.d.ts +62 -0
  253. package/dist/react/widget/types.d.ts.map +1 -0
  254. package/dist/sdk/v2/client.js +806 -0
  255. package/dist/sdk/v2/client.js.map +1 -0
  256. package/dist/svelte/Experience.svelte +522 -0
  257. package/dist/svelte/Experience.svelte.d.ts +15 -0
  258. package/dist/svelte/ExperienceThumbnail.svelte +376 -0
  259. package/dist/svelte/ExperienceThumbnail.svelte.d.ts +22 -0
  260. package/dist/svelte/ExperienceWidget.svelte +146 -0
  261. package/dist/svelte/ExperienceWidget.svelte.d.ts +35 -0
  262. package/dist/svelte/experienceLoadError.d.ts +15 -0
  263. package/dist/svelte/experienceLoadError.js +82 -0
  264. package/dist/svelte/experienceLoadError.test.d.ts +1 -0
  265. package/dist/svelte/experienceLoadError.test.js +18 -0
  266. package/dist/svelte/experienceLoadErrorOverlay.d.ts +7 -0
  267. package/dist/svelte/experienceLoadErrorOverlay.js +48 -0
  268. package/dist/svelte/experienceLoadErrorStyles.d.ts +4 -0
  269. package/dist/svelte/experienceLoadErrorStyles.js +112 -0
  270. package/dist/svelte/index.d.ts +7 -0
  271. package/dist/svelte/index.js +5 -0
  272. package/dist/svelte/types.d.ts +1 -0
  273. package/dist/svelte/types.js +1 -0
  274. package/package.json +154 -0
@@ -0,0 +1,376 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import type { Attachment } from 'svelte/attachments';
4
+
5
+ import {
6
+ attachImageReady,
7
+ attachLazyThumb,
8
+ attachScrollLinkedParallax,
9
+ ensureExperienceThumbnailStyles,
10
+ EXPERIENCE_THUMBNAIL_CSS,
11
+ horizontalParallaxFromViewportY,
12
+ resolveThumbnailHref,
13
+ resolveThumbnailInteraction,
14
+ resolveThumbnailLayers,
15
+ type GalleryThumb
16
+ } from '@liforma/client';
17
+
18
+ let {
19
+ galleryThumb,
20
+ avatarImage,
21
+ backgroundImage,
22
+ foregroundImage,
23
+ alt,
24
+ experienceId,
25
+ href,
26
+ target,
27
+ rel,
28
+ onclick,
29
+ returnUrl,
30
+ playerBaseUrl,
31
+ parallax = true,
32
+ lazy = true,
33
+ class: className = '',
34
+ fallbackImage
35
+ }: {
36
+ galleryThumb?: GalleryThumb;
37
+ avatarImage?: string;
38
+ backgroundImage?: string;
39
+ foregroundImage?: string;
40
+ alt: string;
41
+ experienceId?: string;
42
+ href?: string;
43
+ target?: string;
44
+ rel?: string;
45
+ onclick?: (event: MouseEvent) => void;
46
+ returnUrl?: string;
47
+ playerBaseUrl?: string;
48
+ parallax?: boolean;
49
+ lazy?: boolean;
50
+ class?: string;
51
+ fallbackImage?: string;
52
+ } = $props();
53
+
54
+ const layers = $derived(
55
+ resolveThumbnailLayers({
56
+ galleryThumb,
57
+ avatarImage,
58
+ backgroundImage,
59
+ foregroundImage,
60
+ fallbackImage
61
+ })
62
+ );
63
+
64
+ const interaction = $derived(
65
+ resolveThumbnailInteraction({
66
+ href,
67
+ hasClickHandler: typeof onclick === 'function',
68
+ experienceId
69
+ })
70
+ );
71
+
72
+ const hasScene = $derived(Boolean(layers?.backgroundImage));
73
+ const isHostedLaunch = $derived(interaction === 'anchor' && !href?.trim() && Boolean(experienceId?.trim()));
74
+
75
+ let resolvedHref = $state<string | undefined>(undefined);
76
+ let shouldLoad = $state(false);
77
+ let hovering = $state(false);
78
+ let pointerDown = $state(false);
79
+ let scrollParallax = $state(false);
80
+ let parallaxX = $state(0);
81
+ let parallaxY = $state(0);
82
+ let avatarLoadFailed = $state(false);
83
+ let backgroundReady = $state(false);
84
+ let avatarReady = $state(false);
85
+ let foregroundReady = $state(false);
86
+ let detachGlobalPointerEnd: (() => void) | null = null;
87
+
88
+ const parallaxEnabled = $derived(parallax !== false);
89
+ const parallaxLayersActive = $derived(parallaxEnabled && (hovering || scrollParallax));
90
+ const resolvedAvatarImage = $derived(
91
+ layers
92
+ ? avatarLoadFailed && layers.fallbackImage
93
+ ? layers.fallbackImage
94
+ : layers.avatarImage
95
+ : ''
96
+ );
97
+ const sceneLayersReady = $derived(
98
+ Boolean(layers) &&
99
+ (!layers?.backgroundImage || backgroundReady) &&
100
+ avatarReady &&
101
+ (!layers?.foregroundImage || foregroundReady)
102
+ );
103
+ const revealThumb = $derived(shouldLoad && sceneLayersReady);
104
+
105
+ $effect(() => {
106
+ if (lazy === false) shouldLoad = true;
107
+ });
108
+
109
+ function refreshHref(): void {
110
+ resolvedHref = resolveThumbnailHref({
111
+ href,
112
+ experienceId,
113
+ returnUrl,
114
+ playerBaseUrl,
115
+ useCurrentPageAsReturnUrl: isHostedLaunch && !returnUrl?.trim()
116
+ });
117
+ }
118
+
119
+ $effect(() => {
120
+ void href;
121
+ void experienceId;
122
+ void returnUrl;
123
+ void playerBaseUrl;
124
+ void isHostedLaunch;
125
+ refreshHref();
126
+ });
127
+
128
+ $effect(() => {
129
+ void layers?.backgroundImage;
130
+ void layers?.foregroundImage;
131
+ void resolvedAvatarImage;
132
+ backgroundReady = false;
133
+ avatarReady = false;
134
+ foregroundReady = false;
135
+ avatarLoadFailed = false;
136
+ });
137
+
138
+ onMount(() => {
139
+ ensureExperienceThumbnailStyles(EXPERIENCE_THUMBNAIL_CSS);
140
+ refreshHref();
141
+ });
142
+
143
+ function imageReadyAttachment(onReady: () => void): Attachment<HTMLImageElement> {
144
+ return (element) => attachImageReady(element, onReady);
145
+ }
146
+
147
+ const lazyLoadAttach: Attachment<HTMLElement> = (element) => {
148
+ if (!lazy) {
149
+ shouldLoad = true;
150
+ return () => {};
151
+ }
152
+ return attachLazyThumb(element, {
153
+ onVisibleChange: (visible) => {
154
+ if (visible) shouldLoad = true;
155
+ }
156
+ });
157
+ };
158
+
159
+ const scrollParallaxAttach: Attachment<HTMLElement> = (element) => {
160
+ if (!parallaxEnabled) return () => {};
161
+ return attachScrollLinkedParallax(element, {
162
+ onParallax: (x) => {
163
+ scrollParallax = true;
164
+ if (!pointerDown) {
165
+ parallaxX = x;
166
+ parallaxY = 0;
167
+ }
168
+ },
169
+ pointerActive: () => pointerDown
170
+ });
171
+ };
172
+
173
+ function onPointerMove(event: PointerEvent): void {
174
+ if (!parallaxEnabled || scrollParallax) return;
175
+ const thumb = event.currentTarget as HTMLElement;
176
+ const rect = thumb.getBoundingClientRect();
177
+ if (rect.width <= 0 || rect.height <= 0) return;
178
+ parallaxX = ((event.clientX - rect.left) / rect.width - 0.5) * 2;
179
+ parallaxY = ((event.clientY - rect.top) / rect.height - 0.5) * 2;
180
+ }
181
+
182
+ function refreshScrollParallax(thumb: HTMLElement): void {
183
+ parallaxX = horizontalParallaxFromViewportY(thumb.getBoundingClientRect(), window.innerHeight);
184
+ parallaxY = 0;
185
+ }
186
+
187
+ function clearPointerInteraction(thumb: HTMLElement): void {
188
+ const wasDown = pointerDown;
189
+ pointerDown = false;
190
+ hovering = false;
191
+ if (scrollParallax && wasDown) {
192
+ refreshScrollParallax(thumb);
193
+ } else if (!scrollParallax) {
194
+ parallaxX = 0;
195
+ parallaxY = 0;
196
+ }
197
+ }
198
+
199
+ function onPointerDown(event: PointerEvent): void {
200
+ if (!parallaxEnabled) return;
201
+ const thumb = event.currentTarget as HTMLElement;
202
+ pointerDown = true;
203
+ if (event.pointerType === 'mouse') hovering = true;
204
+
205
+ detachGlobalPointerEnd?.();
206
+ const onGlobalEnd = () => {
207
+ clearPointerInteraction(thumb);
208
+ detachGlobalPointerEnd = null;
209
+ };
210
+ detachGlobalPointerEnd = () => {
211
+ window.removeEventListener('pointerup', onGlobalEnd);
212
+ window.removeEventListener('pointercancel', onGlobalEnd);
213
+ detachGlobalPointerEnd = null;
214
+ };
215
+ window.addEventListener('pointerup', onGlobalEnd);
216
+ window.addEventListener('pointercancel', onGlobalEnd);
217
+ }
218
+
219
+ function endPointerInteraction(event: PointerEvent): void {
220
+ clearPointerInteraction(event.currentTarget as HTMLElement);
221
+ detachGlobalPointerEnd?.();
222
+ }
223
+
224
+ function onPointerEnter(event: PointerEvent): void {
225
+ if (parallaxEnabled && event.pointerType === 'mouse') hovering = true;
226
+ }
227
+
228
+ function onPointerLeave(_event: PointerEvent): void {
229
+ if (!pointerDown) {
230
+ hovering = false;
231
+ if (!scrollParallax) {
232
+ parallaxX = 0;
233
+ parallaxY = 0;
234
+ }
235
+ }
236
+ }
237
+
238
+ function onAvatarError(): void {
239
+ if (layers?.fallbackImage) avatarLoadFailed = true;
240
+ }
241
+
242
+ function handleClick(event: MouseEvent): void {
243
+ onclick?.(event);
244
+ if (event.defaultPrevented) return;
245
+
246
+ if (isHostedLaunch && !returnUrl?.trim()) {
247
+ const next = resolveThumbnailHref({
248
+ experienceId,
249
+ playerBaseUrl,
250
+ useCurrentPageAsReturnUrl: true
251
+ });
252
+ if (next && event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey) {
253
+ event.preventDefault();
254
+ window.location.assign(next);
255
+ }
256
+ }
257
+ }
258
+
259
+ const rootClass = $derived(
260
+ [
261
+ 'liforma-et',
262
+ hasScene ? 'liforma-et--scene' : '',
263
+ parallaxLayersActive ? 'liforma-et--parallax' : '',
264
+ scrollParallax ? 'liforma-et--scroll-parallax' : '',
265
+ shouldLoad && !revealThumb ? 'liforma-et--loading' : '',
266
+ revealThumb ? 'liforma-et--revealed' : '',
267
+ className
268
+ ]
269
+ .filter(Boolean)
270
+ .join(' ')
271
+ );
272
+ </script>
273
+
274
+ {#if layers}
275
+ {#if interaction === 'anchor' && resolvedHref}
276
+ <a
277
+ class={rootClass}
278
+ href={resolvedHref}
279
+ {target}
280
+ {rel}
281
+ aria-label={alt}
282
+ style:--liforma-et-parallax-x={parallaxX}
283
+ style:--liforma-et-parallax-y={parallaxY}
284
+ {@attach lazyLoadAttach}
285
+ {@attach scrollParallaxAttach}
286
+ onclick={handleClick}
287
+ onpointerdown={onPointerDown}
288
+ onpointerup={endPointerInteraction}
289
+ onpointercancel={endPointerInteraction}
290
+ onpointerenter={onPointerEnter}
291
+ onpointermove={onPointerMove}
292
+ onpointerleave={onPointerLeave}
293
+ >
294
+ {@render media()}
295
+ </a>
296
+ {:else if interaction === 'button'}
297
+ <button
298
+ type="button"
299
+ class={rootClass}
300
+ aria-label={alt}
301
+ style:--liforma-et-parallax-x={parallaxX}
302
+ style:--liforma-et-parallax-y={parallaxY}
303
+ {@attach lazyLoadAttach}
304
+ {@attach scrollParallaxAttach}
305
+ onclick={handleClick}
306
+ onpointerdown={onPointerDown}
307
+ onpointerup={endPointerInteraction}
308
+ onpointercancel={endPointerInteraction}
309
+ onpointerenter={onPointerEnter}
310
+ onpointermove={onPointerMove}
311
+ onpointerleave={onPointerLeave}
312
+ >
313
+ {@render media()}
314
+ </button>
315
+ {:else}
316
+ <div
317
+ class={rootClass}
318
+ role={alt ? 'img' : undefined}
319
+ aria-label={alt || undefined}
320
+ aria-hidden={alt ? undefined : 'true'}
321
+ style:--liforma-et-parallax-x={parallaxX}
322
+ style:--liforma-et-parallax-y={parallaxY}
323
+ {@attach lazyLoadAttach}
324
+ {@attach scrollParallaxAttach}
325
+ onpointerdown={onPointerDown}
326
+ onpointerup={endPointerInteraction}
327
+ onpointercancel={endPointerInteraction}
328
+ onpointerenter={onPointerEnter}
329
+ onpointermove={onPointerMove}
330
+ onpointerleave={onPointerLeave}
331
+ >
332
+ {@render media()}
333
+ </div>
334
+ {/if}
335
+ {/if}
336
+
337
+ {#snippet media()}
338
+ <span class="liforma-et__placeholder" aria-hidden="true"></span>
339
+ {#if shouldLoad && layers?.backgroundImage}
340
+ <img
341
+ class="liforma-et__background liforma-et__media"
342
+ class:liforma-et__media--ready={backgroundReady}
343
+ src={layers.backgroundImage}
344
+ alt=""
345
+ decoding="async"
346
+ {@attach imageReadyAttachment(() => {
347
+ backgroundReady = true;
348
+ })}
349
+ />
350
+ {/if}
351
+ {#if shouldLoad && resolvedAvatarImage}
352
+ <img
353
+ class="liforma-et__avatar liforma-et__media"
354
+ class:liforma-et__media--ready={avatarReady}
355
+ src={resolvedAvatarImage}
356
+ alt=""
357
+ decoding="async"
358
+ {@attach imageReadyAttachment(() => {
359
+ avatarReady = true;
360
+ })}
361
+ onerror={onAvatarError}
362
+ />
363
+ {/if}
364
+ {#if shouldLoad && layers?.foregroundImage}
365
+ <img
366
+ class="liforma-et__foreground liforma-et__media"
367
+ class:liforma-et__media--ready={foregroundReady}
368
+ src={layers.foregroundImage}
369
+ alt=""
370
+ decoding="async"
371
+ {@attach imageReadyAttachment(() => {
372
+ foregroundReady = true;
373
+ })}
374
+ />
375
+ {/if}
376
+ {/snippet}
@@ -0,0 +1,22 @@
1
+ import { type GalleryThumb } from '@liforma/client';
2
+ type $$ComponentProps = {
3
+ galleryThumb?: GalleryThumb;
4
+ avatarImage?: string;
5
+ backgroundImage?: string;
6
+ foregroundImage?: string;
7
+ alt: string;
8
+ experienceId?: string;
9
+ href?: string;
10
+ target?: string;
11
+ rel?: string;
12
+ onclick?: (event: MouseEvent) => void;
13
+ returnUrl?: string;
14
+ playerBaseUrl?: string;
15
+ parallax?: boolean;
16
+ lazy?: boolean;
17
+ class?: string;
18
+ fallbackImage?: string;
19
+ };
20
+ declare const ExperienceThumbnail: import("svelte").Component<$$ComponentProps, {}, "">;
21
+ type ExperienceThumbnail = ReturnType<typeof ExperienceThumbnail>;
22
+ export default ExperienceThumbnail;
@@ -0,0 +1,146 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte';
3
+ import {
4
+ mountExperienceWidget,
5
+ type ExperienceFit,
6
+ type ExperienceMode,
7
+ type ExperienceWidgetController,
8
+ type ExperienceWidgetPosition,
9
+ type ExperienceWidgetPrefetch,
10
+ type GalleryThumb,
11
+ type SpeechInputMode,
12
+ type StartButtonOptions
13
+ } from '@liforma/client';
14
+
15
+ let {
16
+ experienceId,
17
+ alt,
18
+ galleryThumb,
19
+ avatarImage,
20
+ backgroundImage,
21
+ foregroundImage,
22
+ fallbackImage,
23
+ prefetch = 'onExpand',
24
+ parallax = true,
25
+ position = 'static',
26
+ offset,
27
+ sessionEndpoint,
28
+ language,
29
+ mode,
30
+ speechInputMode,
31
+ speechOnly,
32
+ fit,
33
+ embedBaseUrl,
34
+ expandedMaxWidth,
35
+ expandedMaxHeight,
36
+ startButtonLabel,
37
+ startButton,
38
+ class: className = '',
39
+ onExpanded,
40
+ onCollapsed,
41
+ onStarted,
42
+ onClose,
43
+ onError,
44
+ onPrefetchReady
45
+ }: {
46
+ experienceId: string;
47
+ alt: string;
48
+ galleryThumb?: GalleryThumb;
49
+ avatarImage?: string;
50
+ backgroundImage?: string;
51
+ foregroundImage?: string;
52
+ fallbackImage?: string;
53
+ prefetch?: ExperienceWidgetPrefetch;
54
+ parallax?: boolean;
55
+ position?: ExperienceWidgetPosition;
56
+ offset?: number | string;
57
+ sessionEndpoint?: string;
58
+ language?: 'en' | 'es';
59
+ mode?: ExperienceMode;
60
+ speechInputMode?: SpeechInputMode;
61
+ speechOnly?: boolean;
62
+ fit?: ExperienceFit;
63
+ embedBaseUrl?: string;
64
+ expandedMaxWidth?: string;
65
+ expandedMaxHeight?: string;
66
+ startButtonLabel?: string;
67
+ startButton?: StartButtonOptions;
68
+ class?: string;
69
+ onExpanded?: () => void;
70
+ onCollapsed?: () => void;
71
+ onStarted?: () => void;
72
+ onClose?: () => void;
73
+ onError?: (error: Error) => void;
74
+ onPrefetchReady?: () => void;
75
+ } = $props();
76
+
77
+ let hostEl: HTMLDivElement | undefined = $state();
78
+ let controller: ExperienceWidgetController | null = null;
79
+
80
+ const anchored = $derived(position === 'bottom-right' || position === 'bottom-left');
81
+
82
+ $effect(() => {
83
+ const host = hostEl;
84
+ if (!host || !experienceId?.trim()) return;
85
+
86
+ controller?.destroy();
87
+ controller = mountExperienceWidget(host, {
88
+ experienceId,
89
+ alt,
90
+ galleryThumb,
91
+ avatarImage,
92
+ backgroundImage,
93
+ foregroundImage,
94
+ fallbackImage,
95
+ prefetch,
96
+ parallax,
97
+ position,
98
+ offset,
99
+ sessionEndpoint,
100
+ language,
101
+ mode,
102
+ speechInputMode,
103
+ speechOnly,
104
+ fit,
105
+ embedBaseUrl,
106
+ expandedMaxWidth,
107
+ expandedMaxHeight,
108
+ startButtonLabel,
109
+ startButton,
110
+ onExpanded,
111
+ onCollapsed,
112
+ onStarted,
113
+ onClose,
114
+ onError,
115
+ onPrefetchReady
116
+ });
117
+
118
+ return () => {
119
+ controller?.destroy();
120
+ controller = null;
121
+ };
122
+ });
123
+
124
+ onDestroy(() => {
125
+ controller?.destroy();
126
+ controller = null;
127
+ });
128
+ </script>
129
+
130
+ <div
131
+ class="liforma-ew-svelte {className}"
132
+ class:liforma-ew-svelte--anchored={anchored}
133
+ bind:this={hostEl}
134
+ ></div>
135
+
136
+ <style>
137
+ .liforma-ew-svelte {
138
+ width: 100%;
139
+ height: 100%;
140
+ min-height: inherit;
141
+ }
142
+
143
+ .liforma-ew-svelte--anchored {
144
+ display: contents;
145
+ }
146
+ </style>
@@ -0,0 +1,35 @@
1
+ import { type ExperienceFit, type ExperienceMode, type ExperienceWidgetPosition, type ExperienceWidgetPrefetch, type GalleryThumb, type SpeechInputMode, type StartButtonOptions } from '@liforma/client';
2
+ type $$ComponentProps = {
3
+ experienceId: string;
4
+ alt: string;
5
+ galleryThumb?: GalleryThumb;
6
+ avatarImage?: string;
7
+ backgroundImage?: string;
8
+ foregroundImage?: string;
9
+ fallbackImage?: string;
10
+ prefetch?: ExperienceWidgetPrefetch;
11
+ parallax?: boolean;
12
+ position?: ExperienceWidgetPosition;
13
+ offset?: number | string;
14
+ sessionEndpoint?: string;
15
+ language?: 'en' | 'es';
16
+ mode?: ExperienceMode;
17
+ speechInputMode?: SpeechInputMode;
18
+ speechOnly?: boolean;
19
+ fit?: ExperienceFit;
20
+ embedBaseUrl?: string;
21
+ expandedMaxWidth?: string;
22
+ expandedMaxHeight?: string;
23
+ startButtonLabel?: string;
24
+ startButton?: StartButtonOptions;
25
+ class?: string;
26
+ onExpanded?: () => void;
27
+ onCollapsed?: () => void;
28
+ onStarted?: () => void;
29
+ onClose?: () => void;
30
+ onError?: (error: Error) => void;
31
+ onPrefetchReady?: () => void;
32
+ };
33
+ declare const ExperienceWidget: import("svelte").Component<$$ComponentProps, {}, "">;
34
+ type ExperienceWidget = ReturnType<typeof ExperienceWidget>;
35
+ export default ExperienceWidget;
@@ -0,0 +1,15 @@
1
+ export type ExperienceLoadErrorKind = 'not_found' | 'origin_not_allowed' | 'browser_embeds_disabled' | 'generic';
2
+ export type ExperienceLoadErrorPresentation = {
3
+ readonly kind: ExperienceLoadErrorKind;
4
+ readonly title: string;
5
+ readonly detail: string;
6
+ readonly technicalMessage: string;
7
+ readonly experienceId?: string;
8
+ readonly origin?: string;
9
+ };
10
+ export declare function extractExperienceIdFromMessage(message: string): string | undefined;
11
+ export declare function extractOriginFromMessage(message: string): string | undefined;
12
+ export declare function presentExperienceLoadError(error: unknown, options?: {
13
+ readonly experienceId?: string | null;
14
+ readonly origin?: string | null;
15
+ }): ExperienceLoadErrorPresentation;
@@ -0,0 +1,82 @@
1
+ const EXPERIENCE_ID_IN_MESSAGE = /\b(exp_[A-Za-z0-9][A-Za-z0-9_-]{2,127})\b/;
2
+ const ORIGIN_IN_MESSAGE = /(?:Your origin|origin)\s+(\S+)\s+is not an allowed origin/i;
3
+ function toErrorMessage(error) {
4
+ if (error instanceof Error)
5
+ return error.message;
6
+ return String(error);
7
+ }
8
+ function currentPageOrigin() {
9
+ try {
10
+ if (typeof globalThis.location?.origin === 'string' && globalThis.location.origin) {
11
+ return globalThis.location.origin;
12
+ }
13
+ }
14
+ catch {
15
+ /* ignore */
16
+ }
17
+ return undefined;
18
+ }
19
+ export function extractExperienceIdFromMessage(message) {
20
+ return EXPERIENCE_ID_IN_MESSAGE.exec(message)?.[1];
21
+ }
22
+ export function extractOriginFromMessage(message) {
23
+ return ORIGIN_IN_MESSAGE.exec(message)?.[1];
24
+ }
25
+ export function presentExperienceLoadError(error, options) {
26
+ const technicalMessage = toErrorMessage(error).trim() || 'Unknown error';
27
+ const experienceId = options?.experienceId?.trim() ||
28
+ extractExperienceIdFromMessage(technicalMessage) ||
29
+ undefined;
30
+ const origin = options?.origin?.trim() ||
31
+ extractOriginFromMessage(technicalMessage) ||
32
+ currentPageOrigin() ||
33
+ undefined;
34
+ if (/No playable experience found/i.test(technicalMessage) ||
35
+ /^Experience is not available\.?$/i.test(technicalMessage) ||
36
+ /^Unknown experience\.?$/i.test(technicalMessage) ||
37
+ /experience_unavailable/i.test(technicalMessage)) {
38
+ const idLabel = experienceId ?? 'the requested experience';
39
+ return {
40
+ kind: 'not_found',
41
+ title: 'The application tried to load an avatar experience that doesn’t exist.',
42
+ detail: `Experience with id ${idLabel} does not exist`,
43
+ technicalMessage,
44
+ experienceId,
45
+ origin
46
+ };
47
+ }
48
+ if (/origin_not_allowed/i.test(technicalMessage) ||
49
+ /is not an allowed origin/i.test(technicalMessage) ||
50
+ /^Origin not allowed/i.test(technicalMessage)) {
51
+ const originLabel = origin ?? 'this page';
52
+ const idLabel = experienceId ?? 'the requested experience';
53
+ return {
54
+ kind: 'origin_not_allowed',
55
+ title: 'The application tried to load an avatar experience from a website that isn’t allowed.',
56
+ detail: `Origin ${originLabel} is not allowed for experience ${idLabel}`,
57
+ technicalMessage,
58
+ experienceId,
59
+ origin
60
+ };
61
+ }
62
+ if (/browser_embeds_disabled/i.test(technicalMessage) ||
63
+ /^Browser embeds are not enabled/i.test(technicalMessage)) {
64
+ const idLabel = experienceId ?? 'the requested experience';
65
+ return {
66
+ kind: 'browser_embeds_disabled',
67
+ title: 'The application tried to load an avatar experience that isn’t enabled for browser embeds.',
68
+ detail: `Browser embeds are not enabled for experience ${idLabel}`,
69
+ technicalMessage,
70
+ experienceId,
71
+ origin
72
+ };
73
+ }
74
+ return {
75
+ kind: 'generic',
76
+ title: 'This avatar experience couldn’t be started.',
77
+ detail: technicalMessage,
78
+ technicalMessage,
79
+ experienceId,
80
+ origin
81
+ };
82
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { presentExperienceLoadError } from './experienceLoadError';
3
+ describe('presentExperienceLoadError', () => {
4
+ it('maps unavailable experiences to a friendly not-found presentation', () => {
5
+ const presentation = presentExperienceLoadError(new Error('No playable experience found for “exp_01MISSING”. Check that the experience id is correct.'), { experienceId: 'exp_01MISSING' });
6
+ expect(presentation.kind).toBe('not_found');
7
+ expect(presentation.title).toBe('The application tried to load an avatar experience that doesn’t exist.');
8
+ expect(presentation.detail).toBe('Experience with id exp_01MISSING does not exist');
9
+ });
10
+ it('maps origin denials to a friendly origin presentation', () => {
11
+ const presentation = presentExperienceLoadError(new Error('Your origin http://localhost:5173 is not an allowed origin for experience exp_01DEMO. If you own this experience, sign in to the Liforma developer portal.'), { experienceId: 'exp_01DEMO', origin: 'http://localhost:5173' });
12
+ expect(presentation.kind).toBe('origin_not_allowed');
13
+ expect(presentation.title).toBe('The application tried to load an avatar experience from a website that isn’t allowed.');
14
+ expect(presentation.detail).toBe('Origin http://localhost:5173 is not allowed for experience exp_01DEMO');
15
+ expect(presentation.experienceId).toBe('exp_01DEMO');
16
+ expect(presentation.origin).toBe('http://localhost:5173');
17
+ });
18
+ });
@@ -0,0 +1,7 @@
1
+ import type { ExperienceLoadErrorPresentation } from './experienceLoadError';
2
+ export type ExperienceLoadErrorOverlay = {
3
+ readonly root: HTMLDivElement;
4
+ setPresentation(presentation: ExperienceLoadErrorPresentation): void;
5
+ remove(): void;
6
+ };
7
+ export declare function createExperienceLoadErrorOverlay(container: HTMLElement, initial: ExperienceLoadErrorPresentation): ExperienceLoadErrorOverlay;