doodle-decoder / static /embed.js
cetusian's picture
Deploy doodle-decoder
b2cddc3 verified
Raw History Blame Contribute Delete
2.5 kB
/* Rune Labs: inside surogate.ai a demo drops its own header (the page row already names it) and
reports its height, so the page sizes the frame to fit and nothing scrolls inside it.
The height is the bottom of main's children, not of main itself: Gradio stretches <main> to fill
the frame, so measuring it would grow the frame, which grows main, forever.
surogate.ai asks for this with ?embed=surogate. Being in a frame is not enough: huggingface.co
shows every Space in one too, and there the header and footer should stay. */
(() => {
if (window.self === window.top || !new URLSearchParams(location.search).has("embed")) return;
document.documentElement.classList.add("rune-embedded");
let last = 0;
const contentBottom = (main) => {
let bottom = 0;
for (const child of main.children) bottom = Math.max(bottom, child.getBoundingClientRect().bottom);
return bottom + window.scrollY + parseFloat(getComputedStyle(main).paddingBottom || "0");
};
const send = () => {
const main = document.querySelector("main");
if (!main) return;
const height = Math.ceil(contentBottom(main) + 28);
if (Math.abs(height - last) < 2) return;
last = height;
window.parent.postMessage({ type: "rune-labs:height", height }, "*");
};
// Gradio renders on the server, then replaces <main> when the browser takes over, and components keep
// filling in after that. So watch the whole body, and let each measurement find the <main> of the moment.
let queued = false;
const schedule = () => {
if (queued) return;
queued = true;
requestAnimationFrame(() => { queued = false; send(); });
};
const sizes = new ResizeObserver(schedule);
const start = () => {
new MutationObserver(() => {
const main = document.querySelector("main");
if (main) for (const child of main.children) sizes.observe(child);
schedule();
}).observe(document.body, { childList: true, subtree: true, attributes: true, characterData: true });
addEventListener("load", schedule);
schedule();
};
// One "used" note on the first real interaction, so the page can tell a demo opened from a demo tried.
const used = () => window.parent.postMessage({ type: "rune-labs:used" }, "*");
addEventListener("pointerdown", used, { once: true, capture: true });
addEventListener("keydown", used, { once: true, capture: true });
if (document.readyState === "loading") addEventListener("DOMContentLoaded", start);
else start();
})();