@vctrl/embed 0.25.0 → 1.0.0
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 +34 -0
- package/README.md +27 -35
- package/embed.d.ts +34 -1
- package/index.cjs +1 -1
- package/index.d.ts +1 -1
- package/index.js +49 -16
- package/package.json +6 -2
- package/protocol.d.ts +25 -0
- package/vectreal-embed.umd.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0](https://github.com/Vectreal/vectreal-platform/compare/embed-v0.25.1...embed-v1.0.0) (2026-09-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* **embed:** `InfoPopoverVectrealFooter` is no longer exported from `@vctrl/viewer`. The info-popover primitives now carry no branding of their own, so a consumer importing it must delete the import and supply their own footer if they want one. The Vectreal mark on hosted embeds is rendered by the platform instead, and is gated on the scene owner's plan.
|
|
9
|
+
* **hooks:** `ShadowsProps` is a plain interface rather than a union discriminated on `type`, so a consumer still passing `type: 'accumulative'` is passing an unknown property.
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **embed:** author-controlled info popover, plan-gated Vectreal mark ([#828](https://github.com/Vectreal/vectreal-platform/issues/828)) ([d557acb](https://github.com/Vectreal/vectreal-platform/commit/d557acb55ae04f878ee30838052accb3b76767e1))
|
|
14
|
+
* **hotspots:** let a hotspot say something ([#831](https://github.com/Vectreal/vectreal-platform/issues/831)) ([d4c9b3d](https://github.com/Vectreal/vectreal-platform/commit/d4c9b3d1d38d3cdd6e2e268b5e9732d3ad7b2970))
|
|
15
|
+
* **viewer:** play glTF animation clips ([11ff088](https://github.com/Vectreal/vectreal-platform/commit/11ff0880eb32ef0bda03196fe74de4955452d2f1))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **build:** make the package typecheck gates actually compile something ([#723](https://github.com/Vectreal/vectreal-platform/issues/723)) ([38c6487](https://github.com/Vectreal/vectreal-platform/commit/38c64879a00c1774df970effa09cff38396233ec))
|
|
21
|
+
* **hooks:** keep a failed upload from hiding a failed scene load ([5e9676f](https://github.com/Vectreal/vectreal-platform/commit/5e9676f01f61aa53ab74ec3e3fb2d78283fe7045))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Dependencies
|
|
25
|
+
|
|
26
|
+
* The following workspace dependencies were updated
|
|
27
|
+
* devDependencies
|
|
28
|
+
* @vctrl/viewer bumped to 1.0.0
|
|
29
|
+
|
|
30
|
+
## [0.25.1](https://github.com/Vectreal/vectreal-platform/compare/embed-v0.25.0...embed-v0.25.1) (2026-08-08)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Miscellaneous Chores
|
|
34
|
+
|
|
35
|
+
* **embed:** Synchronize vectreal-monorepo versions
|
|
36
|
+
|
|
3
37
|
## [0.25.0](https://github.com/Vectreal/vectreal-platform/compare/embed-v0.24.1...embed-v0.25.0) (2026-08-04)
|
|
4
38
|
|
|
5
39
|
|
package/README.md
CHANGED
|
@@ -42,8 +42,9 @@ For production, pin a version (`@vctrl/embed@<version>`) and add a [Subresource
|
|
|
42
42
|
|
|
43
43
|
const embed = new VectrealEmbed(document.getElementById('vectreal-scene'))
|
|
44
44
|
|
|
45
|
-
const { cameras } = await embed.ready()
|
|
45
|
+
const { cameras, hotspots } = await embed.ready()
|
|
46
46
|
console.log('Available cameras:', cameras)
|
|
47
|
+
console.log('Hotspots a visitor can see:', hotspots)
|
|
47
48
|
|
|
48
49
|
embed.on('camera_changed', ({ cameraId }) => {
|
|
49
50
|
console.log('Camera changed to:', cameraId)
|
|
@@ -62,42 +63,33 @@ For production, pin a version (`@vctrl/embed@<version>`) and add a [Subresource
|
|
|
62
63
|
| `iframeOrigin` | `string` | Auto-detected from `iframe.src` | Expected iframe origin for postMessage security. |
|
|
63
64
|
| `readyTimeout` | `number` | `15000` | ms before `ready()` rejects. |
|
|
64
65
|
|
|
65
|
-
###
|
|
66
|
-
|
|
67
|
-
| Method | Description |
|
|
68
|
-
| -------------------------------- | -------------------------------------------------------------- |
|
|
69
|
-
| `ready()` | Resolves with `{ sceneId, cameras }` when the viewer is ready. |
|
|
70
|
-
| `activateCamera(cameraId)` | Switch to a named camera. |
|
|
71
|
-
| `setTransition(options)` | Override transition type, duration, and easing. |
|
|
72
|
-
| `setControlsEnabled(enabled)` | Enable or disable orbit controls. |
|
|
73
|
-
| `setAutoRotate(enabled, speed?)` | Toggle auto-rotate. |
|
|
74
|
-
| `setZoomEnabled(enabled)` | Toggle scroll-zoom. |
|
|
75
|
-
| `setPanEnabled(enabled)` | Toggle right-click pan. |
|
|
76
|
-
| `sendScrollProgress(progress)` | Drive scroll-triggered interactions (0–1). |
|
|
77
|
-
| `sendMessage(message, payload?)` | Trigger a named `host_message` interaction. |
|
|
78
|
-
| `on(type, handler)` | Subscribe to a viewer event. Returns unsubscribe. |
|
|
79
|
-
| `off(type, handler)` | Remove a specific handler. |
|
|
80
|
-
| `destroy()` | Remove all listeners and stop processing messages. |
|
|
81
|
-
|
|
82
|
-
### Events
|
|
83
|
-
|
|
84
|
-
| Type | Payload | When |
|
|
85
|
-
| --------------------- | ----------------------------------------- | ------------------------------------------------------- |
|
|
86
|
-
| `viewer_ready` | `void` | Viewer command surface is registered. |
|
|
87
|
-
| `model_loaded` | `void` | Model finished loading and initial framing is complete. |
|
|
88
|
-
| `camera_changed` | `{ cameraId }` | Active camera changed. |
|
|
89
|
-
| `auto_rotate_changed` | `{ enabled }` | Auto-rotate state changed. |
|
|
90
|
-
| `interaction_event` | `{ eventName, interactionId?, payload? }` | Publisher custom event fired. |
|
|
66
|
+
### What you can control
|
|
91
67
|
|
|
92
|
-
|
|
68
|
+
Cameras and transitions, orbit/zoom/pan, animation playback, hotspots, scroll-driven
|
|
69
|
+
interactions, and named host messages - plus events for each. The full method and event
|
|
70
|
+
tables live in one place, the
|
|
71
|
+
[Embed SDK guide](https://vectreal.com/docs/guides/embed-sdk#api-reference), rather than
|
|
72
|
+
being repeated here where the two copies drift apart.
|
|
73
|
+
|
|
74
|
+
Two behaviours worth knowing before you read it:
|
|
75
|
+
|
|
76
|
+
The camera, controls, hotspot and animation methods are queued and flushed once the
|
|
77
|
+
viewer reports ready, so you can call them immediately after constructing the SDK.
|
|
78
|
+
`sendScrollProgress` and `sendMessage` are not queued: they post straight to the iframe,
|
|
79
|
+
and a call made before the viewer is ready is silently dropped. Await `ready()` before
|
|
80
|
+
wiring a scroll handler or sending a host message. `destroy()` discards anything still
|
|
81
|
+
queued.
|
|
93
82
|
|
|
94
|
-
|
|
83
|
+
The constructor throws when it cannot determine a target origin, which happens when
|
|
84
|
+
`iframeOrigin` is omitted and the iframe's `src` is empty or unparseable. Construct the
|
|
85
|
+
SDK after the `src` is set, or pass `iframeOrigin` explicitly.
|
|
86
|
+
|
|
87
|
+
## URL parameter shorthand
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| `?transition=<type>` | `?transition=linear` | Overrides stored transition type. |
|
|
89
|
+
Opening state, color scheme and hotspot presentation can be set with query parameters on the iframe
|
|
90
|
+
`src`, no JavaScript required — `?camera`, `?autoRotate`, `?transition`, `?theme`,
|
|
91
|
+
`?hotspots`, `?hotspotContent` and `?hotspotColor`. Values and behaviour are tabled once, in the
|
|
92
|
+
[Embed SDK guide](https://vectreal.com/docs/guides/embed-sdk#url-parameters).
|
|
101
93
|
|
|
102
94
|
## Documentation
|
|
103
95
|
|
|
@@ -105,6 +97,6 @@ Full guide and examples: [vectreal.com/docs/guides/embed-sdk](https://vectreal.c
|
|
|
105
97
|
|
|
106
98
|
## License
|
|
107
99
|
|
|
108
|
-
AGPL-3.0-only
|
|
100
|
+
AGPL-3.0-only. See [LICENSE.md](https://github.com/Vectreal/vectreal-platform/blob/main/LICENSE.md).
|
|
109
101
|
|
|
110
102
|
Part of the [Vectreal Platform](https://github.com/vectreal/vectreal-platform) monorepo.
|
package/embed.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EmbedCameraDescriptor } from './protocol';
|
|
1
|
+
import { EmbedCameraDescriptor, EmbedHotspotDescriptor } from './protocol';
|
|
2
2
|
export interface EmbedOptions {
|
|
3
3
|
/** Override the detected iframe origin for postMessage targeting. */
|
|
4
4
|
iframeOrigin?: string;
|
|
@@ -8,6 +8,11 @@ export interface EmbedOptions {
|
|
|
8
8
|
export interface EmbedReadyInfo {
|
|
9
9
|
sceneId: string | undefined;
|
|
10
10
|
cameras: EmbedCameraDescriptor[];
|
|
11
|
+
/**
|
|
12
|
+
* The hotspots a visitor can see. Empty for a scene with none, and also for
|
|
13
|
+
* an iframe older than this field - see `HostedPreviewPongMessage`.
|
|
14
|
+
*/
|
|
15
|
+
hotspots: EmbedHotspotDescriptor[];
|
|
11
16
|
}
|
|
12
17
|
export interface SetTransitionOptions {
|
|
13
18
|
type: 'none' | 'linear' | 'object_avoidance';
|
|
@@ -23,6 +28,20 @@ export type EmbedEventMap = {
|
|
|
23
28
|
auto_rotate_changed: {
|
|
24
29
|
enabled: boolean;
|
|
25
30
|
};
|
|
31
|
+
animation_state_changed: {
|
|
32
|
+
playing: boolean;
|
|
33
|
+
/** The clip driving a sequence; null in simultaneous mode. */
|
|
34
|
+
activeClipId: string | null;
|
|
35
|
+
/** True once the program has run to its end without looping. */
|
|
36
|
+
complete: boolean;
|
|
37
|
+
};
|
|
38
|
+
animation_clip_finished: {
|
|
39
|
+
clipId: string;
|
|
40
|
+
};
|
|
41
|
+
hotspot_activated: {
|
|
42
|
+
hotspotId: string;
|
|
43
|
+
cameraId: string | null;
|
|
44
|
+
};
|
|
26
45
|
interaction_event: {
|
|
27
46
|
interactionId?: string;
|
|
28
47
|
eventName: string;
|
|
@@ -50,6 +69,15 @@ export declare class VectrealEmbed {
|
|
|
50
69
|
destroy(): void;
|
|
51
70
|
/** Switch to a named camera. */
|
|
52
71
|
activateCamera(cameraId: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Focus a hotspot: reveal what it says and fly its camera, as clicking the
|
|
74
|
+
* marker would.
|
|
75
|
+
*
|
|
76
|
+
* Ids come from `ready()`, which reports only the hotspots a visitor can
|
|
77
|
+
* see. A hotspot the author hid or kept internal is not drawn, and naming
|
|
78
|
+
* one here does nothing.
|
|
79
|
+
*/
|
|
80
|
+
focusHotspot(hotspotId: string): void;
|
|
53
81
|
/** Override the transition behaviour for subsequent camera switches. */
|
|
54
82
|
setTransition(options: SetTransitionOptions): void;
|
|
55
83
|
/** Enable or disable orbit controls. */
|
|
@@ -60,6 +88,10 @@ export declare class VectrealEmbed {
|
|
|
60
88
|
setZoomEnabled(enabled: boolean): void;
|
|
61
89
|
/** Toggle right-click pan. */
|
|
62
90
|
setPanEnabled(enabled: boolean): void;
|
|
91
|
+
/** Start or suspend playback of the scene's animation clips. */
|
|
92
|
+
setAnimationPlaying(playing: boolean): void;
|
|
93
|
+
/** Play the animation program again from the beginning. */
|
|
94
|
+
restartAnimation(): void;
|
|
63
95
|
/**
|
|
64
96
|
* Drive scroll-triggered interactions defined in the Publisher.
|
|
65
97
|
* @param progress 0 (page top) – 1 (page bottom)
|
|
@@ -75,6 +107,7 @@ export declare class VectrealEmbed {
|
|
|
75
107
|
off<K extends EmbedEventType>(type: K, handler: EmbedEventHandler<K>): void;
|
|
76
108
|
private sceneId;
|
|
77
109
|
private cameras;
|
|
110
|
+
private hotspots;
|
|
78
111
|
private sendCommand;
|
|
79
112
|
private postToIframe;
|
|
80
113
|
private startPingPolling;
|
package/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`vectreal-host`,t=`vectreal-preview`;function n(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function r(e){if(!n(e)||typeof e.type!=`string`)return!1;switch(e.type){case`activate_camera`:return typeof e.cameraId==`string`&&e.cameraId.trim().length>0;case`set_controls_enabled`:return typeof e.enabled==`boolean`;case`set_auto_rotate`:return typeof e.enabled==`boolean`&&(e.speed===void 0||typeof e.speed==`number`);case`set_controls_options`:return(e.zoom===void 0||typeof e.zoom==`boolean`)&&(e.pan===void 0||typeof e.pan==`boolean`);case`set_transition`:return typeof e.transitionType==`string`&&[`none`,`linear`,`object_avoidance`].includes(e.transitionType);default:return!1}}function i(e){if(!n(e)||e.source!==`vectreal-host`||typeof e.type!=`string`)return!1;switch(e.type){case`ping`:return!0;case`viewer_command`:return r(e.command);case`host_scroll_progress`:return typeof e.progress==`number`&&Number.isFinite(e.progress);case`host_message`:return typeof e.message==`string`&&e.message.trim().length>0&&(e.payload===void 0||n(e.payload));default:return!1}}var a=15e3,o=class{constructor(e,t={}){this.handlers=new Map,this.pendingCommands=[],this.isReady=!1,this.pingIntervalId=null,this.sceneId=void 0,this.cameras=[],this.iframe=e,this.readyTimeout=t.readyTimeout??a;let n=e.src||e.getAttribute(`src`)||``,r;if(n)try{r=new URL(n,window.location.href).origin}catch{}if(!t.iframeOrigin&&!r)throw Error(`VectrealEmbed: cannot determine iframe origin from src. Pass options.iframeOrigin explicitly.`);this.targetOrigin=t.iframeOrigin??r,this.boundListener=this.handleMessage.bind(this),window.addEventListener(`message`,this.boundListener),this.startPingPolling()}ready(){return new Promise((e,t)=>{let n=window.setTimeout(()=>{this.off(`viewer_ready`,r),t(Error(`VectrealEmbed: viewer did not become ready within ${this.readyTimeout}ms`))},this.readyTimeout),r=()=>{window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras})};if(this.isReady){window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras});return}this.on(`viewer_ready`,r)})}destroy(){this.stopPingPolling(),window.removeEventListener(`message`,this.boundListener),this.handlers.clear(),this.pendingCommands=[]}activateCamera(e){this.sendCommand({type:`activate_camera`,cameraId:e})}setTransition(e){this.sendCommand({type:`set_transition`,transitionType:e.type,duration:e.duration,easing:e.easing})}setControlsEnabled(e){this.sendCommand({type:`set_controls_enabled`,enabled:e})}setAutoRotate(e,t){this.sendCommand({type:`set_auto_rotate`,enabled:e,speed:t})}setZoomEnabled(e){this.sendCommand({type:`set_controls_options`,zoom:e})}setPanEnabled(e){this.sendCommand({type:`set_controls_options`,pan:e})}sendScrollProgress(t){this.postToIframe({source:e,type:`host_scroll_progress`,progress:Math.min(1,Math.max(0,t))})}sendMessage(t,n){this.postToIframe({source:e,type:`host_message`,message:t,payload:n})}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}sendCommand(t){let n={source:e,type:`viewer_command`,command:t};if(!this.isReady){this.pendingCommands.push(n);return}this.postToIframe(n)}postToIframe(e){try{this.iframe.contentWindow?.postMessage(e,this.targetOrigin)}catch{}}startPingPolling(){this.postToIframe({source:e,type:`ping`}),this.pingIntervalId=window.setInterval(()=>{if(this.isReady){this.stopPingPolling();return}this.postToIframe({source:e,type:`ping`})},500)}stopPingPolling(){this.pingIntervalId!==null&&(window.clearInterval(this.pingIntervalId),this.pingIntervalId=null)}flushPendingCommands(){for(let e of this.pendingCommands)this.postToIframe(e);this.pendingCommands=[]}emit(e,t){this.handlers.get(e)?.forEach(e=>{e(t)})}handleMessage(e){let t=e.data;if(!(!t||typeof t!=`object`||t.source!==`vectreal-preview`)
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=`vectreal-host`,t=`vectreal-preview`;function n(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function r(e){if(!n(e)||typeof e.type!=`string`)return!1;switch(e.type){case`activate_camera`:return typeof e.cameraId==`string`&&e.cameraId.trim().length>0;case`focus_hotspot`:return typeof e.hotspotId==`string`&&e.hotspotId.trim().length>0;case`set_controls_enabled`:return typeof e.enabled==`boolean`;case`set_auto_rotate`:return typeof e.enabled==`boolean`&&(e.speed===void 0||typeof e.speed==`number`);case`set_controls_options`:return(e.zoom===void 0||typeof e.zoom==`boolean`)&&(e.pan===void 0||typeof e.pan==`boolean`);case`set_transition`:return typeof e.transitionType==`string`&&[`none`,`linear`,`object_avoidance`].includes(e.transitionType);case`set_animation_playing`:return typeof e.playing==`boolean`;case`restart_animation`:return!0;case`seek_animation_clip`:return typeof e.clipId==`string`&&e.clipId.trim().length>0&&typeof e.time==`number`&&Number.isFinite(e.time)&&e.time>=0;default:return!1}}function i(e){if(!n(e)||e.source!==`vectreal-host`||typeof e.type!=`string`)return!1;switch(e.type){case`ping`:return!0;case`viewer_command`:return r(e.command);case`host_scroll_progress`:return typeof e.progress==`number`&&Number.isFinite(e.progress);case`host_message`:return typeof e.message==`string`&&e.message.trim().length>0&&(e.payload===void 0||n(e.payload));default:return!1}}var a=15e3,o=class{constructor(e,t={}){this.handlers=new Map,this.pendingCommands=[],this.isReady=!1,this.pingIntervalId=null,this.sceneId=void 0,this.cameras=[],this.hotspots=[],this.iframe=e,this.readyTimeout=t.readyTimeout??a;let n=e.src||e.getAttribute(`src`)||``,r;if(n)try{r=new URL(n,window.location.href).origin}catch{}if(!t.iframeOrigin&&!r)throw Error(`VectrealEmbed: cannot determine iframe origin from src. Pass options.iframeOrigin explicitly.`);this.targetOrigin=t.iframeOrigin??r,this.boundListener=this.handleMessage.bind(this),window.addEventListener(`message`,this.boundListener),this.startPingPolling()}ready(){return new Promise((e,t)=>{let n=window.setTimeout(()=>{this.off(`viewer_ready`,r),t(Error(`VectrealEmbed: viewer did not become ready within ${this.readyTimeout}ms`))},this.readyTimeout),r=()=>{window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras,hotspots:this.hotspots})};if(this.isReady){window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras,hotspots:this.hotspots});return}this.on(`viewer_ready`,r)})}destroy(){this.stopPingPolling(),window.removeEventListener(`message`,this.boundListener),this.handlers.clear(),this.pendingCommands=[]}activateCamera(e){this.sendCommand({type:`activate_camera`,cameraId:e})}focusHotspot(e){this.sendCommand({type:`focus_hotspot`,hotspotId:e})}setTransition(e){this.sendCommand({type:`set_transition`,transitionType:e.type,duration:e.duration,easing:e.easing})}setControlsEnabled(e){this.sendCommand({type:`set_controls_enabled`,enabled:e})}setAutoRotate(e,t){this.sendCommand({type:`set_auto_rotate`,enabled:e,speed:t})}setZoomEnabled(e){this.sendCommand({type:`set_controls_options`,zoom:e})}setPanEnabled(e){this.sendCommand({type:`set_controls_options`,pan:e})}setAnimationPlaying(e){this.sendCommand({type:`set_animation_playing`,playing:e})}restartAnimation(){this.sendCommand({type:`restart_animation`})}sendScrollProgress(t){this.postToIframe({source:e,type:`host_scroll_progress`,progress:Math.min(1,Math.max(0,t))})}sendMessage(t,n){this.postToIframe({source:e,type:`host_message`,message:t,payload:n})}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}sendCommand(t){let n={source:e,type:`viewer_command`,command:t};if(!this.isReady){this.pendingCommands.push(n);return}this.postToIframe(n)}postToIframe(e){try{this.iframe.contentWindow?.postMessage(e,this.targetOrigin)}catch{}}startPingPolling(){this.postToIframe({source:e,type:`ping`}),this.pingIntervalId=window.setInterval(()=>{if(this.isReady){this.stopPingPolling();return}this.postToIframe({source:e,type:`ping`})},500)}stopPingPolling(){this.pingIntervalId!==null&&(window.clearInterval(this.pingIntervalId),this.pingIntervalId=null)}flushPendingCommands(){for(let e of this.pendingCommands)this.postToIframe(e);this.pendingCommands=[]}emit(e,t){this.handlers.get(e)?.forEach(e=>{e(t)})}handleMessage(e){let t=e.data;if(!(!t||typeof t!=`object`||t.source!==`vectreal-preview`)&&(this.targetOrigin===`*`||e.origin===this.targetOrigin))switch(t.type){case`pong`:this.stopPingPolling(),this.sceneId=t.sceneId,this.cameras=t.cameras,this.hotspots=t.hotspots??[],this.isReady=!0,this.flushPendingCommands();break;case`viewer_event`:this.handleViewerEvent(t.event);break;case`interaction_event`:this.emit(`interaction_event`,{interactionId:t.interactionId,eventName:t.eventName,payload:t.payload})}}handleViewerEvent(e){switch(e.type){case`viewer_ready`:this.isReady=!0,this.flushPendingCommands(),this.emit(`viewer_ready`,void 0);break;case`model_loaded`:this.emit(`model_loaded`,void 0);break;case`camera_changed`:this.emit(`camera_changed`,{cameraId:e.cameraId});break;case`hotspot_activated`:this.emit(`hotspot_activated`,{hotspotId:e.hotspotId,cameraId:e.cameraId});break;case`auto_rotate_changed`:this.emit(`auto_rotate_changed`,{enabled:e.enabled});break;case`animation_state_changed`:this.emit(`animation_state_changed`,{playing:e.playing,activeClipId:e.activeClipId,complete:e.complete});break;case`animation_clip_finished`:this.emit(`animation_clip_finished`,{clipId:e.clipId});break;case`initial_framing_completed`:this.emit(`model_loaded`,void 0)}}};exports.HOSTED_PREVIEW_HOST_SOURCE=e,exports.HOSTED_PREVIEW_VIEWER_SOURCE=t,exports.VectrealEmbed=o,exports.isHostedPreviewIncomingMessage=i,exports.isViewerCommand=r;
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { VectrealEmbed } from './embed';
|
|
2
2
|
export type { EmbedEventHandler, EmbedEventMap, EmbedEventType, EmbedOptions, EmbedReadyInfo, SetTransitionOptions } from './embed';
|
|
3
3
|
export { HOSTED_PREVIEW_HOST_SOURCE, HOSTED_PREVIEW_VIEWER_SOURCE, isHostedPreviewIncomingMessage, isViewerCommand } from './protocol';
|
|
4
|
-
export type { EmbedCameraDescriptor, HostedPreviewCustomEventMessage, HostedPreviewHostMessage, HostedPreviewIncomingMessage, HostedPreviewOutgoingMessage, HostedPreviewPingMessage, HostedPreviewPongMessage, HostedPreviewScrollProgressMessage, HostedPreviewViewerCommandMessage, HostedPreviewViewerEventMessage } from './protocol';
|
|
4
|
+
export type { EmbedCameraDescriptor, EmbedHotspotDescriptor, HostedPreviewCustomEventMessage, HostedPreviewHostMessage, HostedPreviewIncomingMessage, HostedPreviewOutgoingMessage, HostedPreviewPingMessage, HostedPreviewPongMessage, HostedPreviewScrollProgressMessage, HostedPreviewViewerCommandMessage, HostedPreviewViewerEventMessage } from './protocol';
|
package/index.js
CHANGED
|
@@ -7,6 +7,7 @@ function r(e) {
|
|
|
7
7
|
if (!n(e) || typeof e.type != "string") return !1;
|
|
8
8
|
switch (e.type) {
|
|
9
9
|
case "activate_camera": return typeof e.cameraId == "string" && e.cameraId.trim().length > 0;
|
|
10
|
+
case "focus_hotspot": return typeof e.hotspotId == "string" && e.hotspotId.trim().length > 0;
|
|
10
11
|
case "set_controls_enabled": return typeof e.enabled == "boolean";
|
|
11
12
|
case "set_auto_rotate": return typeof e.enabled == "boolean" && (e.speed === void 0 || typeof e.speed == "number");
|
|
12
13
|
case "set_controls_options": return (e.zoom === void 0 || typeof e.zoom == "boolean") && (e.pan === void 0 || typeof e.pan == "boolean");
|
|
@@ -15,6 +16,9 @@ function r(e) {
|
|
|
15
16
|
"linear",
|
|
16
17
|
"object_avoidance"
|
|
17
18
|
].includes(e.transitionType);
|
|
19
|
+
case "set_animation_playing": return typeof e.playing == "boolean";
|
|
20
|
+
case "restart_animation": return !0;
|
|
21
|
+
case "seek_animation_clip": return typeof e.clipId == "string" && e.clipId.trim().length > 0 && typeof e.time == "number" && Number.isFinite(e.time) && e.time >= 0;
|
|
18
22
|
default: return !1;
|
|
19
23
|
}
|
|
20
24
|
}
|
|
@@ -30,9 +34,9 @@ function i(e) {
|
|
|
30
34
|
}
|
|
31
35
|
//#endregion
|
|
32
36
|
//#region packages/embed/src/embed.ts
|
|
33
|
-
var a =
|
|
37
|
+
var a = class {
|
|
34
38
|
constructor(e, t = {}) {
|
|
35
|
-
this.handlers = /* @__PURE__ */ new Map(), this.pendingCommands = [], this.isReady = !1, this.pingIntervalId = null, this.sceneId = void 0, this.cameras = [], this.iframe = e, this.readyTimeout = t.readyTimeout ??
|
|
39
|
+
this.handlers = /* @__PURE__ */ new Map(), this.pendingCommands = [], this.isReady = !1, this.pingIntervalId = null, this.sceneId = void 0, this.cameras = [], this.hotspots = [], this.iframe = e, this.readyTimeout = t.readyTimeout ?? 15e3;
|
|
36
40
|
let n = e.src || e.getAttribute("src") || "", r;
|
|
37
41
|
if (n) try {
|
|
38
42
|
r = new URL(n, window.location.href).origin;
|
|
@@ -47,13 +51,15 @@ var a = 15e3, o = class {
|
|
|
47
51
|
}, this.readyTimeout), r = () => {
|
|
48
52
|
window.clearTimeout(n), e({
|
|
49
53
|
sceneId: this.sceneId,
|
|
50
|
-
cameras: this.cameras
|
|
54
|
+
cameras: this.cameras,
|
|
55
|
+
hotspots: this.hotspots
|
|
51
56
|
});
|
|
52
57
|
};
|
|
53
58
|
if (this.isReady) {
|
|
54
59
|
window.clearTimeout(n), e({
|
|
55
60
|
sceneId: this.sceneId,
|
|
56
|
-
cameras: this.cameras
|
|
61
|
+
cameras: this.cameras,
|
|
62
|
+
hotspots: this.hotspots
|
|
57
63
|
});
|
|
58
64
|
return;
|
|
59
65
|
}
|
|
@@ -69,6 +75,12 @@ var a = 15e3, o = class {
|
|
|
69
75
|
cameraId: e
|
|
70
76
|
});
|
|
71
77
|
}
|
|
78
|
+
focusHotspot(e) {
|
|
79
|
+
this.sendCommand({
|
|
80
|
+
type: "focus_hotspot",
|
|
81
|
+
hotspotId: e
|
|
82
|
+
});
|
|
83
|
+
}
|
|
72
84
|
setTransition(e) {
|
|
73
85
|
this.sendCommand({
|
|
74
86
|
type: "set_transition",
|
|
@@ -102,6 +114,15 @@ var a = 15e3, o = class {
|
|
|
102
114
|
pan: e
|
|
103
115
|
});
|
|
104
116
|
}
|
|
117
|
+
setAnimationPlaying(e) {
|
|
118
|
+
this.sendCommand({
|
|
119
|
+
type: "set_animation_playing",
|
|
120
|
+
playing: e
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
restartAnimation() {
|
|
124
|
+
this.sendCommand({ type: "restart_animation" });
|
|
125
|
+
}
|
|
105
126
|
sendScrollProgress(t) {
|
|
106
127
|
this.postToIframe({
|
|
107
128
|
source: e,
|
|
@@ -169,20 +190,18 @@ var a = 15e3, o = class {
|
|
|
169
190
|
}
|
|
170
191
|
handleMessage(e) {
|
|
171
192
|
let t = e.data;
|
|
172
|
-
if (!(!t || typeof t != "object" || t.source !== "vectreal-preview") &&
|
|
193
|
+
if (!(!t || typeof t != "object" || t.source !== "vectreal-preview") && (this.targetOrigin === "*" || e.origin === this.targetOrigin)) switch (t.type) {
|
|
173
194
|
case "pong":
|
|
174
|
-
this.stopPingPolling(), this.sceneId = t.sceneId, this.cameras = t.cameras, this.isReady = !0, this.flushPendingCommands();
|
|
195
|
+
this.stopPingPolling(), this.sceneId = t.sceneId, this.cameras = t.cameras, this.hotspots = t.hotspots ?? [], this.isReady = !0, this.flushPendingCommands();
|
|
175
196
|
break;
|
|
176
197
|
case "viewer_event":
|
|
177
198
|
this.handleViewerEvent(t.event);
|
|
178
199
|
break;
|
|
179
|
-
case "interaction_event":
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
break;
|
|
200
|
+
case "interaction_event": this.emit("interaction_event", {
|
|
201
|
+
interactionId: t.interactionId,
|
|
202
|
+
eventName: t.eventName,
|
|
203
|
+
payload: t.payload
|
|
204
|
+
});
|
|
186
205
|
}
|
|
187
206
|
}
|
|
188
207
|
handleViewerEvent(e) {
|
|
@@ -196,14 +215,28 @@ var a = 15e3, o = class {
|
|
|
196
215
|
case "camera_changed":
|
|
197
216
|
this.emit("camera_changed", { cameraId: e.cameraId });
|
|
198
217
|
break;
|
|
218
|
+
case "hotspot_activated":
|
|
219
|
+
this.emit("hotspot_activated", {
|
|
220
|
+
hotspotId: e.hotspotId,
|
|
221
|
+
cameraId: e.cameraId
|
|
222
|
+
});
|
|
223
|
+
break;
|
|
199
224
|
case "auto_rotate_changed":
|
|
200
225
|
this.emit("auto_rotate_changed", { enabled: e.enabled });
|
|
201
226
|
break;
|
|
202
|
-
case "
|
|
203
|
-
this.emit("
|
|
227
|
+
case "animation_state_changed":
|
|
228
|
+
this.emit("animation_state_changed", {
|
|
229
|
+
playing: e.playing,
|
|
230
|
+
activeClipId: e.activeClipId,
|
|
231
|
+
complete: e.complete
|
|
232
|
+
});
|
|
233
|
+
break;
|
|
234
|
+
case "animation_clip_finished":
|
|
235
|
+
this.emit("animation_clip_finished", { clipId: e.clipId });
|
|
204
236
|
break;
|
|
237
|
+
case "initial_framing_completed": this.emit("model_loaded", void 0);
|
|
205
238
|
}
|
|
206
239
|
}
|
|
207
240
|
};
|
|
208
241
|
//#endregion
|
|
209
|
-
export { e as HOSTED_PREVIEW_HOST_SOURCE, t as HOSTED_PREVIEW_VIEWER_SOURCE,
|
|
242
|
+
export { e as HOSTED_PREVIEW_HOST_SOURCE, t as HOSTED_PREVIEW_VIEWER_SOURCE, a as VectrealEmbed, i as isHostedPreviewIncomingMessage, r as isViewerCommand };
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "1.0.0",
|
|
3
3
|
"name": "@vctrl/embed",
|
|
4
4
|
"description": "Framework-agnostic JavaScript SDK for controlling Vectreal embedded 3D scene previews via postMessage.",
|
|
5
5
|
"bugs": {
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"type": "module",
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"publishConfig": {
|
|
32
|
-
"access": "public"
|
|
32
|
+
"access": "public",
|
|
33
|
+
"directory": "../../build/packages/vctrl/embed"
|
|
33
34
|
},
|
|
34
35
|
"exports": {
|
|
35
36
|
".": {
|
|
@@ -37,5 +38,8 @@
|
|
|
37
38
|
"import": "./index.js",
|
|
38
39
|
"require": "./index.cjs"
|
|
39
40
|
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@vctrl/viewer": "^1.0.0"
|
|
40
44
|
}
|
|
41
45
|
}
|
package/protocol.d.ts
CHANGED
|
@@ -27,11 +27,36 @@ export interface EmbedCameraDescriptor {
|
|
|
27
27
|
name: string;
|
|
28
28
|
fov?: number;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* One hotspot, as a host page sees it.
|
|
32
|
+
*
|
|
33
|
+
* Carries no body, no link and no world position: a host builds navigation
|
|
34
|
+
* from these, and the content is what the viewer draws. Adding the text here
|
|
35
|
+
* would put a second copy of it on the page with nothing keeping the two in
|
|
36
|
+
* step.
|
|
37
|
+
*/
|
|
38
|
+
export interface EmbedHotspotDescriptor {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
/** The camera this hotspot flies, or null when it only reveals content. */
|
|
42
|
+
cameraId: string | null;
|
|
43
|
+
/** 1-based place in the navigation sequence, or null when it has none. */
|
|
44
|
+
step: number | null;
|
|
45
|
+
}
|
|
30
46
|
export interface HostedPreviewPongMessage {
|
|
31
47
|
source: typeof HOSTED_PREVIEW_VIEWER_SOURCE;
|
|
32
48
|
type: 'pong';
|
|
33
49
|
sceneId?: string;
|
|
34
50
|
cameras: EmbedCameraDescriptor[];
|
|
51
|
+
/**
|
|
52
|
+
* Optional, unlike `cameras`.
|
|
53
|
+
*
|
|
54
|
+
* An iframe and the SDK on the page around it are two separately deployed
|
|
55
|
+
* artifacts and can be versions apart. A required field here would make an
|
|
56
|
+
* older iframe's pong fail a newer SDK's parse, and the host would then hang
|
|
57
|
+
* waiting for a handshake that already arrived.
|
|
58
|
+
*/
|
|
59
|
+
hotspots?: EmbedHotspotDescriptor[];
|
|
35
60
|
}
|
|
36
61
|
export interface HostedPreviewViewerEventMessage {
|
|
37
62
|
source: typeof HOSTED_PREVIEW_VIEWER_SOURCE;
|
package/vectreal-embed.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.VectrealEmbed={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`vectreal-host`,n=`vectreal-preview`;function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e){if(!r(e)||typeof e.type!=`string`)return!1;switch(e.type){case`activate_camera`:return typeof e.cameraId==`string`&&e.cameraId.trim().length>0;case`set_controls_enabled`:return typeof e.enabled==`boolean`;case`set_auto_rotate`:return typeof e.enabled==`boolean`&&(e.speed===void 0||typeof e.speed==`number`);case`set_controls_options`:return(e.zoom===void 0||typeof e.zoom==`boolean`)&&(e.pan===void 0||typeof e.pan==`boolean`);case`set_transition`:return typeof e.transitionType==`string`&&[`none`,`linear`,`object_avoidance`].includes(e.transitionType);default:return!1}}function a(e){if(!r(e)||e.source!==`vectreal-host`||typeof e.type!=`string`)return!1;switch(e.type){case`ping`:return!0;case`viewer_command`:return i(e.command);case`host_scroll_progress`:return typeof e.progress==`number`&&Number.isFinite(e.progress);case`host_message`:return typeof e.message==`string`&&e.message.trim().length>0&&(e.payload===void 0||r(e.payload));default:return!1}}var o=15e3,s=class{constructor(e,t={}){this.handlers=new Map,this.pendingCommands=[],this.isReady=!1,this.pingIntervalId=null,this.sceneId=void 0,this.cameras=[],this.iframe=e,this.readyTimeout=t.readyTimeout??o;let n=e.src||e.getAttribute(`src`)||``,r;if(n)try{r=new URL(n,window.location.href).origin}catch{}if(!t.iframeOrigin&&!r)throw Error(`VectrealEmbed: cannot determine iframe origin from src. Pass options.iframeOrigin explicitly.`);this.targetOrigin=t.iframeOrigin??r,this.boundListener=this.handleMessage.bind(this),window.addEventListener(`message`,this.boundListener),this.startPingPolling()}ready(){return new Promise((e,t)=>{let n=window.setTimeout(()=>{this.off(`viewer_ready`,r),t(Error(`VectrealEmbed: viewer did not become ready within ${this.readyTimeout}ms`))},this.readyTimeout),r=()=>{window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras})};if(this.isReady){window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras});return}this.on(`viewer_ready`,r)})}destroy(){this.stopPingPolling(),window.removeEventListener(`message`,this.boundListener),this.handlers.clear(),this.pendingCommands=[]}activateCamera(e){this.sendCommand({type:`activate_camera`,cameraId:e})}setTransition(e){this.sendCommand({type:`set_transition`,transitionType:e.type,duration:e.duration,easing:e.easing})}setControlsEnabled(e){this.sendCommand({type:`set_controls_enabled`,enabled:e})}setAutoRotate(e,t){this.sendCommand({type:`set_auto_rotate`,enabled:e,speed:t})}setZoomEnabled(e){this.sendCommand({type:`set_controls_options`,zoom:e})}setPanEnabled(e){this.sendCommand({type:`set_controls_options`,pan:e})}sendScrollProgress(e){this.postToIframe({source:t,type:`host_scroll_progress`,progress:Math.min(1,Math.max(0,e))})}sendMessage(e,n){this.postToIframe({source:t,type:`host_message`,message:e,payload:n})}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}sendCommand(e){let n={source:t,type:`viewer_command`,command:e};if(!this.isReady){this.pendingCommands.push(n);return}this.postToIframe(n)}postToIframe(e){try{this.iframe.contentWindow?.postMessage(e,this.targetOrigin)}catch{}}startPingPolling(){this.postToIframe({source:t,type:`ping`}),this.pingIntervalId=window.setInterval(()=>{if(this.isReady){this.stopPingPolling();return}this.postToIframe({source:t,type:`ping`})},500)}stopPingPolling(){this.pingIntervalId!==null&&(window.clearInterval(this.pingIntervalId),this.pingIntervalId=null)}flushPendingCommands(){for(let e of this.pendingCommands)this.postToIframe(e);this.pendingCommands=[]}emit(e,t){this.handlers.get(e)?.forEach(e=>{e(t)})}handleMessage(e){let t=e.data;if(!(!t||typeof t!=`object`||t.source!==`vectreal-preview`)
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.VectrealEmbed={}))})(this,function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=`vectreal-host`,n=`vectreal-preview`;function r(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function i(e){if(!r(e)||typeof e.type!=`string`)return!1;switch(e.type){case`activate_camera`:return typeof e.cameraId==`string`&&e.cameraId.trim().length>0;case`focus_hotspot`:return typeof e.hotspotId==`string`&&e.hotspotId.trim().length>0;case`set_controls_enabled`:return typeof e.enabled==`boolean`;case`set_auto_rotate`:return typeof e.enabled==`boolean`&&(e.speed===void 0||typeof e.speed==`number`);case`set_controls_options`:return(e.zoom===void 0||typeof e.zoom==`boolean`)&&(e.pan===void 0||typeof e.pan==`boolean`);case`set_transition`:return typeof e.transitionType==`string`&&[`none`,`linear`,`object_avoidance`].includes(e.transitionType);case`set_animation_playing`:return typeof e.playing==`boolean`;case`restart_animation`:return!0;case`seek_animation_clip`:return typeof e.clipId==`string`&&e.clipId.trim().length>0&&typeof e.time==`number`&&Number.isFinite(e.time)&&e.time>=0;default:return!1}}function a(e){if(!r(e)||e.source!==`vectreal-host`||typeof e.type!=`string`)return!1;switch(e.type){case`ping`:return!0;case`viewer_command`:return i(e.command);case`host_scroll_progress`:return typeof e.progress==`number`&&Number.isFinite(e.progress);case`host_message`:return typeof e.message==`string`&&e.message.trim().length>0&&(e.payload===void 0||r(e.payload));default:return!1}}var o=15e3,s=class{constructor(e,t={}){this.handlers=new Map,this.pendingCommands=[],this.isReady=!1,this.pingIntervalId=null,this.sceneId=void 0,this.cameras=[],this.hotspots=[],this.iframe=e,this.readyTimeout=t.readyTimeout??o;let n=e.src||e.getAttribute(`src`)||``,r;if(n)try{r=new URL(n,window.location.href).origin}catch{}if(!t.iframeOrigin&&!r)throw Error(`VectrealEmbed: cannot determine iframe origin from src. Pass options.iframeOrigin explicitly.`);this.targetOrigin=t.iframeOrigin??r,this.boundListener=this.handleMessage.bind(this),window.addEventListener(`message`,this.boundListener),this.startPingPolling()}ready(){return new Promise((e,t)=>{let n=window.setTimeout(()=>{this.off(`viewer_ready`,r),t(Error(`VectrealEmbed: viewer did not become ready within ${this.readyTimeout}ms`))},this.readyTimeout),r=()=>{window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras,hotspots:this.hotspots})};if(this.isReady){window.clearTimeout(n),e({sceneId:this.sceneId,cameras:this.cameras,hotspots:this.hotspots});return}this.on(`viewer_ready`,r)})}destroy(){this.stopPingPolling(),window.removeEventListener(`message`,this.boundListener),this.handlers.clear(),this.pendingCommands=[]}activateCamera(e){this.sendCommand({type:`activate_camera`,cameraId:e})}focusHotspot(e){this.sendCommand({type:`focus_hotspot`,hotspotId:e})}setTransition(e){this.sendCommand({type:`set_transition`,transitionType:e.type,duration:e.duration,easing:e.easing})}setControlsEnabled(e){this.sendCommand({type:`set_controls_enabled`,enabled:e})}setAutoRotate(e,t){this.sendCommand({type:`set_auto_rotate`,enabled:e,speed:t})}setZoomEnabled(e){this.sendCommand({type:`set_controls_options`,zoom:e})}setPanEnabled(e){this.sendCommand({type:`set_controls_options`,pan:e})}setAnimationPlaying(e){this.sendCommand({type:`set_animation_playing`,playing:e})}restartAnimation(){this.sendCommand({type:`restart_animation`})}sendScrollProgress(e){this.postToIframe({source:t,type:`host_scroll_progress`,progress:Math.min(1,Math.max(0,e))})}sendMessage(e,n){this.postToIframe({source:t,type:`host_message`,message:e,payload:n})}on(e,t){return this.handlers.has(e)||this.handlers.set(e,new Set),this.handlers.get(e).add(t),()=>this.off(e,t)}off(e,t){this.handlers.get(e)?.delete(t)}sendCommand(e){let n={source:t,type:`viewer_command`,command:e};if(!this.isReady){this.pendingCommands.push(n);return}this.postToIframe(n)}postToIframe(e){try{this.iframe.contentWindow?.postMessage(e,this.targetOrigin)}catch{}}startPingPolling(){this.postToIframe({source:t,type:`ping`}),this.pingIntervalId=window.setInterval(()=>{if(this.isReady){this.stopPingPolling();return}this.postToIframe({source:t,type:`ping`})},500)}stopPingPolling(){this.pingIntervalId!==null&&(window.clearInterval(this.pingIntervalId),this.pingIntervalId=null)}flushPendingCommands(){for(let e of this.pendingCommands)this.postToIframe(e);this.pendingCommands=[]}emit(e,t){this.handlers.get(e)?.forEach(e=>{e(t)})}handleMessage(e){let t=e.data;if(!(!t||typeof t!=`object`||t.source!==`vectreal-preview`)&&(this.targetOrigin===`*`||e.origin===this.targetOrigin))switch(t.type){case`pong`:this.stopPingPolling(),this.sceneId=t.sceneId,this.cameras=t.cameras,this.hotspots=t.hotspots??[],this.isReady=!0,this.flushPendingCommands();break;case`viewer_event`:this.handleViewerEvent(t.event);break;case`interaction_event`:this.emit(`interaction_event`,{interactionId:t.interactionId,eventName:t.eventName,payload:t.payload})}}handleViewerEvent(e){switch(e.type){case`viewer_ready`:this.isReady=!0,this.flushPendingCommands(),this.emit(`viewer_ready`,void 0);break;case`model_loaded`:this.emit(`model_loaded`,void 0);break;case`camera_changed`:this.emit(`camera_changed`,{cameraId:e.cameraId});break;case`hotspot_activated`:this.emit(`hotspot_activated`,{hotspotId:e.hotspotId,cameraId:e.cameraId});break;case`auto_rotate_changed`:this.emit(`auto_rotate_changed`,{enabled:e.enabled});break;case`animation_state_changed`:this.emit(`animation_state_changed`,{playing:e.playing,activeClipId:e.activeClipId,complete:e.complete});break;case`animation_clip_finished`:this.emit(`animation_clip_finished`,{clipId:e.clipId});break;case`initial_framing_completed`:this.emit(`model_loaded`,void 0)}}};e.HOSTED_PREVIEW_HOST_SOURCE=t,e.HOSTED_PREVIEW_VIEWER_SOURCE=n,e.VectrealEmbed=s,e.isHostedPreviewIncomingMessage=a,e.isViewerCommand=i});
|