@scenar/embed 0.10.0 → 0.11.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/README.md CHANGED
@@ -64,6 +64,26 @@ const mount = createEmbedMount(iframeElement, {
64
64
  `theme` is `auto` (default), `light`, or `dark`. `auto` tracks the host page's
65
65
  `dark` class on `<html>` and re-themes the embed when it toggles.
66
66
 
67
+ ## Corners
68
+
69
+ The embed paints edge-to-edge; the host boundary owns the corner radius. Round
70
+ the React component's wrapper (via `className` or `style`) or the
71
+ `<scenar-embed>` element itself (via CSS `border-radius`) — the boundary
72
+ carries `overflow: hidden`, so everything inside clips to your radius:
73
+
74
+ ```tsx
75
+ <ScenarEmbed src="…" style={{ borderRadius: 12 }} />
76
+ ```
77
+
78
+ ```css
79
+ scenar-embed { border-radius: 12px; }
80
+ ```
81
+
82
+ Do not style the internal iframe. Under iframe-as-screen scaling it is laid
83
+ out at the tour's canonical viewport and transform-scaled to fit, so a radius
84
+ on the iframe would shrink with the scale factor instead of matching your
85
+ layout.
86
+
67
87
  ## License
68
88
 
69
89
  Apache-2.0
package/element.d.ts CHANGED
@@ -18,6 +18,13 @@ declare const HTMLElementBase: typeof HTMLElement;
18
18
  * - `title` — accessible iframe title (defaults to a generic label).
19
19
  * - `theme` — `auto` (default) | `light` | `dark`.
20
20
  *
21
+ * Corners: the element is the one clipping surface — it carries
22
+ * `overflow: hidden`, and the host rounds by styling `border-radius` on the
23
+ * element itself. The internal iframe must NOT carry a radius: under
24
+ * iframe-as-screen the mount lays it out at the canonical viewport and scales
25
+ * it down with a transform, so an iframe radius lives in pre-transform space
26
+ * and renders at `radius x scale` — a mismatched, mostly ineffective clip.
27
+ *
21
28
  * Events: re-dispatches every embed event as a DOM `CustomEvent` named
22
29
  * `scenar:<type>` (e.g. `scenar:ready`, `scenar:completed`) with the event in
23
30
  * `detail`, so a vanilla host can listen without touching `postMessage`.
package/element.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AAOA,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAK/C;;;;;GAKG;AACH,QAAA,MAAM,eAAe,EAAE,OAAO,WAGmB,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,kBAAmB,SAAQ,eAAe;IACrD,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAA2B;IAExC,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAU9F,IAAI,IAAI,IAAI;IAIZ,KAAK,IAAI,IAAI;IAIb,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,cAAc;IAItB,uDAAuD;IACvD,OAAO,CAAC,MAAM;IAkBd,2DAA2D;IAC3D,OAAO,CAAC,MAAM;CAaf;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAyB,GAAG,IAAI,CAI1E"}
1
+ {"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AAOA,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAK/C;;;;;GAKG;AACH,QAAA,MAAM,eAAe,EAAE,OAAO,WAGmB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,kBAAmB,SAAQ,eAAe;IACrD,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAA2B;IAExC,iBAAiB,IAAI,IAAI;IAKzB,oBAAoB,IAAI,IAAI;IAK5B,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAU9F,IAAI,IAAI,IAAI;IAIZ,KAAK,IAAI,IAAI;IAIb,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI1B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAI9B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,cAAc;IAItB,uDAAuD;IACvD,OAAO,CAAC,MAAM;IAkBd,2DAA2D;IAC3D,OAAO,CAAC,MAAM;CAaf;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAyB,GAAG,IAAI,CAI1E"}
package/element.js CHANGED
@@ -25,6 +25,13 @@ const HTMLElementBase = typeof HTMLElement !== "undefined"
25
25
  * - `title` — accessible iframe title (defaults to a generic label).
26
26
  * - `theme` — `auto` (default) | `light` | `dark`.
27
27
  *
28
+ * Corners: the element is the one clipping surface — it carries
29
+ * `overflow: hidden`, and the host rounds by styling `border-radius` on the
30
+ * element itself. The internal iframe must NOT carry a radius: under
31
+ * iframe-as-screen the mount lays it out at the canonical viewport and scales
32
+ * it down with a transform, so an iframe radius lives in pre-transform space
33
+ * and renders at `radius x scale` — a mismatched, mostly ineffective clip.
34
+ *
28
35
  * Events: re-dispatches every embed event as a DOM `CustomEvent` named
29
36
  * `scenar:<type>` (e.g. `scenar:ready`, `scenar:completed`) with the event in
30
37
  * `detail`, so a vanilla host can listen without touching `postMessage`.
@@ -89,14 +96,14 @@ export class ScenarEmbedElement extends HTMLElementBase {
89
96
  this.style.display = "block";
90
97
  this.style.position = "relative";
91
98
  this.style.width = "100%";
99
+ this.style.overflow = "hidden";
92
100
  this.setAspectRatio(EMBED_BASE_ASPECT_WIDTH, EMBED_BASE_ASPECT_HEIGHT);
93
101
  const iframe = document.createElement("iframe");
94
102
  iframe.setAttribute("title", this.titleAttr());
95
103
  iframe.setAttribute("loading", "lazy");
96
104
  iframe.setAttribute("allow", "autoplay; fullscreen");
97
105
  iframe.setAttribute("allowfullscreen", "");
98
- iframe.style.cssText =
99
- "position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit";
106
+ iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0";
100
107
  this.appendChild(iframe);
101
108
  this.iframe = iframe;
102
109
  }
package/element.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"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"}
package/embed.global.js CHANGED
@@ -1 +1 @@
1
- var ScenarEmbed=function(l){"use strict";var W=Object.defineProperty;var G=(l,c,m)=>c in l?W(l,c,{enumerable:!0,configurable:!0,writable:!0,value:m}):l[c]=m;var S=(l,c,m)=>G(l,typeof c!="symbol"?c+"":c,m);const c="scenar-embed";function M(t){return{source:c,v:1,...t}}function T(t){if(typeof t!="object"||t===null)return!1;const n=t;return n.source===c&&n.v===1}function u(t){return typeof t=="number"&&Number.isFinite(t)}function _(t){if(t===void 0)return{};if(typeof t!="object"||t===null)return null;const n=t;return u(n.widthPx)&&n.widthPx>0&&u(n.heightPx)&&n.heightPx>0?{viewport:{widthPx:n.widthPx,heightPx:n.heightPx}}:null}function O(t){if(!T(t))return null;const n=t.type;switch(n){case"started":case"paused":case"completed":case"audioBlocked":return{type:n};case"ready":{if(!u(t.totalSteps)||typeof t.hasNarration!="boolean")return null;const e=_(t.viewport);return e===null?null:{type:n,totalSteps:t.totalSteps,hasNarration:t.hasNarration,...e}}case"resize":return u(t.widthPx)&&u(t.heightPx)?{type:n,widthPx:t.widthPx,heightPx:t.heightPx}:null;case"stepchange":return u(t.stepIndex)&&u(t.totalSteps)?{type:n,stepIndex:t.stepIndex,totalSteps:t.totalSteps}:null;case"progress":return u(t.stepIndex)&&u(t.totalSteps)&&u(t.fraction)?{type:n,stepIndex:t.stepIndex,totalSteps:t.totalSteps,fraction:t.fraction}:null;case"error":return typeof t.message=="string"?{type:n,message:t.message}:null;default:return null}}function k(t,n={}){const{iframe:e,origin:s}=t,{onEvent:o}=n,d=typeof window<"u",a=r=>{var h;(h=e.contentWindow)==null||h.postMessage(M(r),s)},E=r=>{if(r.source!==e.contentWindow||r.origin!==s)return;const h=O(r.data);h&&(o==null||o(h))};return d&&window.addEventListener("message",E),{play:()=>a({type:"play"}),pause:()=>a({type:"pause"}),seek:r=>a({type:"seek",timeMs:r}),setMuted:r=>a({type:"setMuted",muted:r}),setVolume:r=>a({type:"setVolume",volume:r}),setHostScale:r=>a({type:"setHostScale",scale:r}),prefetch:()=>a({type:"prefetch"}),destroy:()=>{a({type:"destroy"}),d&&window.removeEventListener("message",E)}}}const L=896,I=480;function B(t,n){return t==="light"?"light":t==="dark"||n?"dark":"light"}function N(t,n){const e=new URL(t);return e.searchParams.set("theme",n),e.toString()}function V(t){return new URL(t).origin}function v(){return typeof document<"u"&&document.documentElement.classList.contains("dark")}function D(t,n){const{src:e,theme:s="auto",onAspectRatio:o,onEvent:d}=n,a=V(e);let E=null;const r=()=>{const i=B(s,v());i!==E&&(E=i,t.src=N(e,i))};let h=null,p,A=null;const R=()=>{const i=t.parentElement;if(!h||!i)return;const{widthPx:w,heightPx:C}=h,g=i.getBoundingClientRect();if(g.width<=0)return;const f=Math.min(g.width/w,1),U=(g.width-w*f)/2,j=(g.height-C*f)/2;t.style.width=`${w}px`,t.style.height=`${C}px`,t.style.transformOrigin="0 0",t.style.transform=`translate(${U}px, ${j}px) scale(${f})`,f!==A&&(A=f,b.setHostScale(f))},F=i=>{h=i,R(),!p&&typeof ResizeObserver<"u"&&t.parentElement&&(p=new ResizeObserver(R),p.observe(t.parentElement))},b=k({iframe:t,origin:a},{onEvent:i=>{i.type==="resize"&&(o==null||o({widthPx:i.widthPx,heightPx:i.heightPx})),i.type==="ready"&&i.viewport&&F(i.viewport),d==null||d(i)}});let y;return s==="auto"&&typeof MutationObserver<"u"&&typeof document<"u"&&(y=new MutationObserver(r),y.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]})),r(),{controller:b,destroy(){y==null||y.disconnect(),p==null||p.disconnect(),b.destroy()}}}const H="scenar-embed",$="Interactive product tour",z=typeof HTMLElement<"u"?HTMLElement:class{};class x extends z{constructor(){super(...arguments);S(this,"iframe",null);S(this,"mount",null)}static get observedAttributes(){return["src","title","theme"]}connectedCallback(){this.iframe||this.render(),this.attach()}disconnectedCallback(){var e;(e=this.mount)==null||e.destroy(),this.mount=null}attributeChangedCallback(e,s,o){if(s!==o){if(e==="title"){this.iframe&&this.iframe.setAttribute("title",this.titleAttr());return}this.isConnected&&this.attach()}}play(){var e;(e=this.mount)==null||e.controller.play()}pause(){var e;(e=this.mount)==null||e.controller.pause()}seek(e){var s;(s=this.mount)==null||s.controller.seek(e)}setMuted(e){var s;(s=this.mount)==null||s.controller.setMuted(e)}setVolume(e){var s;(s=this.mount)==null||s.controller.setVolume(e)}titleAttr(){return this.getAttribute("title")??$}themeAttr(){const e=this.getAttribute("theme");return e==="light"||e==="dark"?e:"auto"}setAspectRatio(e,s){this.style.aspectRatio=`${e} / ${s}`}render(){this.style.display="block",this.style.position="relative",this.style.width="100%",this.setAspectRatio(L,I);const e=document.createElement("iframe");e.setAttribute("title",this.titleAttr()),e.setAttribute("loading","lazy"),e.setAttribute("allow","autoplay; fullscreen"),e.setAttribute("allowfullscreen",""),e.style.cssText="position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit",this.appendChild(e),this.iframe=e}attach(){var s;const e=this.getAttribute("src");!e||!this.iframe||((s=this.mount)==null||s.destroy(),this.mount=D(this.iframe,{src:e,theme:this.themeAttr(),onAspectRatio:({widthPx:o,heightPx:d})=>this.setAspectRatio(o,d),onEvent:o=>this.dispatchEvent(new CustomEvent(`scenar:${o.type}`,{detail:o}))}))}}function P(t=H){typeof customElements>"u"||customElements.get(t)||customElements.define(t,x)}return P(),l.ScenarEmbedElement=x,l.defineScenarEmbed=P,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"}),l}({});
1
+ var ScenarEmbed=function(l){"use strict";var W=Object.defineProperty;var G=(l,c,m)=>c in l?W(l,c,{enumerable:!0,configurable:!0,writable:!0,value:m}):l[c]=m;var S=(l,c,m)=>G(l,typeof c!="symbol"?c+"":c,m);const c="scenar-embed";function M(t){return{source:c,v:1,...t}}function T(t){if(typeof t!="object"||t===null)return!1;const n=t;return n.source===c&&n.v===1}function u(t){return typeof t=="number"&&Number.isFinite(t)}function _(t){if(t===void 0)return{};if(typeof t!="object"||t===null)return null;const n=t;return u(n.widthPx)&&n.widthPx>0&&u(n.heightPx)&&n.heightPx>0?{viewport:{widthPx:n.widthPx,heightPx:n.heightPx}}:null}function O(t){if(!T(t))return null;const n=t.type;switch(n){case"started":case"paused":case"completed":case"audioBlocked":return{type:n};case"ready":{if(!u(t.totalSteps)||typeof t.hasNarration!="boolean")return null;const e=_(t.viewport);return e===null?null:{type:n,totalSteps:t.totalSteps,hasNarration:t.hasNarration,...e}}case"resize":return u(t.widthPx)&&u(t.heightPx)?{type:n,widthPx:t.widthPx,heightPx:t.heightPx}:null;case"stepchange":return u(t.stepIndex)&&u(t.totalSteps)?{type:n,stepIndex:t.stepIndex,totalSteps:t.totalSteps}:null;case"progress":return u(t.stepIndex)&&u(t.totalSteps)&&u(t.fraction)?{type:n,stepIndex:t.stepIndex,totalSteps:t.totalSteps,fraction:t.fraction}:null;case"error":return typeof t.message=="string"?{type:n,message:t.message}:null;default:return null}}function k(t,n={}){const{iframe:e,origin:s}=t,{onEvent:o}=n,d=typeof window<"u",h=r=>{var a;(a=e.contentWindow)==null||a.postMessage(M(r),s)},E=r=>{if(r.source!==e.contentWindow||r.origin!==s)return;const a=O(r.data);a&&(o==null||o(a))};return d&&window.addEventListener("message",E),{play:()=>h({type:"play"}),pause:()=>h({type:"pause"}),seek:r=>h({type:"seek",timeMs:r}),setMuted:r=>h({type:"setMuted",muted:r}),setVolume:r=>h({type:"setVolume",volume:r}),setHostScale:r=>h({type:"setHostScale",scale:r}),prefetch:()=>h({type:"prefetch"}),destroy:()=>{h({type:"destroy"}),d&&window.removeEventListener("message",E)}}}const L=896,I=480;function v(t,n){return t==="light"?"light":t==="dark"||n?"dark":"light"}function B(t,n){const e=new URL(t);return e.searchParams.set("theme",n),e.toString()}function N(t){return new URL(t).origin}function V(){return typeof document<"u"&&document.documentElement.classList.contains("dark")}function D(t,n){const{src:e,theme:s="auto",onAspectRatio:o,onEvent:d}=n,h=N(e);let E=null;const r=()=>{const i=v(s,V());i!==E&&(E=i,t.src=B(e,i))};let a=null,p,A=null;const R=()=>{const i=t.parentElement;if(!a||!i)return;const{widthPx:w,heightPx:C}=a,g=i.getBoundingClientRect();if(g.width<=0)return;const f=Math.min(g.width/w,1),U=(g.width-w*f)/2,j=(g.height-C*f)/2;t.style.width=`${w}px`,t.style.height=`${C}px`,t.style.transformOrigin="0 0",t.style.transform=`translate(${U}px, ${j}px) scale(${f})`,f!==A&&(A=f,b.setHostScale(f))},F=i=>{a=i,R(),!p&&typeof ResizeObserver<"u"&&t.parentElement&&(p=new ResizeObserver(R),p.observe(t.parentElement))},b=k({iframe:t,origin:h},{onEvent:i=>{i.type==="resize"&&(o==null||o({widthPx:i.widthPx,heightPx:i.heightPx})),i.type==="ready"&&i.viewport&&F(i.viewport),d==null||d(i)}});let y;return s==="auto"&&typeof MutationObserver<"u"&&typeof document<"u"&&(y=new MutationObserver(r),y.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]})),r(),{controller:b,destroy(){y==null||y.disconnect(),p==null||p.disconnect(),b.destroy()}}}const H="scenar-embed",$="Interactive product tour",z=typeof HTMLElement<"u"?HTMLElement:class{};class x extends z{constructor(){super(...arguments);S(this,"iframe",null);S(this,"mount",null)}static get observedAttributes(){return["src","title","theme"]}connectedCallback(){this.iframe||this.render(),this.attach()}disconnectedCallback(){var e;(e=this.mount)==null||e.destroy(),this.mount=null}attributeChangedCallback(e,s,o){if(s!==o){if(e==="title"){this.iframe&&this.iframe.setAttribute("title",this.titleAttr());return}this.isConnected&&this.attach()}}play(){var e;(e=this.mount)==null||e.controller.play()}pause(){var e;(e=this.mount)==null||e.controller.pause()}seek(e){var s;(s=this.mount)==null||s.controller.seek(e)}setMuted(e){var s;(s=this.mount)==null||s.controller.setMuted(e)}setVolume(e){var s;(s=this.mount)==null||s.controller.setVolume(e)}titleAttr(){return this.getAttribute("title")??$}themeAttr(){const e=this.getAttribute("theme");return e==="light"||e==="dark"?e:"auto"}setAspectRatio(e,s){this.style.aspectRatio=`${e} / ${s}`}render(){this.style.display="block",this.style.position="relative",this.style.width="100%",this.style.overflow="hidden",this.setAspectRatio(L,I);const e=document.createElement("iframe");e.setAttribute("title",this.titleAttr()),e.setAttribute("loading","lazy"),e.setAttribute("allow","autoplay; fullscreen"),e.setAttribute("allowfullscreen",""),e.style.cssText="position:absolute;inset:0;width:100%;height:100%;border:0",this.appendChild(e),this.iframe=e}attach(){var s;const e=this.getAttribute("src");!e||!this.iframe||((s=this.mount)==null||s.destroy(),this.mount=D(this.iframe,{src:e,theme:this.themeAttr(),onAspectRatio:({widthPx:o,heightPx:d})=>this.setAspectRatio(o,d),onEvent:o=>this.dispatchEvent(new CustomEvent(`scenar:${o.type}`,{detail:o}))}))}}function P(t=H){typeof customElements>"u"||customElements.get(t)||customElements.define(t,x)}return P(),l.ScenarEmbedElement=x,l.defineScenarEmbed=P,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"}),l}({});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scenar/embed",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Drop-in host-side embedding for Scenar tours — a framework-agnostic <scenar-embed> web component and a matching React wrapper.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -44,7 +44,7 @@
44
44
  "./loader": "./embed.global.js"
