/** * In-memory localStorage/sessionStorage shim, injected as the FIRST thing in the * document so the page's own scripts see working storage. * * The interactive iframe is sandboxed `allow-scripts` WITHOUT `allow-same-origin` * (intentional — combining them negates the sandbox for LLM-authored HTML). In a * null-origin document, touching `window.localStorage` throws a SecurityError; * many generated pages read/write storage in their setup code, so that throw * crashes the script before anything renders → a blank/black widget. This shim * replaces both storages with an in-memory implementation when the real ones are * inaccessible, keeping the sandbox intact while letting storage-using pages run. */ const STORAGE_SHIM = ``; /** * Runtime-error capture, injected as the VERY FIRST script so it observes errors * from the storage shim and every page script that follows. Generated interactive * pages frequently die on a runtime error (a `JSON.parse` of malformed config, a * reference to a CDN lib that failed to load, …) → the script aborts and the * widget renders blank. The sandboxed (null-origin) iframe can't be read by the * editor, but it CAN `postMessage` out: this forwards `window.onerror`, unhandled * rejections and `console.error` to the parent, which stores them per scene and * feeds them to the editor agent — so it can diagnose a blank page instead of * guessing. Only touches `window.*` so it stays sandbox-safe and unit-testable. * * The most important errors (a `JSON.parse` that aborts setup) fire SYNCHRONOUSLY * while srcDoc parses — potentially before the parent has subscribed its `message` * listener (which it installs from a passive effect after inserting the iframe). * To avoid losing exactly the errors this feature exists to surface, every post is * also buffered, and the shim re-emits the whole buffer when the parent sends a * `{ __maicErrorReplayRequest: true }` message once its listener is ready. The * parent dedups, so the live + replayed copies collapse to one. */ const ERROR_CAPTURE_SHIM = ``; /** * Patch embedded HTML to display correctly inside an iframe. * * Injects a runtime-error capture shim + a storage shim (so sandboxed pages that * use localStorage don't crash) plus CSS that ensures proper sizing and scrolling * behavior when HTML content is rendered via srcDoc in an iframe. The shims are * placed first so they run before the page's own scripts (error capture first, so * it also observes the storage shim). */ export function patchHtmlForIframe(html: string): string { const iframeCss = ``; const injection = '\n' + ERROR_CAPTURE_SHIM + '\n' + STORAGE_SHIM + '\n' + iframeCss; // Insert right after or at the start of the document const headIdx = html.indexOf(''); if (headIdx !== -1) { const insertPos = headIdx + 6; // after return html.substring(0, insertPos) + injection + html.substring(insertPos); } const headWithAttrs = html.indexOf('', headWithAttrs); if (closeAngle !== -1) { const insertPos = closeAngle + 1; return html.substring(0, insertPos) + injection + html.substring(insertPos); } } // Fallback: prepend return injection + html; }