sunpeak 0.13.5 → 0.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/chatgpt/conversation.d.ts +1 -7
  2. package/dist/chatgpt/iframe-resource.d.ts +13 -8
  3. package/dist/chatgpt/index.cjs +2 -1
  4. package/dist/chatgpt/index.cjs.map +1 -1
  5. package/dist/chatgpt/index.d.ts +1 -1
  6. package/dist/chatgpt/index.js +4 -3
  7. package/dist/chatgpt/mcp-app-host.d.ts +5 -0
  8. package/dist/{index-FiqdlIXV.cjs → index-B9MMk69u.cjs} +93 -59
  9. package/dist/index-B9MMk69u.cjs.map +1 -0
  10. package/dist/{index-BMqwRYBo.js → index-DqOCq5r8.js} +92 -58
  11. package/dist/index-DqOCq5r8.js.map +1 -0
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.js +2 -2
  14. package/package.json +1 -1
  15. package/template/dist/albums/albums.json +1 -1
  16. package/template/dist/carousel/carousel.json +1 -1
  17. package/template/dist/map/map.json +1 -1
  18. package/template/dist/review/review.json +1 -1
  19. package/template/node_modules/.vite/deps/_metadata.json +25 -25
  20. package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  21. package/dist/index-BMqwRYBo.js.map +0 -1
  22. package/dist/index-FiqdlIXV.cjs.map +0 -1
  23. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps.js +0 -0
  24. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps.js.map +0 -0
  25. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps_app-bridge.js +0 -0
  26. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps_app-bridge.js.map +0 -0
  27. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps_react.js +3 -3
  28. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@modelcontextprotocol_ext-apps_react.js.map +0 -0
  29. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Avatar.js +0 -0
  30. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Avatar.js.map +0 -0
  31. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Button.js +0 -0
  32. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Button.js.map +0 -0
  33. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Checkbox.js +1 -1
  34. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Checkbox.js.map +0 -0
  35. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Icon.js +0 -0
  36. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Icon.js.map +0 -0
  37. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Input.js +0 -0
  38. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Input.js.map +0 -0
  39. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_SegmentedControl.js +4 -4
  40. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_SegmentedControl.js.map +0 -0
  41. package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Select.js +7 -7
  42. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Select.js.map +0 -0
  43. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Textarea.js +0 -0
  44. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_components_Textarea.js.map +0 -0
  45. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_theme.js +0 -0
  46. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/@openai_apps-sdk-ui_theme.js.map +0 -0
  47. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-3FUH6LFP.js +0 -0
  48. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-3FUH6LFP.js.map +0 -0
  49. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-4EQ7FTMQ.js +0 -0
  50. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-4EQ7FTMQ.js.map +0 -0
  51. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-4WVD247F.js +0 -0
  52. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-4WVD247F.js.map +0 -0
  53. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-ABGJ7IDC.js +0 -0
  54. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-ABGJ7IDC.js.map +0 -0
  55. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-DP4XHQAG.js +0 -0
  56. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-DP4XHQAG.js.map +0 -0
  57. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-EGRHWZRV.js +0 -0
  58. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-EGRHWZRV.js.map +0 -0
  59. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-EHI2XMPP.js +0 -0
  60. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-EHI2XMPP.js.map +0 -0
  61. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-JWMBYPFX.js +0 -0
  62. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-JWMBYPFX.js.map +0 -0
  63. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-PZDCUP6P.js +0 -0
  64. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-PZDCUP6P.js.map +0 -0
  65. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-Q2RBUOJ3.js +0 -0
  66. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-Q2RBUOJ3.js.map +0 -0
  67. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-SPDZ46BB.js +0 -0
  68. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-SPDZ46BB.js.map +0 -0
  69. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-WEIC4XKX.js +0 -0
  70. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-WEIC4XKX.js.map +0 -0
  71. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-WSHFT23M.js +0 -0
  72. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-WSHFT23M.js.map +0 -0
  73. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-XQARMNNG.js +0 -0
  74. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/chunk-XQARMNNG.js.map +0 -0
  75. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/clsx.js +0 -0
  76. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/clsx.js.map +0 -0
  77. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/embla-carousel-react.js +0 -0
  78. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/embla-carousel-react.js.map +0 -0
  79. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/embla-carousel-wheel-gestures.js +0 -0
  80. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/embla-carousel-wheel-gestures.js.map +0 -0
  81. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/mapbox-gl.js +0 -0
  82. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/mapbox-gl.js.map +0 -0
  83. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/package.json +0 -0
  84. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react-dom.js +0 -0
  85. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react-dom.js.map +0 -0
  86. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react-dom_client.js +0 -0
  87. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react-dom_client.js.map +0 -0
  88. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react.js +0 -0
  89. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react.js.map +0 -0
  90. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react_jsx-dev-runtime.js +0 -0
  91. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react_jsx-dev-runtime.js.map +0 -0
  92. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react_jsx-runtime.js +0 -0
  93. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/react_jsx-runtime.js.map +0 -0
  94. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/tailwind-merge.js +0 -0
  95. /package/template/node_modules/.vite-mcp/{deps_temp_f77cfa16 → deps_temp_78ab0da5}/tailwind-merge.js.map +0 -0