45
45
  },
46
46
  "dependencies": {
47
- "@scenar/core": "0.10.0"
47
+ "@scenar/core": "0.11.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": ">=18.0.0",
package/react.d.ts CHANGED
@@ -30,6 +30,15 @@ export interface ScenarEmbedProps extends EmbedSource {
30
30
  * SSR-safe: the iframe renders without a `src` on the server and on the first
31
31
  * client render, so hydration never mismatches; the effect then assigns the
32
32
  * themed `src` and adopts the embed's reported aspect ratio.
33
+ *
34
+ * Corners: the wrapper is the one clipping surface — it carries
35
+ * `overflow: hidden`, and the host supplies any corner radius via `className`
36
+ * or `style`. The iframe itself must NOT carry a `border-radius`: under
37
+ * iframe-as-screen the mount lays the iframe out at the canonical viewport and
38
+ * scales it down with a transform, so a radius on the iframe lives in
39
+ * pre-transform space and renders at `radius x scale` — a mismatched, mostly
40
+ * ineffective clip. The embed paints edge-to-edge and the host boundary owns
41
+ * the corners.
33
42
  */
34
43
  export declare const ScenarEmbed: import("react").ForwardRefExoticComponent<ScenarEmbedProps & import("react").RefAttributes<ScenarEmbedHandle>>;
35
44
  //# sourceMappingURL=react.d.ts.map
package/react.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAMnB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAItB,MAAM,cAAc,CAAC;AAKtB,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACrD,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,gHAiEvB,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAMnB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EAItB,MAAM,cAAc,CAAC;AAKtB,sEAAsE;AACtE,MAAM,WAAW,iBAAiB;IAChC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACrD,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,gHAsEvB,CAAC"}
package/react.js CHANGED
@@ -14,6 +14,15 @@ const DEFAULT_TITLE = "Interactive product tour";
14
14
  * SSR-safe: the iframe renders without a `src` on the server and on the first
15
15
  * client render, so hydration never mismatches; the effect then assigns the
16
16
  * themed `src` and adopts the embed's reported aspect ratio.
17
+ *
18
+ * Corners: the wrapper is the one clipping surface — it carries
19
+ * `overflow: hidden`, and the host supplies any corner radius via `className`
20
+ * or `style`. The iframe itself must NOT carry a `border-radius`: under
21
+ * iframe-as-screen the mount lays the iframe out at the canonical viewport and
22
+ * scales it down with a transform, so a radius on the iframe lives in
23
+ * pre-transform space and renders at `radius x scale` — a mismatched, mostly
24
+ * ineffective clip. The embed paints edge-to-edge and the host boundary owns
25
+ * the corners.
17
26
  */
18
27
  export const ScenarEmbed = forwardRef(function ScenarEmbed({ src, id, base, title, theme = "auto", onEvent, className, style }, ref) {
19
28
  const iframeRef = useRef(null);
@@ -46,13 +55,18 @@ export const ScenarEmbed = forwardRef(function ScenarEmbed({ src, id, base, titl
46
55
  setMuted: (muted) => mountRef.current?.controller.setMuted(muted),
47
56
  setVolume: (volume) => mountRef.current?.controller.setVolume(volume),
48
57
  }), []);
49
- return (_jsx("div", { className: className, style: { position: "relative", width: "100%", aspectRatio: ratio, ...style }, children: _jsx("iframe", { ref: iframeRef, title: title ?? DEFAULT_TITLE, loading: "lazy", allow: "autoplay; fullscreen", allowFullScreen: true, style: {
58
+ return (_jsx("div", { className: className, style: {
59
+ position: "relative",
60
+ width: "100%",
61
+ aspectRatio: ratio,
62
+ overflow: "hidden",
63
+ ...style,
64
+ }, children: _jsx("iframe", { ref: iframeRef, title: title ?? DEFAULT_TITLE, loading: "lazy", allow: "autoplay; fullscreen", allowFullScreen: true, style: {
50
65
  position: "absolute",
51
66
  inset: 0,
52
67
  width: "100%",
53
68
  height: "100%",
54
69
  border: 0,
55
- borderRadius: "inherit",
56
70
  } }) }));
57
71
  });
58
72
  //# sourceMappingURL=react.js.map
package/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAEL,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,MAAM,EACN,QAAQ,GACT,MAAM,OAAO,CAAC;AAEf,OAAO,EAAmB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAGL,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,yDAAyD;AACzD,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAwBjD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CACnC,SAAS,WAAW,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,GAAG;IAC3F,MAAM,SAAS,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,MAAM,CAAoB,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAChC,GAAG,uBAAuB,MAAM,wBAAwB,EAAE,CAC3D,CAAC;IAEF,gFAAgF;IAChF,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,WAAW,GAAG,eAAe,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC;QACjC,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE;YACrC,GAAG,EAAE,WAAW;YAChB,KAAK;YACL,aAAa,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,OAAO,MAAM,QAAQ,EAAE,CAAC;YAC9E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC;SAChD,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;QACzB,OAAO,GAAG,EAAE;YACV,KAAK,CAAC,OAAO,EAAE,CAAC;YAChB,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QAC1B,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;IAEzB,mBAAmB,CACjB,GAAG,EACH,GAAG,EAAE,CAAC,CAAC;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,EAAE;QAC/C,KAAK,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE;QACjD,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAC3D,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjE,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC;KACtE,CAAC,EACF,EAAE,CACH,CAAC;IAEF,OAAO,CACL,cACE,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,YAE5E,iBACE,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,KAAK,IAAI,aAAa,EAC7B,OAAO,EAAC,MAAM,EACd,KAAK,EAAC,sBAAsB,EAC5B,eAAe,QACf,KAAK,EAAE;gBACL,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,MAAM;gBACd,MAAM,EAAE,CAAC;gBACT,YAAY,EAAE,SAAS;aACxB,GACD,GACE,CACP,CAAC;AACJ,CAAC,CACF,CAAC"}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
@@ -65,6 +65,16 @@ describe("<scenar-embed>", () => {
65
65
  expect(el.style.aspectRatio).toBe("896 / 480");
66
66
  });
67
67
 
68
+ it("clips its content so a host-applied border-radius rounds the embed", () => {
69
+ const { el, iframe } = mountElement({ src: SRC });
70
+ // The element is the one clipping surface: the host rounds the element,
71
+ // and everything inside — including the transform-scaled iframe — clips
72
+ // to it. The iframe itself must carry no radius (it would live in
73
+ // pre-transform space and render at radius x scale).
74
+ expect(el.style.overflow).toBe("hidden");
75
+ expect(iframe.style.borderRadius).toBe("");
76
+ });
77
+
68
78
  it("falls back to a generic accessible title", () => {
69
79
  const { iframe } = mountElement({ src: SRC });
70
80
  expect(iframe.getAttribute("title")).toBe("Interactive product tour");
package/src/element.ts CHANGED
@@ -33,6 +33,13 @@ const HTMLElementBase: typeof HTMLElement =
33
33
  * - `title` — accessible iframe title (defaults to a generic label).
34
34
  * - `theme` — `auto` (default) | `light` | `dark`.
35
35
  *
36
+ * Corners: the element is the one clipping surface — it carries
37
+ * `overflow: hidden`, and the host rounds by styling `border-radius` on the
38
+ * element itself. The internal iframe must NOT carry a radius: under
39
+ * iframe-as-screen the mount lays it out at the canonical viewport and scales
40
+ * it down with a transform, so an iframe radius lives in pre-transform space
41
+ * and renders at `radius x scale` — a mismatched, mostly ineffective clip.
42
+ *
36
43
  * Events: re-dispatches every embed event as a DOM `CustomEvent` named
37
44
  * `scenar:<type>` (e.g. `scenar:ready`, `scenar:completed`) with the event in
38
45
  * `detail`, so a vanilla host can listen without touching `postMessage`.
@@ -106,6 +113,7 @@ export class ScenarEmbedElement extends HTMLElementBase {
106
113
  this.style.display = "block";
107
114
  this.style.position = "relative";
108
115
  this.style.width = "100%";
116
+ this.style.overflow = "hidden";
109
117
  this.setAspectRatio(EMBED_BASE_ASPECT_WIDTH, EMBED_BASE_ASPECT_HEIGHT);
110
118
 
111
119
  const iframe = document.createElement("iframe");
@@ -113,8 +121,7 @@ export class ScenarEmbedElement extends HTMLElementBase {
113
121
  iframe.setAttribute("loading", "lazy");
114
122
  iframe.setAttribute("allow", "autoplay; fullscreen");
115
123
  iframe.setAttribute("allowfullscreen", "");
116
- iframe.style.cssText =
117
- "position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:inherit";
124
+ iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0";
118
125
 
119
126
  this.appendChild(iframe);
120
127
  this.iframe = iframe;
@@ -45,6 +45,20 @@ describe("ScenarEmbed (React)", () => {
45
45
  expect(wrapper.style.aspectRatio).toBe("896 / 480");
46
46
  });
47
47
 
48
+ it("clips the wrapper so a host-applied border-radius rounds the embed", () => {
49
+ render(<ScenarEmbed src={SRC} title="t" />);
50
+ const iframe = screen.getByTitle("t") as HTMLIFrameElement;
51
+ stubFrame(iframe);
52
+
53
+ // The wrapper is the one clipping surface: the host rounds it via
54
+ // className/style, and everything inside — including the transform-scaled
55
+ // iframe — clips to it. The iframe itself must carry no radius (it would
56
+ // live in pre-transform space and render at radius x scale).
57
+ const wrapper = iframe.parentElement as HTMLElement;
58
+ expect(wrapper.style.overflow).toBe("hidden");
59
+ expect(iframe.style.borderRadius).toBe("");
60
+ });
61
+
48
62
  it("resolves id + base into the embed src", () => {
49
63
  render(<ScenarEmbed id="welcome" base="https://embed.example/demos" title="t" />);
50
64
  const iframe = screen.getByTitle("t") as HTMLIFrameElement;
package/src/react.tsx CHANGED
@@ -52,6 +52,15 @@ export interface ScenarEmbedProps extends EmbedSource {
52
52
  * SSR-safe: the iframe renders without a `src` on the server and on the first
53
53
  * client render, so hydration never mismatches; the effect then assigns the
54
54
  * themed `src` and adopts the embed's reported aspect ratio.
55
+ *
56
+ * Corners: the wrapper is the one clipping surface — it carries
57
+ * `overflow: hidden`, and the host supplies any corner radius via `className`
58
+ * or `style`. The iframe itself must NOT carry a `border-radius`: under
59
+ * iframe-as-screen the mount lays the iframe out at the canonical viewport and
60
+ * scales it down with a transform, so a radius on the iframe lives in
61
+ * pre-transform space and renders at `radius x scale` — a mismatched, mostly
62
+ * ineffective clip. The embed paints edge-to-edge and the host boundary owns
63
+ * the corners.
55
64
  */
56
65
  export const ScenarEmbed = forwardRef<ScenarEmbedHandle, ScenarEmbedProps>(
57
66
  function ScenarEmbed({ src, id, base, title, theme = "auto", onEvent, className, style }, ref) {
@@ -98,7 +107,13 @@ export const ScenarEmbed = forwardRef<ScenarEmbedHandle, ScenarEmbedProps>(
98
107
  return (
99
108
  <div
100
109
  className={className}
101
- style={{ position: "relative", width: "100%", aspectRatio: ratio, ...style }}
110
+ style={{
111
+ position: "relative",
112
+ width: "100%",
113
+ aspectRatio: ratio,
114
+ overflow: "hidden",
115
+ ...style,
116
+ }}
102
117
  >
103
118
  <iframe
104
119
  ref={iframeRef}
@@ -112,7 +127,6 @@ export const ScenarEmbed = forwardRef<ScenarEmbedHandle, ScenarEmbedProps>(
112
127
  width: "100%",
113
128
  height: "100%",
114
129
  border: 0,
115
- borderRadius: "inherit",
116
130
  }}
117
131
  />
118
132
  </div>