{"version":3,"file":"buildStaticSrcDoc.mjs","names":[],"sources":["../../src/HtmlPreview/buildStaticSrcDoc.ts"],"sourcesContent":["import { buildAutoHeightScript } from './injectAutoHeightScript';\nimport { STORAGE_SHIM_SCRIPT } from './injectStorageShim';\n\ninterface BuildStaticSrcDocOptions {\n  background?: string;\n  content: string;\n  frameId: string;\n}\n\nconst shimsBlock = (frameId: string) =>\n  `<script>${STORAGE_SHIM_SCRIPT}</script><script>${buildAutoHeightScript(frameId)}</script>`;\n\nconst baseStyle = (background?: string) =>\n  `<style>html,body{margin:0;padding:0;${background ? `background:${background};` : ''}color-scheme:light dark;}</style>`;\n\n/**\n * Wrap a user's HTML document with our shims so the iframe can load it as\n * a single self-contained srcDoc. This path is used when the content is\n * *static* (not streaming) — the browser parses it via the normal HTML\n * pipeline, so external `<script src=…>` tags load and execute exactly\n * like they would on a regular page. Tailwind CDN, Chart.js, p5.js — all\n * the things a Play CDN expects to see at parse time — work naturally.\n *\n * For *streaming* content we instead use `buildShellSrcDoc` + postMessage\n * morph, which trades reliable script execution for the ability to fade\n * in new nodes without reloading the iframe.\n *\n * Strategy: inject our shims (storage shim, auto-height) as early in the\n * resulting `<head>` as possible so they run before any user script. If\n * the user supplied a `<head>` open tag we slot the shims in right after\n * it; otherwise we wrap a minimal document around fragments.\n */\nexport const buildStaticSrcDoc = ({\n  background,\n  content,\n  frameId,\n}: BuildStaticSrcDocOptions): string => {\n  const head = `${baseStyle(background)}${shimsBlock(frameId)}`;\n  const lower = content.toLowerCase();\n  const hasHtmlTag = lower.includes('<html');\n\n  if (!hasHtmlTag) {\n    return `<!DOCTYPE html><html><head><meta charset=\"utf-8\">${head}</head><body>${content}</body></html>`;\n  }\n\n  const headOpenMatch = content.match(/<head\\b[^>]*>/i);\n  if (headOpenMatch) {\n    const idx = headOpenMatch.index! + headOpenMatch[0].length;\n    return content.slice(0, idx) + head + content.slice(idx);\n  }\n\n  const headCloseIdx = lower.indexOf('</head>');\n  if (headCloseIdx !== -1) {\n    return content.slice(0, headCloseIdx) + head + content.slice(headCloseIdx);\n  }\n\n  const htmlOpenMatch = content.match(/<html\\b[^>]*>/i);\n  if (htmlOpenMatch) {\n    const idx = htmlOpenMatch.index! + htmlOpenMatch[0].length;\n    return content.slice(0, idx) + `<head>${head}</head>` + content.slice(idx);\n  }\n\n  return `<!DOCTYPE html><html><head>${head}</head><body>${content}</body></html>`;\n};\n"],"mappings":";;;AASA,MAAM,cAAc,YAClB,WAAW,oBAAoB,oBAAmB,sBAAsB,QAAQ,CAAC;AAEnF,MAAM,aAAa,eACjB,uCAAuC,aAAa,cAAc,WAAW,KAAK,GAAG;;;;;;;;;;;;;;;;;;AAmBvF,MAAa,qBAAqB,EAChC,YACA,SACA,cACsC;CACtC,MAAM,OAAO,GAAG,UAAU,WAAW,GAAG,WAAW,QAAQ;CAC3D,MAAM,QAAQ,QAAQ,aAAa;AAGnC,KAAI,CAFe,MAAM,SAAS,QAEnB,CACb,QAAO,oDAAoD,KAAK,eAAe,QAAQ;CAGzF,MAAM,gBAAgB,QAAQ,MAAM,iBAAiB;AACrD,KAAI,eAAe;EACjB,MAAM,MAAM,cAAc,QAAS,cAAc,GAAG;AACpD,SAAO,QAAQ,MAAM,GAAG,IAAI,GAAG,OAAO,QAAQ,MAAM,IAAI;;CAG1D,MAAM,eAAe,MAAM,QAAQ,UAAU;AAC7C,KAAI,iBAAiB,GACnB,QAAO,QAAQ,MAAM,GAAG,aAAa,GAAG,OAAO,QAAQ,MAAM,aAAa;CAG5E,MAAM,gBAAgB,QAAQ,MAAM,iBAAiB;AACrD,KAAI,eAAe;EACjB,MAAM,MAAM,cAAc,QAAS,cAAc,GAAG;AACpD,SAAO,QAAQ,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,WAAW,QAAQ,MAAM,IAAI;;AAG5E,QAAO,8BAA8B,KAAK,eAAe,QAAQ"}