@@ -7527,8 +7527,7 @@ function Conversation({
7527
7527
  onRequestDisplayMode,
7528
7528
  appName = "Sunpeak",
7529
7529
  appIcon,
7530
- userMessage = "What have you got for me today?",
7531
- isTransitioning = false
7530
+ userMessage = "What have you got for me today?"
7532
7531
  }) {
7533
7532
  const isDesktop = platform2 === "desktop";
7534
7533
  const containerWidth = screenWidth === "full" ? "100%" : `${SCREEN_WIDTHS[screenWidth]}px`;
@@ -7650,12 +7649,6 @@ function Conversation({
7650
7649
  "div",
7651
7650
  {
7652
7651
  className: isPip ? "h-full w-full max-w-full overflow-auto bg-white dark:bg-[#212121]" : isFullscreen ? "h-full w-full max-w-full overflow-auto bg-surface" : "h-full w-full max-w-full bg-transparent",
7653
- style: {
7654
- opacity: isTransitioning ? 0 : 1,
7655
- // Only animate the reveal — the hide must be instant
7656
- // to prevent old content from being visible in the new layout.
7657
- transition: isTransitioning ? "none" : "opacity 100ms"
7658
- },
7659
7652
  children
7660
7653
  }
7661
7654
  )
@@ -14053,10 +14046,12 @@ class McpAppHost {
14053
14046
  _contentWindow = null;
14054
14047
  _fenceId = 0;
14055
14048
  _fenceCleanup = null;
14049
+ _prevDisplayMode;
14056
14050
  _pendingToolInput = null;
14057
14051
  _pendingToolResult = null;
14058
14052
  constructor(options = {}) {
14059
14053
  this.options = options;
14054
+ this._prevDisplayMode = options.hostContext?.displayMode;
14060
14055
  this.bridge = new j_(null, HOST_INFO, HOST_CAPABILITIES, {
14061
14056
  hostContext: options.hostContext
14062
14057
  });
@@ -14076,6 +14071,16 @@ class McpAppHost {
14076
14071
  if (this.options.onOpenLink) {
14077
14072
  this.options.onOpenLink(url);
14078
14073
  } else {
14074
+ try {
14075
+ const parsed = new URL(url);
14076
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
14077
+ console.warn("[MCP App] openLink blocked non-http(s) URL:", url);
14078
+ return {};
14079
+ }
14080
+ } catch {
14081
+ console.warn("[MCP App] openLink blocked invalid URL:", url);
14082
+ return {};
14083
+ }
14079
14084
  window.open(url, "_blank");
14080
14085
  }
14081
14086
  return {};
@@ -14157,6 +14162,7 @@ class McpAppHost {
14157
14162
  const id = ++this._fenceId;
14158
14163
  return new Promise((resolve) => {
14159
14164
  const handler = (event) => {
14165
+ if (event.source !== win) return;
14160
14166
  if (event.data?.method === "sunpeak/fence-ack" && event.data.params?.fenceId === id) {
14161
14167
  cleanup();
14162
14168
  resolve();
@@ -14180,9 +14186,19 @@ class McpAppHost {
14180
14186
  }
14181
14187
  /**
14182
14188
  * Update the host context and notify the connected app.
14189
+ * Automatically detects display mode changes and waits for the iframe
14190
+ * to commit its DOM before firing onDisplayModeReady.
14183
14191
  */
14184
14192
  setHostContext(context) {
14185
14193
  this.bridge.setHostContext(context);
14194
+ const currentMode = context.displayMode;
14195
+ if (currentMode && currentMode !== this._prevDisplayMode) {
14196
+ this._prevDisplayMode = currentMode;
14197
+ const mode = currentMode;
14198
+ this.waitForPaint().then(() => {
14199
+ this.options.onDisplayModeReady?.(mode);
14200
+ });
14201
+ }
14186
14202
  }
14187
14203
  /**
14188
14204
  * Send tool input to the app.
@@ -14241,7 +14257,6 @@ class McpAppHost {
14241
14257
  }
14242
14258
  await this.bridge.close();
14243
14259
  this._initialized = false;
14244
- this._contentWindow = null;
14245
14260
  }
14246
14261
  /**
14247
14262
  * Debug: Inject state directly into the app's useAppState hook.
@@ -14298,7 +14313,21 @@ function isAllowedUrl(src) {
14298
14313
  return false;
14299
14314
  }
14300
14315
  }
14316
+ function extractResourceCSP(resource) {
14317
+ const meta = resource._meta;
14318
+ const ui2 = meta?.ui;
14319
+ return ui2?.csp;
14320
+ }
14301
14321
  const SDK_RESOURCE_DOMAINS = ["https://cdn.openai.com"];
14322
+ function isValidCspSource(source) {
14323
+ if (!source || /[\s;,']/.test(source) || source === "*") return false;
14324
+ try {
14325
+ const url = new URL(source);
14326
+ return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "ws:" || url.protocol === "wss:";
14327
+ } catch {
14328
+ return false;
14329
+ }
14330
+ }
14302
14331
  function generateCSP(csp, scriptSrc) {
14303
14332
  let scriptOrigin = "";
14304
14333
  try {
@@ -14316,14 +14345,26 @@ function generateCSP(csp, scriptSrc) {
14316
14345
  const connectSources = /* @__PURE__ */ new Set(["'self'"]);
14317
14346
  if (scriptOrigin) connectSources.add(scriptOrigin);
14318
14347
  if (csp?.connectDomains) {
14319
- for (const domain of csp.connectDomains) connectSources.add(domain);
14348
+ for (const domain of csp.connectDomains) {
14349
+ if (isValidCspSource(domain)) {
14350
+ connectSources.add(domain);
14351
+ } else {
14352
+ console.warn("[IframeResource] Ignoring invalid CSP connect domain:", domain);
14353
+ }
14354
+ }
14320
14355
  }
14321
14356
  directives.push(`connect-src ${Array.from(connectSources).join(" ")}`);
14322
14357
  const resourceSources = /* @__PURE__ */ new Set(["'self'", "data:", "blob:"]);
14323
14358
  if (scriptOrigin) resourceSources.add(scriptOrigin);
14324
14359
  for (const domain of SDK_RESOURCE_DOMAINS) resourceSources.add(domain);
14325
14360
  if (csp?.resourceDomains) {
14326
- for (const domain of csp.resourceDomains) resourceSources.add(domain);
14361
+ for (const domain of csp.resourceDomains) {
14362
+ if (isValidCspSource(domain)) {
14363
+ resourceSources.add(domain);
14364
+ } else {
14365
+ console.warn("[IframeResource] Ignoring invalid CSP resource domain:", domain);
14366
+ }
14367
+ }
14327
14368
  }
14328
14369
  const resourceList = Array.from(resourceSources).join(" ");
14329
14370
  directives.push(`img-src ${resourceList}`);
@@ -14331,11 +14372,29 @@ function generateCSP(csp, scriptSrc) {
14331
14372
  directives.push(`media-src ${resourceList}`);
14332
14373
  return directives.join("; ");
14333
14374
  }
14375
+ const PAINT_FENCE_SCRIPT = `window.addEventListener("message",function(e){
14376
+ if(e.data&&e.data.method==="sunpeak/fence"){
14377
+ var fid=e.data.params&&e.data.params.fenceId;
14378
+ requestAnimationFrame(function(){
14379
+ e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");
14380
+ });}});`;
14381
+ function injectPaintFence(iframe) {
14382
+ try {
14383
+ const doc = iframe.contentDocument;
14384
+ if (!doc || doc.querySelector("script[data-sunpeak-fence]")) return;
14385
+ const script = doc.createElement("script");
14386
+ script.setAttribute("data-sunpeak-fence", "");
14387
+ script.textContent = PAINT_FENCE_SCRIPT;
14388
+ doc.head.appendChild(script);
14389
+ } catch {
14390
+ }
14391
+ }
14334
14392
  function generateScriptHtml(scriptSrc, theme, cspPolicy) {
14335
14393
  const safeScriptSrc = escapeHtml(scriptSrc);
14336
14394
  const safeCsp = escapeHtml(cspPolicy);
14395
+ const safeTheme = escapeHtml(theme);
14337
14396
  return `<!DOCTYPE html>
14338
- <html lang="en" data-theme="${theme}">
14397
+ <html lang="en" data-theme="${safeTheme}">
14339
14398
  <head>
14340
14399
  <meta charset="UTF-8" />
14341
14400
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -14354,19 +14413,7 @@ function generateScriptHtml(scriptSrc, theme, cspPolicy) {
14354
14413
  color-scheme: dark light;
14355
14414
  }
14356
14415
  </style>
14357
- <script>
14358
- // Paint fence responder — allows the host to wait for this iframe to
14359
- // process pending messages and commit DOM updates before revealing content.
14360
- // Formatted as JSON-RPC 2.0 notifications to avoid PostMessageTransport parse errors.
14361
- window.addEventListener("message",function(e){
14362
- if(e.data&&e.data.method==="sunpeak/fence"){
14363
- var fid=e.data.params&&e.data.params.fenceId;
14364
- requestAnimationFrame(function(){
14365
- e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");
14366
- });
14367
- }
14368
- });
14369
- <\/script>
14416
+ <script>${PAINT_FENCE_SCRIPT}<\/script>
14370
14417
  </head>
14371
14418
  <body>
14372
14419
  <div id="root"></div>
@@ -14384,14 +14431,13 @@ function IframeResource({
14384
14431
  csp,
14385
14432
  className,
14386
14433
  style,
14387
- onDisplayModeReady,
14388
14434
  debugInjectState
14389
14435
  }) {
14390
14436
  const iframeRef = useRef(null);
14391
14437
  const hostRef = useRef(null);
14392
- const prevDisplayModeRef = useRef(hostContext?.displayMode);
14393
- const onDisplayModeReadyRef = useRef(onDisplayModeReady);
14394
- onDisplayModeReadyRef.current = onDisplayModeReady;
14438
+ const [readyDisplayMode, setReadyDisplayMode] = useState(
14439
+ hostContext?.displayMode
14440
+ );
14395
14441
  const resourceUrl = src ?? scriptSrc;
14396
14442
  const hasReceivedSizeRef = useRef(false);
14397
14443
  const host = useMemo(
@@ -14404,7 +14450,8 @@ function IframeResource({
14404
14450
  hasReceivedSizeRef.current = true;
14405
14451
  iframeRef.current.style.height = `${params.height}px`;
14406
14452
  }
14407
- }
14453
+ },
14454
+ onDisplayModeReady: (mode) => setReadyDisplayMode(mode)
14408
14455
  }),
14409
14456
  // eslint-disable-next-line react-hooks/exhaustive-deps
14410
14457
  []
@@ -14421,22 +14468,13 @@ function IframeResource({
14421
14468
  [host]
14422
14469
  );
14423
14470
  const handleLoad = useCallback(() => {
14424
- if (toolInput) host.sendToolInput(toolInput);
14425
- if (toolResult) host.sendToolResult(toolResult);
14426
- }, [host, toolInput, toolResult]);
14471
+ if (src && iframeRef.current) {
14472
+ injectPaintFence(iframeRef.current);
14473
+ }
14474
+ }, [src]);
14427
14475
  useEffect(() => {
14428
14476
  if (hostContext) {
14429
14477
  host.setHostContext(hostContext);
14430
- const currentMode = hostContext.displayMode;
14431
- if (currentMode !== prevDisplayModeRef.current) {
14432
- prevDisplayModeRef.current = currentMode;
14433
- if (currentMode) {
14434
- const mode = currentMode;
14435
- host.waitForPaint().then(() => {
14436
- onDisplayModeReadyRef.current?.(mode);
14437
- });
14438
- }
14439
- }
14440
14478
  }
14441
14479
  }, [host, hostContext]);
14442
14480
  useEffect(() => {
@@ -14472,6 +14510,7 @@ function IframeResource({
14472
14510
  const theme = hostContext?.theme ?? "dark";
14473
14511
  return generateScriptHtml(absoluteScriptSrc, theme, cspPolicy);
14474
14512
  }, [scriptSrc, isValidUrl, csp, hostContext?.theme]);
14513
+ const isTransitioning = hostContext?.displayMode !== readyDisplayMode;
14475
14514
  if (src) {
14476
14515
  if (!isValidUrl) {
14477
14516
  console.error("[IframeResource] URL not allowed:", src);
@@ -14495,6 +14534,10 @@ function IframeResource({
14495
14534
  // Start with minHeight to prevent collapse, but allow auto-resize to set actual height.
14496
14535
  // Don't use height: 100% as it requires explicit height in parent chain.
14497
14536
  minHeight: "200px",
14537
+ // Hide during display mode transitions; reveal with a short fade once
14538
+ // the iframe has committed its DOM for the new mode.
14539
+ opacity: isTransitioning ? 0 : 1,
14540
+ transition: isTransitioning ? "none" : "opacity 100ms",
14498
14541
  ...style
14499
14542
  },
14500
14543
  title: "Resource Preview",
@@ -14518,6 +14561,8 @@ function IframeResource({
14518
14561
  // Start with minHeight to prevent collapse, but allow auto-resize to set actual height.
14519
14562
  // Don't use height: 100% as it requires explicit height in parent chain.
14520
14563
  minHeight: "200px",
14564
+ opacity: isTransitioning ? 0 : 1,
14565
+ transition: isTransitioning ? "none" : "opacity 100ms",
14521
14566
  ...style
14522
14567
  },
14523
14568
  title: "Resource Preview",
@@ -14643,12 +14688,6 @@ function ChatGPTSimulator({
14643
14688
  _setDisplayMode(mode);
14644
14689
  }
14645
14690
  };
14646
- const [readyDisplayMode, setReadyDisplayMode] = useState(
14647
- urlParams.displayMode ?? DEFAULT_DISPLAY_MODE
14648
- );
14649
- const handleDisplayModeReady = useCallback((mode) => {
14650
- setReadyDisplayMode(mode);
14651
- }, []);
14652
14691
  const hostContext = useMemo(
14653
14692
  () => ({
14654
14693
  theme,
@@ -14739,11 +14778,7 @@ function ChatGPTSimulator({
14739
14778
  }, [toolResult, modelContext]);
14740
14779
  const resourceUrl = selectedSim?.resourceUrl;
14741
14780
  const resourceScript = selectedSim?.resourceScript;
14742
- const resourceMeta = selectedSim?.resource._meta;
14743
- const resourceUi = resourceMeta?.ui;
14744
- const csp = resourceUi?.csp;
14745
- const hasIframeContent = !!(resourceUrl || resourceScript);
14746
- const isTransitioning = hasIframeContent && displayMode !== readyDisplayMode;
14781
+ const csp = selectedSim ? extractResourceCSP(selectedSim.resource) : void 0;
14747
14782
  let content;
14748
14783
  if (resourceUrl) {
14749
14784
  content = /* @__PURE__ */ jsx(
@@ -14757,7 +14792,6 @@ function ChatGPTSimulator({
14757
14792
  onDisplayModeChange: handleDisplayModeChange,
14758
14793
  onUpdateModelContext: handleUpdateModelContext
14759
14794
  },
14760
- onDisplayModeReady: handleDisplayModeReady,
14761
14795
  debugInjectState: modelContext,
14762
14796
  className: "h-full w-full"
14763
14797
  }
@@ -14775,7 +14809,6 @@ function ChatGPTSimulator({
14775
14809
  onDisplayModeChange: handleDisplayModeChange,
14776
14810
  onUpdateModelContext: handleUpdateModelContext
14777
14811
  },
14778
- onDisplayModeReady: handleDisplayModeReady,
14779
14812
  debugInjectState: modelContext,
14780
14813
  className: "h-full w-full"
14781
14814
  }
@@ -15001,7 +15034,6 @@ function ChatGPTSimulator({
15001
15034
  appName,
15002
15035
  appIcon,
15003
15036
  userMessage: selectedSim?.userMessage,
15004
- isTransitioning,
15005
15037
  children: content
15006
15038
  },
15007
15039
  selectedSimulationName
@@ -15062,6 +15094,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
15062
15094
  buildSimulations,
15063
15095
  createResourceExports,
15064
15096
  createSimulatorUrl,
15097
+ extractResourceCSP,
15065
15098
  extractResourceKey,
15066
15099
  extractSimulationKey,
15067
15100
  extractSimulationName,
@@ -15081,7 +15114,8 @@ export {
15081
15114
  ThemeProvider as T,
15082
15115
  createSimulatorUrl as a,
15083
15116
  clsx as c,
15117
+ extractResourceCSP as e,
15084
15118
  index as i,
15085
15119
  useThemeContext as u
15086
15120
  };
15087
- //# sourceMappingURL=index-BMqwRYBo.js.map
15121
+ //# sourceMappingURL=index-DqOCq5r8.js.map