sunpeak 0.9.3 → 0.9.6

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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { a as clsx } from "./simulator-url-CexnaL-e.js";
2
- import { C, S, T, c, v, s, q, i, r, w, t, e, f, g, h, j, u, k, l, m, n, d, b, o, p } from "./simulator-url-CexnaL-e.js";
1
+ import { a as clsx } from "./simulator-url-BOSS60NS.js";
2
+ import { C, I, S, T, c, v, s, q, i, r, w, t, e, f, g, h, j, u, k, l, m, n, d, b, o, p } from "./simulator-url-BOSS60NS.js";
3
3
  import * as React from "react";
4
4
  import { i as i2, d as d2, h as h2, c as c2, b as b2, e as e2, a, f as f2, g as g2, t as t2 } from "./discovery-ft3cd2dW.js";
5
5
  const MOBILE_BREAKPOINT = 768;
@@ -3037,6 +3037,7 @@ const isPrimarilyTouchDevice = createMediaQueryFn("(pointer: coarse)");
3037
3037
  const isHoverAvailable = createMediaQueryFn("(hover: hover)");
3038
3038
  export {
3039
3039
  C as ChatGPTSimulator,
3040
+ I as IframeResource,
3040
3041
  S as SCREEN_WIDTHS,
3041
3042
  T as ThemeProvider,
3042
3043
  i2 as buildDevSimulations,
@@ -7542,8 +7542,591 @@ const SCREEN_WIDTHS = {
7542
7542
  tablet: 768,
7543
7543
  full: 1024
7544
7544
  };
7545
+ const ALLOWED_SCRIPT_ORIGINS = [
7546
+ "https://sandbox.sunpeakai.com",
7547
+ "http://localhost",
7548
+ "https://localhost",
7549
+ "http://127.0.0.1",
7550
+ "https://127.0.0.1"
7551
+ ];
7552
+ const ALLOWED_PARENT_ORIGINS = [
7553
+ "https://app.sunpeak.ai",
7554
+ "http://localhost",
7555
+ "https://localhost",
7556
+ "http://127.0.0.1",
7557
+ "https://127.0.0.1"
7558
+ ];
7559
+ function escapeHtml(str) {
7560
+ return str.replace(/[&<>"']/g, (c) => {
7561
+ const entities = {
7562
+ "&": "&amp;",
7563
+ "<": "&lt;",
7564
+ ">": "&gt;",
7565
+ '"': "&quot;",
7566
+ "'": "&#39;"
7567
+ };
7568
+ return entities[c] ?? c;
7569
+ });
7570
+ }
7571
+ function isAllowedScriptSrc(src) {
7572
+ if (!src) {
7573
+ return false;
7574
+ }
7575
+ if (src.startsWith("/") && !src.startsWith("//")) {
7576
+ return true;
7577
+ }
7578
+ if (!src.includes("://")) {
7579
+ return false;
7580
+ }
7581
+ try {
7582
+ const url = new URL(src);
7583
+ if (url.origin === window.location.origin) {
7584
+ return true;
7585
+ }
7586
+ if (url.hostname === "localhost" || url.hostname === "127.0.0.1") {
7587
+ return true;
7588
+ }
7589
+ return ALLOWED_SCRIPT_ORIGINS.some((allowed) => {
7590
+ try {
7591
+ const allowedUrl = new URL(allowed);
7592
+ return url.origin === allowedUrl.origin;
7593
+ } catch {
7594
+ return false;
7595
+ }
7596
+ });
7597
+ } catch {
7598
+ return false;
7599
+ }
7600
+ }
7601
+ function generateCSP(csp, scriptSrc) {
7602
+ let scriptOrigin = "";
7603
+ try {
7604
+ const scriptUrl = new URL(scriptSrc, window.location.origin);
7605
+ scriptOrigin = scriptUrl.origin;
7606
+ } catch {
7607
+ }
7608
+ const directives = [
7609
+ "default-src 'self'",
7610
+ // Allow inline scripts for the bridge, blob: for dynamic content, and the script origin
7611
+ `script-src 'self' 'unsafe-inline' blob: ${scriptOrigin}`.trim(),
7612
+ // Allow inline styles and data: URIs for images embedded in CSS
7613
+ `style-src 'self' 'unsafe-inline' ${scriptOrigin}`.trim(),
7614
+ // Disallow iframes within the iframe (no nesting)
7615
+ "frame-src 'none'",
7616
+ // Disallow form submissions
7617
+ "form-action 'none'",
7618
+ // Disallow changing the base URL
7619
+ "base-uri 'self'"
7620
+ ];
7621
+ const connectSources = /* @__PURE__ */ new Set(["'self'"]);
7622
+ if (scriptOrigin) {
7623
+ connectSources.add(scriptOrigin);
7624
+ }
7625
+ if (csp == null ? void 0 : csp.connect_domains) {
7626
+ for (const domain of csp.connect_domains) {
7627
+ connectSources.add(domain);
7628
+ }
7629
+ }
7630
+ directives.push(`connect-src ${Array.from(connectSources).join(" ")}`);
7631
+ const resourceSources = /* @__PURE__ */ new Set(["'self'", "data:", "blob:"]);
7632
+ if (scriptOrigin) {
7633
+ resourceSources.add(scriptOrigin);
7634
+ }
7635
+ if (csp == null ? void 0 : csp.resource_domains) {
7636
+ for (const domain of csp.resource_domains) {
7637
+ resourceSources.add(domain);
7638
+ }
7639
+ }
7640
+ const resourceList = Array.from(resourceSources).join(" ");
7641
+ directives.push(`img-src ${resourceList}`);
7642
+ directives.push(`font-src ${resourceList}`);
7643
+ directives.push(`media-src ${resourceList}`);
7644
+ return directives.join("; ");
7645
+ }
7646
+ function generateBridgeScript(allowedParentOrigins) {
7647
+ const originsJson = JSON.stringify(allowedParentOrigins);
7648
+ return `
7649
+ <script>
7650
+ (function() {
7651
+ // Allowed origins that can send messages to this iframe
7652
+ var allowedOrigins = ${originsJson};
7653
+
7654
+ // MessagePort for secure communication with parent (set during handshake)
7655
+ var messagePort = null;
7656
+
7657
+ // Queue messages until port is ready
7658
+ var messageQueue = [];
7659
+
7660
+ // Check if an origin is allowed (handles localhost with any port)
7661
+ function isAllowedOrigin(origin) {
7662
+ // Note: We no longer accept 'null' origin - MessageChannel provides security
7663
+ for (var i = 0; i < allowedOrigins.length; i++) {
7664
+ if (origin === allowedOrigins[i]) return true;
7665
+ // Handle localhost/127.0.0.1 with any port
7666
+ if (allowedOrigins[i].indexOf('localhost') !== -1 || allowedOrigins[i].indexOf('127.0.0.1') !== -1) {
7667
+ try {
7668
+ var allowed = new URL(allowedOrigins[i]);
7669
+ var test = new URL(origin);
7670
+ if (test.hostname === allowed.hostname && test.protocol === allowed.protocol) return true;
7671
+ } catch (e) {}
7672
+ }
7673
+ }
7674
+ return false;
7675
+ }
7676
+
7677
+ // Send message via MessagePort (queues if port not ready)
7678
+ function sendToParent(message) {
7679
+ if (messagePort) {
7680
+ messagePort.postMessage(message);
7681
+ } else {
7682
+ messageQueue.push(message);
7683
+ }
7684
+ }
7685
+
7686
+ // Flush queued messages once port is ready
7687
+ function flushMessageQueue() {
7688
+ while (messageQueue.length > 0) {
7689
+ var msg = messageQueue.shift();
7690
+ messagePort.postMessage(msg);
7691
+ }
7692
+ }
7693
+
7694
+ // Set up window.openai with placeholder values (updated via MessageChannel)
7695
+ window.openai = {
7696
+ theme: 'dark',
7697
+ locale: 'en-US',
7698
+ displayMode: 'inline',
7699
+ maxHeight: undefined,
7700
+ userAgent: { device: { type: 'desktop' }, capabilities: { hover: true, touch: false } },
7701
+ safeArea: { insets: { top: 0, bottom: 0, left: 0, right: 0 } },
7702
+ view: null,
7703
+ toolInput: {},
7704
+ toolOutput: null,
7705
+ toolResponseMetadata: null,
7706
+ widgetState: null,
7707
+
7708
+ // API methods that send messages to parent via MessagePort
7709
+ callTool: function(name, args) {
7710
+ sendToParent({ type: 'openai:callTool', payload: { name, args } });
7711
+ },
7712
+ sendFollowUpMessage: function(params) {
7713
+ sendToParent({ type: 'openai:sendFollowUpMessage', payload: params });
7714
+ },
7715
+ openExternal: function(params) {
7716
+ sendToParent({ type: 'openai:openExternal', payload: params });
7717
+ },
7718
+ requestDisplayMode: function(params) {
7719
+ sendToParent({ type: 'openai:requestDisplayMode', payload: params });
7720
+ },
7721
+ requestModal: function(params) {
7722
+ sendToParent({ type: 'openai:requestModal', payload: params });
7723
+ },
7724
+ notifyIntrinsicHeight: function(height) {
7725
+ // Height updates use postMessage directly to avoid delays during handshake.
7726
+ // This is safe because height values are validated on the parent side.
7727
+ window.parent.postMessage({ type: 'openai:notifyIntrinsicHeight', payload: { height } }, '*');
7728
+ },
7729
+ setWidgetState: function(state) {
7730
+ sendToParent({ type: 'openai:setWidgetState', payload: state });
7731
+ },
7732
+ };
7733
+
7734
+ // Handle incoming messages on the MessagePort
7735
+ function handlePortMessage(event) {
7736
+ var data = event.data;
7737
+ if (!data || typeof data !== 'object') return;
7738
+
7739
+ if (data.type === 'openai:init' || data.type === 'openai:update') {
7740
+ var payload = data.payload || {};
7741
+
7742
+ // Update window.openai with new values
7743
+ if (payload.theme !== undefined) {
7744
+ window.openai.theme = payload.theme;
7745
+ // Also set data-theme attribute for CSS theming
7746
+ document.documentElement.dataset.theme = payload.theme;
7747
+ }
7748
+ if (payload.displayMode !== undefined) window.openai.displayMode = payload.displayMode;
7749
+ if (payload.locale !== undefined) window.openai.locale = payload.locale;
7750
+ if (payload.maxHeight !== undefined) window.openai.maxHeight = payload.maxHeight;
7751
+ if (payload.userAgent !== undefined) window.openai.userAgent = payload.userAgent;
7752
+ if (payload.safeArea !== undefined) window.openai.safeArea = payload.safeArea;
7753
+ if (payload.view !== undefined) window.openai.view = payload.view;
7754
+ if (payload.toolInput !== undefined) window.openai.toolInput = payload.toolInput;
7755
+ if (payload.toolOutput !== undefined) window.openai.toolOutput = payload.toolOutput;
7756
+ if (payload.toolResponseMetadata !== undefined) window.openai.toolResponseMetadata = payload.toolResponseMetadata;
7757
+ if (payload.widgetState !== undefined) window.openai.widgetState = payload.widgetState;
7758
+
7759
+ // Dispatch custom event so widgets can react to changes
7760
+ // Must match SET_GLOBALS_EVENT_TYPE ('openai:set_globals') in providers/openai/types.ts
7761
+ window.dispatchEvent(new CustomEvent('openai:set_globals', { detail: { globals: payload } }));
7762
+ }
7763
+ }
7764
+
7765
+ // Listen for handshake message from parent (transfers MessagePort)
7766
+ window.addEventListener('message', function(event) {
7767
+ // Strict source validation: only accept messages from parent window
7768
+ if (event.source !== window.parent) {
7769
+ console.warn('[IframeBridge] Rejected message from non-parent source');
7770
+ return;
7771
+ }
7772
+
7773
+ // Validate message origin (allows localhost with any port)
7774
+ if (!isAllowedOrigin(event.origin)) {
7775
+ console.warn('[IframeBridge] Rejected message from untrusted origin:', event.origin);
7776
+ return;
7777
+ }
7778
+
7779
+ // Handle handshake with MessagePort transfer
7780
+ if (event.data && event.data.type === 'openai:handshake' && event.ports && event.ports.length > 0) {
7781
+ messagePort = event.ports[0];
7782
+ messagePort.onmessage = handlePortMessage;
7783
+ messagePort.start();
7784
+
7785
+ // Flush any queued messages
7786
+ flushMessageQueue();
7787
+
7788
+ // Confirm handshake complete
7789
+ sendToParent({ type: 'openai:handshake_complete' });
7790
+ }
7791
+ });
7792
+
7793
+ // Signal to parent that we're ready for handshake
7794
+ window.parent.postMessage({ type: 'openai:ready' }, '*');
7795
+
7796
+ // Auto-measure and report content height immediately
7797
+ var lastHeight = 0;
7798
+ var pendingFrame = null;
7799
+ function reportHeight() {
7800
+ var height = document.documentElement.scrollHeight || document.body.scrollHeight;
7801
+ if (height > 0 && height !== lastHeight) {
7802
+ lastHeight = height;
7803
+ window.openai.notifyIntrinsicHeight(height);
7804
+ }
7805
+ }
7806
+
7807
+ // Schedule height report on next animation frame (batches rapid changes)
7808
+ function scheduleHeightReport() {
7809
+ if (pendingFrame) return;
7810
+ pendingFrame = requestAnimationFrame(function() {
7811
+ pendingFrame = null;
7812
+ reportHeight();
7813
+ });
7814
+ }
7815
+
7816
+ // Report height immediately when ready
7817
+ if (document.readyState === 'complete') {
7818
+ reportHeight();
7819
+ } else {
7820
+ window.addEventListener('load', reportHeight);
7821
+ }
7822
+
7823
+ // Use ResizeObserver to track size changes
7824
+ if (typeof ResizeObserver !== 'undefined') {
7825
+ var resizeObserver = new ResizeObserver(function() {
7826
+ scheduleHeightReport();
7827
+ });
7828
+ function observeElements() {
7829
+ if (document.body) resizeObserver.observe(document.body);
7830
+ if (document.documentElement) resizeObserver.observe(document.documentElement);
7831
+ // Also observe the root element if it exists
7832
+ var root = document.getElementById('root');
7833
+ if (root) resizeObserver.observe(root);
7834
+ }
7835
+ if (document.body) {
7836
+ observeElements();
7837
+ } else {
7838
+ document.addEventListener('DOMContentLoaded', observeElements);
7839
+ }
7840
+ }
7841
+
7842
+ // Use MutationObserver to detect DOM changes that may affect height
7843
+ if (typeof MutationObserver !== 'undefined') {
7844
+ var mutationObserver = new MutationObserver(function() {
7845
+ scheduleHeightReport();
7846
+ });
7847
+ function observeMutations() {
7848
+ mutationObserver.observe(document.body, {
7849
+ childList: true,
7850
+ subtree: true,
7851
+ attributes: true,
7852
+ characterData: true
7853
+ });
7854
+ }
7855
+ if (document.body) {
7856
+ observeMutations();
7857
+ } else {
7858
+ document.addEventListener('DOMContentLoaded', observeMutations);
7859
+ }
7860
+ }
7861
+ })();
7862
+ <\/script>
7863
+ `;
7864
+ }
7865
+ function generateScriptHtml(scriptSrc, theme, cspPolicy) {
7866
+ const safeScriptSrc = escapeHtml(scriptSrc);
7867
+ const safeCsp = escapeHtml(cspPolicy);
7868
+ return `<!DOCTYPE html>
7869
+ <html lang="en" data-theme="${theme}">
7870
+ <head>
7871
+ <meta charset="UTF-8" />
7872
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7873
+ <meta http-equiv="Content-Security-Policy" content="${safeCsp}" />
7874
+ <title>Resource</title>
7875
+ <style>
7876
+ html, body, #root {
7877
+ margin: 0;
7878
+ padding: 0;
7879
+ height: 100%;
7880
+ width: 100%;
7881
+ }
7882
+ body {
7883
+ background: transparent;
7884
+ }
7885
+ </style>
7886
+ </head>
7887
+ <body>
7888
+ <div id="root"></div>
7889
+ <script src="${safeScriptSrc}"><\/script>
7890
+ </body>
7891
+ </html>`;
7892
+ }
7893
+ function injectBridgeScript(htmlContent, bridgeScript) {
7894
+ const headMatch = htmlContent.match(/<head[^>]*>/i);
7895
+ if (headMatch) {
7896
+ const insertPos = headMatch.index + headMatch[0].length;
7897
+ return htmlContent.slice(0, insertPos) + bridgeScript + htmlContent.slice(insertPos);
7898
+ }
7899
+ const doctypeMatch = htmlContent.match(/<!DOCTYPE[^>]*>/i);
7900
+ if (doctypeMatch) {
7901
+ const insertPos = doctypeMatch.index + doctypeMatch[0].length;
7902
+ return htmlContent.slice(0, insertPos) + bridgeScript + htmlContent.slice(insertPos);
7903
+ }
7904
+ return bridgeScript + htmlContent;
7905
+ }
7906
+ function IframeResource({ scriptSrc, className, style, csp }) {
7907
+ const iframeRef = useRef(null);
7908
+ const messageChannelRef = useRef(null);
7909
+ const messagePortRef = useRef(null);
7910
+ const [isHeightReady, setIsHeightReady] = React.useState(false);
7911
+ React.useEffect(() => {
7912
+ setIsHeightReady(false);
7913
+ const timeout = setTimeout(() => {
7914
+ setIsHeightReady(true);
7915
+ }, 500);
7916
+ return () => clearTimeout(timeout);
7917
+ }, [scriptSrc]);
7918
+ const theme = useTheme();
7919
+ const displayMode = useDisplayMode();
7920
+ const locale = useLocale();
7921
+ const maxHeight = useMaxHeight();
7922
+ const userAgent = useUserAgent();
7923
+ const safeArea = useSafeArea();
7924
+ const view = useView();
7925
+ const toolInput = useToolInput();
7926
+ const toolOutput = useWidgetProps();
7927
+ const toolResponseMetadata = useToolResponseMetadata();
7928
+ const [widgetState] = useWidgetState();
7929
+ const currentState = React.useMemo(
7930
+ () => ({
7931
+ theme,
7932
+ displayMode,
7933
+ locale,
7934
+ maxHeight,
7935
+ userAgent,
7936
+ safeArea,
7937
+ view,
7938
+ toolInput,
7939
+ toolOutput,
7940
+ toolResponseMetadata,
7941
+ widgetState
7942
+ }),
7943
+ [
7944
+ theme,
7945
+ displayMode,
7946
+ locale,
7947
+ maxHeight,
7948
+ userAgent,
7949
+ safeArea,
7950
+ view,
7951
+ toolInput,
7952
+ toolOutput,
7953
+ toolResponseMetadata,
7954
+ widgetState
7955
+ ]
7956
+ );
7957
+ const sendUpdate = useCallback(
7958
+ (type) => {
7959
+ const port = messagePortRef.current;
7960
+ if (port) {
7961
+ const message = {
7962
+ type,
7963
+ payload: currentState
7964
+ };
7965
+ port.postMessage(message);
7966
+ }
7967
+ },
7968
+ [currentState]
7969
+ );
7970
+ const sendUpdateRef = useRef(sendUpdate);
7971
+ useEffect(() => {
7972
+ sendUpdateRef.current = sendUpdate;
7973
+ }, [sendUpdate]);
7974
+ const handlePortMessage = useCallback(
7975
+ (event) => {
7976
+ var _a2, _b2;
7977
+ if (!event.data || typeof event.data !== "object" || typeof event.data.type !== "string") {
7978
+ return;
7979
+ }
7980
+ const { type } = event.data;
7981
+ switch (type) {
7982
+ case "openai:handshake_complete":
7983
+ sendUpdateRef.current("openai:init");
7984
+ break;
7985
+ case "openai:requestDisplayMode": {
7986
+ const payload = event.data.payload;
7987
+ if (!payload || typeof payload !== "object" || !("mode" in payload) || typeof payload.mode !== "string") {
7988
+ console.warn("[IframeResource] Invalid requestDisplayMode payload");
7989
+ return;
7990
+ }
7991
+ const validModes = ["inline", "pip", "fullscreen"];
7992
+ const mode = payload.mode;
7993
+ if (!validModes.includes(mode)) {
7994
+ console.warn("[IframeResource] Invalid display mode:", mode);
7995
+ return;
7996
+ }
7997
+ if (typeof window !== "undefined" && ((_a2 = window.openai) == null ? void 0 : _a2.requestDisplayMode)) {
7998
+ window.openai.requestDisplayMode({ mode });
7999
+ }
8000
+ break;
8001
+ }
8002
+ case "openai:setWidgetState": {
8003
+ const payload = event.data.payload;
8004
+ if (payload !== null && (typeof payload !== "object" || Array.isArray(payload))) {
8005
+ console.warn("[IframeResource] Invalid widgetState payload");
8006
+ return;
8007
+ }
8008
+ if (typeof window !== "undefined" && ((_b2 = window.openai) == null ? void 0 : _b2.setWidgetState)) {
8009
+ window.openai.setWidgetState(payload);
8010
+ }
8011
+ break;
8012
+ }
8013
+ case "openai:notifyIntrinsicHeight": {
8014
+ const payload = event.data.payload;
8015
+ if (!payload || typeof payload !== "object" || !("height" in payload) || typeof payload.height !== "number") {
8016
+ console.warn("[IframeResource] Invalid notifyIntrinsicHeight payload");
8017
+ return;
8018
+ }
8019
+ const height = payload.height;
8020
+ if (height <= 0 || height > 1e5) {
8021
+ console.warn("[IframeResource] Height out of range:", height);
8022
+ return;
8023
+ }
8024
+ if (iframeRef.current) {
8025
+ iframeRef.current.style.height = `${height}px`;
8026
+ }
8027
+ break;
8028
+ }
8029
+ case "openai:callTool":
8030
+ case "openai:sendFollowUpMessage":
8031
+ case "openai:openExternal":
8032
+ case "openai:requestModal":
8033
+ console.log(`[IframeResource] Received ${type}:`, event.data.payload);
8034
+ break;
8035
+ }
8036
+ },
8037
+ []
8038
+ // No dependencies - uses refs for changing values
8039
+ );
8040
+ useEffect(() => {
8041
+ const handleMessage = (event) => {
8042
+ if (iframeRef.current && event.source !== iframeRef.current.contentWindow) {
8043
+ return;
8044
+ }
8045
+ if (!event.data || typeof event.data !== "object" || typeof event.data.type !== "string") {
8046
+ return;
8047
+ }
8048
+ if (event.data.type === "openai:ready") {
8049
+ const channel = new MessageChannel();
8050
+ messageChannelRef.current = channel;
8051
+ messagePortRef.current = channel.port1;
8052
+ channel.port1.onmessage = handlePortMessage;
8053
+ channel.port1.start();
8054
+ const iframe = iframeRef.current;
8055
+ if (iframe == null ? void 0 : iframe.contentWindow) {
8056
+ iframe.contentWindow.postMessage({ type: "openai:handshake" }, "*", [channel.port2]);
8057
+ }
8058
+ }
8059
+ if (event.data.type === "openai:notifyIntrinsicHeight") {
8060
+ const payload = event.data.payload;
8061
+ if (!payload || typeof payload !== "object" || !("height" in payload) || typeof payload.height !== "number") {
8062
+ return;
8063
+ }
8064
+ const height = payload.height;
8065
+ if (height <= 0 || height > 1e5) {
8066
+ return;
8067
+ }
8068
+ if (iframeRef.current) {
8069
+ iframeRef.current.style.height = `${height}px`;
8070
+ setIsHeightReady(true);
8071
+ }
8072
+ }
8073
+ };
8074
+ window.addEventListener("message", handleMessage);
8075
+ return () => {
8076
+ window.removeEventListener("message", handleMessage);
8077
+ if (messagePortRef.current) {
8078
+ messagePortRef.current.close();
8079
+ }
8080
+ };
8081
+ }, [handlePortMessage]);
8082
+ useEffect(() => {
8083
+ sendUpdate("openai:update");
8084
+ }, [sendUpdate]);
8085
+ const isValidScriptSrc = useMemo(() => isAllowedScriptSrc(scriptSrc), [scriptSrc]);
8086
+ const blobUrl = useMemo(() => {
8087
+ if (!isValidScriptSrc) {
8088
+ console.error("[IframeResource] Script source not allowed:", scriptSrc);
8089
+ const errorHtml = `<!DOCTYPE html><html><body><h1>Error</h1><p>Script source not allowed.</p></body></html>`;
8090
+ const blob2 = new Blob([errorHtml], { type: "text/html" });
8091
+ return URL.createObjectURL(blob2);
8092
+ }
8093
+ const absoluteScriptSrc = scriptSrc.startsWith("/") ? `${window.location.origin}${scriptSrc}` : scriptSrc;
8094
+ const cspPolicy = generateCSP(csp, absoluteScriptSrc);
8095
+ const bridgeScript = generateBridgeScript(ALLOWED_PARENT_ORIGINS);
8096
+ const html = injectBridgeScript(
8097
+ generateScriptHtml(absoluteScriptSrc, theme ?? "dark", cspPolicy),
8098
+ bridgeScript
8099
+ );
8100
+ const blob = new Blob([html], { type: "text/html" });
8101
+ return URL.createObjectURL(blob);
8102
+ }, [scriptSrc, theme, isValidScriptSrc, csp]);
8103
+ useEffect(() => {
8104
+ return () => URL.revokeObjectURL(blobUrl);
8105
+ }, [blobUrl]);
8106
+ return /* @__PURE__ */ jsx(
8107
+ "iframe",
8108
+ {
8109
+ ref: iframeRef,
8110
+ src: blobUrl,
8111
+ className,
8112
+ style: {
8113
+ border: "none",
8114
+ width: "100%",
8115
+ height: "100%",
8116
+ // Hide until first height update to prevent flash of incorrect size
8117
+ opacity: isHeightReady ? 1 : 0,
8118
+ transition: "opacity 0.1s ease-in",
8119
+ ...style
8120
+ },
8121
+ title: "Resource Preview",
8122
+ sandbox: "allow-scripts",
8123
+ allow: "accelerometer 'none'; autoplay 'none'; camera 'none'; display-capture 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none'; publickey-credentials-get 'none'; usb 'none'; xr-spatial-tracking 'none'"
8124
+ }
8125
+ );
8126
+ }
7545
8127
  function Conversation({
7546
8128
  children,
8129
+ iframeScriptSrc,
7547
8130
  screenWidth,
7548
8131
  appName = "Sunpeak",
7549
8132
  appIcon,
@@ -7555,6 +8138,8 @@ function Conversation({
7555
8138
  const userAgent = useUserAgent();
7556
8139
  const isDesktop = (userAgent == null ? void 0 : userAgent.device.type) === "desktop";
7557
8140
  const containerWidth = screenWidth === "full" ? "100%" : `${SCREEN_WIDTHS[screenWidth]}px`;
8141
+ const widgetCSP = resourceMeta == null ? void 0 : resourceMeta["openai/widgetCSP"];
8142
+ const content = iframeScriptSrc ? /* @__PURE__ */ jsx(IframeResource, { scriptSrc: iframeScriptSrc, className: "h-full w-full", csp: widgetCSP }) : children;
7558
8143
  if (displayMode === "fullscreen") {
7559
8144
  const handleClose = () => {
7560
8145
  if (api == null ? void 0 : api.requestDisplayMode) {
@@ -7604,7 +8189,7 @@ function Conversation({
7604
8189
  }
7605
8190
  ) })
7606
8191
  ] }),
7607
- /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden flex-1 min-h-0", children: /* @__PURE__ */ jsx("div", { className: "h-full w-full max-w-full overflow-auto", children }) }),
8192
+ /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden flex-1 min-h-0", children: /* @__PURE__ */ jsx("div", { className: "h-full w-full max-w-full overflow-auto", children: content }) }),
7608
8193
  /* @__PURE__ */ jsx("footer", { className: "bg-surface", children: /* @__PURE__ */ jsx("div", { className: "max-w-[48rem] mx-auto px-4 py-4", children: /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
7609
8194
  "input",
7610
8195
  {
@@ -7694,10 +8279,10 @@ function Conversation({
7694
8279
  children: /* @__PURE__ */ jsx(CloseBold, { className: "h-4 w-4" })
7695
8280
  }
7696
8281
  ),
7697
- /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden h-full rounded-2xl sm:rounded-3xl shadow-[0px_0px_0px_1px_#fff3,0px_6px_20px_rgba(0,0,0,0.1)] md:-mx-4", children: /* @__PURE__ */ jsx("div", { className: "h-full w-full max-w-full overflow-auto bg-white dark:bg-[#212121]", children }) })
8282
+ /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden h-full rounded-2xl sm:rounded-3xl shadow-[0px_0px_0px_1px_#fff3,0px_6px_20px_rgba(0,0,0,0.1)] md:-mx-4", children: /* @__PURE__ */ jsx("div", { className: "h-full w-full max-w-full overflow-auto bg-white dark:bg-[#212121]", children: content }) })
7698
8283
  ]
7699
8284
  }
7700
- ) : /* @__PURE__ */ jsx("div", { className: "no-scrollbar relative mb-2 @w-sm/main:w-full mx-0 max-sm:-mx-[1rem] max-sm:w-[100cqw] max-sm:overflow-hidden overflow-visible", children: /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden h-full", children }) }) })
8285
+ ) : /* @__PURE__ */ jsx("div", { className: "no-scrollbar relative mb-2 @w-sm/main:w-full mx-0 max-sm:-mx-[1rem] max-sm:w-[100cqw] max-sm:overflow-hidden overflow-visible", children: /* @__PURE__ */ jsx("div", { className: "relative overflow-hidden h-full", children: content }) }) })
7701
8286
  }
7702
8287
  )
7703
8288
  ] }) }) })
