sunpeak 0.16.21 → 0.16.27
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/README.md +4 -3
- package/bin/commands/dev.mjs +120 -8
- package/bin/commands/new.mjs +6 -2
- package/bin/commands/start.mjs +7 -2
- package/bin/lib/get-port.mjs +60 -0
- package/bin/lib/live/browser-auth.mjs +161 -0
- package/bin/lib/live/chatgpt-config.d.mts +5 -0
- package/bin/lib/live/chatgpt-config.mjs +12 -0
- package/bin/lib/live/chatgpt-fixtures.d.mts +12 -0
- package/bin/lib/live/chatgpt-fixtures.mjs +25 -0
- package/bin/lib/live/chatgpt-page.mjs +210 -0
- package/bin/lib/live/global-setup.mjs +158 -0
- package/bin/lib/live/host-fixtures.mjs +61 -0
- package/bin/lib/live/host-page.mjs +294 -0
- package/bin/lib/live/live-config.d.mts +38 -0
- package/bin/lib/live/live-config.mjs +98 -0
- package/bin/lib/live/live-fixtures.d.mts +11 -0
- package/bin/lib/live/live-fixtures.mjs +102 -0
- package/bin/lib/live/test-config.d.mts +10 -0
- package/bin/lib/live/test-config.mjs +35 -0
- package/bin/lib/live/types.d.mts +54 -0
- package/bin/lib/live/utils.mjs +70 -0
- package/bin/lib/sandbox-server.mjs +304 -0
- package/bin/sunpeak.js +1 -1
- package/dist/chatgpt/chatgpt-conversation.d.ts +3 -7
- package/dist/chatgpt/globals.css +18 -0
- package/dist/chatgpt/index.cjs +1 -1
- package/dist/chatgpt/index.js +1 -1
- package/dist/claude/claude-conversation.d.ts +3 -2
- package/dist/claude/index.cjs +1 -1
- package/dist/claude/index.js +1 -1
- package/dist/{index-bKBBCBK6.cjs → index-BEWVLFfB.cjs} +2 -2
- package/dist/index-BEWVLFfB.cjs.map +1 -0
- package/dist/{index-CX6Z4bED.js → index-C6XYFOmh.js} +2 -2
- package/dist/index-C6XYFOmh.js.map +1 -0
- package/dist/{index-CKabCJyV.cjs → index-D0FsXP3Y.cjs} +2 -2
- package/dist/index-D0FsXP3Y.cjs.map +1 -0
- package/dist/{index-B4aC3vjH.js → index-Rg7SWjvl.js} +2 -2
- package/dist/index-Rg7SWjvl.js.map +1 -0
- package/dist/index.cjs +13 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/favicon.d.ts +3 -1
- package/dist/mcp/index.cjs +90 -49
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.d.ts +2 -2
- package/dist/mcp/index.js +90 -49
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/production-server.d.ts +7 -1
- package/dist/mcp/types.d.ts +32 -1
- package/dist/simulator/hosts.d.ts +11 -2
- package/dist/simulator/iframe-resource.d.ts +8 -1
- package/dist/simulator/index.cjs +1 -1
- package/dist/simulator/index.js +1 -1
- package/dist/simulator/mcp-app-host.d.ts +17 -0
- package/dist/simulator/sandbox-proxy.d.ts +38 -0
- package/dist/simulator/simple-sidebar.d.ts +3 -1
- package/dist/simulator/simulator.d.ts +7 -1
- package/dist/simulator/use-simulator-state.d.ts +2 -4
- package/dist/{simulator-D8t-r7HH.js → simulator-B-CrMHVs.js} +504 -192
- package/dist/simulator-B-CrMHVs.js.map +1 -0
- package/dist/{simulator-FFNttkqL.cjs → simulator-Gc6n_fT4.cjs} +503 -191
- package/dist/simulator-Gc6n_fT4.cjs.map +1 -0
- package/dist/style.css +18 -0
- package/package.json +25 -1
- package/template/.sunpeak/dev.tsx +9 -3
- package/template/README.md +24 -2
- package/template/_gitignore +1 -0
- package/template/package.json +3 -2
- package/template/playwright.config.ts +34 -6
- package/template/src/server.ts +16 -2
- package/template/src/tools/show-albums.ts +17 -0
- package/template/tests/e2e/albums.spec.ts +37 -5
- package/template/tests/e2e/carousel.spec.ts +6 -6
- package/template/tests/e2e/global-setup.ts +6 -21
- package/template/tests/e2e/map.spec.ts +11 -11
- package/template/tests/e2e/review.spec.ts +24 -24
- package/template/tests/live/albums.spec.ts +53 -0
- package/template/tests/live/carousel.spec.ts +52 -0
- package/template/tests/live/map.spec.ts +31 -0
- package/template/tests/live/playwright.config.ts +3 -0
- package/template/tests/live/review.spec.ts +54 -0
- package/template/vitest.config.ts +1 -1
- package/dist/index-B4aC3vjH.js.map +0 -1
- package/dist/index-CKabCJyV.cjs.map +0 -1
- package/dist/index-CX6Z4bED.js.map +0 -1
- package/dist/index-bKBBCBK6.cjs.map +0 -1
- package/dist/simulator-D8t-r7HH.js.map +0 -1
- package/dist/simulator-FFNttkqL.cjs.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { readFileSync, rmSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { createRequire } from 'module';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Recursively remove a directory (rm -rf equivalent).
|
|
7
|
+
*/
|
|
8
|
+
export function rimrafSync(dir) {
|
|
9
|
+
rmSync(dir, { recursive: true, force: true });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Browser launch args that bypass Cloudflare/ChatGPT bot detection.
|
|
14
|
+
* Used in browser-auth, global-setup, and live-config.
|
|
15
|
+
*/
|
|
16
|
+
export const ANTI_BOT_ARGS = [
|
|
17
|
+
'--disable-blink-features=AutomationControlled',
|
|
18
|
+
'--no-first-run',
|
|
19
|
+
'--no-default-browser-check',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Real Chrome user agent string to avoid Cloudflare challenges.
|
|
24
|
+
* Update periodically to match the Playwright-bundled Chromium version.
|
|
25
|
+
*/
|
|
26
|
+
export const CHROME_USER_AGENT =
|
|
27
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolve @playwright/test from the user's project via CJS require.
|
|
31
|
+
* Use for non-test code (global-setup, config) where duplicate instances don't matter.
|
|
32
|
+
*/
|
|
33
|
+
export function resolvePlaywright(projectRoot) {
|
|
34
|
+
const require = createRequire(join(projectRoot, 'package.json'));
|
|
35
|
+
return require('@playwright/test');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve @playwright/test ESM module from the user's project.
|
|
40
|
+
* Use for test fixtures that call test.extend() — avoids CJS/ESM duplicate module issues.
|
|
41
|
+
* Cached: safe to call multiple times.
|
|
42
|
+
*/
|
|
43
|
+
let _cachedPlaywright = null;
|
|
44
|
+
let _cachedProjectRoot = null;
|
|
45
|
+
export async function resolvePlaywrightESM(projectRoot) {
|
|
46
|
+
if (_cachedPlaywright && _cachedProjectRoot === projectRoot) return _cachedPlaywright;
|
|
47
|
+
const require = createRequire(join(projectRoot, 'package.json'));
|
|
48
|
+
const playwrightPath = require.resolve('@playwright/test');
|
|
49
|
+
const mod = await import(playwrightPath);
|
|
50
|
+
// Dynamic import() of @playwright/test (CJS) puts the test function at mod.default.
|
|
51
|
+
// The test function doubles as the module namespace — test.extend, test.expect, etc.
|
|
52
|
+
// Normalize so callers can destructure { test, expect } directly.
|
|
53
|
+
const pw = mod.default || mod;
|
|
54
|
+
_cachedPlaywright = { test: pw, expect: pw.expect };
|
|
55
|
+
_cachedProjectRoot = projectRoot;
|
|
56
|
+
return _cachedPlaywright;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Read the app name from the project's package.json.
|
|
61
|
+
* Cached: safe to call multiple times.
|
|
62
|
+
*/
|
|
63
|
+
let _cachedAppName = null;
|
|
64
|
+
let _cachedAppNameRoot = null;
|
|
65
|
+
export function getAppName(projectRoot) {
|
|
66
|
+
if (_cachedAppName && _cachedAppNameRoot === projectRoot) return _cachedAppName;
|
|
67
|
+
_cachedAppName = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8')).name;
|
|
68
|
+
_cachedAppNameRoot = projectRoot;
|
|
69
|
+
return _cachedAppName;
|
|
70
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Separate-origin sandbox server for the simulator's double-iframe architecture.
|
|
3
|
+
*
|
|
4
|
+
* Real hosts (ChatGPT, Claude) run the sandbox proxy iframe on a separate origin
|
|
5
|
+
* (e.g., web-sandbox.oaiusercontent.com). This server replicates that by serving
|
|
6
|
+
* the proxy HTML on a different localhost port, giving real origin isolation.
|
|
7
|
+
*
|
|
8
|
+
* This means:
|
|
9
|
+
* - window.top.location access from inside the iframe is blocked (cross-origin)
|
|
10
|
+
* - document.referrer is empty (cross-origin navigation)
|
|
11
|
+
* - The iframe sandbox attribute behaves identically to production
|
|
12
|
+
*
|
|
13
|
+
* The server is started by `sunpeak dev` and its URL is injected into the Simulator
|
|
14
|
+
* via `__SUNPEAK_SANDBOX_URL__`.
|
|
15
|
+
*
|
|
16
|
+
* NOTE: The proxy HTML and mock openai script are duplicated from sandbox-proxy.ts
|
|
17
|
+
* and mock-openai-runtime.ts because this file runs in Node.js at dev time and
|
|
18
|
+
* cannot import TypeScript modules. Keep them in sync when making changes.
|
|
19
|
+
*/
|
|
20
|
+
import { createServer } from 'http';
|
|
21
|
+
import { getPort } from './get-port.mjs';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Start the sandbox proxy server on a separate port.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} options
|
|
27
|
+
* @param {number} [options.preferredPort=24680] - Port to try first
|
|
28
|
+
* @returns {Promise<{ url: string, port: number, close: () => Promise<void> }>}
|
|
29
|
+
*/
|
|
30
|
+
export async function startSandboxServer({ preferredPort = 24680 } = {}) {
|
|
31
|
+
const port = await getPort(preferredPort);
|
|
32
|
+
|
|
33
|
+
const server = createServer((req, res) => {
|
|
34
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
35
|
+
|
|
36
|
+
if (url.pathname === '/proxy') {
|
|
37
|
+
const theme = url.searchParams.get('theme') || 'dark';
|
|
38
|
+
const platform = url.searchParams.get('platform') || '';
|
|
39
|
+
const html = generateProxyHtml(theme, platform);
|
|
40
|
+
|
|
41
|
+
res.writeHead(200, {
|
|
42
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
43
|
+
'Cache-Control': 'no-cache',
|
|
44
|
+
// No CORS headers needed — iframe src loads don't check CORS.
|
|
45
|
+
// PostMessage works cross-origin by design.
|
|
46
|
+
});
|
|
47
|
+
res.end(html);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Health check
|
|
52
|
+
if (url.pathname === '/health') {
|
|
53
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
54
|
+
res.end('{"status":"ok"}');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
res.writeHead(404);
|
|
59
|
+
res.end('Not found');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await new Promise((resolve, reject) => {
|
|
63
|
+
server.listen(port, () => resolve());
|
|
64
|
+
server.on('error', reject);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const sandboxUrl = `http://localhost:${port}`;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
url: sandboxUrl,
|
|
71
|
+
port,
|
|
72
|
+
close: () => new Promise((resolve) => server.close(resolve)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generate the sandbox proxy HTML.
|
|
78
|
+
*
|
|
79
|
+
* This is the same proxy logic as sandbox-proxy.ts but as a plain string
|
|
80
|
+
* (no TypeScript imports needed at dev server runtime). The proxy:
|
|
81
|
+
* 1. Signals readiness via `ui/notifications/sandbox-proxy-ready`
|
|
82
|
+
* 2. Listens for resource content or URL to load into the inner iframe
|
|
83
|
+
* 3. Relays all PostMessage between parent and inner iframe
|
|
84
|
+
* 4. Optionally injects platform runtime scripts (e.g., mock window.openai)
|
|
85
|
+
*/
|
|
86
|
+
function generateProxyHtml(theme, platform) {
|
|
87
|
+
const colorScheme = theme === 'light' ? 'light' : 'dark';
|
|
88
|
+
|
|
89
|
+
// Platform-specific runtime script (injected into the inner iframe)
|
|
90
|
+
let platformScript = 'null';
|
|
91
|
+
if (platform === 'chatgpt') {
|
|
92
|
+
platformScript = JSON.stringify(MOCK_OPENAI_SCRIPT);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return `<!DOCTYPE html>
|
|
96
|
+
<html style="color-scheme:${colorScheme}">
|
|
97
|
+
<head>
|
|
98
|
+
<meta name="color-scheme" content="${colorScheme}" />
|
|
99
|
+
<style>
|
|
100
|
+
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
|
|
101
|
+
iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
102
|
+
</style>
|
|
103
|
+
</head>
|
|
104
|
+
<body>
|
|
105
|
+
<script>
|
|
106
|
+
(function() {
|
|
107
|
+
var innerFrame = null;
|
|
108
|
+
var innerWindow = null;
|
|
109
|
+
var platformScript = ${platformScript};
|
|
110
|
+
|
|
111
|
+
// Relay messages between parent (host) and inner iframe (app)
|
|
112
|
+
window.addEventListener('message', function(event) {
|
|
113
|
+
var data = event.data;
|
|
114
|
+
if (!data || typeof data !== 'object') return;
|
|
115
|
+
|
|
116
|
+
if (event.source === window.parent) {
|
|
117
|
+
// sandbox-resource-ready: load HTML into inner iframe (scriptSrc/prod mode)
|
|
118
|
+
if (data.method === 'ui/notifications/sandbox-resource-ready' && data.params) {
|
|
119
|
+
createInnerFrame(data.params);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// sunpeak/sandbox-load-src: load URL into inner iframe (src/dev mode)
|
|
124
|
+
if (data.method === 'sunpeak/sandbox-load-src' && data.params) {
|
|
125
|
+
createInnerFrameWithSrc(data.params);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Handle paint fence. Forward to the inner iframe and wait for its ack.
|
|
130
|
+
// The inner iframe has a fence responder (injected by the Vite dev page
|
|
131
|
+
// or embedded in the production HTML). If the ack arrives, relay it to
|
|
132
|
+
// the host. If not (cross-origin injection failed), fall back to a
|
|
133
|
+
// timeout-based ack after 150ms.
|
|
134
|
+
if (data.method === 'sunpeak/fence' && data.params) {
|
|
135
|
+
var fenceId = data.params.fenceId;
|
|
136
|
+
var acked = false;
|
|
137
|
+
|
|
138
|
+
// Listen for the inner iframe's fence-ack
|
|
139
|
+
var onAck = function(e) {
|
|
140
|
+
if (e.source !== innerWindow) return;
|
|
141
|
+
if (e.data && e.data.method === 'sunpeak/fence-ack' &&
|
|
142
|
+
e.data.params && e.data.params.fenceId === fenceId) {
|
|
143
|
+
acked = true;
|
|
144
|
+
window.removeEventListener('message', onAck);
|
|
145
|
+
window.parent.postMessage(e.data, '*');
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
window.addEventListener('message', onAck);
|
|
149
|
+
|
|
150
|
+
// Forward fence to inner iframe
|
|
151
|
+
if (innerWindow) {
|
|
152
|
+
try { innerWindow.postMessage(data, '*'); } catch(e) {}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Fallback: if no ack within 150ms (fence responder not available),
|
|
156
|
+
// ack from the proxy after allowing time for the inner iframe to
|
|
157
|
+
// process the preceding hostContext change.
|
|
158
|
+
setTimeout(function() {
|
|
159
|
+
if (!acked) {
|
|
160
|
+
window.removeEventListener('message', onAck);
|
|
161
|
+
window.parent.postMessage({
|
|
162
|
+
jsonrpc: '2.0',
|
|
163
|
+
method: 'sunpeak/fence-ack',
|
|
164
|
+
params: { fenceId: fenceId }
|
|
165
|
+
}, '*');
|
|
166
|
+
}
|
|
167
|
+
}, 150);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Forward all other messages to the inner iframe
|
|
172
|
+
if (innerWindow) {
|
|
173
|
+
try { innerWindow.postMessage(data, '*'); } catch(e) { /* detached */ }
|
|
174
|
+
}
|
|
175
|
+
} else if (innerWindow && event.source === innerWindow) {
|
|
176
|
+
// Messages from the app -> forward to host
|
|
177
|
+
try { window.parent.postMessage(data, '*'); } catch(e) { /* detached */ }
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
function createInnerFrame(params) {
|
|
182
|
+
if (innerFrame) innerFrame.remove();
|
|
183
|
+
|
|
184
|
+
innerFrame = document.createElement('iframe');
|
|
185
|
+
innerFrame.sandbox = params.sandbox ||
|
|
186
|
+
'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox';
|
|
187
|
+
if (params.allow) innerFrame.allow = params.allow;
|
|
188
|
+
document.body.appendChild(innerFrame);
|
|
189
|
+
innerWindow = innerFrame.contentWindow;
|
|
190
|
+
|
|
191
|
+
var doc = innerFrame.contentDocument;
|
|
192
|
+
if (doc && params.html) {
|
|
193
|
+
doc.open();
|
|
194
|
+
doc.write(params.html);
|
|
195
|
+
doc.close();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function createInnerFrameWithSrc(params) {
|
|
200
|
+
if (innerFrame) innerFrame.remove();
|
|
201
|
+
|
|
202
|
+
innerFrame = document.createElement('iframe');
|
|
203
|
+
innerFrame.sandbox =
|
|
204
|
+
'allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox';
|
|
205
|
+
if (params.allow) innerFrame.allow = params.allow;
|
|
206
|
+
innerFrame.src = params.src;
|
|
207
|
+
innerFrame.style.height = '100%';
|
|
208
|
+
|
|
209
|
+
if (params.theme) {
|
|
210
|
+
innerFrame.style.colorScheme = params.theme;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
innerFrame.addEventListener('load', function() {
|
|
214
|
+
innerWindow = innerFrame.contentWindow;
|
|
215
|
+
|
|
216
|
+
// Inject platform runtime (e.g., mock window.openai for ChatGPT)
|
|
217
|
+
if (platformScript && innerWindow) {
|
|
218
|
+
try {
|
|
219
|
+
var pScript = innerFrame.contentDocument.createElement('script');
|
|
220
|
+
pScript.textContent = platformScript;
|
|
221
|
+
innerFrame.contentDocument.head.appendChild(pScript);
|
|
222
|
+
} catch(e) { /* cross-origin */ }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Inject paint fence responder
|
|
226
|
+
try {
|
|
227
|
+
var fenceScript = innerFrame.contentDocument.createElement('script');
|
|
228
|
+
fenceScript.setAttribute('data-sunpeak-fence', '');
|
|
229
|
+
fenceScript.textContent = PAINT_FENCE_SCRIPT;
|
|
230
|
+
innerFrame.contentDocument.head.appendChild(fenceScript);
|
|
231
|
+
} catch(e) { /* cross-origin */ }
|
|
232
|
+
|
|
233
|
+
// Inject background rule
|
|
234
|
+
if (params.theme) {
|
|
235
|
+
try {
|
|
236
|
+
innerFrame.contentDocument.documentElement.style.colorScheme = params.theme;
|
|
237
|
+
var bgStyle = innerFrame.contentDocument.createElement('style');
|
|
238
|
+
bgStyle.setAttribute('data-sunpeak-bg', '');
|
|
239
|
+
bgStyle.textContent = 'html { background-color: var(--color-background-primary, Canvas); }';
|
|
240
|
+
innerFrame.contentDocument.head.appendChild(bgStyle);
|
|
241
|
+
} catch(e) { /* cross-origin */ }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Inject style variables
|
|
245
|
+
if (params.styleVars) {
|
|
246
|
+
try {
|
|
247
|
+
var root = innerFrame.contentDocument.documentElement;
|
|
248
|
+
for (var key in params.styleVars) {
|
|
249
|
+
if (params.styleVars[key]) root.style.setProperty(key, params.styleVars[key]);
|
|
250
|
+
}
|
|
251
|
+
} catch(e) { /* cross-origin */ }
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
innerFrame.style.opacity = '1';
|
|
255
|
+
innerFrame.style.transition = 'opacity 100ms';
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
innerFrame.style.opacity = '0';
|
|
259
|
+
document.body.appendChild(innerFrame);
|
|
260
|
+
innerWindow = innerFrame.contentWindow;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
var PAINT_FENCE_SCRIPT = 'window.addEventListener("message",function(e){' +
|
|
264
|
+
'if(e.data&&e.data.method==="sunpeak/fence"){' +
|
|
265
|
+
'var fid=e.data.params&&e.data.params.fenceId;' +
|
|
266
|
+
'requestAnimationFrame(function(){' +
|
|
267
|
+
'e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");' +
|
|
268
|
+
'});}});';
|
|
269
|
+
|
|
270
|
+
// Signal readiness to the host
|
|
271
|
+
setTimeout(function() {
|
|
272
|
+
window.parent.postMessage({
|
|
273
|
+
jsonrpc: '2.0',
|
|
274
|
+
method: 'ui/notifications/sandbox-proxy-ready',
|
|
275
|
+
params: {}
|
|
276
|
+
}, '*');
|
|
277
|
+
}, 0);
|
|
278
|
+
})();
|
|
279
|
+
</script>
|
|
280
|
+
</body>
|
|
281
|
+
</html>`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Mock OpenAI runtime script — same as mock-openai-runtime.ts MOCK_OPENAI_RUNTIME_SCRIPT.
|
|
286
|
+
* Duplicated here to avoid TypeScript import in the Node.js dev server.
|
|
287
|
+
*/
|
|
288
|
+
const MOCK_OPENAI_SCRIPT = [
|
|
289
|
+
'window.openai={',
|
|
290
|
+
'uploadFile:function(f){console.log("[Simulator] uploadFile:",f.name);',
|
|
291
|
+
'return Promise.resolve({fileId:"sim_file_"+Date.now()})},',
|
|
292
|
+
'getFileDownloadUrl:function(p){console.log("[Simulator] getFileDownloadUrl:",p.fileId);',
|
|
293
|
+
'return Promise.resolve({downloadUrl:"https://simulator.local/files/"+p.fileId})},',
|
|
294
|
+
'requestModal:function(p){console.log("[Simulator] requestModal:",JSON.stringify(p));',
|
|
295
|
+
'return Promise.resolve()},',
|
|
296
|
+
'requestCheckout:function(s){console.log("[Simulator] requestCheckout:",JSON.stringify(s));',
|
|
297
|
+
'return Promise.resolve({id:"sim_order_"+Date.now(),checkout_session_id:s.id||"sim_session",status:"completed"})},',
|
|
298
|
+
'requestClose:function(){console.log("[Simulator] requestClose")},',
|
|
299
|
+
'requestDisplayMode:function(p){console.log("[Simulator] requestDisplayMode:",p.mode);',
|
|
300
|
+
'return Promise.resolve()},',
|
|
301
|
+
'sendFollowUpMessage:function(p){console.log("[Simulator] sendFollowUpMessage:",p.prompt)},',
|
|
302
|
+
'openExternal:function(p){console.log("[Simulator] openExternal:",p.href);window.open(p.href,"_blank")}',
|
|
303
|
+
'};',
|
|
304
|
+
].join('');
|
package/bin/sunpeak.js
CHANGED
|
@@ -104,7 +104,7 @@ Usage:
|
|
|
104
104
|
sunpeak --version Show version number
|
|
105
105
|
|
|
106
106
|
Resources: ${resources.join(', ')} (comma/space separated)
|
|
107
|
-
Example: sunpeak new
|
|
107
|
+
Example: sunpeak new sunpeak-app "${resources.slice(0, 2).join(',')}"
|
|
108
108
|
`);
|
|
109
109
|
}
|
|
110
110
|
break;
|
|
@@ -11,14 +11,10 @@ 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 the pip
|
|
17
|
-
* border from flashing at a stale height before the iframe resizes.
|
|
18
|
-
*/
|
|
19
|
-
isTransitioning?: boolean;
|
|
20
14
|
/** Optional action element rendered in the conversation header (e.g., Run button) */
|
|
21
15
|
headerAction?: React.ReactNode;
|
|
16
|
+
/** Called when the content container width changes */
|
|
17
|
+
onContentWidthChange?: (width: number) => void;
|
|
22
18
|
}
|
|
23
19
|
/**
|
|
24
20
|
* Conversation layout that renders children (iframe) at a stable tree position.
|
|
@@ -33,5 +29,5 @@ interface ConversationProps {
|
|
|
33
29
|
* - **fullscreen**: content wrapper becomes `position: fixed` covering the viewport;
|
|
34
30
|
* fullscreen chrome (header/footer) rendered as a separate fixed overlay
|
|
35
31
|
*/
|
|
36
|
-
export declare function Conversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage,
|
|
32
|
+
export declare function Conversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage, headerAction, onContentWidthChange, }: ConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
37
33
|
export {};
|
package/dist/chatgpt/globals.css
CHANGED
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
--tw-backdrop-saturate: initial;
|
|
152
152
|
--tw-backdrop-sepia: initial;
|
|
153
153
|
--tw-duration: initial;
|
|
154
|
+
--tw-ease: initial;
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
}
|
|
@@ -191,6 +192,7 @@
|
|
|
191
192
|
--shadow-sm: 0 1px 3px 0 #0000001a, 0 1px 2px -1px #0000001a;
|
|
192
193
|
--shadow-md: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
|
193
194
|
--shadow-lg: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;
|
|
195
|
+
--ease-out: cubic-bezier(0, 0, .2, 1);
|
|
194
196
|
--animate-spin: spin 1s linear infinite;
|
|
195
197
|
--blur-sm: 8px;
|
|
196
198
|
--default-transition-duration: .15s;
|
|
@@ -1847,6 +1849,11 @@
|
|
|
1847
1849
|
transition-duration: .2s;
|
|
1848
1850
|
}
|
|
1849
1851
|
|
|
1852
|
+
.ease-out {
|
|
1853
|
+
--tw-ease: var(--ease-out);
|
|
1854
|
+
transition-timing-function: var(--ease-out);
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1850
1857
|
.outline-none {
|
|
1851
1858
|
--tw-outline-style: none;
|
|
1852
1859
|
outline-style: none;
|
|
@@ -2037,6 +2044,12 @@
|
|
|
2037
2044
|
}
|
|
2038
2045
|
}
|
|
2039
2046
|
|
|
2047
|
+
@media (min-width: 1440px) {
|
|
2048
|
+
.min-\[1440px\]\:max-w-\[48rem\] {
|
|
2049
|
+
max-width: 48rem;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2040
2053
|
@media (min-width: 40rem) {
|
|
2041
2054
|
.sm\:start-0 {
|
|
2042
2055
|
inset-inline-start: calc(var(--spacing) * 0);
|
|
@@ -2679,6 +2692,11 @@
|
|
|
2679
2692
|
inherits: false
|
|
2680
2693
|
}
|
|
2681
2694
|
|
|
2695
|
+
@property --tw-ease {
|
|
2696
|
+
syntax: "*";
|
|
2697
|
+
inherits: false
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2682
2700
|
@keyframes spin {
|
|
2683
2701
|
to {
|
|
2684
2702
|
transform: rotate(360deg);
|
package/dist/chatgpt/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const simulator = require("../simulator-
|
|
3
|
+
const simulator = require("../simulator-Gc6n_fT4.cjs");
|
|
4
4
|
const simulatorUrl = require("../simulator-url-DcSYRl-P.cjs");
|
|
5
5
|
const discovery = require("../discovery-D1gpaVz4.cjs");
|
|
6
6
|
exports.IframeResource = simulator.IframeResource;
|
package/dist/chatgpt/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I, M, a, S, T, j, m, n } from "../simulator-
|
|
1
|
+
import { I, M, a, S, T, j, m, n } from "../simulator-B-CrMHVs.js";
|
|
2
2
|
import { c } from "../simulator-url-j_XV3EoP.js";
|
|
3
3
|
import { b, a as a2, c as c2, d, e, f, g, h, i, t } from "../discovery-BVqD-JsT.js";
|
|
4
4
|
export {
|
|
@@ -11,9 +11,10 @@ interface ClaudeConversationProps {
|
|
|
11
11
|
appName?: string;
|
|
12
12
|
appIcon?: string;
|
|
13
13
|
userMessage?: string;
|
|
14
|
-
isTransitioning?: boolean;
|
|
15
14
|
/** Optional action element rendered in the conversation header (e.g., Run button) */
|
|
16
15
|
headerAction?: React.ReactNode;
|
|
16
|
+
/** Called when the content container width changes */
|
|
17
|
+
onContentWidthChange?: (width: number) => void;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* Claude conversation shell — mimics Claude's chat UI chrome.
|
|
@@ -21,5 +22,5 @@ interface ClaudeConversationProps {
|
|
|
21
22
|
* All three display modes (inline, pip, fullscreen) share the same React tree
|
|
22
23
|
* shape so that the iframe never unmounts when switching modes.
|
|
23
24
|
*/
|
|
24
|
-
export declare function ClaudeConversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage,
|
|
25
|
+
export declare function ClaudeConversation({ children, screenWidth, displayMode, platform, onRequestDisplayMode, appName, appIcon, userMessage, headerAction, onContentWidthChange, }: ClaudeConversationProps): import("react/jsx-runtime").JSX.Element;
|
|
25
26
|
export {};
|
package/dist/claude/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const simulator = require("../simulator-
|
|
3
|
+
const simulator = require("../simulator-Gc6n_fT4.cjs");
|
|
4
4
|
exports.Simulator = simulator.Simulator;
|
|
5
5
|
//# sourceMappingURL=index.cjs.map
|
package/dist/claude/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const simulator = require("./simulator-
|
|
2
|
+
const simulator = require("./simulator-Gc6n_fT4.cjs");
|
|
3
3
|
const simulatorUrl = require("./simulator-url-DcSYRl-P.cjs");
|
|
4
4
|
const discovery = require("./discovery-D1gpaVz4.cjs");
|
|
5
5
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
@@ -25,4 +25,4 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
25
25
|
useThemeContext: simulator.useThemeContext
|
|
26
26
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
27
27
|
exports.index = index;
|
|
28
|
-
//# sourceMappingURL=index-
|
|
28
|
+
//# sourceMappingURL=index-BEWVLFfB.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BEWVLFfB.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as IframeResource, M as McpAppHost, a as SCREEN_WIDTHS, S as Simulator, T as ThemeProvider, j as extractResourceCSP, m as resolveServerToolResult, n as useThemeContext } from "./simulator-
|
|
1
|
+
import { I as IframeResource, M as McpAppHost, a as SCREEN_WIDTHS, S as Simulator, T as ThemeProvider, j as extractResourceCSP, m as resolveServerToolResult, n as useThemeContext } from "./simulator-B-CrMHVs.js";
|
|
2
2
|
import { c as createSimulatorUrl } from "./simulator-url-j_XV3EoP.js";
|
|
3
3
|
import { b as buildDevSimulations, a as buildResourceMap, c as buildSimulations, d as createResourceExports, e as extractResourceKey, f as extractSimulationKey, g as findResourceDirs, h as findResourceKey, i as getComponentName, t as toPascalCase } from "./discovery-BVqD-JsT.js";
|
|
4
4
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
@@ -26,4 +26,4 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
26
26
|
export {
|
|
27
27
|
index as i
|
|
28
28
|
};
|
|
29
|
-
//# sourceMappingURL=index-
|
|
29
|
+
//# sourceMappingURL=index-C6XYFOmh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-C6XYFOmh.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const simulator = require("./simulator-
|
|
2
|
+
const simulator = require("./simulator-Gc6n_fT4.cjs");
|
|
3
3
|
const simulatorUrl = require("./simulator-url-DcSYRl-P.cjs");
|
|
4
4
|
const discovery = require("./discovery-D1gpaVz4.cjs");
|
|
5
5
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
@@ -37,4 +37,4 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
37
37
|
useThemeContext: simulator.useThemeContext
|
|
38
38
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
39
39
|
exports.index = index;
|
|
40
|
-
//# sourceMappingURL=index-
|
|
40
|
+
//# sourceMappingURL=index-D0FsXP3Y.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D0FsXP3Y.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as IframeResource, M as McpAppHost, a as SCREEN_WIDTHS, b as SidebarCheckbox, c as SidebarCollapsibleControl, d as SidebarControl, e as SidebarInput, f as SidebarSelect, g as SidebarTextarea, h as SidebarToggle, i as SimpleSidebar, S as Simulator, T as ThemeProvider, j as extractResourceCSP, k as getHostShell, l as getRegisteredHosts, r as registerHostShell, m as resolveServerToolResult, u as useSimulatorState, n as useThemeContext } from "./simulator-
|
|
1
|
+
import { I as IframeResource, M as McpAppHost, a as SCREEN_WIDTHS, b as SidebarCheckbox, c as SidebarCollapsibleControl, d as SidebarControl, e as SidebarInput, f as SidebarSelect, g as SidebarTextarea, h as SidebarToggle, i as SimpleSidebar, S as Simulator, T as ThemeProvider, j as extractResourceCSP, k as getHostShell, l as getRegisteredHosts, r as registerHostShell, m as resolveServerToolResult, u as useSimulatorState, n as useThemeContext } from "./simulator-B-CrMHVs.js";
|
|
2
2
|
import { c as createSimulatorUrl } from "./simulator-url-j_XV3EoP.js";
|
|
3
3
|
import { b as buildDevSimulations, a as buildResourceMap, c as buildSimulations, d as createResourceExports, e as extractResourceKey, f as extractSimulationKey, g as findResourceDirs, h as findResourceKey, i as getComponentName, t as toPascalCase } from "./discovery-BVqD-JsT.js";
|
|
4
4
|
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
@@ -38,4 +38,4 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
38
38
|
export {
|
|
39
39
|
index as i
|
|
40
40
|
};
|
|
41
|
-
//# sourceMappingURL=index-
|
|
41
|
+
//# sourceMappingURL=index-Rg7SWjvl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-Rg7SWjvl.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.cjs
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const useApp = require("./use-app-D09O2swh.cjs");
|
|
4
4
|
const host_index = require("./host/index.cjs");
|
|
5
|
-
const simulator_index = require("./index-
|
|
6
|
-
const chatgpt_index = require("./index-
|
|
5
|
+
const simulator_index = require("./index-D0FsXP3Y.cjs");
|
|
6
|
+
const chatgpt_index = require("./index-BEWVLFfB.cjs");
|
|
7
7
|
const jsxRuntime = require("react/jsx-runtime");
|
|
8
8
|
const React = require("react");
|
|
9
|
-
const simulator = require("./simulator-
|
|
9
|
+
const simulator = require("./simulator-Gc6n_fT4.cjs");
|
|
10
10
|
const discovery = require("./discovery-D1gpaVz4.cjs");
|
|
11
11
|
const protocol = require("./protocol-DkDHRwOW.cjs");
|
|
12
12
|
function _interopNamespaceDefault(e) {
|
|
@@ -177,6 +177,7 @@ function getRegistry$1(app) {
|
|
|
177
177
|
if (ctx?.styles?.css?.fonts) {
|
|
178
178
|
useApp.TQ(ctx.styles.css.fonts);
|
|
179
179
|
}
|
|
180
|
+
let debounceTimer = null;
|
|
180
181
|
app.onhostcontextchanged = () => {
|
|
181
182
|
const ctx2 = app.getHostContext();
|
|
182
183
|
if (ctx2?.theme) {
|
|
@@ -186,7 +187,11 @@ function getRegistry$1(app) {
|
|
|
186
187
|
if (ctx2?.styles?.css?.fonts) {
|
|
187
188
|
useApp.TQ(ctx2.styles.css.fonts);
|
|
188
189
|
}
|
|
189
|
-
|
|
190
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
191
|
+
debounceTimer = setTimeout(() => {
|
|
192
|
+
debounceTimer = null;
|
|
193
|
+
for (const fn of subs) fn();
|
|
194
|
+
}, 50);
|
|
190
195
|
};
|
|
191
196
|
}
|
|
192
197
|
return subs;
|
|
@@ -373,7 +378,7 @@ const SafeArea = React.forwardRef(function SafeArea2({ children, style, ...props
|
|
|
373
378
|
const viewport = useViewport();
|
|
374
379
|
const displayMode = useDisplayMode();
|
|
375
380
|
const isFullscreen = displayMode === "fullscreen";
|
|
376
|
-
const height =
|
|
381
|
+
const height = isFullscreen ? "100dvh" : void 0;
|
|
377
382
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
378
383
|
"div",
|
|
379
384
|
{
|
|
@@ -386,7 +391,10 @@ const SafeArea = React.forwardRef(function SafeArea2({ children, style, ...props
|
|
|
386
391
|
paddingLeft: safeArea.left || void 0,
|
|
387
392
|
paddingRight: safeArea.right || void 0,
|
|
388
393
|
height,
|
|
394
|
+
// overflow:hidden ensures content doesn't escape the maxHeight boundary,
|
|
395
|
+
// which also lets apps fill the space with their own scrollable container.
|
|
389
396
|
maxHeight: viewport?.maxHeight,
|
|
397
|
+
overflow: viewport?.maxHeight != null ? "hidden" : void 0,
|
|
390
398
|
width: viewport?.width,
|
|
391
399
|
maxWidth: viewport?.maxWidth,
|
|
392
400
|
...style
|