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
@@ -11,12 +11,6 @@ interface ConversationProps {
11
11
  appName?: string;
12
12
  appIcon?: string;
13
13
  userMessage?: string;
14
- /**
15
- * Whether the content is transitioning between display modes.
16
- * When true, the content area is hidden (opacity 0) to prevent flashing
17
- * stale content in the new layout.
18
- */
19
- isTransitioning?: boolean;
20
14
  }
21
15
  /**
22
16
  * Conversation layout that renders children (iframe) at a stable tree position.
@@ -31,5 +25,5 @@ interface ConversationProps {
31
25
  * - **fullscreen**: content wrapper becomes `position: fixed` covering the viewport;
32
26
  * fullscreen chrome (header/footer) rendered as a separate fixed overlay
33
27
  */
34
- export declare function Conversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage, isTransitioning, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
28
+ export declare function Conversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
35
29
  export {};
@@ -20,6 +20,17 @@ export interface ResourceCSP {
20
20
  /** Domains allowed for scripts, images, styles, fonts */
21
21
  resourceDomains?: string[];
22
22
  }
23
+ /**
24
+ * Extract CSP configuration from a resource's _meta.ui.csp field.
25
+ */
26
+ export declare function extractResourceCSP(resource: {
27
+ _meta?: unknown;
28
+ }): ResourceCSP | undefined;
29
+ /**
30
+ * Validates a CSP source entry is a safe origin URL (scheme + host + optional port).
31
+ * Rejects wildcards, CSP keywords, and whitespace that could inject extra directives.
32
+ */
33
+ declare function isValidCspSource(source: string): boolean;
23
34
  /**
24
35
  * Generates a Content Security Policy string.
25
36
  */
