// Embed (iframe) → host resize handshake (Phase 17 wave 17.3, OQ1 Option A). // // The Fixed-mode player renders an always-shown queue panel below the controls. A collapse/expand // toggle lets the embedder's viewer reclaim the panel's vertical space — but collapsing inside the // iframe only reclaims space if the *outer* iframe element also shrinks. The iframe cannot resize // itself, so it posts its desired pixel height to the host page; the embed snippet (minted by // EmbedSnippetBuilder.ForRelease) carries a tiny listener that sets iframe.style.height. // // Degrades safely: if the host page ignores or strips the snippet's listener (Option B's value), the // panel still renders and toggles inside the iframe — only the outer resize is lost. We post nothing // when not framed (window === parent), so the docked player is unaffected. // // Multi-embed isolation: EmbedSnippetBuilder.ForRelease mints a per-snippet random token and passes // it as ?EmbedId= in the iframe src. We read it here from window.location.search and include // it in the postMessage payload as `embedId`. The host-side resize script matches on this token so // only the correct iframe is resized when multiple embeds share a host page. If EmbedId is absent // (older already-pasted snippets), embedId is omitted from the payload — those snippets' scripts // ignore messages without a matching embedId, so there is no cross-talk either way. const MESSAGE_TYPE = "deepdrft-embed-resize"; /** Read the EmbedId query param from the iframe's own URL, if present. */ function readEmbedId(): string | null { try { return new URLSearchParams(window.location.search).get("EmbedId"); } catch { return null; } } // Resolved once at module load — the URL does not change while the iframe is alive. const embedId: string | null = readEmbedId(); /** * Measure the live rendered height of the player element and post it to the host page so it can size * the iframe to match. No-op when not embedded in a frame, or when the element is unmeasurable. * * targetOrigin is "*" deliberately: the embedder's origin is unknown (any blog can embed us) and the * payload carries no secrets — just a height the host is free to ignore. * * The payload includes `embedId` when the iframe src carried an EmbedId query param. The host-side * resize script matches on this field to isolate multiple embeds on the same page. */ export function postHeight(element: HTMLElement): void { if (window.parent === window) return; // Not framed — nothing to resize. if (!element) return; // ceil + a hairline guard against sub-pixel rounding that would otherwise clip the bottom edge. const height = Math.ceil(element.getBoundingClientRect().height) + 2; if (!Number.isFinite(height) || height <= 0) return; const payload: Record = { type: MESSAGE_TYPE, height }; if (embedId !== null) payload.embedId = embedId; window.parent.postMessage(payload, "*"); }