@mochabug/adapt-sdk 0.1.13 → 0.1.15
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/dist/cjs/frontend.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var x=(e,t)=>{for(var n in t)m(e,n,{get:t[n],enumerable:!0})},H=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of I(t))!P.call(e,i)&&i!==n&&m(e,i,{get:()=>t[i],enumerable:!(o=C(t,i))||o.enumerable});return e};var U=e=>H(m({},"__esModule",{value:!0}),e);var Y={};x(Y,{getDarkMode:()=>N,getToken:()=>_,listenDarkMode:()=>j});module.exports=U(Y);var d=null;function _(e=!1){if(e)return"dev-token";if(d!==null)return d;if(typeof window>"u"||typeof location>"u")throw new Error("getToken() is only available in browser environments");let t=location.hash.slice(1);if(!t)throw new Error("No token found in URL hash");let n=new URLSearchParams(t).get("mb_token");if(!n)throw new Error('Token "mb_token" not found in URL hash');return d=decodeURIComponent(n),d}var u=null,b=new Set;function N(){return u}function j(e){if(b.add(e),u!==null)try{e(u)}catch{}return()=>{b.delete(e)}}function y(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function V(e){return y(e)&&e.type==="adapt-darkMode"&&typeof e.darkMode=="boolean"}function Z(e){return y(e)&&e.type==="adapt-autoResizing"&&typeof e.autoResizing=="boolean"}function w(e){return Number.isFinite(e)&&e>=1}function q(e){return y(e)&&typeof e.postMessage=="function"}function f(){return typeof window<"u"&&typeof document<"u"&&typeof window.postMessage=="function"}function R(){if(!f())return!1;try{return window.self!==window.top}catch{return!0}}var a=-1,l=-1,g=null,G=16,c=0,J=5;function K(){let{body:e,documentElement:t}=document;if(!e)return null;let n=getComputedStyle(e),o=parseFloat(n.marginTop)||0,i=parseFloat(n.marginBottom)||0,T=parseFloat(n.marginLeft)||0,v=parseFloat(n.marginRight)||0,r=getComputedStyle(t),z=parseFloat(r.paddingTop)||0,L=parseFloat(r.paddingBottom)||0,E=parseFloat(r.paddingLeft)||0,F=parseFloat(r.paddingRight)||0,S=parseFloat(r.borderTopWidth)||0,A=parseFloat(r.borderBottomWidth)||0,B=parseFloat(r.borderLeftWidth)||0,D=parseFloat(r.borderRightWidth)||0,O=Math.max(e.scrollHeight,e.offsetHeight),W=Math.max(e.scrollWidth,e.offsetWidth),h=O+o+i+z+L+S+A,M=W+T+v+E+F+B+D;return!w(M)||!w(h)||c>=J&&h===l?null:{width:M,height:h+1}}function s(){if(!f()||!R())return;let e=window.parent;if(!q(e))return;let t=K();if(!t)return;let{width:n,height:o}=t;if(!(n===a&&o===l)){n!==a&&a!==-1&&(c=0),a=n,l=o,c++;try{e.postMessage({type:"adapt-resize",width:n,height:o},"*")}catch{}}}function p(){g===null&&(g=setTimeout(()=>{g=null,s()},G))}function Q(){a=-1,l=-1,c=0,s()}function X(e){let t;try{t=e.data}catch{return}if(V(t)){u=t.darkMode;for(let n of b)try{n(t.darkMode)}catch{}}Z(t)&&t.autoResizing&&Q()}function k(){if(!f()||!R())return;window.addEventListener("message",X);let e=new ResizeObserver(p);document.body&&e.observe(document.body),document.documentElement&&e.observe(document.documentElement),document.body&&new MutationObserver(p).observe(document.body,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),document.addEventListener("load",p,!0),document.fonts?.ready?.then(()=>{s()}),document.readyState==="complete"?s():window.addEventListener("load",()=>{s()}),setTimeout(s,50),setTimeout(s,150)}f()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",k):k());0&&(module.exports={getDarkMode,getToken,listenDarkMode});
|
|
2
2
|
//# sourceMappingURL=frontend.cjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/frontend.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Frontend utilities for Adapt plugins running in browser/iframe contexts\n *\n * Iframe resizing is handled automatically - no initialization required.\n * Just import the functions you need and the resize communication starts automatically.\n */\n\n// =============================================================================\n// Token Management\n// =============================================================================\n\nlet cachedToken: string | null = null;\n\n/**\n * Get the authentication token from the URL hash.\n * @param isDevelopment If true, returns a dev token for local development\n */\nexport function getToken(isDevelopment = false): string {\n if (isDevelopment) return 'dev-token';\n if (cachedToken !== null) return cachedToken;\n\n if (typeof window === 'undefined' || typeof location === 'undefined') {\n throw new Error('getToken() is only available in browser environments');\n }\n\n const hash = location.hash.slice(1);\n if (!hash) throw new Error('No token found in URL hash');\n\n const token = new URLSearchParams(hash).get('mb_token');\n if (!token) throw new Error('Token \"mb_token\" not found in URL hash');\n\n cachedToken = decodeURIComponent(token);\n return cachedToken;\n}\n\n// =============================================================================\n// Dark Mode\n// =============================================================================\n\nlet currentDarkMode: boolean | null = null;\nconst darkModeListeners = new Set<(darkMode: boolean) => void>();\n\n/**\n * Get the current dark mode state.\n * Returns null if dark mode state hasn't been received from parent yet.\n */\nexport function getDarkMode(): boolean | null {\n return currentDarkMode;\n}\n\n/**\n * Listen for dark mode changes from the parent window.\n * Returns an unsubscribe function.\n * @param callback Called immediately with current state (if known) and on every change\n */\nexport function listenDarkMode(\n callback: (darkMode: boolean) => void\n): () => void {\n darkModeListeners.add(callback);\n\n // Call immediately with current value if we have one\n if (currentDarkMode !== null) {\n try {\n callback(currentDarkMode);\n } catch {}\n }\n\n return () => {\n darkModeListeners.delete(callback);\n };\n}\n\n// =============================================================================\n// Internal: Message Types and Type Guards\n// =============================================================================\n\ninterface AdaptResizeMessage {\n type: 'adapt-resize';\n width: number;\n height: number;\n}\n\ninterface AdaptDarkModeMessage {\n type: 'adapt-darkMode';\n darkMode: boolean;\n}\n\ninterface AdaptAutoResizingMessage {\n type: 'adapt-autoResizing';\n autoResizing: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDarkModeMessage(data: unknown): data is AdaptDarkModeMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-darkMode' &&\n typeof data.darkMode === 'boolean'\n );\n}\n\nfunction isAutoResizingMessage(\n data: unknown\n): data is AdaptAutoResizingMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-autoResizing' &&\n typeof data.autoResizing === 'boolean'\n );\n}\n\nfunction isValidDimension(value: number): boolean {\n return Number.isFinite(value) && value >= 1;\n}\n\nfunction hasPostMessage(\n obj: unknown\n): obj is { postMessage: typeof postMessage } {\n return isObject(obj) && typeof obj.postMessage === 'function';\n}\n\n// =============================================================================\n// Internal: Environment Detection\n// =============================================================================\n\nfunction isBrowser(): boolean {\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof window.postMessage === 'function'\n );\n}\n\nfunction isInIframe(): boolean {\n if (!isBrowser()) return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n}\n\n// =============================================================================\n// Internal: Resize Logic\n// =============================================================================\n\nlet lastSentWidth = -1;\nlet lastSentHeight = -1;\nlet throttleTimer: ReturnType<typeof setTimeout> | null = null;\nconst THROTTLE_MS = 16;\nlet resizeCount = 0;\nconst RESIZE_SETTLE_COUNT = 5;\n\nfunction getContentDimensions(): { width: number; height: number } | null {\n const { body } = document;\n if (!body) return null;\n\n // Only use body dimensions - html dimensions reflect viewport size (iframe's current size)\n // which causes feedback loops when iframe is larger than content\n const height = Math.max(body.scrollHeight, body.offsetHeight);\n const width = Math.max(body.scrollWidth, body.offsetWidth);\n\n if (!isValidDimension(width) || !isValidDimension(height)) return null;\n\n // Feedback loop detection: only after content has settled (first N resizes)\n // If measured height equals what we last sent, content is using viewport-relative sizing\n if (resizeCount >= RESIZE_SETTLE_COUNT && height === lastSentHeight) {\n return null;\n }\n\n return { width, height: height + 1 };\n}\n\nfunction sendResizeImmediate(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n const parent = window.parent;\n if (!hasPostMessage(parent)) return;\n\n const dims = getContentDimensions();\n if (!dims) return;\n\n const { width, height } = dims;\n if (width === lastSentWidth && height === lastSentHeight) return;\n\n // Reset counter on width change (window resize) to allow content to re-settle\n if (width !== lastSentWidth && lastSentWidth !== -1) {\n resizeCount = 0;\n }\n\n lastSentWidth = width;\n lastSentHeight = height;\n resizeCount++;\n\n try {\n parent.postMessage(\n { type: 'adapt-resize', width, height } satisfies AdaptResizeMessage,\n '*'\n );\n } catch {}\n}\n\nfunction sendResizeThrottled(): void {\n if (throttleTimer !== null) return;\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n sendResizeImmediate();\n }, THROTTLE_MS);\n}\n\nfunction sendResizeForced(): void {\n // Reset last sent values to force sending even if dimensions haven't changed\n lastSentWidth = -1;\n lastSentHeight = -1;\n resizeCount = 0;\n sendResizeImmediate();\n}\n\n// =============================================================================\n// Internal: Message Handling\n// =============================================================================\n\nfunction handleMessage(event: MessageEvent): void {\n let data: unknown;\n try {\n data = event.data;\n } catch {\n return;\n }\n\n if (isDarkModeMessage(data)) {\n currentDarkMode = data.darkMode;\n for (const listener of darkModeListeners) {\n try {\n listener(data.darkMode);\n } catch {}\n }\n }\n\n if (isAutoResizingMessage(data) && data.autoResizing) {\n // Parent requested auto-resizing - immediately send current dimensions\n sendResizeForced();\n }\n}\n\n// =============================================================================\n// Internal: Auto-initialization\n// =============================================================================\n\nfunction autoInit(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n // Listen for messages from parent\n window.addEventListener('message', handleMessage);\n\n // Set up resize observation\n const resizeObserver = new ResizeObserver(sendResizeThrottled);\n if (document.body) resizeObserver.observe(document.body);\n if (document.documentElement)\n resizeObserver.observe(document.documentElement);\n\n // Set up mutation observation for DOM changes\n if (document.body) {\n const mutationObserver = new MutationObserver(sendResizeThrottled);\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true\n });\n }\n\n // Capture load events on resources (images, iframes, etc.)\n document.addEventListener('load', sendResizeThrottled, true);\n\n // Wait for fonts to load\n document.fonts?.ready?.then(() => {\n sendResizeImmediate();\n });\n\n // Send initial size when fully loaded\n if (document.readyState === 'complete') {\n // Already loaded, send immediately\n sendResizeImmediate();\n } else {\n // Wait for full load\n window.addEventListener('load', () => {\n sendResizeImmediate();\n });\n }\n\n // Send again with delays to handle timing issues where parent sends\n // adapt-autoResizing before we're fully ready\n setTimeout(sendResizeImmediate, 50);\n setTimeout(sendResizeImmediate, 150);\n}\n\n// Run initialization when DOM is ready\nif (isBrowser()) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n // DOM already ready, init now\n autoInit();\n }\n}\n"],
|
|
5
|
-
"mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,iBAAAE,EAAA,aAAAC,EAAA,mBAAAC,IAAA,eAAAC,EAAAL,GAWA,IAAIM,EAA6B,KAM1B,SAASH,EAASI,EAAgB,GAAe,CACtD,GAAIA,EAAe,MAAO,YAC1B,GAAID,IAAgB,KAAM,OAAOA,EAEjC,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IACvD,MAAM,IAAI,MAAM,sDAAsD,EAGxE,IAAME,EAAO,SAAS,KAAK,MAAM,CAAC,EAClC,GAAI,CAACA,EAAM,MAAM,IAAI,MAAM,4BAA4B,EAEvD,IAAMC,EAAQ,IAAI,gBAAgBD,CAAI,EAAE,IAAI,UAAU,EACtD,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,wCAAwC,EAEpE,OAAAH,EAAc,mBAAmBG,CAAK,EAC/BH,CACT,CAMA,IAAII,EAAkC,KAChCC,EAAoB,IAAI,IAMvB,SAAST,GAA8B,CAC5C,OAAOQ,CACT,CAOO,SAASN,EACdQ,EACY,CAIZ,GAHAD,EAAkB,IAAIC,CAAQ,EAG1BF,IAAoB,KACtB,GAAI,CACFE,EAASF,CAAe,CAC1B,MAAQ,CAAC,CAGX,MAAO,IAAM,CACXC,EAAkB,OAAOC,CAAQ,CACnC,CACF,CAsBA,SAASC,EAASC,EAAkD,CAClE,OAAOA,IAAU,MAAQ,OAAOA,GAAU,UAAY,CAAC,MAAM,QAAQA,CAAK,CAC5E,CAEA,SAASC,EAAkBC,EAA6C,CACtE,OACEH,EAASG,CAAI,GACbA,EAAK,OAAS,kBACd,OAAOA,EAAK,UAAa,SAE7B,CAEA,SAASC,EACPD,EACkC,CAClC,OACEH,EAASG,CAAI,GACbA,EAAK,OAAS,sBACd,OAAOA,EAAK,cAAiB,SAEjC,CAEA,SAASE,EAAiBJ,EAAwB,CAChD,OAAO,OAAO,SAASA,CAAK,GAAKA,GAAS,CAC5C,CAEA,SAASK,EACPC,EAC4C,CAC5C,OAAOP,EAASO,CAAG,GAAK,OAAOA,EAAI,aAAgB,UACrD,CAMA,SAASC,GAAqB,CAC5B,OACE,OAAO,OAAW,KAClB,OAAO,SAAa,KACpB,OAAO,OAAO,aAAgB,UAElC,CAEA,SAASC,GAAsB,CAC7B,GAAI,CAACD,EAAU,EAAG,MAAO,GACzB,GAAI,CACF,OAAO,OAAO,OAAS,OAAO,GAChC,MAAQ,CACN,MAAO,EACT,CACF,CAMA,IAAIE,EAAgB,GAChBC,EAAiB,GACjBC,EAAsD,KACpDC,EAAc,GAChBC,EAAc,EACZC,EAAsB,EAE5B,SAASC,GAAiE,CACxE,GAAM,CAAE,KAAAC,CAAK,EAAI,
|
|
6
|
-
"names": ["frontend_exports", "__export", "getDarkMode", "getToken", "listenDarkMode", "__toCommonJS", "cachedToken", "isDevelopment", "hash", "token", "currentDarkMode", "darkModeListeners", "callback", "isObject", "value", "isDarkModeMessage", "data", "isAutoResizingMessage", "isValidDimension", "hasPostMessage", "obj", "isBrowser", "isInIframe", "lastSentWidth", "lastSentHeight", "throttleTimer", "THROTTLE_MS", "resizeCount", "RESIZE_SETTLE_COUNT", "getContentDimensions", "body", "height", "width", "sendResizeImmediate", "parent", "dims", "sendResizeThrottled", "sendResizeForced", "handleMessage", "event", "listener", "autoInit", "resizeObserver"]
|
|
4
|
+
"sourcesContent": ["/**\n * Frontend utilities for Adapt plugins running in browser/iframe contexts\n *\n * Iframe resizing is handled automatically - no initialization required.\n * Just import the functions you need and the resize communication starts automatically.\n */\n\n// =============================================================================\n// Token Management\n// =============================================================================\n\nlet cachedToken: string | null = null;\n\n/**\n * Get the authentication token from the URL hash.\n * @param isDevelopment If true, returns a dev token for local development\n */\nexport function getToken(isDevelopment = false): string {\n if (isDevelopment) return 'dev-token';\n if (cachedToken !== null) return cachedToken;\n\n if (typeof window === 'undefined' || typeof location === 'undefined') {\n throw new Error('getToken() is only available in browser environments');\n }\n\n const hash = location.hash.slice(1);\n if (!hash) throw new Error('No token found in URL hash');\n\n const token = new URLSearchParams(hash).get('mb_token');\n if (!token) throw new Error('Token \"mb_token\" not found in URL hash');\n\n cachedToken = decodeURIComponent(token);\n return cachedToken;\n}\n\n// =============================================================================\n// Dark Mode\n// =============================================================================\n\nlet currentDarkMode: boolean | null = null;\nconst darkModeListeners = new Set<(darkMode: boolean) => void>();\n\n/**\n * Get the current dark mode state.\n * Returns null if dark mode state hasn't been received from parent yet.\n */\nexport function getDarkMode(): boolean | null {\n return currentDarkMode;\n}\n\n/**\n * Listen for dark mode changes from the parent window.\n * Returns an unsubscribe function.\n * @param callback Called immediately with current state (if known) and on every change\n */\nexport function listenDarkMode(\n callback: (darkMode: boolean) => void\n): () => void {\n darkModeListeners.add(callback);\n\n // Call immediately with current value if we have one\n if (currentDarkMode !== null) {\n try {\n callback(currentDarkMode);\n } catch {}\n }\n\n return () => {\n darkModeListeners.delete(callback);\n };\n}\n\n// =============================================================================\n// Internal: Message Types and Type Guards\n// =============================================================================\n\ninterface AdaptResizeMessage {\n type: 'adapt-resize';\n width: number;\n height: number;\n}\n\ninterface AdaptDarkModeMessage {\n type: 'adapt-darkMode';\n darkMode: boolean;\n}\n\ninterface AdaptAutoResizingMessage {\n type: 'adapt-autoResizing';\n autoResizing: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDarkModeMessage(data: unknown): data is AdaptDarkModeMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-darkMode' &&\n typeof data.darkMode === 'boolean'\n );\n}\n\nfunction isAutoResizingMessage(\n data: unknown\n): data is AdaptAutoResizingMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-autoResizing' &&\n typeof data.autoResizing === 'boolean'\n );\n}\n\nfunction isValidDimension(value: number): boolean {\n return Number.isFinite(value) && value >= 1;\n}\n\nfunction hasPostMessage(\n obj: unknown\n): obj is { postMessage: typeof postMessage } {\n return isObject(obj) && typeof obj.postMessage === 'function';\n}\n\n// =============================================================================\n// Internal: Environment Detection\n// =============================================================================\n\nfunction isBrowser(): boolean {\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof window.postMessage === 'function'\n );\n}\n\nfunction isInIframe(): boolean {\n if (!isBrowser()) return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n}\n\n// =============================================================================\n// Internal: Resize Logic\n// =============================================================================\n\nlet lastSentWidth = -1;\nlet lastSentHeight = -1;\nlet throttleTimer: ReturnType<typeof setTimeout> | null = null;\nconst THROTTLE_MS = 16;\nlet resizeCount = 0;\nconst RESIZE_SETTLE_COUNT = 5;\n\nfunction getContentDimensions(): { width: number; height: number } | null {\n const { body, documentElement: html } = document;\n if (!body) return null;\n\n // Get computed styles for body margin\n // body.scrollHeight/offsetHeight include padding and border but NOT margin\n const bodyStyle = getComputedStyle(body);\n const bodyMarginTop = parseFloat(bodyStyle.marginTop) || 0;\n const bodyMarginBottom = parseFloat(bodyStyle.marginBottom) || 0;\n const bodyMarginLeft = parseFloat(bodyStyle.marginLeft) || 0;\n const bodyMarginRight = parseFloat(bodyStyle.marginRight) || 0;\n\n // Get computed styles for html element padding and border\n // html padding creates space around the body, html border adds to outer edge\n const htmlStyle = getComputedStyle(html);\n const htmlPaddingTop = parseFloat(htmlStyle.paddingTop) || 0;\n const htmlPaddingBottom = parseFloat(htmlStyle.paddingBottom) || 0;\n const htmlPaddingLeft = parseFloat(htmlStyle.paddingLeft) || 0;\n const htmlPaddingRight = parseFloat(htmlStyle.paddingRight) || 0;\n const htmlBorderTop = parseFloat(htmlStyle.borderTopWidth) || 0;\n const htmlBorderBottom = parseFloat(htmlStyle.borderBottomWidth) || 0;\n const htmlBorderLeft = parseFloat(htmlStyle.borderLeftWidth) || 0;\n const htmlBorderRight = parseFloat(htmlStyle.borderRightWidth) || 0;\n\n // Calculate total dimensions:\n // body content (scrollHeight/offsetHeight includes body padding + border)\n // + body margin (space between body and html's inner edge)\n // + html padding (space between html's inner edge and body)\n // + html border (html element's border)\n const bodyHeight = Math.max(body.scrollHeight, body.offsetHeight);\n const bodyWidth = Math.max(body.scrollWidth, body.offsetWidth);\n\n const height =\n bodyHeight +\n bodyMarginTop +\n bodyMarginBottom +\n htmlPaddingTop +\n htmlPaddingBottom +\n htmlBorderTop +\n htmlBorderBottom;\n\n const width =\n bodyWidth +\n bodyMarginLeft +\n bodyMarginRight +\n htmlPaddingLeft +\n htmlPaddingRight +\n htmlBorderLeft +\n htmlBorderRight;\n\n if (!isValidDimension(width) || !isValidDimension(height)) return null;\n\n // Feedback loop detection: only after content has settled (first N resizes)\n // If measured height equals what we last sent, content is using viewport-relative sizing\n if (resizeCount >= RESIZE_SETTLE_COUNT && height === lastSentHeight) {\n return null;\n }\n\n return { width, height: height + 1 };\n}\n\nfunction sendResizeImmediate(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n const parent = window.parent;\n if (!hasPostMessage(parent)) return;\n\n const dims = getContentDimensions();\n if (!dims) return;\n\n const { width, height } = dims;\n if (width === lastSentWidth && height === lastSentHeight) return;\n\n // Reset counter on width change (window resize) to allow content to re-settle\n if (width !== lastSentWidth && lastSentWidth !== -1) {\n resizeCount = 0;\n }\n\n lastSentWidth = width;\n lastSentHeight = height;\n resizeCount++;\n\n try {\n parent.postMessage(\n { type: 'adapt-resize', width, height } satisfies AdaptResizeMessage,\n '*'\n );\n } catch {}\n}\n\nfunction sendResizeThrottled(): void {\n if (throttleTimer !== null) return;\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n sendResizeImmediate();\n }, THROTTLE_MS);\n}\n\nfunction sendResizeForced(): void {\n // Reset last sent values to force sending even if dimensions haven't changed\n lastSentWidth = -1;\n lastSentHeight = -1;\n resizeCount = 0;\n sendResizeImmediate();\n}\n\n// =============================================================================\n// Internal: Message Handling\n// =============================================================================\n\nfunction handleMessage(event: MessageEvent): void {\n let data: unknown;\n try {\n data = event.data;\n } catch {\n return;\n }\n\n if (isDarkModeMessage(data)) {\n currentDarkMode = data.darkMode;\n for (const listener of darkModeListeners) {\n try {\n listener(data.darkMode);\n } catch {}\n }\n }\n\n if (isAutoResizingMessage(data) && data.autoResizing) {\n // Parent requested auto-resizing - immediately send current dimensions\n sendResizeForced();\n }\n}\n\n// =============================================================================\n// Internal: Auto-initialization\n// =============================================================================\n\nfunction autoInit(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n // Listen for messages from parent\n window.addEventListener('message', handleMessage);\n\n // Set up resize observation\n const resizeObserver = new ResizeObserver(sendResizeThrottled);\n if (document.body) resizeObserver.observe(document.body);\n if (document.documentElement)\n resizeObserver.observe(document.documentElement);\n\n // Set up mutation observation for DOM changes\n if (document.body) {\n const mutationObserver = new MutationObserver(sendResizeThrottled);\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true\n });\n }\n\n // Capture load events on resources (images, iframes, etc.)\n document.addEventListener('load', sendResizeThrottled, true);\n\n // Wait for fonts to load\n document.fonts?.ready?.then(() => {\n sendResizeImmediate();\n });\n\n // Send initial size when fully loaded\n if (document.readyState === 'complete') {\n // Already loaded, send immediately\n sendResizeImmediate();\n } else {\n // Wait for full load\n window.addEventListener('load', () => {\n sendResizeImmediate();\n });\n }\n\n // Send again with delays to handle timing issues where parent sends\n // adapt-autoResizing before we're fully ready\n setTimeout(sendResizeImmediate, 50);\n setTimeout(sendResizeImmediate, 150);\n}\n\n// Run initialization when DOM is ready\nif (isBrowser()) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n // DOM already ready, init now\n autoInit();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["frontend_exports", "__export", "getDarkMode", "getToken", "listenDarkMode", "__toCommonJS", "cachedToken", "isDevelopment", "hash", "token", "currentDarkMode", "darkModeListeners", "callback", "isObject", "value", "isDarkModeMessage", "data", "isAutoResizingMessage", "isValidDimension", "hasPostMessage", "obj", "isBrowser", "isInIframe", "lastSentWidth", "lastSentHeight", "throttleTimer", "THROTTLE_MS", "resizeCount", "RESIZE_SETTLE_COUNT", "getContentDimensions", "body", "html", "bodyStyle", "bodyMarginTop", "bodyMarginBottom", "bodyMarginLeft", "bodyMarginRight", "htmlStyle", "htmlPaddingTop", "htmlPaddingBottom", "htmlPaddingLeft", "htmlPaddingRight", "htmlBorderTop", "htmlBorderBottom", "htmlBorderLeft", "htmlBorderRight", "bodyHeight", "bodyWidth", "height", "width", "sendResizeImmediate", "parent", "dims", "sendResizeThrottled", "sendResizeForced", "handleMessage", "event", "listener", "autoInit", "resizeObserver"]
|
|
7
7
|
}
|
package/dist/esm/frontend.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var a=null;function N(e=!1){if(e)return"dev-token";if(a!==null)return a;if(typeof window>"u"||typeof location>"u")throw new Error("getToken() is only available in browser environments");let t=location.hash.slice(1);if(!t)throw new Error("No token found in URL hash");let n=new URLSearchParams(t).get("mb_token");if(!n)throw new Error('Token "mb_token" not found in URL hash');return a=decodeURIComponent(n),a}var d=null,g=new Set;function j(){return d}function V(e){if(g.add(e),d!==null)try{e(d)}catch{}return()=>{g.delete(e)}}function p(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function W(e){return p(e)&&e.type==="adapt-darkMode"&&typeof e.darkMode=="boolean"}function C(e){return p(e)&&e.type==="adapt-autoResizing"&&typeof e.autoResizing=="boolean"}function y(e){return Number.isFinite(e)&&e>=1}function I(e){return p(e)&&typeof e.postMessage=="function"}function c(){return typeof window<"u"&&typeof document<"u"&&typeof window.postMessage=="function"}function w(){if(!c())return!1;try{return window.self!==window.top}catch{return!0}}var s=-1,u=-1,h=null,P=16,l=0,x=5;function H(){let{body:e,documentElement:t}=document;if(!e)return null;let n=getComputedStyle(e),i=parseFloat(n.marginTop)||0,k=parseFloat(n.marginBottom)||0,R=parseFloat(n.marginLeft)||0,T=parseFloat(n.marginRight)||0,o=getComputedStyle(t),v=parseFloat(o.paddingTop)||0,z=parseFloat(o.paddingBottom)||0,L=parseFloat(o.paddingLeft)||0,E=parseFloat(o.paddingRight)||0,F=parseFloat(o.borderTopWidth)||0,S=parseFloat(o.borderBottomWidth)||0,A=parseFloat(o.borderLeftWidth)||0,B=parseFloat(o.borderRightWidth)||0,D=Math.max(e.scrollHeight,e.offsetHeight),O=Math.max(e.scrollWidth,e.offsetWidth),f=D+i+k+v+z+F+S,b=O+R+T+L+E+A+B;return!y(b)||!y(f)||l>=x&&f===u?null:{width:b,height:f+1}}function r(){if(!c()||!w())return;let e=window.parent;if(!I(e))return;let t=H();if(!t)return;let{width:n,height:i}=t;if(!(n===s&&i===u)){n!==s&&s!==-1&&(l=0),s=n,u=i,l++;try{e.postMessage({type:"adapt-resize",width:n,height:i},"*")}catch{}}}function m(){h===null&&(h=setTimeout(()=>{h=null,r()},P))}function U(){s=-1,u=-1,l=0,r()}function _(e){let t;try{t=e.data}catch{return}if(W(t)){d=t.darkMode;for(let n of g)try{n(t.darkMode)}catch{}}C(t)&&t.autoResizing&&U()}function M(){if(!c()||!w())return;window.addEventListener("message",_);let e=new ResizeObserver(m);document.body&&e.observe(document.body),document.documentElement&&e.observe(document.documentElement),document.body&&new MutationObserver(m).observe(document.body,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),document.addEventListener("load",m,!0),document.fonts?.ready?.then(()=>{r()}),document.readyState==="complete"?r():window.addEventListener("load",()=>{r()}),setTimeout(r,50),setTimeout(r,150)}c()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",M):M());export{j as getDarkMode,N as getToken,V as listenDarkMode};
|
|
2
2
|
//# sourceMappingURL=frontend.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/frontend.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Frontend utilities for Adapt plugins running in browser/iframe contexts\n *\n * Iframe resizing is handled automatically - no initialization required.\n * Just import the functions you need and the resize communication starts automatically.\n */\n\n// =============================================================================\n// Token Management\n// =============================================================================\n\nlet cachedToken: string | null = null;\n\n/**\n * Get the authentication token from the URL hash.\n * @param isDevelopment If true, returns a dev token for local development\n */\nexport function getToken(isDevelopment = false): string {\n if (isDevelopment) return 'dev-token';\n if (cachedToken !== null) return cachedToken;\n\n if (typeof window === 'undefined' || typeof location === 'undefined') {\n throw new Error('getToken() is only available in browser environments');\n }\n\n const hash = location.hash.slice(1);\n if (!hash) throw new Error('No token found in URL hash');\n\n const token = new URLSearchParams(hash).get('mb_token');\n if (!token) throw new Error('Token \"mb_token\" not found in URL hash');\n\n cachedToken = decodeURIComponent(token);\n return cachedToken;\n}\n\n// =============================================================================\n// Dark Mode\n// =============================================================================\n\nlet currentDarkMode: boolean | null = null;\nconst darkModeListeners = new Set<(darkMode: boolean) => void>();\n\n/**\n * Get the current dark mode state.\n * Returns null if dark mode state hasn't been received from parent yet.\n */\nexport function getDarkMode(): boolean | null {\n return currentDarkMode;\n}\n\n/**\n * Listen for dark mode changes from the parent window.\n * Returns an unsubscribe function.\n * @param callback Called immediately with current state (if known) and on every change\n */\nexport function listenDarkMode(\n callback: (darkMode: boolean) => void\n): () => void {\n darkModeListeners.add(callback);\n\n // Call immediately with current value if we have one\n if (currentDarkMode !== null) {\n try {\n callback(currentDarkMode);\n } catch {}\n }\n\n return () => {\n darkModeListeners.delete(callback);\n };\n}\n\n// =============================================================================\n// Internal: Message Types and Type Guards\n// =============================================================================\n\ninterface AdaptResizeMessage {\n type: 'adapt-resize';\n width: number;\n height: number;\n}\n\ninterface AdaptDarkModeMessage {\n type: 'adapt-darkMode';\n darkMode: boolean;\n}\n\ninterface AdaptAutoResizingMessage {\n type: 'adapt-autoResizing';\n autoResizing: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDarkModeMessage(data: unknown): data is AdaptDarkModeMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-darkMode' &&\n typeof data.darkMode === 'boolean'\n );\n}\n\nfunction isAutoResizingMessage(\n data: unknown\n): data is AdaptAutoResizingMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-autoResizing' &&\n typeof data.autoResizing === 'boolean'\n );\n}\n\nfunction isValidDimension(value: number): boolean {\n return Number.isFinite(value) && value >= 1;\n}\n\nfunction hasPostMessage(\n obj: unknown\n): obj is { postMessage: typeof postMessage } {\n return isObject(obj) && typeof obj.postMessage === 'function';\n}\n\n// =============================================================================\n// Internal: Environment Detection\n// =============================================================================\n\nfunction isBrowser(): boolean {\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof window.postMessage === 'function'\n );\n}\n\nfunction isInIframe(): boolean {\n if (!isBrowser()) return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n}\n\n// =============================================================================\n// Internal: Resize Logic\n// =============================================================================\n\nlet lastSentWidth = -1;\nlet lastSentHeight = -1;\nlet throttleTimer: ReturnType<typeof setTimeout> | null = null;\nconst THROTTLE_MS = 16;\nlet resizeCount = 0;\nconst RESIZE_SETTLE_COUNT = 5;\n\nfunction getContentDimensions(): { width: number; height: number } | null {\n const { body } = document;\n if (!body) return null;\n\n // Only use body dimensions - html dimensions reflect viewport size (iframe's current size)\n // which causes feedback loops when iframe is larger than content\n const height = Math.max(body.scrollHeight, body.offsetHeight);\n const width = Math.max(body.scrollWidth, body.offsetWidth);\n\n if (!isValidDimension(width) || !isValidDimension(height)) return null;\n\n // Feedback loop detection: only after content has settled (first N resizes)\n // If measured height equals what we last sent, content is using viewport-relative sizing\n if (resizeCount >= RESIZE_SETTLE_COUNT && height === lastSentHeight) {\n return null;\n }\n\n return { width, height: height + 1 };\n}\n\nfunction sendResizeImmediate(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n const parent = window.parent;\n if (!hasPostMessage(parent)) return;\n\n const dims = getContentDimensions();\n if (!dims) return;\n\n const { width, height } = dims;\n if (width === lastSentWidth && height === lastSentHeight) return;\n\n // Reset counter on width change (window resize) to allow content to re-settle\n if (width !== lastSentWidth && lastSentWidth !== -1) {\n resizeCount = 0;\n }\n\n lastSentWidth = width;\n lastSentHeight = height;\n resizeCount++;\n\n try {\n parent.postMessage(\n { type: 'adapt-resize', width, height } satisfies AdaptResizeMessage,\n '*'\n );\n } catch {}\n}\n\nfunction sendResizeThrottled(): void {\n if (throttleTimer !== null) return;\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n sendResizeImmediate();\n }, THROTTLE_MS);\n}\n\nfunction sendResizeForced(): void {\n // Reset last sent values to force sending even if dimensions haven't changed\n lastSentWidth = -1;\n lastSentHeight = -1;\n resizeCount = 0;\n sendResizeImmediate();\n}\n\n// =============================================================================\n// Internal: Message Handling\n// =============================================================================\n\nfunction handleMessage(event: MessageEvent): void {\n let data: unknown;\n try {\n data = event.data;\n } catch {\n return;\n }\n\n if (isDarkModeMessage(data)) {\n currentDarkMode = data.darkMode;\n for (const listener of darkModeListeners) {\n try {\n listener(data.darkMode);\n } catch {}\n }\n }\n\n if (isAutoResizingMessage(data) && data.autoResizing) {\n // Parent requested auto-resizing - immediately send current dimensions\n sendResizeForced();\n }\n}\n\n// =============================================================================\n// Internal: Auto-initialization\n// =============================================================================\n\nfunction autoInit(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n // Listen for messages from parent\n window.addEventListener('message', handleMessage);\n\n // Set up resize observation\n const resizeObserver = new ResizeObserver(sendResizeThrottled);\n if (document.body) resizeObserver.observe(document.body);\n if (document.documentElement)\n resizeObserver.observe(document.documentElement);\n\n // Set up mutation observation for DOM changes\n if (document.body) {\n const mutationObserver = new MutationObserver(sendResizeThrottled);\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true\n });\n }\n\n // Capture load events on resources (images, iframes, etc.)\n document.addEventListener('load', sendResizeThrottled, true);\n\n // Wait for fonts to load\n document.fonts?.ready?.then(() => {\n sendResizeImmediate();\n });\n\n // Send initial size when fully loaded\n if (document.readyState === 'complete') {\n // Already loaded, send immediately\n sendResizeImmediate();\n } else {\n // Wait for full load\n window.addEventListener('load', () => {\n sendResizeImmediate();\n });\n }\n\n // Send again with delays to handle timing issues where parent sends\n // adapt-autoResizing before we're fully ready\n setTimeout(sendResizeImmediate, 50);\n setTimeout(sendResizeImmediate, 150);\n}\n\n// Run initialization when DOM is ready\nif (isBrowser()) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n // DOM already ready, init now\n autoInit();\n }\n}\n"],
|
|
5
|
-
"mappings": "AAWA,IAAIA,EAA6B,KAM1B,SAASC,EAASC,EAAgB,GAAe,CACtD,GAAIA,EAAe,MAAO,YAC1B,GAAIF,IAAgB,KAAM,OAAOA,EAEjC,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IACvD,MAAM,IAAI,MAAM,sDAAsD,EAGxE,IAAMG,EAAO,SAAS,KAAK,MAAM,CAAC,EAClC,GAAI,CAACA,EAAM,MAAM,IAAI,MAAM,4BAA4B,EAEvD,IAAMC,EAAQ,IAAI,gBAAgBD,CAAI,EAAE,IAAI,UAAU,EACtD,GAAI,CAACC,EAAO,MAAM,IAAI,MAAM,wCAAwC,EAEpE,OAAAJ,EAAc,mBAAmBI,CAAK,EAC/BJ,CACT,CAMA,IAAIK,EAAkC,KAChCC,EAAoB,IAAI,IAMvB,SAASC,GAA8B,CAC5C,OAAOF,CACT,CAOO,SAASG,EACdC,EACY,CAIZ,GAHAH,EAAkB,IAAIG,CAAQ,EAG1BJ,IAAoB,KACtB,GAAI,CACFI,EAASJ,CAAe,CAC1B,MAAQ,CAAC,CAGX,MAAO,IAAM,CACXC,EAAkB,OAAOG,CAAQ,CACnC,CACF,CAsBA,SAASC,EAASC,EAAkD,CAClE,OAAOA,IAAU,MAAQ,OAAOA,GAAU,UAAY,CAAC,MAAM,QAAQA,CAAK,CAC5E,CAEA,SAASC,EAAkBC,EAA6C,CACtE,OACEH,EAASG,CAAI,GACbA,EAAK,OAAS,kBACd,OAAOA,EAAK,UAAa,SAE7B,CAEA,SAASC,EACPD,EACkC,CAClC,OACEH,EAASG,CAAI,GACbA,EAAK,OAAS,sBACd,OAAOA,EAAK,cAAiB,SAEjC,CAEA,SAASE,EAAiBJ,EAAwB,CAChD,OAAO,OAAO,SAASA,CAAK,GAAKA,GAAS,CAC5C,CAEA,SAASK,EACPC,EAC4C,CAC5C,OAAOP,EAASO,CAAG,GAAK,OAAOA,EAAI,aAAgB,UACrD,CAMA,SAASC,GAAqB,CAC5B,OACE,OAAO,OAAW,KAClB,OAAO,SAAa,KACpB,OAAO,OAAO,aAAgB,UAElC,CAEA,SAASC,GAAsB,CAC7B,GAAI,CAACD,EAAU,EAAG,MAAO,GACzB,GAAI,CACF,OAAO,OAAO,OAAS,OAAO,GAChC,MAAQ,CACN,MAAO,EACT,CACF,CAMA,IAAIE,EAAgB,GAChBC,EAAiB,GACjBC,EAAsD,KACpDC,EAAc,GAChBC,EAAc,EACZC,EAAsB,EAE5B,SAASC,GAAiE,CACxE,GAAM,CAAE,KAAAC,CAAK,EAAI,
|
|
6
|
-
"names": ["cachedToken", "getToken", "isDevelopment", "hash", "token", "currentDarkMode", "darkModeListeners", "getDarkMode", "listenDarkMode", "callback", "isObject", "value", "isDarkModeMessage", "data", "isAutoResizingMessage", "isValidDimension", "hasPostMessage", "obj", "isBrowser", "isInIframe", "lastSentWidth", "lastSentHeight", "throttleTimer", "THROTTLE_MS", "resizeCount", "RESIZE_SETTLE_COUNT", "getContentDimensions", "body", "height", "width", "sendResizeImmediate", "parent", "dims", "sendResizeThrottled", "sendResizeForced", "handleMessage", "event", "listener", "autoInit", "resizeObserver"]
|
|
4
|
+
"sourcesContent": ["/**\n * Frontend utilities for Adapt plugins running in browser/iframe contexts\n *\n * Iframe resizing is handled automatically - no initialization required.\n * Just import the functions you need and the resize communication starts automatically.\n */\n\n// =============================================================================\n// Token Management\n// =============================================================================\n\nlet cachedToken: string | null = null;\n\n/**\n * Get the authentication token from the URL hash.\n * @param isDevelopment If true, returns a dev token for local development\n */\nexport function getToken(isDevelopment = false): string {\n if (isDevelopment) return 'dev-token';\n if (cachedToken !== null) return cachedToken;\n\n if (typeof window === 'undefined' || typeof location === 'undefined') {\n throw new Error('getToken() is only available in browser environments');\n }\n\n const hash = location.hash.slice(1);\n if (!hash) throw new Error('No token found in URL hash');\n\n const token = new URLSearchParams(hash).get('mb_token');\n if (!token) throw new Error('Token \"mb_token\" not found in URL hash');\n\n cachedToken = decodeURIComponent(token);\n return cachedToken;\n}\n\n// =============================================================================\n// Dark Mode\n// =============================================================================\n\nlet currentDarkMode: boolean | null = null;\nconst darkModeListeners = new Set<(darkMode: boolean) => void>();\n\n/**\n * Get the current dark mode state.\n * Returns null if dark mode state hasn't been received from parent yet.\n */\nexport function getDarkMode(): boolean | null {\n return currentDarkMode;\n}\n\n/**\n * Listen for dark mode changes from the parent window.\n * Returns an unsubscribe function.\n * @param callback Called immediately with current state (if known) and on every change\n */\nexport function listenDarkMode(\n callback: (darkMode: boolean) => void\n): () => void {\n darkModeListeners.add(callback);\n\n // Call immediately with current value if we have one\n if (currentDarkMode !== null) {\n try {\n callback(currentDarkMode);\n } catch {}\n }\n\n return () => {\n darkModeListeners.delete(callback);\n };\n}\n\n// =============================================================================\n// Internal: Message Types and Type Guards\n// =============================================================================\n\ninterface AdaptResizeMessage {\n type: 'adapt-resize';\n width: number;\n height: number;\n}\n\ninterface AdaptDarkModeMessage {\n type: 'adapt-darkMode';\n darkMode: boolean;\n}\n\ninterface AdaptAutoResizingMessage {\n type: 'adapt-autoResizing';\n autoResizing: boolean;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isDarkModeMessage(data: unknown): data is AdaptDarkModeMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-darkMode' &&\n typeof data.darkMode === 'boolean'\n );\n}\n\nfunction isAutoResizingMessage(\n data: unknown\n): data is AdaptAutoResizingMessage {\n return (\n isObject(data) &&\n data.type === 'adapt-autoResizing' &&\n typeof data.autoResizing === 'boolean'\n );\n}\n\nfunction isValidDimension(value: number): boolean {\n return Number.isFinite(value) && value >= 1;\n}\n\nfunction hasPostMessage(\n obj: unknown\n): obj is { postMessage: typeof postMessage } {\n return isObject(obj) && typeof obj.postMessage === 'function';\n}\n\n// =============================================================================\n// Internal: Environment Detection\n// =============================================================================\n\nfunction isBrowser(): boolean {\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n typeof window.postMessage === 'function'\n );\n}\n\nfunction isInIframe(): boolean {\n if (!isBrowser()) return false;\n try {\n return window.self !== window.top;\n } catch {\n return true;\n }\n}\n\n// =============================================================================\n// Internal: Resize Logic\n// =============================================================================\n\nlet lastSentWidth = -1;\nlet lastSentHeight = -1;\nlet throttleTimer: ReturnType<typeof setTimeout> | null = null;\nconst THROTTLE_MS = 16;\nlet resizeCount = 0;\nconst RESIZE_SETTLE_COUNT = 5;\n\nfunction getContentDimensions(): { width: number; height: number } | null {\n const { body, documentElement: html } = document;\n if (!body) return null;\n\n // Get computed styles for body margin\n // body.scrollHeight/offsetHeight include padding and border but NOT margin\n const bodyStyle = getComputedStyle(body);\n const bodyMarginTop = parseFloat(bodyStyle.marginTop) || 0;\n const bodyMarginBottom = parseFloat(bodyStyle.marginBottom) || 0;\n const bodyMarginLeft = parseFloat(bodyStyle.marginLeft) || 0;\n const bodyMarginRight = parseFloat(bodyStyle.marginRight) || 0;\n\n // Get computed styles for html element padding and border\n // html padding creates space around the body, html border adds to outer edge\n const htmlStyle = getComputedStyle(html);\n const htmlPaddingTop = parseFloat(htmlStyle.paddingTop) || 0;\n const htmlPaddingBottom = parseFloat(htmlStyle.paddingBottom) || 0;\n const htmlPaddingLeft = parseFloat(htmlStyle.paddingLeft) || 0;\n const htmlPaddingRight = parseFloat(htmlStyle.paddingRight) || 0;\n const htmlBorderTop = parseFloat(htmlStyle.borderTopWidth) || 0;\n const htmlBorderBottom = parseFloat(htmlStyle.borderBottomWidth) || 0;\n const htmlBorderLeft = parseFloat(htmlStyle.borderLeftWidth) || 0;\n const htmlBorderRight = parseFloat(htmlStyle.borderRightWidth) || 0;\n\n // Calculate total dimensions:\n // body content (scrollHeight/offsetHeight includes body padding + border)\n // + body margin (space between body and html's inner edge)\n // + html padding (space between html's inner edge and body)\n // + html border (html element's border)\n const bodyHeight = Math.max(body.scrollHeight, body.offsetHeight);\n const bodyWidth = Math.max(body.scrollWidth, body.offsetWidth);\n\n const height =\n bodyHeight +\n bodyMarginTop +\n bodyMarginBottom +\n htmlPaddingTop +\n htmlPaddingBottom +\n htmlBorderTop +\n htmlBorderBottom;\n\n const width =\n bodyWidth +\n bodyMarginLeft +\n bodyMarginRight +\n htmlPaddingLeft +\n htmlPaddingRight +\n htmlBorderLeft +\n htmlBorderRight;\n\n if (!isValidDimension(width) || !isValidDimension(height)) return null;\n\n // Feedback loop detection: only after content has settled (first N resizes)\n // If measured height equals what we last sent, content is using viewport-relative sizing\n if (resizeCount >= RESIZE_SETTLE_COUNT && height === lastSentHeight) {\n return null;\n }\n\n return { width, height: height + 1 };\n}\n\nfunction sendResizeImmediate(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n const parent = window.parent;\n if (!hasPostMessage(parent)) return;\n\n const dims = getContentDimensions();\n if (!dims) return;\n\n const { width, height } = dims;\n if (width === lastSentWidth && height === lastSentHeight) return;\n\n // Reset counter on width change (window resize) to allow content to re-settle\n if (width !== lastSentWidth && lastSentWidth !== -1) {\n resizeCount = 0;\n }\n\n lastSentWidth = width;\n lastSentHeight = height;\n resizeCount++;\n\n try {\n parent.postMessage(\n { type: 'adapt-resize', width, height } satisfies AdaptResizeMessage,\n '*'\n );\n } catch {}\n}\n\nfunction sendResizeThrottled(): void {\n if (throttleTimer !== null) return;\n\n throttleTimer = setTimeout(() => {\n throttleTimer = null;\n sendResizeImmediate();\n }, THROTTLE_MS);\n}\n\nfunction sendResizeForced(): void {\n // Reset last sent values to force sending even if dimensions haven't changed\n lastSentWidth = -1;\n lastSentHeight = -1;\n resizeCount = 0;\n sendResizeImmediate();\n}\n\n// =============================================================================\n// Internal: Message Handling\n// =============================================================================\n\nfunction handleMessage(event: MessageEvent): void {\n let data: unknown;\n try {\n data = event.data;\n } catch {\n return;\n }\n\n if (isDarkModeMessage(data)) {\n currentDarkMode = data.darkMode;\n for (const listener of darkModeListeners) {\n try {\n listener(data.darkMode);\n } catch {}\n }\n }\n\n if (isAutoResizingMessage(data) && data.autoResizing) {\n // Parent requested auto-resizing - immediately send current dimensions\n sendResizeForced();\n }\n}\n\n// =============================================================================\n// Internal: Auto-initialization\n// =============================================================================\n\nfunction autoInit(): void {\n if (!isBrowser() || !isInIframe()) return;\n\n // Listen for messages from parent\n window.addEventListener('message', handleMessage);\n\n // Set up resize observation\n const resizeObserver = new ResizeObserver(sendResizeThrottled);\n if (document.body) resizeObserver.observe(document.body);\n if (document.documentElement)\n resizeObserver.observe(document.documentElement);\n\n // Set up mutation observation for DOM changes\n if (document.body) {\n const mutationObserver = new MutationObserver(sendResizeThrottled);\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true\n });\n }\n\n // Capture load events on resources (images, iframes, etc.)\n document.addEventListener('load', sendResizeThrottled, true);\n\n // Wait for fonts to load\n document.fonts?.ready?.then(() => {\n sendResizeImmediate();\n });\n\n // Send initial size when fully loaded\n if (document.readyState === 'complete') {\n // Already loaded, send immediately\n sendResizeImmediate();\n } else {\n // Wait for full load\n window.addEventListener('load', () => {\n sendResizeImmediate();\n });\n }\n\n // Send again with delays to handle timing issues where parent sends\n // adapt-autoResizing before we're fully ready\n setTimeout(sendResizeImmediate, 50);\n setTimeout(sendResizeImmediate, 150);\n}\n\n// Run initialization when DOM is ready\nif (isBrowser()) {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit);\n } else {\n // DOM already ready, init now\n autoInit();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["cachedToken", "getToken", "isDevelopment", "hash", "token", "currentDarkMode", "darkModeListeners", "getDarkMode", "listenDarkMode", "callback", "isObject", "value", "isDarkModeMessage", "data", "isAutoResizingMessage", "isValidDimension", "hasPostMessage", "obj", "isBrowser", "isInIframe", "lastSentWidth", "lastSentHeight", "throttleTimer", "THROTTLE_MS", "resizeCount", "RESIZE_SETTLE_COUNT", "getContentDimensions", "body", "html", "bodyStyle", "bodyMarginTop", "bodyMarginBottom", "bodyMarginLeft", "bodyMarginRight", "htmlStyle", "htmlPaddingTop", "htmlPaddingBottom", "htmlPaddingLeft", "htmlPaddingRight", "htmlBorderTop", "htmlBorderBottom", "htmlBorderLeft", "htmlBorderRight", "bodyHeight", "bodyWidth", "height", "width", "sendResizeImmediate", "parent", "dims", "sendResizeThrottled", "sendResizeForced", "handleMessage", "event", "listener", "autoInit", "resizeObserver"]
|
|
7
7
|
}
|