@@ -56,13 +67,6 @@ interface IframeResourceProps {
56
67
  className?: string;
57
68
  /** Optional style for the iframe */
58
69
  style?: React.CSSProperties;
59
- /**
60
- * Called after the iframe has rendered following a display mode change.
61
- * The callback receives the display mode that was confirmed.
62
- * Used by the simulator to hide content during transitions and only
63
- * reveal it once the app has committed its DOM for the new mode.
64
- */
65
- onDisplayModeReady?: (mode: string) => void;
66
70
  /**
67
71
  * Debug: State to inject directly into the app's useAppState hook.
68
72
  * This bypasses the normal MCP Apps protocol and is for simulator testing.
@@ -81,10 +85,11 @@ interface IframeResourceProps {
81
85
  * connects via PostMessageTransport to window.parent. The parent side uses
82
86
  * McpAppHost (wrapping AppBridge) to communicate.
83
87
  */
84
- export declare function IframeResource({ src, scriptSrc, hostContext, toolInput, toolResult, hostOptions, csp, className, style, onDisplayModeReady, debugInjectState, }: IframeResourceProps): import("react/jsx-runtime").JSX.Element;
88
+ export declare function IframeResource({ src, scriptSrc, hostContext, toolInput, toolResult, hostOptions, csp, className, style, debugInjectState, }: IframeResourceProps): import("react/jsx-runtime").JSX.Element;
85
89
  export declare const _testExports: {
86
90
  escapeHtml: typeof escapeHtml;
87
91
  isAllowedUrl: typeof isAllowedUrl;
92
+ isValidCspSource: typeof isValidCspSource;
88
93
  generateCSP: typeof generateCSP;
89
94
  generateScriptHtml: typeof generateScriptHtml;
90
95
  ALLOWED_SCRIPT_ORIGINS: string[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const chatgpt_index = require("../index-FiqdlIXV.cjs");
3
+ const chatgpt_index = require("../index-B9MMk69u.cjs");
4
4
  const discovery = require("../discovery-CRR3SlyI.cjs");
5
5
  exports.ChatGPTSimulator = chatgpt_index.ChatGPTSimulator;
6
6
  exports.IframeResource = chatgpt_index.IframeResource;
@@ -8,6 +8,7 @@ exports.McpAppHost = chatgpt_index.McpAppHost;
8
8
  exports.SCREEN_WIDTHS = chatgpt_index.SCREEN_WIDTHS;
9
9
  exports.ThemeProvider = chatgpt_index.ThemeProvider;
10
10
  exports.createSimulatorUrl = chatgpt_index.createSimulatorUrl;
11
+ exports.extractResourceCSP = chatgpt_index.extractResourceCSP;
11
12
  exports.useThemeContext = chatgpt_index.useThemeContext;
12
13
  exports.buildDevSimulations = discovery.buildDevSimulations;
13
14
  exports.buildResourceMap = discovery.buildResourceMap;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -27,7 +27,7 @@ export type { ScreenWidth, SimulatorConfig } from './chatgpt-simulator-types';
27
27
  export { SCREEN_WIDTHS } from './chatgpt-simulator-types';
28
28
  export { McpAppHost } from './mcp-app-host';
29
29
  export type { McpAppHostOptions } from './mcp-app-host';
30
- export { IframeResource } from './iframe-resource';
30
+ export { IframeResource, extractResourceCSP } from './iframe-resource';
31
31
  export type { ResourceCSP } from './iframe-resource';
32
32
  export * from './theme-provider';
33
33
  export { createSimulatorUrl } from './simulator-url';
@@ -1,5 +1,5 @@
1
- import { C, I, M, S, T, a, u } from "../index-BMqwRYBo.js";
2
- import { a as a2, b, d, c, e, f, g, h, i, j, k, l, t } from "../discovery-COZUnY6a.js";
1
+ import { C, I, M, S, T, a, e, u } from "../index-DqOCq5r8.js";
2
+ import { a as a2, b, d, c, e as e2, f, g, h, i, j, k, l, t } from "../discovery-COZUnY6a.js";
3
3
  export {
4
4
  C as ChatGPTSimulator,
5
5
  I as IframeResource,
@@ -11,7 +11,8 @@ export {
11
11
  d as buildSimulations,
12
12
  c as createResourceExports,
13
13
  a as createSimulatorUrl,
14
- e as extractResourceKey,
14
+ e as extractResourceCSP,
15
+ e2 as extractResourceKey,
15
16
  f as extractSimulationKey,
16
17
  g as extractSimulationName,
17
18
  h as findResourceDirs,
@@ -12,6 +12,8 @@ export interface McpAppHostOptions {
12
12
  }) => void;
13
13
  onLog?: (params: LoggingMessageNotification['params']) => void;
14
14
  onCallTool?: (params: CallToolRequest['params']) => CallToolResult | Promise<CallToolResult>;
15
+ /** Called after the iframe confirms rendering in a new display mode (paint fence resolved). */
16
+ onDisplayModeReady?: (mode: string) => void;
15
17
  }
16
18
  /**
17
19
  * MCP Apps host for the Sunpeak simulator.
@@ -25,6 +27,7 @@ export declare class McpAppHost {
25
27
  private _contentWindow;
26
28
  private _fenceId;
27
29
  private _fenceCleanup;
30
+ private _prevDisplayMode;
28
31
  private _pendingToolInput;
29
32
  private _pendingToolResult;
30
33
  constructor(options?: McpAppHostOptions);
@@ -46,6 +49,8 @@ export declare class McpAppHost {
46
49
  waitForPaint(): Promise<void>;
47
50
  /**
48
51
  * Update the host context and notify the connected app.
52
+ * Automatically detects display mode changes and waits for the iframe
53
+ * to commit its DOM before firing onDisplayModeReady.
49
54
  */
50
55
  setHostContext(context: McpUiHostContext): void;
51
56
  /**
@@ -5442,7 +5442,7 @@ const useEscCloseStack = (listening, cb) => {
5442
5442
  }, [id, listening, latestCallback]);
5443
5443
  };
5444
5444
  const __vite_import_meta_env__ = { "DEV": false, "MODE": "production" };
5445
- const META_ENV = typeof { url: typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index-FiqdlIXV.cjs", document.baseURI).href } !== "undefined" ? __vite_import_meta_env__ : void 0;
5445
+ const META_ENV = typeof { url: typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("index-B9MMk69u.cjs", document.baseURI).href } !== "undefined" ? __vite_import_meta_env__ : void 0;
5446
5446
  const NODE_ENV = typeof process !== "undefined" && process.env?.NODE_ENV ? process.env?.NODE_ENV : "production";
5447
5447
  const isDev = NODE_ENV === "development" || !!META_ENV?.DEV;
5448
5448
  const isJSDomLike = typeof navigator !== "undefined" && /(jsdom|happy-dom)/i.test(navigator.userAgent) || typeof globalThis.happyDOM === "object";
@@ -7545,8 +7545,7 @@ function Conversation({
7545
7545
  onRequestDisplayMode,
7546
7546
  appName = "Sunpeak",
7547
7547
  appIcon,
7548
- userMessage = "What have you got for me today?",
7549
- isTransitioning = false
7548
+ userMessage = "What have you got for me today?"
7550
7549
  }) {
7551
7550
  const isDesktop = platform2 === "desktop";
7552
7551
  const containerWidth = screenWidth === "full" ? "100%" : `${SCREEN_WIDTHS[screenWidth]}px`;
@@ -7668,12 +7667,6 @@ function Conversation({
7668
7667
  "div",
7669
7668
  {
7670
7669
  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",
7671
- style: {
7672
- opacity: isTransitioning ? 0 : 1,
7673
- // Only animate the reveal — the hide must be instant
7674
- // to prevent old content from being visible in the new layout.
7675
- transition: isTransitioning ? "none" : "opacity 100ms"
7676
- },
7677
7670
  children
7678
7671
  }
7679
7672
  )
@@ -14071,10 +14064,12 @@ class McpAppHost {
14071
14064
  _contentWindow = null;
14072
14065
  _fenceId = 0;
14073
14066
  _fenceCleanup = null;
14067
+ _prevDisplayMode;
14074
14068
  _pendingToolInput = null;
14075
14069
  _pendingToolResult = null;
14076
14070
  constructor(options = {}) {
14077
14071
  this.options = options;
14072
+ this._prevDisplayMode = options.hostContext?.displayMode;
14078
14073
  this.bridge = new j_(null, HOST_INFO, HOST_CAPABILITIES, {
14079
14074
  hostContext: options.hostContext
14080
14075
  });
@@ -14094,6 +14089,16 @@ class McpAppHost {
14094
14089
  if (this.options.onOpenLink) {
14095
14090
  this.options.onOpenLink(url);
14096
14091
  } else {
14092
+ try {
14093
+ const parsed = new URL(url);
14094
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
14095
+ console.warn("[MCP App] openLink blocked non-http(s) URL:", url);
14096
+ return {};
14097
+ }
14098
+ } catch {
14099
+ console.warn("[MCP App] openLink blocked invalid URL:", url);
14100
+ return {};
14101
+ }
14097
14102
  window.open(url, "_blank");
14098
14103
  }
14099
14104
  return {};
@@ -14175,6 +14180,7 @@ class McpAppHost {
14175
14180
  const id = ++this._fenceId;
14176
14181
  return new Promise((resolve) => {
14177
14182
  const handler = (event) => {
14183
+ if (event.source !== win) return;
14178
14184
  if (event.data?.method === "sunpeak/fence-ack" && event.data.params?.fenceId === id) {
14179
14185
  cleanup();
14180
14186
  resolve();
@@ -14198,9 +14204,19 @@ class McpAppHost {
14198
14204
  }
14199
14205
  /**
14200
14206
  * Update the host context and notify the connected app.
14207
+ * Automatically detects display mode changes and waits for the iframe
14208
+ * to commit its DOM before firing onDisplayModeReady.
14201
14209
  */
14202
14210
  setHostContext(context) {
14203
14211
  this.bridge.setHostContext(context);
14212
+ const currentMode = context.displayMode;
14213
+ if (currentMode && currentMode !== this._prevDisplayMode) {
14214
+ this._prevDisplayMode = currentMode;
14215
+ const mode = currentMode;
14216
+ this.waitForPaint().then(() => {
14217
+ this.options.onDisplayModeReady?.(mode);
14218
+ });
14219
+ }
14204
14220
  }
14205
14221
  /**
14206
14222
  * Send tool input to the app.
@@ -14259,7 +14275,6 @@ class McpAppHost {
14259
14275
  }
14260
14276
  await this.bridge.close();
14261
14277
  this._initialized = false;
14262
- this._contentWindow = null;
14263
14278
  }
14264
14279
  /**
14265
14280
  * Debug: Inject state directly into the app's useAppState hook.
@@ -14316,7 +14331,21 @@ function isAllowedUrl(src) {
14316
14331
  return false;
14317
14332
  }
14318
14333
  }
14334
+ function extractResourceCSP(resource) {
14335
+ const meta = resource._meta;
14336
+ const ui2 = meta?.ui;
14337
+ return ui2?.csp;
14338
+ }
14319
14339
  const SDK_RESOURCE_DOMAINS = ["https://cdn.openai.com"];
14340
+ function isValidCspSource(source) {
14341
+ if (!source || /[\s;,']/.test(source) || source === "*") return false;
14342
+ try {
14343
+ const url = new URL(source);
14344
+ return url.protocol === "http:" || url.protocol === "https:" || url.protocol === "ws:" || url.protocol === "wss:";
14345
+ } catch {
14346
+ return false;
14347
+ }
14348
+ }
14320
14349
  function generateCSP(csp, scriptSrc) {
14321
14350
  let scriptOrigin = "";
14322
14351
  try {
@@ -14334,14 +14363,26 @@ function generateCSP(csp, scriptSrc) {
14334
14363
  const connectSources = /* @__PURE__ */ new Set(["'self'"]);
14335
14364
  if (scriptOrigin) connectSources.add(scriptOrigin);
14336
14365
  if (csp?.connectDomains) {
14337
- for (const domain of csp.connectDomains) connectSources.add(domain);
14366
+ for (const domain of csp.connectDomains) {
14367
+ if (isValidCspSource(domain)) {
14368
+ connectSources.add(domain);
14369
+ } else {
14370
+ console.warn("[IframeResource] Ignoring invalid CSP connect domain:", domain);
14371
+ }
14372
+ }
14338
14373
  }
14339
14374
  directives.push(`connect-src ${Array.from(connectSources).join(" ")}`);
14340
14375
  const resourceSources = /* @__PURE__ */ new Set(["'self'", "data:", "blob:"]);
14341
14376
  if (scriptOrigin) resourceSources.add(scriptOrigin);
14342
14377
  for (const domain of SDK_RESOURCE_DOMAINS) resourceSources.add(domain);
14343
14378
  if (csp?.resourceDomains) {
14344
- for (const domain of csp.resourceDomains) resourceSources.add(domain);
14379
+ for (const domain of csp.resourceDomains) {
14380
+ if (isValidCspSource(domain)) {
14381
+ resourceSources.add(domain);
14382
+ } else {
14383
+ console.warn("[IframeResource] Ignoring invalid CSP resource domain:", domain);
14384
+ }
14385
+ }
14345
14386
  }
14346
14387
  const resourceList = Array.from(resourceSources).join(" ");
14347
14388
  directives.push(`img-src ${resourceList}`);
@@ -14349,11 +14390,29 @@ function generateCSP(csp, scriptSrc) {
14349
14390
  directives.push(`media-src ${resourceList}`);
14350
14391
  return directives.join("; ");
14351
14392
  }
14393
+ const PAINT_FENCE_SCRIPT = `window.addEventListener("message",function(e){
14394
+ if(e.data&&e.data.method==="sunpeak/fence"){
14395
+ var fid=e.data.params&&e.data.params.fenceId;
14396
+ requestAnimationFrame(function(){
14397
+ e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");
14398
+ });}});`;
14399
+ function injectPaintFence(iframe) {
14400
+ try {
14401
+ const doc = iframe.contentDocument;
14402
+ if (!doc || doc.querySelector("script[data-sunpeak-fence]")) return;
14403
+ const script = doc.createElement("script");
14404
+ script.setAttribute("data-sunpeak-fence", "");
14405
+ script.textContent = PAINT_FENCE_SCRIPT;
14406
+ doc.head.appendChild(script);
14407
+ } catch {
14408
+ }
14409
+ }
14352
14410
  function generateScriptHtml(scriptSrc, theme, cspPolicy) {
14353
14411
  const safeScriptSrc = escapeHtml(scriptSrc);
14354
14412
  const safeCsp = escapeHtml(cspPolicy);
14413
+ const safeTheme = escapeHtml(theme);
14355
14414
  return `<!DOCTYPE html>
14356
- <html lang="en" data-theme="${theme}">
14415
+ <html lang="en" data-theme="${safeTheme}">
14357
14416
  <head>
14358
14417
  <meta charset="UTF-8" />
14359
14418
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -14372,19 +14431,7 @@ function generateScriptHtml(scriptSrc, theme, cspPolicy) {
14372
14431
  color-scheme: dark light;
14373
14432
  }
14374
14433
  </style>
14375
- <script>
14376
- // Paint fence responder — allows the host to wait for this iframe to
14377
- // process pending messages and commit DOM updates before revealing content.
14378
- // Formatted as JSON-RPC 2.0 notifications to avoid PostMessageTransport parse errors.
14379
- window.addEventListener("message",function(e){
14380
- if(e.data&&e.data.method==="sunpeak/fence"){
14381
- var fid=e.data.params&&e.data.params.fenceId;
14382
- requestAnimationFrame(function(){
14383
- e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");
14384
- });
14385
- }
14386
- });
14387
- <\/script>
14434
+ <script>${PAINT_FENCE_SCRIPT}<\/script>
14388
14435
  </head>
14389
14436
  <body>
14390
14437
  <div id="root"></div>
@@ -14402,14 +14449,13 @@ function IframeResource({
14402
14449
  csp,
14403
14450
  className,
14404
14451
  style,
14405
- onDisplayModeReady,
14406
14452
  debugInjectState
14407
14453
  }) {
14408
14454
  const iframeRef = React.useRef(null);
14409
14455
  const hostRef = React.useRef(null);
14410
- const prevDisplayModeRef = React.useRef(hostContext?.displayMode);
14411
- const onDisplayModeReadyRef = React.useRef(onDisplayModeReady);
14412
- onDisplayModeReadyRef.current = onDisplayModeReady;
14456
+ const [readyDisplayMode, setReadyDisplayMode] = React.useState(
14457
+ hostContext?.displayMode
14458
+ );
14413
14459
  const resourceUrl = src ?? scriptSrc;
14414
14460
  const hasReceivedSizeRef = React.useRef(false);
14415
14461
  const host = React.useMemo(
@@ -14422,7 +14468,8 @@ function IframeResource({
14422
14468
  hasReceivedSizeRef.current = true;
14423
14469
  iframeRef.current.style.height = `${params.height}px`;
14424
14470
  }
14425
- }
14471
+ },
14472
+ onDisplayModeReady: (mode) => setReadyDisplayMode(mode)
14426
14473
  }),
14427
14474
  // eslint-disable-next-line react-hooks/exhaustive-deps
14428
14475
  []
@@ -14439,22 +14486,13 @@ function IframeResource({
14439
14486
  [host]
14440
14487
  );
14441
14488
  const handleLoad = React.useCallback(() => {
14442
- if (toolInput) host.sendToolInput(toolInput);
14443
- if (toolResult) host.sendToolResult(toolResult);
14444
- }, [host, toolInput, toolResult]);
14489
+ if (src && iframeRef.current) {
14490
+ injectPaintFence(iframeRef.current);
14491
+ }
14492
+ }, [src]);
14445
14493
  React.useEffect(() => {
14446
14494
  if (hostContext) {
14447
14495
  host.setHostContext(hostContext);
14448
- const currentMode = hostContext.displayMode;
14449
- if (currentMode !== prevDisplayModeRef.current) {
14450
- prevDisplayModeRef.current = currentMode;
14451
- if (currentMode) {
14452
- const mode = currentMode;
14453
- host.waitForPaint().then(() => {
14454
- onDisplayModeReadyRef.current?.(mode);
14455
- });
14456
- }
14457
- }
14458
14496
  }
14459
14497
  }, [host, hostContext]);
14460
14498
  React.useEffect(() => {
@@ -14490,6 +14528,7 @@ function IframeResource({
14490
14528
  const theme = hostContext?.theme ?? "dark";
14491
14529
  return generateScriptHtml(absoluteScriptSrc, theme, cspPolicy);
14492
14530
  }, [scriptSrc, isValidUrl, csp, hostContext?.theme]);
14531
+ const isTransitioning = hostContext?.displayMode !== readyDisplayMode;
14493
14532
  if (src) {
14494
14533
  if (!isValidUrl) {
14495
14534
  console.error("[IframeResource] URL not allowed:", src);
@@ -14513,6 +14552,10 @@ function IframeResource({
14513
14552
  // Start with minHeight to prevent collapse, but allow auto-resize to set actual height.
14514
14553
  // Don't use height: 100% as it requires explicit height in parent chain.
14515
14554
  minHeight: "200px",
14555
+ // Hide during display mode transitions; reveal with a short fade once
14556
+ // the iframe has committed its DOM for the new mode.
14557
+ opacity: isTransitioning ? 0 : 1,
14558
+ transition: isTransitioning ? "none" : "opacity 100ms",
14516
14559
  ...style
14517
14560
  },
14518
14561
  title: "Resource Preview",
@@ -14536,6 +14579,8 @@ function IframeResource({
14536
14579
  // Start with minHeight to prevent collapse, but allow auto-resize to set actual height.
14537
14580
  // Don't use height: 100% as it requires explicit height in parent chain.
14538
14581
  minHeight: "200px",
14582
+ opacity: isTransitioning ? 0 : 1,
14583
+ transition: isTransitioning ? "none" : "opacity 100ms",
14539
14584
  ...style
14540
14585
  },
14541
14586
  title: "Resource Preview",
@@ -14661,12 +14706,6 @@ function ChatGPTSimulator({
14661
14706
  _setDisplayMode(mode);
14662
14707
  }
14663
14708
  };
14664
- const [readyDisplayMode, setReadyDisplayMode] = React.useState(
14665
- urlParams.displayMode ?? DEFAULT_DISPLAY_MODE
14666
- );
14667
- const handleDisplayModeReady = React.useCallback((mode) => {
14668
- setReadyDisplayMode(mode);
14669
- }, []);
14670
14709
  const hostContext = React.useMemo(
14671
14710
  () => ({
14672
14711
  theme,
@@ -14757,11 +14796,7 @@ function ChatGPTSimulator({
14757
14796
  }, [toolResult, modelContext]);
14758
14797
  const resourceUrl = selectedSim?.resourceUrl;
14759
14798
  const resourceScript = selectedSim?.resourceScript;
14760
- const resourceMeta = selectedSim?.resource._meta;
14761
- const resourceUi = resourceMeta?.ui;
14762
- const csp = resourceUi?.csp;
14763
- const hasIframeContent = !!(resourceUrl || resourceScript);
14764
- const isTransitioning = hasIframeContent && displayMode !== readyDisplayMode;
14799
+ const csp = selectedSim ? extractResourceCSP(selectedSim.resource) : void 0;
14765
14800
  let content;
14766
14801
  if (resourceUrl) {
14767
14802
  content = /* @__PURE__ */ jsxRuntime.jsx(
@@ -14775,7 +14810,6 @@ function ChatGPTSimulator({
14775
14810
  onDisplayModeChange: handleDisplayModeChange,
14776
14811
  onUpdateModelContext: handleUpdateModelContext
14777
14812
  },
14778
- onDisplayModeReady: handleDisplayModeReady,
14779
14813
  debugInjectState: modelContext,
14780
14814
  className: "h-full w-full"
14781
14815
  }
@@ -14793,7 +14827,6 @@ function ChatGPTSimulator({
14793
14827
  onDisplayModeChange: handleDisplayModeChange,
14794
14828
  onUpdateModelContext: handleUpdateModelContext
14795
14829
  },
14796
- onDisplayModeReady: handleDisplayModeReady,
14797
14830
  debugInjectState: modelContext,
14798
14831
  className: "h-full w-full"
14799
14832
  }
@@ -15019,7 +15052,6 @@ function ChatGPTSimulator({
15019
15052
  appName,
15020
15053
  appIcon,
15021
15054
  userMessage: selectedSim?.userMessage,
15022
- isTransitioning,
15023
15055
  children: content
15024
15056
  },
15025
15057
  selectedSimulationName
@@ -15080,6 +15112,7 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
15080
15112
  buildSimulations: discovery.buildSimulations,
15081
15113
  createResourceExports: discovery.createResourceExports,
15082
15114
  createSimulatorUrl,
15115
+ extractResourceCSP,
15083
15116
  extractResourceKey: discovery.extractResourceKey,
15084
15117
  extractSimulationKey: discovery.extractSimulationKey,
15085
15118
  extractSimulationName: discovery.extractSimulationName,
@@ -15098,6 +15131,7 @@ exports.SCREEN_WIDTHS = SCREEN_WIDTHS;
15098
15131
  exports.ThemeProvider = ThemeProvider;
15099
15132
  exports.clsx = clsx;
15100
15133
  exports.createSimulatorUrl = createSimulatorUrl;
15134
+ exports.extractResourceCSP = extractResourceCSP;
15101
15135
  exports.index = index;
15102
15136
  exports.useThemeContext = useThemeContext;
15103
- //# sourceMappingURL=index-FiqdlIXV.cjs.map
15137
+ //# sourceMappingURL=index-B9MMk69u.cjs.map