import { customBlockHost } from "./bridge/sandboxClient.js" /** Limits host resize messages to 10 per second while preserving the latest height. */ const AUTO_RESIZE_THROTTLE_MS = 100 export function autoResize(args: { target: HTMLElement | null | undefined }): () => void { const { target } = args if (target === null || target === undefined) { return () => {} } // Use a leading/trailing throttle: send the first changed height immediately, then retain only // the latest height observed and send it when the throttling period ends. let lastHeightReportedToHost = -1 let pendingHeight: number | undefined let throttleTimeout: ReturnType | undefined /** Sends a height immediately and records it as the deduplication baseline. */ const sendHeightToHost = (height: number) => { lastHeightReportedToHost = height customBlockHost.postResize(height) } /** Sends the latest height queued during the throttling period, if one exists. */ const flushPendingHeight = () => { if (throttleTimeout !== undefined) { clearTimeout(throttleTimeout) throttleTimeout = undefined } if (pendingHeight === undefined) { return } const height = pendingHeight pendingHeight = undefined sendHeightToHost(height) throttleTimeout = setTimeout(flushPendingHeight, AUTO_RESIZE_THROTTLE_MS) } /** Measures the target and either sends, queues, or deduplicates its height. */ const measureAndScheduleResize = () => { const next = Math.ceil(target.getBoundingClientRect().height) if (next === lastHeightReportedToHost) { // The height has not changed since the last measurement. Do nothing. pendingHeight = undefined return } if (throttleTimeout !== undefined) { // The height has changed since the last measurement, but the throttling period has not // ended. Queue the new height for later. pendingHeight = next return } // The height has changed since the last measurement and the throttling period has ended. // Send the new height immediately and start a new throttling period. sendHeightToHost(next) throttleTimeout = setTimeout(flushPendingHeight, AUTO_RESIZE_THROTTLE_MS) } // Measure the initial height and schedule the first throttling period. measureAndScheduleResize() // If the ResizeObserver API is not available, return a function that cleans up the throttling // period started by the initial measurement. if (typeof ResizeObserver === "undefined") { return () => { clearTimeout(throttleTimeout) throttleTimeout = undefined } } // Observe the target and schedule subsequent measurements and throttling periods. const observer = new ResizeObserver(measureAndScheduleResize) observer.observe(target) // Clean up the observer and the throttling period. return () => { observer.disconnect() clearTimeout(throttleTimeout) throttleTimeout = undefined } }