@momentco-ai/moment-sdk 0.3.1-dev.20 → 0.3.3-dev.22

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.
@@ -1,4 +1,4 @@
1
- (function(g){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),o=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${o},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=s=>{if(s.origin!==this.opts.liveOrigin||s.source!==this.popup)return;const r=s.data;!r||typeof r!="object"||r.source!=="moment-live-embed"||r.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(r,this.opts.liveOrigin),this.opts.onResult(r),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}function x(n,e,t=typeof window<"u"?window.location.origin:""){const i=new URLSearchParams;return i.set("teamSlug",e.teamSlug),e.momentSlug&&i.set("momentSlug",e.momentSlug),e.listSlug&&i.set("listSlug",e.listSlug),i.set("triggerType",e.triggerType),e.ids?.length&&i.set("ids",e.ids.join(",")),e.calendar&&i.set("calendar",e.calendar),e.apiSourceId&&i.set("subscriptionId",e.apiSourceId),e.externalEventId&&i.set("externalId",e.externalEventId),e.externalEventIdPrefix&&!e.externalEventId&&i.set("externalIdPrefix",e.externalEventIdPrefix),i.set("returnOrigin",t),i.set("embed","1"),`${n}/en/embed/sync?${i.toString()}`}const f="moment-sdk-trigger-styles",E=`
1
+ (function(f){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),l=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${l},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=s=>{if(s.origin!==this.opts.liveOrigin||s.source!==this.popup)return;const r=s.data;!r||typeof r!="object"||r.source!=="moment-live-embed"||r.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(r,this.opts.liveOrigin),this.opts.onResult(r),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),e.selection&&i.set("selection",e.selection),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}function x(n,e,t=typeof window<"u"?window.location.origin:""){const i=new URLSearchParams;return i.set("teamSlug",e.teamSlug),e.momentSlug&&i.set("momentSlug",e.momentSlug),e.listSlug&&i.set("listSlug",e.listSlug),i.set("triggerType",e.triggerType),e.ids?.length&&i.set("ids",e.ids.join(",")),e.playlistSlugs?.length&&i.set("playlistSlugs",e.playlistSlugs.join(",")),e.calendar&&i.set("calendar",e.calendar),e.apiSourceId&&i.set("subscriptionId",e.apiSourceId),e.externalEventId&&i.set("externalId",e.externalEventId),e.externalEventIdPrefix&&!e.externalEventId&&i.set("externalIdPrefix",e.externalEventIdPrefix),i.set("returnOrigin",t),i.set("embed","1"),`${n}/en/embed/sync?${i.toString()}`}const v="moment-sdk-trigger-styles",T=`
2
2
  :where(.moment-sync-trigger) {
3
3
  background-color: #1c1917;
4
4
  color: #ffffff;
@@ -22,4 +22,4 @@
22
22
  outline: 2px solid #1c1917;
23
23
  outline-offset: 2px;
24
24
  }
25
- `;function T(){if(typeof document>"u"||document.getElementById(f))return;const n=document.createElement("style");n.id=f,n.textContent=E,document.head.appendChild(n)}const S=".moment-sync-trigger",I="https://live.dev.momentco.ai";class v{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:S,...e},this.liveBaseUrl=I;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}T(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){e.externalEventId&&e.externalEventIdPrefix&&console.warn("[MomentSdk] Both externalEventId and externalEventIdPrefix provided; using externalEventId only.");const t=this.scopeOpenPayload(e);this.emitAnalytics("embed.open",{teamSlug:t.teamSlug,triggerType:t.triggerType}),this.createModal(t)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=s=>{s.preventDefault(),this.handleTriggerClick(t)},o=this.boundClickHandlers.get(t);o&&t.removeEventListener("click",o),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,o=e.getAttribute("data-moment-list-slug")??void 0,s=e.getAttribute("data-moment-trigger-type");let r="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"||s==="brand-playlist"?r=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const c=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",u=c?c.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let p;a!=null&&a!==""&&(a==="google"||a==="outlook"?p=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a));const l=e.getAttribute("data-api-source-id")??void 0,d=e.getAttribute("data-external-event-id")??void 0,m=e.getAttribute("data-external-event-id-prefix")??void 0;if(d&&m&&console.warn("[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const h=r==="team"||r==="brand-playlist";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:h?[]:u}),this.open({teamSlug:t,momentSlug:i,listSlug:o,triggerType:r,ids:u,calendar:p??void 0,apiSourceId:l,externalEventId:m&&!d?void 0:d,externalEventIdPrefix:d?void 0:m})}scopeOpenPayload(e){return e.triggerType==="team"||e.triggerType==="brand-playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,calendar:e.calendar}:e}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const o=document.createElement("div");Object.assign(o.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const s=document.createElement("div");s.setAttribute("role","dialog"),s.setAttribute("aria-modal","true"),s.setAttribute("aria-label","Calendar sync modal"),s.tabIndex=-1,Object.assign(s.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const r=document.createElement("button");r.type="button",r.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',r.setAttribute("aria-label","Close"),Object.assign(r.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),r.addEventListener("mouseenter",()=>{r.style.color="#666"}),r.addEventListener("mouseleave",()=>{r.style.color="#999"}),r.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),s.appendChild(r),s.appendChild(this.iframe);const c=document.createElement("div");Object.assign(c.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const u=document.createElement("span");u.textContent="Powered by",Object.assign(u.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),c.appendChild(u),c.appendChild(a),o.appendChild(s),o.appendChild(c),this.overlay.appendChild(o),document.body.appendChild(this.overlay);const p=l=>{if(l.key!=="Tab")return;const d=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(d.length===0){l.preventDefault(),s.focus();return}const m=d[0],h=d[d.length-1],y=document.activeElement;l.shiftKey&&y===m?(l.preventDefault(),h.focus()):!l.shiftKey&&y===h&&(l.preventDefault(),m.focus())};s.addEventListener("keydown",p),r.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=p}buildIframeUrl(e){return x(this.liveBaseUrl,e)}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const o=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,o)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function b(n){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),n.className&&n.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",n.teamSlug),n.momentSlug&&e.setAttribute("data-moment-slug",n.momentSlug),n.listSlug&&e.setAttribute("data-moment-list-slug",n.listSlug),n.triggerType&&e.setAttribute("data-moment-trigger-type",n.triggerType),n.ids?.length&&e.setAttribute("data-moment-ids",n.ids.join(",")),n.calendar&&e.setAttribute("data-moment-calendar",n.calendar),n.apiSourceId&&e.setAttribute("data-api-source-id",n.apiSourceId),n.externalEventId&&e.setAttribute("data-external-event-id",n.externalEventId),n.externalEventIdPrefix&&e.setAttribute("data-external-event-id-prefix",n.externalEventIdPrefix),e.textContent=n.label??"Add to Calendar",e}if(typeof window<"u"){const n=window;n.MomentSdk=v,n.createMomentButton=b}g.MomentSdk=v,g.createMomentButton=b,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
25
+ `;function S(){if(typeof document>"u"||document.getElementById(v))return;const n=document.createElement("style");n.id=v,n.textContent=T,document.head.appendChild(n)}const E=".moment-sync-trigger",I="https://live.dev.momentco.ai";class b{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:E,...e},this.liveBaseUrl=I;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}S(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){e.externalEventId&&e.externalEventIdPrefix&&console.warn("[MomentSdk] Both externalEventId and externalEventIdPrefix provided; using externalEventId only.");const t=this.scopeOpenPayload(e);this.emitAnalytics("embed.open",{teamSlug:t.teamSlug,triggerType:t.triggerType}),this.createModal(t)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=s=>{s.preventDefault(),this.handleTriggerClick(t)},l=this.boundClickHandlers.get(t);l&&t.removeEventListener("click",l),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,l=e.getAttribute("data-moment-list-slug")??void 0,s=e.getAttribute("data-moment-trigger-type");let r="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"||s==="brand-playlist"||s==="playlist"||s==="schedule"?r=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const a=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",m=a?a.split(",").filter(Boolean):[],d=e.getAttribute("data-moment-playlist-slugs")??"",g=d?d.split(",").map(A=>A.trim()).filter(Boolean):[],o=e.getAttribute("data-moment-calendar");let c;o!=null&&o!==""&&(o==="google"||o==="outlook"?c=o:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",o));const h=e.getAttribute("data-api-source-id")??void 0,u=e.getAttribute("data-external-event-id")??void 0,p=e.getAttribute("data-external-event-id-prefix")??void 0;if(u&&p&&console.warn("[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const P=r==="team"||r==="brand-playlist"||r==="playlist"||r==="schedule";this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:r,momentIds:P?[]:m}),this.open({teamSlug:t,momentSlug:i,listSlug:l,triggerType:r,ids:m,playlistSlugs:g,calendar:c??void 0,apiSourceId:h,externalEventId:p&&!u?void 0:u,externalEventIdPrefix:u?void 0:p})}scopeOpenPayload(e){return e.triggerType==="playlist"||e.triggerType==="schedule"?{teamSlug:e.teamSlug,triggerType:e.triggerType,playlistSlugs:e.playlistSlugs,calendar:e.calendar}:e.triggerType==="team"||e.triggerType==="brand-playlist"?{teamSlug:e.teamSlug,triggerType:e.triggerType,calendar:e.calendar}:e}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",o=>{o.target===this.overlay&&this.handleCancel()});const i=o=>{o.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const l=document.createElement("div");Object.assign(l.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const s=document.createElement("div");s.setAttribute("role","dialog"),s.setAttribute("aria-modal","true"),s.setAttribute("aria-label","Calendar sync modal"),s.tabIndex=-1,Object.assign(s.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const r=document.createElement("button");r.type="button",r.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',r.setAttribute("aria-label","Close"),Object.assign(r.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),r.addEventListener("mouseenter",()=>{r.style.color="#666"}),r.addEventListener("mouseleave",()=>{r.style.color="#999"}),r.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),s.appendChild(r),s.appendChild(this.iframe);const a=document.createElement("div");Object.assign(a.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const m=document.createElement("span");m.textContent="Powered by",Object.assign(m.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const d=document.createElement("img");d.alt="Moment",d.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(d.style,{height:"auto",width:"67px"}),a.appendChild(m),a.appendChild(d),l.appendChild(s),l.appendChild(a),this.overlay.appendChild(l),document.body.appendChild(this.overlay);const g=o=>{if(o.key!=="Tab")return;const c=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(c.length===0){o.preventDefault(),s.focus();return}const h=c[0],u=c[c.length-1],p=document.activeElement;o.shiftKey&&p===h?(o.preventDefault(),u.focus()):!o.shiftKey&&p===u&&(o.preventDefault(),h.focus())};s.addEventListener("keydown",g),r.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=g}buildIframeUrl(e){return x(this.liveBaseUrl,e)}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const l=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,l)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function y(n){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),n.className&&n.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",n.teamSlug),n.momentSlug&&e.setAttribute("data-moment-slug",n.momentSlug),n.listSlug&&e.setAttribute("data-moment-list-slug",n.listSlug),n.triggerType&&e.setAttribute("data-moment-trigger-type",n.triggerType),n.ids?.length&&e.setAttribute("data-moment-ids",n.ids.join(",")),n.playlistSlugs?.length&&e.setAttribute("data-moment-playlist-slugs",n.playlistSlugs.join(",")),n.calendar&&e.setAttribute("data-moment-calendar",n.calendar),n.apiSourceId&&e.setAttribute("data-api-source-id",n.apiSourceId),n.externalEventId&&e.setAttribute("data-external-event-id",n.externalEventId),n.externalEventIdPrefix&&e.setAttribute("data-external-event-id-prefix",n.externalEventIdPrefix),e.textContent=n.label??"Add to Calendar",e}if(typeof window<"u"){const n=window;n.MomentSdk=b,n.createMomentButton=y}f.MomentSdk=b,f.createMomentButton=y,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
@@ -1,4 +1,4 @@
1
- class v {
1
+ class y {
2
2
  popup = null;
3
3
  pollTimer = null;
4
4
  messageHandler = null;
@@ -12,11 +12,11 @@ class v {
12
12
  */
13
13
  open(e) {
14
14
  this.cleanup(), this.resultReceived = !1;
15
- const t = this.buildPopupUrl(e), i = Math.round(window.screenX + (window.outerWidth - 500) / 2), o = Math.round(window.screenY + (window.outerHeight - 700) / 2);
15
+ const t = this.buildPopupUrl(e), i = Math.round(window.screenX + (window.outerWidth - 500) / 2), l = Math.round(window.screenY + (window.outerHeight - 700) / 2);
16
16
  if (this.popup = window.open(
17
17
  t,
18
18
  "moment-oauth-popup",
19
- `width=500,height=700,left=${i},top=${o},menubar=no,toolbar=no,location=yes,status=no`
19
+ `width=500,height=700,left=${i},top=${l},menubar=no,toolbar=no,location=yes,status=no`
20
20
  ), !this.popup || this.popup.closed) {
21
21
  this.sendStatusToIframe("popup-blocked"), this.opts.onPopupBlocked();
22
22
  return;
@@ -46,7 +46,7 @@ class v {
46
46
  triggerType: e.triggerType,
47
47
  returnOrigin: t
48
48
  });
49
- return e.subscriptionType && i.set("subscriptionType", e.subscriptionType), `${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`;
49
+ return e.subscriptionType && i.set("subscriptionType", e.subscriptionType), e.selection && i.set("selection", e.selection), `${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`;
50
50
  }
51
51
  startPolling() {
52
52
  this.pollTimer = setInterval(() => {
@@ -63,11 +63,11 @@ class v {
63
63
  this.opts.iframeWindow.postMessage(t, this.opts.liveOrigin);
64
64
  }
65
65
  }
66
- function b(n, e, t = typeof window < "u" ? window.location.origin : "") {
66
+ function w(n, e, t = typeof window < "u" ? window.location.origin : "") {
67
67
  const i = new URLSearchParams();
68
- return i.set("teamSlug", e.teamSlug), e.momentSlug && i.set("momentSlug", e.momentSlug), e.listSlug && i.set("listSlug", e.listSlug), i.set("triggerType", e.triggerType), e.ids?.length && i.set("ids", e.ids.join(",")), e.calendar && i.set("calendar", e.calendar), e.apiSourceId && i.set("subscriptionId", e.apiSourceId), e.externalEventId && i.set("externalId", e.externalEventId), e.externalEventIdPrefix && !e.externalEventId && i.set("externalIdPrefix", e.externalEventIdPrefix), i.set("returnOrigin", t), i.set("embed", "1"), `${n}/en/embed/sync?${i.toString()}`;
68
+ return i.set("teamSlug", e.teamSlug), e.momentSlug && i.set("momentSlug", e.momentSlug), e.listSlug && i.set("listSlug", e.listSlug), i.set("triggerType", e.triggerType), e.ids?.length && i.set("ids", e.ids.join(",")), e.playlistSlugs?.length && i.set("playlistSlugs", e.playlistSlugs.join(",")), e.calendar && i.set("calendar", e.calendar), e.apiSourceId && i.set("subscriptionId", e.apiSourceId), e.externalEventId && i.set("externalId", e.externalEventId), e.externalEventIdPrefix && !e.externalEventId && i.set("externalIdPrefix", e.externalEventIdPrefix), i.set("returnOrigin", t), i.set("embed", "1"), `${n}/en/embed/sync?${i.toString()}`;
69
69
  }
70
- const f = "moment-sdk-trigger-styles", y = `
70
+ const f = "moment-sdk-trigger-styles", x = `
71
71
  :where(.moment-sync-trigger) {
72
72
  background-color: #1c1917;
73
73
  color: #ffffff;
@@ -92,13 +92,13 @@ const f = "moment-sdk-trigger-styles", y = `
92
92
  outline-offset: 2px;
93
93
  }
94
94
  `;
95
- function w() {
95
+ function T() {
96
96
  if (typeof document > "u" || document.getElementById(f)) return;
97
97
  const n = document.createElement("style");
98
- n.id = f, n.textContent = y, document.head.appendChild(n);
98
+ n.id = f, n.textContent = x, document.head.appendChild(n);
99
99
  }
100
- const x = ".moment-sync-trigger", E = "https://live.dev.momentco.ai";
101
- class T {
100
+ const E = ".moment-sync-trigger", S = "https://live.dev.momentco.ai";
101
+ class I {
102
102
  opts;
103
103
  liveBaseUrl;
104
104
  liveOrigin;
@@ -114,15 +114,15 @@ class T {
114
114
  closeTimer = null;
115
115
  constructor(e) {
116
116
  this.opts = {
117
- triggerSelector: x,
117
+ triggerSelector: E,
118
118
  ...e
119
- }, this.liveBaseUrl = E;
119
+ }, this.liveBaseUrl = S;
120
120
  try {
121
121
  this.liveOrigin = new URL(this.liveBaseUrl).origin;
122
122
  } catch {
123
123
  throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`);
124
124
  }
125
- w(), this.bindTriggers(), this.emitAnalytics("embed.init");
125
+ T(), this.bindTriggers(), this.emitAnalytics("embed.init");
126
126
  }
127
127
  /**
128
128
  * Programmatically open the sync modal.
@@ -160,8 +160,8 @@ class T {
160
160
  document.querySelectorAll(this.opts.triggerSelector).forEach((t) => {
161
161
  const i = (s) => {
162
162
  s.preventDefault(), this.handleTriggerClick(t);
163
- }, o = this.boundClickHandlers.get(t);
164
- o && t.removeEventListener("click", o), t.addEventListener("click", i), this.boundClickHandlers.set(t, i);
163
+ }, l = this.boundClickHandlers.get(t);
164
+ l && t.removeEventListener("click", l), t.addEventListener("click", i), this.boundClickHandlers.set(t, i);
165
165
  });
166
166
  }
167
167
  unbindTriggers() {
@@ -170,42 +170,48 @@ class T {
170
170
  }), this.boundClickHandlers.clear();
171
171
  }
172
172
  handleTriggerClick(e) {
173
- const t = e.getAttribute("data-moment-team-slug") ?? "", i = e.getAttribute("data-moment-slug") ?? void 0, o = e.getAttribute("data-moment-list-slug") ?? void 0, s = e.getAttribute("data-moment-trigger-type");
173
+ const t = e.getAttribute("data-moment-team-slug") ?? "", i = e.getAttribute("data-moment-slug") ?? void 0, l = e.getAttribute("data-moment-list-slug") ?? void 0, s = e.getAttribute("data-moment-trigger-type");
174
174
  let r = "moment";
175
- s != null && s !== "" && (s === "moment" || s === "list" || s === "team" || s === "brand-playlist" ? r = s : console.warn(
175
+ s != null && s !== "" && (s === "moment" || s === "list" || s === "team" || s === "brand-playlist" || s === "playlist" || s === "schedule" ? r = s : console.warn(
176
176
  "[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",
177
177
  s
178
178
  ));
179
- const c = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", u = c ? c.split(",").filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
180
- let p;
181
- a != null && a !== "" && (a === "google" || a === "outlook" ? p = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a));
182
- const l = e.getAttribute("data-api-source-id") ?? void 0, d = e.getAttribute("data-external-event-id") ?? void 0, m = e.getAttribute("data-external-event-id-prefix") ?? void 0;
183
- if (d && m && console.warn(
179
+ const a = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", m = a ? a.split(",").filter(Boolean) : [], d = e.getAttribute("data-moment-playlist-slugs") ?? "", g = d ? d.split(",").map((b) => b.trim()).filter(Boolean) : [], o = e.getAttribute("data-moment-calendar");
180
+ let c;
181
+ o != null && o !== "" && (o === "google" || o === "outlook" ? c = o : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", o));
182
+ const h = e.getAttribute("data-api-source-id") ?? void 0, u = e.getAttribute("data-external-event-id") ?? void 0, p = e.getAttribute("data-external-event-id-prefix") ?? void 0;
183
+ if (u && p && console.warn(
184
184
  "[MomentSdk] Both data-external-event-id and data-external-event-id-prefix are set; using data-external-event-id only."
185
185
  ), !t) {
186
186
  console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");
187
187
  return;
188
188
  }
189
- const h = r === "team" || r === "brand-playlist";
189
+ const v = r === "team" || r === "brand-playlist" || r === "playlist" || r === "schedule";
190
190
  this.emitAnalytics("embed.trigger.click", {
191
191
  teamSlug: t,
192
192
  triggerType: r,
193
- momentIds: h ? [] : u
193
+ momentIds: v ? [] : m
194
194
  }), this.open({
195
195
  teamSlug: t,
196
196
  momentSlug: i,
197
- listSlug: o,
197
+ listSlug: l,
198
198
  triggerType: r,
199
- ids: u,
200
- calendar: p ?? void 0,
201
- apiSourceId: l,
202
- externalEventId: m && !d ? void 0 : d,
203
- externalEventIdPrefix: d ? void 0 : m
199
+ ids: m,
200
+ playlistSlugs: g,
201
+ calendar: c ?? void 0,
202
+ apiSourceId: h,
203
+ externalEventId: p && !u ? void 0 : u,
204
+ externalEventIdPrefix: u ? void 0 : p
204
205
  });
205
206
  }
206
207
  /** Drop moment/list/source scoping for brand-level subscription triggers. */
207
208
  scopeOpenPayload(e) {
208
- return e.triggerType === "team" || e.triggerType === "brand-playlist" ? {
209
+ return e.triggerType === "playlist" || e.triggerType === "schedule" ? {
210
+ teamSlug: e.teamSlug,
211
+ triggerType: e.triggerType,
212
+ playlistSlugs: e.playlistSlugs,
213
+ calendar: e.calendar
214
+ } : e.triggerType === "team" || e.triggerType === "brand-playlist" ? {
209
215
  teamSlug: e.teamSlug,
210
216
  triggerType: e.triggerType,
211
217
  calendar: e.calendar
@@ -225,15 +231,15 @@ class T {
225
231
  backgroundColor: "rgba(0, 0, 0, 0.5)",
226
232
  backdropFilter: "blur(2px)",
227
233
  padding: "16px"
228
- }), this.overlay.addEventListener("click", (l) => {
229
- l.target === this.overlay && this.handleCancel();
234
+ }), this.overlay.addEventListener("click", (o) => {
235
+ o.target === this.overlay && this.handleCancel();
230
236
  });
231
- const i = (l) => {
232
- l.key === "Escape" && this.handleCancel();
237
+ const i = (o) => {
238
+ o.key === "Escape" && this.handleCancel();
233
239
  };
234
240
  document.addEventListener("keydown", i);
235
- const o = document.createElement("div");
236
- Object.assign(o.style, {
241
+ const l = document.createElement("div");
242
+ Object.assign(l.style, {
237
243
  position: "relative",
238
244
  width: "100%",
239
245
  maxWidth: "380px",
@@ -281,42 +287,42 @@ class T {
281
287
  overflow: "hidden",
282
288
  transition: "height 0.15s ease"
283
289
  }), s.appendChild(r), s.appendChild(this.iframe);
284
- const c = document.createElement("div");
285
- Object.assign(c.style, {
290
+ const a = document.createElement("div");
291
+ Object.assign(a.style, {
286
292
  display: "flex",
287
293
  alignItems: "center",
288
294
  justifyContent: "center",
289
295
  gap: "6px",
290
296
  padding: "12px 0"
291
297
  });
292
- const u = document.createElement("span");
293
- u.textContent = "Powered by", Object.assign(u.style, {
298
+ const m = document.createElement("span");
299
+ m.textContent = "Powered by", Object.assign(m.style, {
294
300
  fontFamily: "Inter, system-ui, -apple-system, sans-serif",
295
301
  fontSize: "12px",
296
302
  lineHeight: "16px",
297
303
  color: "#ffffff"
298
304
  });
299
- const a = document.createElement("img");
300
- a.alt = "Moment", a.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(a.style, {
305
+ const d = document.createElement("img");
306
+ d.alt = "Moment", d.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(d.style, {
301
307
  height: "auto",
302
308
  width: "67px"
303
- }), c.appendChild(u), c.appendChild(a), o.appendChild(s), o.appendChild(c), this.overlay.appendChild(o), document.body.appendChild(this.overlay);
304
- const p = (l) => {
305
- if (l.key !== "Tab") return;
306
- const d = s.querySelectorAll(
309
+ }), a.appendChild(m), a.appendChild(d), l.appendChild(s), l.appendChild(a), this.overlay.appendChild(l), document.body.appendChild(this.overlay);
310
+ const g = (o) => {
311
+ if (o.key !== "Tab") return;
312
+ const c = s.querySelectorAll(
307
313
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
308
314
  );
309
- if (d.length === 0) {
310
- l.preventDefault(), s.focus();
315
+ if (c.length === 0) {
316
+ o.preventDefault(), s.focus();
311
317
  return;
312
318
  }
313
- const m = d[0], h = d[d.length - 1], g = document.activeElement;
314
- l.shiftKey && g === m ? (l.preventDefault(), h.focus()) : !l.shiftKey && g === h && (l.preventDefault(), m.focus());
319
+ const h = c[0], u = c[c.length - 1], p = document.activeElement;
320
+ o.shiftKey && p === h ? (o.preventDefault(), u.focus()) : !o.shiftKey && p === u && (o.preventDefault(), h.focus());
315
321
  };
316
- s.addEventListener("keydown", p), r.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = p;
322
+ s.addEventListener("keydown", g), r.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = g;
317
323
  }
318
324
  buildIframeUrl(e) {
319
- return b(this.liveBaseUrl, e);
325
+ return w(this.liveBaseUrl, e);
320
326
  }
321
327
  // ── Private: message handling ─────────────────────────────────────
322
328
  setupMessageListener() {
@@ -335,8 +341,8 @@ class T {
335
341
  if (t.source === "moment-live-embed" && t.type === "moment.embed.resize") {
336
342
  const i = typeof t.height == "number" ? t.height : 0;
337
343
  if (i > 0 && this.iframe) {
338
- const o = Math.round(window.innerHeight * 0.8);
339
- this.iframe.style.height = `${Math.min(i, o)}px`;
344
+ const l = Math.round(window.innerHeight * 0.8);
345
+ this.iframe.style.height = `${Math.min(i, l)}px`;
340
346
  }
341
347
  return;
342
348
  }
@@ -353,7 +359,7 @@ class T {
353
359
  }, window.addEventListener("message", this.messageHandler);
354
360
  }
355
361
  handleOAuthStart(e) {
356
- this.popupBridge && (this.popupBridge.cleanup(), this.popupBridge = null), this.popupBridge = new v({
362
+ this.popupBridge && (this.popupBridge.cleanup(), this.popupBridge = null), this.popupBridge = new y({
357
363
  liveBaseUrl: this.liveBaseUrl,
358
364
  liveOrigin: this.liveOrigin,
359
365
  iframeWindow: this.iframe?.contentWindow ?? null,
@@ -419,17 +425,17 @@ class T {
419
425
  });
420
426
  }
421
427
  }
422
- function I(n) {
428
+ function P(n) {
423
429
  const e = document.createElement("button");
424
430
  return e.type = "button", e.classList.add("moment-sync-trigger"), n.className && n.className.split(" ").forEach((t) => {
425
431
  t && e.classList.add(t);
426
- }), e.setAttribute("data-moment-team-slug", n.teamSlug), n.momentSlug && e.setAttribute("data-moment-slug", n.momentSlug), n.listSlug && e.setAttribute("data-moment-list-slug", n.listSlug), n.triggerType && e.setAttribute("data-moment-trigger-type", n.triggerType), n.ids?.length && e.setAttribute("data-moment-ids", n.ids.join(",")), n.calendar && e.setAttribute("data-moment-calendar", n.calendar), n.apiSourceId && e.setAttribute("data-api-source-id", n.apiSourceId), n.externalEventId && e.setAttribute("data-external-event-id", n.externalEventId), n.externalEventIdPrefix && e.setAttribute("data-external-event-id-prefix", n.externalEventIdPrefix), e.textContent = n.label ?? "Add to Calendar", e;
432
+ }), e.setAttribute("data-moment-team-slug", n.teamSlug), n.momentSlug && e.setAttribute("data-moment-slug", n.momentSlug), n.listSlug && e.setAttribute("data-moment-list-slug", n.listSlug), n.triggerType && e.setAttribute("data-moment-trigger-type", n.triggerType), n.ids?.length && e.setAttribute("data-moment-ids", n.ids.join(",")), n.playlistSlugs?.length && e.setAttribute("data-moment-playlist-slugs", n.playlistSlugs.join(",")), n.calendar && e.setAttribute("data-moment-calendar", n.calendar), n.apiSourceId && e.setAttribute("data-api-source-id", n.apiSourceId), n.externalEventId && e.setAttribute("data-external-event-id", n.externalEventId), n.externalEventIdPrefix && e.setAttribute("data-external-event-id-prefix", n.externalEventIdPrefix), e.textContent = n.label ?? "Add to Calendar", e;
427
433
  }
428
434
  if (typeof window < "u") {
429
435
  const n = window;
430
- n.MomentSdk = T, n.createMomentButton = I;
436
+ n.MomentSdk = I, n.createMomentButton = P;
431
437
  }
432
438
  export {
433
- T as MomentSdk,
434
- I as createMomentButton
439
+ I as MomentSdk,
440
+ P as createMomentButton
435
441
  };
@@ -10,6 +10,11 @@ export declare function createMomentButton(options: {
10
10
  listSlug?: string;
11
11
  triggerType?: MomentTriggerType;
12
12
  ids?: string[];
13
+ /**
14
+ * Playlist slugs offered under a `playlist` trigger (picker when >1) or the
15
+ * collections shown in the `schedule` trigger's multi-select modal.
16
+ */
17
+ playlistSlugs?: string[];
13
18
  calendar?: CalendarProvider;
14
19
  /** Sync subscription source id; pairs with `externalEventId` for server-side resolution. */
15
20
  apiSourceId?: string;
@@ -6,8 +6,14 @@
6
6
  * - `list` → subscribe to a curated list of moments
7
7
  * - `team` → subscribe to a team / brand schedule (all published moments)
8
8
  * - `brand-playlist` → subscribe to the brand's default playlist
9
+ * - `playlist` → subscribe to one of several specific playlists. When more than
10
+ * one slug is supplied, Live fetches each playlist and prompts the user to
11
+ * pick which one to sync.
12
+ * - `schedule` → open the brand's schedule modal. Live fetches the supplied
13
+ * collection playlists and lets the fan multi-select which ones to sync in a
14
+ * single pass (the generic "Add to Calendar" experience).
9
15
  */
10
- export type MomentTriggerType = 'moment' | 'list' | 'team' | 'brand-playlist';
16
+ export type MomentTriggerType = 'moment' | 'list' | 'team' | 'brand-playlist' | 'playlist' | 'schedule';
11
17
  export type CalendarProvider = 'google' | 'outlook';
12
18
  export interface MomentSdkInitOptions {
13
19
  /** CSS selector for trigger elements. Default: ".moment-sync-trigger" */
@@ -35,6 +41,14 @@ export interface MomentSdkInitOptions {
35
41
  * `externalEventId` for this trigger type.
36
42
  * - `brand-playlist` → `teamSlug` only (Live resolves the brand's
37
43
  * `defaultPlaylistId`). Omit the same fields as `team`.
44
+ * - `playlist` → `teamSlug` plus `playlistSlugs` (one or more playlist slugs).
45
+ * With a single slug Live syncs that playlist directly; with several it shows
46
+ * a picker. Omit `momentSlug`, `listSlug`, `ids`, `apiSourceId`, and
47
+ * `externalEventId` for this trigger type.
48
+ * - `schedule` → `teamSlug` plus `playlistSlugs` (the collections to surface).
49
+ * Live renders the schedule modal where the fan multi-selects collections and
50
+ * syncs them together. Omit `momentSlug`, `listSlug`, `ids`, `apiSourceId`,
51
+ * and `externalEventId` for this trigger type.
38
52
  */
39
53
  export interface MomentSdkOpenPayload {
40
54
  teamSlug: string;
@@ -43,6 +57,14 @@ export interface MomentSdkOpenPayload {
43
57
  listSlug?: string;
44
58
  triggerType: MomentTriggerType;
45
59
  ids?: string[];
60
+ /**
61
+ * Playlist slugs to offer under a `playlist` or `schedule` trigger. Live
62
+ * fetches each playlist's details. Under `playlist`, more than one slug
63
+ * renders a single-choice picker; under `schedule`, the slugs become the
64
+ * multi-select collections in the schedule modal. Forwarded to the Live embed
65
+ * as the `playlistSlugs` query param (comma-separated).
66
+ */
67
+ playlistSlugs?: string[];
46
68
  calendar?: CalendarProvider;
47
69
  /**
48
70
  * Sync subscription source identifier (the publisher's sync-subscription id).
@@ -84,6 +106,12 @@ export interface MomentOAuthStartPayload {
84
106
  * under a `list`/`team` trigger).
85
107
  */
86
108
  subscriptionType?: MomentSubscriptionType;
109
+ /**
110
+ * Mixed-type selection encoded as `type:id,id;type:id`. Set by the `schedule`
111
+ * trigger when the cart mixes collections and individual events. Forwarded
112
+ * verbatim to the popup, which subscribes each group; opaque to the SDK.
113
+ */
114
+ selection?: string;
87
115
  returnOrigin: string;
88
116
  }
89
117
  /** Parent → Iframe: relay OAuth popup status */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentco-ai/moment-sdk",
3
- "version": "0.3.1-dev.20",
3
+ "version": "0.3.3-dev.22",
4
4
  "type": "module",
5
5
  "description": "Embeddable calendar sync widget for external team websites",
6
6
  "author": "Moment Co. <support@momentco.ai> (https://momentco.ai)",
@@ -55,6 +55,7 @@
55
55
  "build": "vite build && tsc --emitDeclarationOnly --outDir dist/types",
56
56
  "build:dev": "vite build --mode development && tsc --emitDeclarationOnly --outDir dist/types",
57
57
  "build:prod": "vite build --mode production && tsc --emitDeclarationOnly --outDir dist/types",
58
+ "build:local": "VITE_LIVE_BASE_URL=http://localhost:8081 vite build && tsc --emitDeclarationOnly --outDir dist/types",
58
59
  "type-check": "tsc --noEmit",
59
60
  "typecheck": "pnpm run type-check",
60
61
  "lint": "eslint .",
@@ -64,6 +65,8 @@
64
65
  "quality": "pnpm run type-check && pnpm run lint && pnpm run format",
65
66
  "test": "vitest run",
66
67
  "examples": "pnpm build && node examples/serve.mjs",
68
+ "integration-test": "node integration-test/serve.mjs",
69
+ "integration-test:local": "INTEGRATION_USE_LOCAL_SDK=1 node integration-test/serve.mjs",
67
70
  "prepare": "husky"
68
71
  },
69
72
  "devDependencies": {