sunpeak 0.18.7 → 0.18.12
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 +12 -0
- package/bin/commands/dev.mjs +3 -3
- package/bin/commands/inspect.mjs +4 -1
- package/bin/lib/sandbox-server.mjs +31 -1
- package/dist/chatgpt/index.cjs +1 -2
- package/dist/chatgpt/index.js +1 -2
- package/dist/claude/index.cjs +1 -2
- package/dist/claude/index.js +1 -2
- package/dist/host/chatgpt/index.cjs +1 -2
- package/dist/host/chatgpt/index.cjs.map +1 -1
- package/dist/host/chatgpt/index.js +1 -2
- package/dist/host/chatgpt/index.js.map +1 -1
- package/dist/index.cjs +93 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +54 -54
- package/dist/index.js.map +1 -1
- package/dist/inspector/hosts.d.ts +12 -0
- package/dist/inspector/index.cjs +1 -2
- package/dist/inspector/index.js +1 -2
- package/dist/{inspector-CKc58UuI.cjs → inspector-8nPV2A-z.cjs} +104 -54
- package/dist/inspector-8nPV2A-z.cjs.map +1 -0
- package/dist/{inspector-DZrN0kej.js → inspector-Cdo5BK2D.js} +103 -53
- package/dist/inspector-Cdo5BK2D.js.map +1 -0
- package/dist/mcp/index.cjs +85 -71
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +79 -65
- package/dist/mcp/index.js.map +1 -1
- package/dist/{protocol-DJmRaBzO.js → protocol-BfAACnv0.js} +14 -9
- package/dist/{protocol-DJmRaBzO.js.map → protocol-BfAACnv0.js.map} +1 -1
- package/dist/{protocol-jbxhzcnS.cjs → protocol-C7kTcBr_.cjs} +14 -9
- package/dist/{protocol-jbxhzcnS.cjs.map → protocol-C7kTcBr_.cjs.map} +1 -1
- package/dist/style.css +36 -1
- package/dist/{use-app-BNbz1uzj.js → use-app-CfP9VypY.js} +107 -56
- package/dist/use-app-CfP9VypY.js.map +1 -0
- package/dist/{use-app-Dqh20JPP.cjs → use-app-CzcYw1Kz.cjs} +165 -114
- package/dist/use-app-CzcYw1Kz.cjs.map +1 -0
- package/package.json +7 -7
- package/template/README.md +12 -0
- package/template/dist/albums/albums.html +16 -15
- package/template/dist/albums/albums.json +1 -1
- package/template/dist/carousel/carousel.html +20 -19
- package/template/dist/carousel/carousel.json +1 -1
- package/template/dist/map/map.html +7 -6
- package/template/dist/map/map.json +1 -1
- package/template/dist/review/review.html +6 -5
- package/template/dist/review/review.json +1 -1
- package/template/node_modules/.bin/vite +2 -2
- package/template/node_modules/.bin/vitest +2 -2
- package/template/node_modules/.vite/deps/_metadata.json +4 -4
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js +49 -49
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js +49 -49
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_app-bridge.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js +89 -89
- package/template/node_modules/.vite-mcp/deps/@modelcontextprotocol_ext-apps_react.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/@testing-library_react.js +9 -6
- package/template/node_modules/.vite-mcp/deps/@testing-library_react.js.map +1 -1
- package/template/node_modules/.vite-mcp/deps/_metadata.json +26 -26
- package/template/node_modules/.vite-mcp/deps/{protocol-CTflwIfG.js → protocol-B_qKkui_.js} +14 -9
- package/template/node_modules/.vite-mcp/deps/protocol-B_qKkui_.js.map +1 -0
- package/template/node_modules/.vite-mcp/deps/vitest.js +366 -128
- package/template/node_modules/.vite-mcp/deps/vitest.js.map +1 -1
- package/template/package.json +2 -2
- package/template/src/resources/carousel/carousel.test.tsx +16 -2
- package/template/src/resources/carousel/carousel.tsx +42 -14
- package/template/src/resources/carousel/components/carousel.tsx +20 -0
- package/template/src/resources/carousel/components/index.ts +1 -0
- package/template/src/resources/carousel/components/place-detail.tsx +153 -0
- package/template/tests/e2e/carousel.spec.ts +125 -0
- package/template/tests/live/carousel.spec.ts +2 -2
- package/template/tests/simulations/show-carousel.json +54 -5
- package/dist/inspector-CKc58UuI.cjs.map +0 -1
- package/dist/inspector-DZrN0kej.js.map +0 -1
- package/dist/use-app-BNbz1uzj.js.map +0 -1
- package/dist/use-app-Dqh20JPP.cjs.map +0 -1
- package/template/node_modules/.vite-mcp/deps/protocol-CTflwIfG.js.map +0 -1
package/README.md
CHANGED
|
@@ -230,6 +230,18 @@ Install the `create-sunpeak-app` skill to give your coding agent (Claude Code, C
|
|
|
230
230
|
npx skills add Sunpeak-AI/sunpeak@create-sunpeak-app
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
+
## Troubleshooting
|
|
234
|
+
|
|
235
|
+
If your app doesn't render in ChatGPT or Claude:
|
|
236
|
+
|
|
237
|
+
1. **Check your tunnel** is running and pointing to the correct port
|
|
238
|
+
2. **Restart `sunpeak dev`** to clear stale connections
|
|
239
|
+
3. **Refresh or re-add the MCP server** in the host's settings (Settings > MCP Servers)
|
|
240
|
+
4. **Hard refresh** the host page (`Cmd+Shift+R` / `Ctrl+Shift+R`)
|
|
241
|
+
5. **Open a new chat** in the host (cached iframes persist per-conversation)
|
|
242
|
+
|
|
243
|
+
Full guide: [sunpeak.ai/docs/guides/troubleshooting](https://sunpeak.ai/docs/guides/troubleshooting)
|
|
244
|
+
|
|
233
245
|
## Resources
|
|
234
246
|
|
|
235
247
|
- [MCP Apps Documentation](https://sunpeak.ai/docs/mcp-apps/introduction)
|
package/bin/commands/dev.mjs
CHANGED
|
@@ -239,7 +239,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
239
239
|
const { tool } = await extractToolExport(toolPath);
|
|
240
240
|
toolMap.set(toolName, { tool, path: toolPath });
|
|
241
241
|
} catch (err) {
|
|
242
|
-
console.warn(`Warning: Could not extract metadata from tool ${toolName}
|
|
242
|
+
console.warn(`Warning: Could not extract metadata from tool "${toolName}" (${toolPath}):\n ${err.message}\n Expected: export const tool: AppToolConfig = { ... }`);
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -271,7 +271,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
271
271
|
toolHandlerMap.set(toolName, { handler: mod.default, outputSchema: mod.outputSchema });
|
|
272
272
|
}
|
|
273
273
|
} catch (err) {
|
|
274
|
-
console.warn(`Warning: Could not load handler for tool "${toolName}"
|
|
274
|
+
console.warn(`Warning: Could not load handler for tool "${toolName}" (${relativePath}):\n ${err.message}`);
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
@@ -287,7 +287,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
|
|
|
287
287
|
const toolEntry = toolMap.get(toolName);
|
|
288
288
|
const tool = toolEntry?.tool;
|
|
289
289
|
if (!tool) {
|
|
290
|
-
console.warn(`Warning: Tool "${toolName}" not found for simulation "${simName}".
|
|
290
|
+
console.warn(`Warning: Tool "${toolName}" not found for simulation "${simName}". Expected file: src/tools/${toolName}.ts`);
|
|
291
291
|
continue;
|
|
292
292
|
}
|
|
293
293
|
|
package/bin/commands/inspect.mjs
CHANGED
|
@@ -1144,10 +1144,13 @@ export async function inspectServer(opts) {
|
|
|
1144
1144
|
server.printUrls();
|
|
1145
1145
|
server.bindCLIShortcuts({ print: true });
|
|
1146
1146
|
|
|
1147
|
+
// Print troubleshooting link (dimmed)
|
|
1148
|
+
console.log('\n \x1b[2mApp not loading? \u2192 https://sunpeak.ai/docs/guides/troubleshooting\x1b[0m');
|
|
1149
|
+
|
|
1147
1150
|
// Print star-begging message unless suppressed
|
|
1148
1151
|
if (!noBegging) {
|
|
1149
1152
|
// #FFB800 in 24-bit ANSI color
|
|
1150
|
-
console.log('\n\
|
|
1153
|
+
console.log('\n\x1b[38;2;255;184;0m\u2b50\ufe0f \u2192 \u2764\ufe0f https://github.com/Sunpeak-AI/sunpeak\x1b[0m\n');
|
|
1151
1154
|
}
|
|
1152
1155
|
|
|
1153
1156
|
// Cleanup on exit
|
|
@@ -59,6 +59,25 @@ export async function startSandboxServer({ preferredPort = 24680 } = {}) {
|
|
|
59
59
|
res.end('Not found');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
server.on('clientError', (err, socket) => {
|
|
63
|
+
if (err.code === 'ECONNRESET') {
|
|
64
|
+
// Normal when browser tabs close abruptly
|
|
65
|
+
} else if (
|
|
66
|
+
err.code === 'HPE_INVALID_METHOD' &&
|
|
67
|
+
err.rawPacket instanceof Buffer &&
|
|
68
|
+
err.rawPacket[0] === 0x16
|
|
69
|
+
) {
|
|
70
|
+
console.error(
|
|
71
|
+
'Received HTTPS request on sandbox server (port ' + port + '). ' +
|
|
72
|
+
'If you\'re using ngrok, make sure the upstream is http:// (not https://). ' +
|
|
73
|
+
'Example: ngrok http 8000'
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
console.error('Sandbox server client error', err);
|
|
77
|
+
}
|
|
78
|
+
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
|
|
79
|
+
});
|
|
80
|
+
|
|
62
81
|
await new Promise((resolve, reject) => {
|
|
63
82
|
server.listen(port, () => resolve());
|
|
64
83
|
server.on('error', reject);
|
|
@@ -189,6 +208,7 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
|
189
208
|
});
|
|
190
209
|
|
|
191
210
|
function createInnerFrame(params) {
|
|
211
|
+
clearInterval(readyInterval);
|
|
192
212
|
if (innerFrame) innerFrame.remove();
|
|
193
213
|
|
|
194
214
|
innerFrame = document.createElement('iframe');
|
|
@@ -207,6 +227,7 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
|
207
227
|
}
|
|
208
228
|
|
|
209
229
|
function createInnerFrameWithSrc(params) {
|
|
230
|
+
clearInterval(readyInterval);
|
|
210
231
|
if (innerFrame) innerFrame.remove();
|
|
211
232
|
|
|
212
233
|
innerFrame = document.createElement('iframe');
|
|
@@ -277,7 +298,15 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
|
277
298
|
'e.source.postMessage({jsonrpc:"2.0",method:"sunpeak/fence-ack",params:{fenceId:fid}},"*");' +
|
|
278
299
|
'});}});';
|
|
279
300
|
|
|
280
|
-
// Signal readiness to the host
|
|
301
|
+
// Signal readiness to the host. Retry every 200ms in case the host's
|
|
302
|
+
// PostMessage listener isn't attached yet (srcdoc race with React refs).
|
|
303
|
+
var readyInterval = setInterval(function() {
|
|
304
|
+
window.parent.postMessage({
|
|
305
|
+
jsonrpc: '2.0',
|
|
306
|
+
method: 'ui/notifications/sandbox-proxy-ready',
|
|
307
|
+
params: {}
|
|
308
|
+
}, '*');
|
|
309
|
+
}, 200);
|
|
281
310
|
setTimeout(function() {
|
|
282
311
|
window.parent.postMessage({
|
|
283
312
|
jsonrpc: '2.0',
|
|
@@ -285,6 +314,7 @@ iframe { border: none; width: 100%; height: 100%; display: block; }
|
|
|
285
314
|
params: {}
|
|
286
315
|
}, '*');
|
|
287
316
|
}, 0);
|
|
317
|
+
setTimeout(function() { clearInterval(readyInterval); }, 10000);
|
|
288
318
|
})();
|
|
289
319
|
</script>
|
|
290
320
|
</body>
|
package/dist/chatgpt/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../
|
|
4
|
-
const require_inspector = require("../inspector-CKc58UuI.cjs");
|
|
3
|
+
const require_inspector = require("../inspector-8nPV2A-z.cjs");
|
|
5
4
|
const require_inspector_url = require("../inspector-url-C3LTKgXt.cjs");
|
|
6
5
|
const require_discovery = require("../discovery-Clu4uHp1.cjs");
|
|
7
6
|
//#region src/chatgpt/index.ts
|
package/dist/chatgpt/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { r as __exportAll } from "../chunk-D6g4UhsZ.js";
|
|
2
|
-
import "../
|
|
3
|
-
import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Inspector, v as SCREEN_WIDTHS } from "../inspector-DZrN0kej.js";
|
|
2
|
+
import { _ as McpAppHost, d as ThemeProvider, f as useThemeContext, g as extractResourceCSP, h as IframeResource, n as resolveServerToolResult, t as Inspector, v as SCREEN_WIDTHS } from "../inspector-Cdo5BK2D.js";
|
|
4
3
|
import { t as createInspectorUrl } from "../inspector-url-CyQcuBI9.js";
|
|
5
4
|
import { c as toPascalCase, i as findResourceKey, n as extractSimulationKey, r as findResourceDirs, s as getComponentName, t as extractResourceKey } from "../discovery-Cgoegt62.js";
|
|
6
5
|
//#region src/chatgpt/index.ts
|
package/dist/claude/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../
|
|
4
|
-
const require_inspector = require("../inspector-CKc58UuI.cjs");
|
|
3
|
+
const require_inspector = require("../inspector-8nPV2A-z.cjs");
|
|
5
4
|
exports.Inspector = require_inspector.Inspector;
|
package/dist/claude/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-9hOWP6kD.cjs");
|
|
3
|
-
require("../../
|
|
4
|
-
const require_use_app = require("../../use-app-Dqh20JPP.cjs");
|
|
3
|
+
const require_use_app = require("../../use-app-CzcYw1Kz.cjs");
|
|
5
4
|
let react = require("react");
|
|
6
5
|
//#region src/host/chatgpt/openai-types.ts
|
|
7
6
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":";;;;;;;;;AAuFA,SAAgB,mBAA8C;AAC5D,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAQ,OAAgD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzD5D,SAAgB,gBAAiE;CAC/E,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,SAAe;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,WACZ,OAAM,IAAI,MAAM,yDAAyD;AAE3E,SAAO,QAAQ,WAAW,KAAK;IAEjC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBH,SAAgB,kBAAuE;CACrF,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,WAAqC;AAC1C,MAAI,CAAC,KAAK;AACR,WAAQ,KAAK,sCAAsC;AACnD;;EAEF,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,aACZ,OAAM,IAAI,MAAM,6DAA6D;AAE/E,SAAO,QAAQ,aAAa,OAAO;IAErC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOH,SAAgB,qBAEkB;CAChC,MAAM,MAAM,gBAAA,QAAQ;AACpB,SAAA,GAAA,MAAA,aACE,OAAO,YAAmC;AACxC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,yCAAyC;EAE3D,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,gBACZ,OAAM,IAAI,MAAM,mEAAmE;AAErF,SAAO,QAAQ,gBAAgB,QAAQ;IAEzC,CAAC,IAAI,CACN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/host/chatgpt/openai-types.ts","../../../src/host/chatgpt/use-create-file.ts","../../../src/host/chatgpt/use-open-modal.ts","../../../src/host/chatgpt/use-request-checkout.ts"],"sourcesContent":["/**\n * TypeScript declarations for the `window.openai` ChatGPT runtime.\n *\n * These APIs are available inside ChatGPT-hosted iframes and provide\n * platform-specific capabilities beyond the MCP Apps standard.\n *\n * @see https://developers.openai.com/apps-sdk/mcp-apps-in-chatgpt\n */\n\nexport interface OpenAIUploadFileResult {\n fileId: string;\n}\n\nexport interface OpenAIFileDownloadUrlResult {\n downloadUrl: string;\n}\n\nexport interface OpenAIRequestModalParams {\n /** URL of an alternate UI template to load in the modal. Omit to reuse the current UI. */\n template?: string;\n /** Arbitrary params forwarded to the modal content. */\n params?: Record<string, unknown>;\n}\n\nexport interface OpenAICheckoutPaymentProvider {\n provider: string;\n merchant_id: string;\n supported_payment_methods: string[];\n}\n\nexport interface OpenAICheckoutTotal {\n type: string;\n display_text: string;\n amount: number;\n}\n\nexport interface OpenAICheckoutLink {\n type: 'terms_of_use' | 'privacy_policy' | string;\n url: string;\n}\n\nexport interface OpenAICheckoutSession {\n id: string;\n payment_provider: OpenAICheckoutPaymentProvider;\n status: 'ready_for_payment';\n currency: string;\n totals: OpenAICheckoutTotal[];\n links: OpenAICheckoutLink[];\n payment_mode: 'live' | 'test';\n}\n\nexport interface OpenAICheckoutOrder {\n id: string;\n checkout_session_id: string;\n status: 'completed' | string;\n permalink_url?: string;\n}\n\n/**\n * The `window.openai` runtime object injected by ChatGPT into hosted iframes.\n */\nexport interface OpenAIRuntime {\n // --- File APIs ---\n uploadFile?(file: File): Promise<OpenAIUploadFileResult>;\n getFileDownloadUrl?(params: { fileId: string }): Promise<OpenAIFileDownloadUrlResult>;\n\n // --- Modal API ---\n requestModal?(params: OpenAIRequestModalParams): Promise<void>;\n\n // --- Checkout API ---\n requestCheckout?(session: OpenAICheckoutSession): Promise<OpenAICheckoutOrder>;\n\n // --- Display ---\n requestClose?(): void;\n requestDisplayMode?(params: { mode: 'inline' | 'PiP' | 'fullscreen' }): Promise<void>;\n\n // --- Messaging ---\n sendFollowUpMessage?(params: { prompt: string }): void;\n\n // --- Other ---\n openExternal?(params: { href: string }): void;\n}\n\n/**\n * Get the `window.openai` runtime if available.\n * Returns `undefined` outside of ChatGPT or in SSR.\n */\nexport function getOpenAIRuntime(): OpenAIRuntime | undefined {\n if (typeof window !== 'undefined' && 'openai' in window) {\n return (window as unknown as { openai: OpenAIRuntime }).openai;\n }\n return undefined;\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIUploadFileResult } from './openai-types';\n\nexport type { OpenAIUploadFileResult as CreateFileResult };\n\n/**\n * Upload a file from the app UI into the ChatGPT conversation.\n *\n * Wraps `window.openai.uploadFile` which is only available inside ChatGPT.\n * Supported formats: `image/png`, `image/jpeg`, `image/webp`.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useUploadFile } from 'sunpeak/host/chatgpt';\n *\n * function ImageUploader() {\n * const uploadFile = useUploadFile();\n *\n * const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.currentTarget.files?.[0];\n * if (!file) return;\n * const { fileId } = await uploadFile(file);\n * console.log('Uploaded:', fileId);\n * };\n *\n * return <input type=\"file\" accept=\"image/*\" onChange={handleChange} />;\n * }\n * ```\n */\nexport function useUploadFile(): (file: File) => Promise<OpenAIUploadFileResult> {\n const app = useApp();\n return useCallback(\n async (file: File) => {\n if (!app) {\n throw new Error('[useUploadFile] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.uploadFile) {\n throw new Error('[useUploadFile] window.openai.uploadFile not available');\n }\n return runtime.uploadFile(file);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport { getOpenAIRuntime, type OpenAIRequestModalParams } from './openai-types';\n\nexport type { OpenAIRequestModalParams as OpenModalParams };\n\n/**\n * Open a host-controlled modal in ChatGPT.\n *\n * Wraps `window.openai.requestModal` which is only available inside ChatGPT.\n * Pass a `template` URL to load alternate UI content in the modal, or omit\n * it to reuse the current UI.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestModal } from 'sunpeak/host/chatgpt';\n *\n * function CheckoutButton() {\n * const requestModal = useRequestModal();\n *\n * return (\n * <button onClick={() => requestModal({ template: 'ui://widget/checkout.html' })}>\n * Checkout\n * </button>\n * );\n * }\n * ```\n */\nexport function useRequestModal(): (params: OpenAIRequestModalParams) => Promise<void> {\n const app = useApp();\n return useCallback(\n async (params: OpenAIRequestModalParams) => {\n if (!app) {\n console.warn('[useRequestModal] App not connected');\n return;\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestModal) {\n throw new Error('[useRequestModal] window.openai.requestModal not available');\n }\n return runtime.requestModal(params);\n },\n [app]\n );\n}\n","import { useCallback } from 'react';\nimport { useApp } from '../../hooks/use-app';\nimport {\n getOpenAIRuntime,\n type OpenAICheckoutSession,\n type OpenAICheckoutOrder,\n} from './openai-types';\n\nexport type { OpenAICheckoutSession as CheckoutSession };\nexport type { OpenAICheckoutOrder as CheckoutOrder };\n\n/**\n * Trigger the ChatGPT instant checkout flow.\n *\n * Wraps `window.openai.requestCheckout` which is only available inside\n * ChatGPT. Opens the host checkout UI, handles payment display, and\n * resolves with the finalized order. Rejects on error or user cancel.\n *\n * Import from `sunpeak/host/chatgpt`:\n *\n * @example\n * ```tsx\n * import { useRequestCheckout } from 'sunpeak/host/chatgpt';\n *\n * function BuyButton() {\n * const requestCheckout = useRequestCheckout();\n *\n * const handleBuy = async () => {\n * try {\n * const order = await requestCheckout({\n * id: 'session-1',\n * payment_provider: {\n * provider: 'stripe',\n * merchant_id: 'acct_xxx',\n * supported_payment_methods: ['card', 'apple_pay'],\n * },\n * status: 'ready_for_payment',\n * currency: 'USD',\n * totals: [{ type: 'total', display_text: 'Total', amount: 999 }],\n * links: [],\n * payment_mode: 'live',\n * });\n * console.log('Order completed:', order.id);\n * } catch {\n * console.log('Checkout cancelled or failed');\n * }\n * };\n *\n * return <button onClick={handleBuy}>Buy Now</button>;\n * }\n * ```\n */\nexport function useRequestCheckout(): (\n session: OpenAICheckoutSession\n) => Promise<OpenAICheckoutOrder> {\n const app = useApp();\n return useCallback(\n async (session: OpenAICheckoutSession) => {\n if (!app) {\n throw new Error('[useRequestCheckout] App not connected');\n }\n const runtime = getOpenAIRuntime();\n if (!runtime?.requestCheckout) {\n throw new Error('[useRequestCheckout] window.openai.requestCheckout not available');\n }\n return runtime.requestCheckout(session);\n },\n [app]\n );\n}\n"],"mappings":";;;;;;;AAuFA,SAAgB,mBAA8C;AAC5D,KAAI,OAAO,WAAW,eAAe,YAAY,OAC/C,QAAQ,OAAgD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzD5D,SAAgB,gBAAiE;CAC/E,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,SAAe;AACpB,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,oCAAoC;EAEtD,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,WACZ,OAAM,IAAI,MAAM,yDAAyD;AAE3E,SAAO,QAAQ,WAAW,KAAK;IAEjC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChBH,SAAgB,kBAAuE;CACrF,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,WAAqC;AAC1C,MAAI,CAAC,KAAK;AACR,WAAQ,KAAK,sCAAsC;AACnD;;EAEF,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,aACZ,OAAM,IAAI,MAAM,6DAA6D;AAE/E,SAAO,QAAQ,aAAa,OAAO;IAErC,CAAC,IAAI,CACN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACOH,SAAgB,qBAEkB;CAChC,MAAM,MAAM,QAAQ;AACpB,QAAO,YACL,OAAO,YAAmC;AACxC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,yCAAyC;EAE3D,MAAM,UAAU,kBAAkB;AAClC,MAAI,CAAC,SAAS,gBACZ,OAAM,IAAI,MAAM,mEAAmE;AAErF,SAAO,QAAQ,gBAAgB,QAAQ;IAEzC,CAAC,IAAI,CACN"}
|