From 00fa4207436723735afcb2be010e6fe63ba308e8 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 7 Mar 2026 23:45:10 -0700 Subject: [PATCH] Add iframe auto-resize via postMessage Embeds now report content height to the parent frame using a ResizeObserver on document.body. The EmbedDialog snippet includes a listener script that adjusts the iframe height on each resize event. Documented the spicebook-resize protocol in llms.txt. --- frontend/public/llms.txt | 22 ++++++++++++++++++- frontend/src/components/embed/EmbedViewer.tsx | 20 +++++++++++++++++ .../notebook/toolbar/EmbedDialog.tsx | 15 +++++++++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/frontend/public/llms.txt b/frontend/public/llms.txt index 9fef57d..6b14104 100644 --- a/frontend/public/llms.txt +++ b/frontend/public/llms.txt @@ -598,6 +598,26 @@ iframe.contentWindow.postMessage( Accepted values for `theme`: `"dark"`, `"light"`. Other messages are ignored. +### Auto-resize via postMessage + +The embed reports its content height to the parent page whenever the layout changes (initial render, simulation results, expanded cells). The parent listens for `spicebook-resize` messages and updates the iframe height: + +```javascript +window.addEventListener('message', function(e) { + if (e.data && e.data.type === 'spicebook-resize') { + document.getElementById('spicebook-{notebook_id}').style.height = e.data.height + 'px'; + } +}); +``` + +The message payload: + +```json +{ "type": "spicebook-resize", "height": 1842 } +``` + +`height` is `document.documentElement.scrollHeight` in pixels. The embed sends this message on every `ResizeObserver` callback, deduplicated to only fire when height actually changes. The **Embed** button's generated snippet includes this listener automatically. + ### Discovering the embed snippet -In the notebook editor UI, the **Embed** button in the toolbar opens a popover with a ready-to-copy iframe snippet and a theme toggle. +In the notebook editor UI, the **Embed** button in the toolbar opens a popover with a ready-to-copy iframe snippet (including auto-resize listener) and a theme toggle. diff --git a/frontend/src/components/embed/EmbedViewer.tsx b/frontend/src/components/embed/EmbedViewer.tsx index cf1339e..c7764a5 100644 --- a/frontend/src/components/embed/EmbedViewer.tsx +++ b/frontend/src/components/embed/EmbedViewer.tsx @@ -38,6 +38,26 @@ export default function EmbedViewer({ notebookId, initialTheme }: EmbedViewerPro return () => window.removeEventListener('message', handleMessage); }, []); + // Report content height to parent frame for auto-resize + useEffect(() => { + if (typeof window === 'undefined' || window.parent === window) return; + + let lastHeight = 0; + function reportHeight() { + const height = document.documentElement.scrollHeight; + if (height !== lastHeight) { + lastHeight = height; + window.parent.postMessage({ type: 'spicebook-resize', height }, '*'); + } + } + + const observer = new ResizeObserver(reportHeight); + observer.observe(document.body); + reportHeight(); + + return () => observer.disconnect(); + }, []); + // Keep a ref to notebook for use inside handleRun to avoid stale closures const notebookRef = useRef(notebook); notebookRef.current = notebook; diff --git a/frontend/src/components/notebook/toolbar/EmbedDialog.tsx b/frontend/src/components/notebook/toolbar/EmbedDialog.tsx index aa0f5b4..f99452c 100644 --- a/frontend/src/components/notebook/toolbar/EmbedDialog.tsx +++ b/frontend/src/components/notebook/toolbar/EmbedDialog.tsx @@ -14,12 +14,23 @@ export function EmbedDialog() { const origin = typeof window !== 'undefined' ? window.location.origin : ''; + const iframeId = `spicebook-${notebookId}`; + const snippet = ``; +> +`; function handleCopy() { navigator.clipboard.writeText(snippet).then(() => { @@ -72,7 +83,7 @@ export function EmbedDialog() {