@@ -8113,6 +8698,7 @@ function ChatGPTSimulator({
8113
8698
  }
8114
8699
  };
8115
8700
  const SelectedComponent = selectedSim == null ? void 0 : selectedSim.resourceComponent;
8701
+ const iframeScriptSrc = !SelectedComponent ? selectedSim == null ? void 0 : selectedSim.resourceScript : void 0;
8116
8702
  const content = SelectedComponent ? /* @__PURE__ */ jsx(SelectedComponent, {}) : children;
8117
8703
  return /* @__PURE__ */ jsx(ThemeProvider, { theme, children: /* @__PURE__ */ jsx(
8118
8704
  SimpleSidebar,
@@ -8447,6 +9033,7 @@ function ChatGPTSimulator({
8447
9033
  appIcon,
8448
9034
  userMessage,
8449
9035
  resourceMeta: selectedSim == null ? void 0 : selectedSim.resource._meta,
9036
+ iframeScriptSrc,
8450
9037
  children: content
8451
9038
  },
8452
9039
  selectedSimulationName
@@ -8500,6 +9087,7 @@ function createSimulatorUrl(params, basePath = "/") {
8500
9087
  }
8501
9088
  export {
8502
9089
  ChatGPTSimulator as C,
9090
+ IframeResource as I,
8503
9091
  SCREEN_WIDTHS as S,
8504
9092
  ThemeProvider as T,
8505
9093
  clsx as a,
@@ -8526,4 +9114,4 @@ export {
8526
9114
  getAPI as v,
8527
9115
  resetProviderCache as w
8528
9116
  };
8529
- //# sourceMappingURL=simulator-url-CexnaL-e.js.map
9117
+ //# sourceMappingURL=simulator-url-BOSS60NS.js.map