skybridge 0.0.0-dev.f53cd1b → 0.0.0-dev.f552a52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/README.md +123 -124
  2. package/dist/cli/detect-port.js.map +1 -1
  3. package/dist/cli/header.js +1 -1
  4. package/dist/cli/header.js.map +1 -1
  5. package/dist/cli/resolve-views-dir.d.ts +1 -0
  6. package/dist/cli/resolve-views-dir.js +17 -0
  7. package/dist/cli/resolve-views-dir.js.map +1 -0
  8. package/dist/cli/run-command.js.map +1 -1
  9. package/dist/cli/telemetry.js.map +1 -1
  10. package/dist/cli/tunnel-control-server.d.ts +9 -0
  11. package/dist/cli/tunnel-control-server.js +31 -0
  12. package/dist/cli/tunnel-control-server.js.map +1 -0
  13. package/dist/cli/tunnel-control-server.test.js +39 -0
  14. package/dist/cli/tunnel-control-server.test.js.map +1 -0
  15. package/dist/cli/tunnel-handler.d.ts +3 -0
  16. package/dist/cli/tunnel-handler.js +48 -0
  17. package/dist/cli/tunnel-handler.js.map +1 -0
  18. package/dist/cli/tunnel-handler.test.js +105 -0
  19. package/dist/cli/tunnel-handler.test.js.map +1 -0
  20. package/dist/cli/tunnel.d.ts +57 -0
  21. package/dist/cli/tunnel.js +154 -0
  22. package/dist/cli/tunnel.js.map +1 -0
  23. package/dist/cli/tunnel.test.js +190 -0
  24. package/dist/cli/tunnel.test.js.map +1 -0
  25. package/dist/cli/types.js.map +1 -1
  26. package/dist/cli/use-execute-steps.js.map +1 -1
  27. package/dist/cli/use-messages.d.ts +3 -0
  28. package/dist/cli/use-messages.js +11 -0
  29. package/dist/cli/use-messages.js.map +1 -0
  30. package/dist/cli/use-nodemon.d.ts +2 -2
  31. package/dist/cli/use-nodemon.js +18 -25
  32. package/dist/cli/use-nodemon.js.map +1 -1
  33. package/dist/cli/use-open-browser.d.ts +1 -0
  34. package/dist/cli/use-open-browser.js +44 -0
  35. package/dist/cli/use-open-browser.js.map +1 -0
  36. package/dist/cli/use-tunnel.d.ts +13 -7
  37. package/dist/cli/use-tunnel.js +103 -73
  38. package/dist/cli/use-tunnel.js.map +1 -1
  39. package/dist/cli/use-typescript-check.d.ts +1 -0
  40. package/dist/cli/use-typescript-check.js +42 -7
  41. package/dist/cli/use-typescript-check.js.map +1 -1
  42. package/dist/commands/build.js +48 -7
  43. package/dist/commands/build.js.map +1 -1
  44. package/dist/commands/create.d.ts +9 -0
  45. package/dist/commands/create.js +30 -0
  46. package/dist/commands/create.js.map +1 -0
  47. package/dist/commands/dev.d.ts +2 -0
  48. package/dist/commands/dev.js +58 -6
  49. package/dist/commands/dev.js.map +1 -1
  50. package/dist/commands/start.js +7 -10
  51. package/dist/commands/start.js.map +1 -1
  52. package/dist/commands/telemetry/disable.js.map +1 -1
  53. package/dist/commands/telemetry/enable.js.map +1 -1
  54. package/dist/commands/telemetry/status.js.map +1 -1
  55. package/dist/server/asset-base-url-transform-plugin.d.ts +6 -6
  56. package/dist/server/asset-base-url-transform-plugin.js +25 -11
  57. package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
  58. package/dist/server/asset-base-url-transform-plugin.test.js +92 -14
  59. package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
  60. package/dist/server/auth.d.ts +20 -0
  61. package/dist/server/auth.js +28 -0
  62. package/dist/server/auth.js.map +1 -0
  63. package/dist/server/content-helpers.d.ts +67 -0
  64. package/dist/server/content-helpers.js +79 -0
  65. package/dist/server/content-helpers.js.map +1 -0
  66. package/dist/server/content-helpers.test.d.ts +1 -0
  67. package/dist/server/content-helpers.test.js +70 -0
  68. package/dist/server/content-helpers.test.js.map +1 -0
  69. package/dist/server/express.d.ts +2 -6
  70. package/dist/server/express.js +34 -10
  71. package/dist/server/express.js.map +1 -1
  72. package/dist/server/express.test.js +249 -71
  73. package/dist/server/express.test.js.map +1 -1
  74. package/dist/server/file-ref.d.ts +28 -0
  75. package/dist/server/file-ref.js +27 -0
  76. package/dist/server/file-ref.js.map +1 -0
  77. package/dist/server/index.d.ts +6 -3
  78. package/dist/server/index.js +5 -2
  79. package/dist/server/index.js.map +1 -1
  80. package/dist/server/inferUtilityTypes.d.ts +6 -6
  81. package/dist/server/inferUtilityTypes.js.map +1 -1
  82. package/dist/server/metric.d.ts +14 -0
  83. package/dist/server/metric.js +62 -0
  84. package/dist/server/metric.js.map +1 -0
  85. package/dist/server/middleware.d.ts +16 -3
  86. package/dist/server/middleware.js.map +1 -1
  87. package/dist/server/middleware.test-d.js.map +1 -1
  88. package/dist/server/middleware.test.js +12 -9
  89. package/dist/server/middleware.test.js.map +1 -1
  90. package/dist/server/server.d.ts +321 -73
  91. package/dist/server/server.js +404 -131
  92. package/dist/server/server.js.map +1 -1
  93. package/dist/server/templateHelper.d.ts +5 -7
  94. package/dist/server/templateHelper.js +3 -22
  95. package/dist/server/templateHelper.js.map +1 -1
  96. package/dist/server/templates.generated.d.ts +4 -0
  97. package/dist/server/templates.generated.js +47 -0
  98. package/dist/server/templates.generated.js.map +1 -0
  99. package/dist/server/tunnel-proxy-router.d.ts +7 -0
  100. package/dist/server/tunnel-proxy-router.js +110 -0
  101. package/dist/server/tunnel-proxy-router.js.map +1 -0
  102. package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
  103. package/dist/server/tunnel-proxy-router.test.js +229 -0
  104. package/dist/server/tunnel-proxy-router.test.js.map +1 -0
  105. package/dist/server/viewsDevServer.d.ts +14 -0
  106. package/dist/server/viewsDevServer.js +45 -0
  107. package/dist/server/viewsDevServer.js.map +1 -0
  108. package/dist/test/utils.d.ts +13 -21
  109. package/dist/test/utils.js +42 -37
  110. package/dist/test/utils.js.map +1 -1
  111. package/dist/test/view.test.d.ts +1 -0
  112. package/dist/test/view.test.js +568 -0
  113. package/dist/test/view.test.js.map +1 -0
  114. package/dist/version.d.ts +1 -0
  115. package/dist/version.js +3 -0
  116. package/dist/version.js.map +1 -0
  117. package/dist/web/bridges/apps-sdk/adaptor.d.ts +10 -4
  118. package/dist/web/bridges/apps-sdk/adaptor.js +55 -17
  119. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
  120. package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
  121. package/dist/web/bridges/apps-sdk/bridge.js +1 -0
  122. package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
  123. package/dist/web/bridges/apps-sdk/index.js.map +1 -1
  124. package/dist/web/bridges/apps-sdk/types.d.ts +18 -6
  125. package/dist/web/bridges/apps-sdk/types.js.map +1 -1
  126. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
  127. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
  128. package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
  129. package/dist/web/bridges/get-adaptor.d.ts +7 -0
  130. package/dist/web/bridges/get-adaptor.js +7 -0
  131. package/dist/web/bridges/get-adaptor.js.map +1 -1
  132. package/dist/web/bridges/index.js.map +1 -1
  133. package/dist/web/bridges/mcp-app/adaptor.d.ts +20 -6
  134. package/dist/web/bridges/mcp-app/adaptor.js +124 -28
  135. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
  136. package/dist/web/bridges/mcp-app/bridge.d.ts +1 -0
  137. package/dist/web/bridges/mcp-app/bridge.js +1 -0
  138. package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
  139. package/dist/web/bridges/mcp-app/index.js.map +1 -1
  140. package/dist/web/bridges/mcp-app/types.js.map +1 -1
  141. package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
  142. package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
  143. package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
  144. package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
  145. package/dist/web/bridges/types.d.ts +79 -11
  146. package/dist/web/bridges/types.js.map +1 -1
  147. package/dist/web/bridges/use-host-context.d.ts +5 -0
  148. package/dist/web/bridges/use-host-context.js +5 -0
  149. package/dist/web/bridges/use-host-context.js.map +1 -1
  150. package/dist/web/components/modal-provider.js +2 -2
  151. package/dist/web/components/modal-provider.js.map +1 -1
  152. package/dist/web/create-store.d.ts +26 -0
  153. package/dist/web/create-store.js +43 -3
  154. package/dist/web/create-store.js.map +1 -1
  155. package/dist/web/create-store.test.js +14 -16
  156. package/dist/web/create-store.test.js.map +1 -1
  157. package/dist/web/data-llm.d.ts +34 -1
  158. package/dist/web/data-llm.js +31 -3
  159. package/dist/web/data-llm.js.map +1 -1
  160. package/dist/web/data-llm.test.js +22 -22
  161. package/dist/web/data-llm.test.js.map +1 -1
  162. package/dist/web/generate-helpers.d.ts +22 -18
  163. package/dist/web/generate-helpers.js +22 -18
  164. package/dist/web/generate-helpers.js.map +1 -1
  165. package/dist/web/generate-helpers.test-d.js +26 -26
  166. package/dist/web/generate-helpers.test-d.js.map +1 -1
  167. package/dist/web/generate-helpers.test.js.map +1 -1
  168. package/dist/web/helpers/state.d.ts +2 -2
  169. package/dist/web/helpers/state.js +11 -11
  170. package/dist/web/helpers/state.js.map +1 -1
  171. package/dist/web/helpers/state.test.js +9 -9
  172. package/dist/web/helpers/state.test.js.map +1 -1
  173. package/dist/web/hooks/index.d.ts +4 -1
  174. package/dist/web/hooks/index.js +4 -1
  175. package/dist/web/hooks/index.js.map +1 -1
  176. package/dist/web/hooks/test/utils.d.ts +6 -2
  177. package/dist/web/hooks/test/utils.js +13 -2
  178. package/dist/web/hooks/test/utils.js.map +1 -1
  179. package/dist/web/hooks/use-call-tool.d.ts +45 -0
  180. package/dist/web/hooks/use-call-tool.js +28 -0
  181. package/dist/web/hooks/use-call-tool.js.map +1 -1
  182. package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
  183. package/dist/web/hooks/use-call-tool.test.js +27 -6
  184. package/dist/web/hooks/use-call-tool.test.js.map +1 -1
  185. package/dist/web/hooks/use-display-mode.d.ts +20 -0
  186. package/dist/web/hooks/use-display-mode.js +20 -0
  187. package/dist/web/hooks/use-display-mode.js.map +1 -1
  188. package/dist/web/hooks/use-display-mode.test-d.js.map +1 -1
  189. package/dist/web/hooks/use-display-mode.test.js.map +1 -1
  190. package/dist/web/hooks/use-download.d.ts +5 -0
  191. package/dist/web/hooks/use-download.js +8 -0
  192. package/dist/web/hooks/use-download.js.map +1 -0
  193. package/dist/web/hooks/use-download.test.d.ts +1 -0
  194. package/dist/web/hooks/use-download.test.js +95 -0
  195. package/dist/web/hooks/use-download.test.js.map +1 -0
  196. package/dist/web/hooks/use-files.d.ts +34 -1
  197. package/dist/web/hooks/use-files.js +33 -0
  198. package/dist/web/hooks/use-files.js.map +1 -1
  199. package/dist/web/hooks/use-files.test.js +22 -2
  200. package/dist/web/hooks/use-files.test.js.map +1 -1
  201. package/dist/web/hooks/use-layout.d.ts +2 -0
  202. package/dist/web/hooks/use-layout.js +2 -0
  203. package/dist/web/hooks/use-layout.js.map +1 -1
  204. package/dist/web/hooks/use-layout.test.js.map +1 -1
  205. package/dist/web/hooks/use-open-external.d.ts +17 -0
  206. package/dist/web/hooks/use-open-external.js +16 -0
  207. package/dist/web/hooks/use-open-external.js.map +1 -1
  208. package/dist/web/hooks/use-open-external.test.js.map +1 -1
  209. package/dist/web/hooks/use-request-close.d.ts +16 -0
  210. package/dist/web/hooks/use-request-close.js +21 -0
  211. package/dist/web/hooks/use-request-close.js.map +1 -0
  212. package/dist/web/hooks/use-request-close.test.d.ts +1 -0
  213. package/dist/web/hooks/use-request-close.test.js +52 -0
  214. package/dist/web/hooks/use-request-close.test.js.map +1 -0
  215. package/dist/web/hooks/use-request-modal.d.ts +16 -1
  216. package/dist/web/hooks/use-request-modal.js +19 -4
  217. package/dist/web/hooks/use-request-modal.js.map +1 -1
  218. package/dist/web/hooks/use-request-modal.test.js +1 -1
  219. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  220. package/dist/web/hooks/use-request-size.d.ts +20 -0
  221. package/dist/web/hooks/use-request-size.js +24 -0
  222. package/dist/web/hooks/use-request-size.js.map +1 -0
  223. package/dist/web/hooks/use-request-size.test.d.ts +1 -0
  224. package/dist/web/hooks/use-request-size.test.js +65 -0
  225. package/dist/web/hooks/use-request-size.test.js.map +1 -0
  226. package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -1
  227. package/dist/web/hooks/use-send-follow-up-message.js +19 -2
  228. package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
  229. package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
  230. package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
  231. package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
  232. package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
  233. package/dist/web/hooks/use-tool-info.d.ts +33 -0
  234. package/dist/web/hooks/use-tool-info.js +26 -0
  235. package/dist/web/hooks/use-tool-info.js.map +1 -1
  236. package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
  237. package/dist/web/hooks/use-tool-info.test.js.map +1 -1
  238. package/dist/web/hooks/use-user.d.ts +2 -0
  239. package/dist/web/hooks/use-user.js +2 -0
  240. package/dist/web/hooks/use-user.js.map +1 -1
  241. package/dist/web/hooks/use-user.test.js.map +1 -1
  242. package/dist/web/hooks/use-view-state.d.ts +25 -0
  243. package/dist/web/hooks/use-view-state.js +32 -0
  244. package/dist/web/hooks/use-view-state.js.map +1 -0
  245. package/dist/web/hooks/use-view-state.test.d.ts +1 -0
  246. package/dist/web/hooks/use-view-state.test.js +177 -0
  247. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  248. package/dist/web/index.d.ts +1 -2
  249. package/dist/web/index.js +1 -2
  250. package/dist/web/index.js.map +1 -1
  251. package/dist/web/mount-view.d.ts +20 -0
  252. package/dist/web/{mount-widget.js → mount-view.js} +21 -2
  253. package/dist/web/mount-view.js.map +1 -0
  254. package/dist/web/plugin/data-llm.test.js.map +1 -1
  255. package/dist/web/plugin/plugin.d.ts +32 -1
  256. package/dist/web/plugin/plugin.js +160 -25
  257. package/dist/web/plugin/plugin.js.map +1 -1
  258. package/dist/web/plugin/scan-views.d.ts +16 -0
  259. package/dist/web/plugin/scan-views.js +88 -0
  260. package/dist/web/plugin/scan-views.js.map +1 -0
  261. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  262. package/dist/web/plugin/scan-views.test.js +99 -0
  263. package/dist/web/plugin/scan-views.test.js.map +1 -0
  264. package/dist/web/plugin/transform-data-llm.js +1 -1
  265. package/dist/web/plugin/transform-data-llm.js.map +1 -1
  266. package/dist/web/plugin/transform-data-llm.test.js.map +1 -1
  267. package/dist/web/plugin/validate-view.d.ts +1 -0
  268. package/dist/web/plugin/validate-view.js +9 -0
  269. package/dist/web/plugin/validate-view.js.map +1 -0
  270. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  271. package/dist/web/plugin/validate-view.test.js +24 -0
  272. package/dist/web/plugin/validate-view.test.js.map +1 -0
  273. package/dist/web/proxy.js.map +1 -1
  274. package/dist/web/types.d.ts +4 -0
  275. package/dist/web/types.js.map +1 -1
  276. package/package.json +27 -13
  277. package/tsconfig.base.json +2 -0
  278. package/dist/server/templates/development.hbs +0 -12
  279. package/dist/server/templates/production.hbs +0 -6
  280. package/dist/server/widgetsDevServer.d.ts +0 -13
  281. package/dist/server/widgetsDevServer.js +0 -57
  282. package/dist/server/widgetsDevServer.js.map +0 -1
  283. package/dist/test/widget.test.js +0 -263
  284. package/dist/test/widget.test.js.map +0 -1
  285. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  286. package/dist/web/hooks/use-widget-state.js +0 -32
  287. package/dist/web/hooks/use-widget-state.js.map +0 -1
  288. package/dist/web/hooks/use-widget-state.test.js +0 -64
  289. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  290. package/dist/web/mount-widget.d.ts +0 -1
  291. package/dist/web/mount-widget.js.map +0 -1
  292. package/dist/web/plugin/validate-widget.d.ts +0 -5
  293. package/dist/web/plugin/validate-widget.js +0 -27
  294. package/dist/web/plugin/validate-widget.js.map +0 -1
  295. package/dist/web/plugin/validate-widget.test.js +0 -42
  296. package/dist/web/plugin/validate-widget.test.js.map +0 -1
  297. /package/dist/{test/widget.test.d.ts → cli/tunnel-control-server.test.d.ts} +0 -0
  298. /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/tunnel-handler.test.d.ts} +0 -0
  299. /package/dist/{web/plugin/validate-widget.test.d.ts → cli/tunnel.test.d.ts} +0 -0
@@ -1,6 +1,39 @@
1
+ /**
2
+ * File operations bound to the current host: `upload` a `File`, resolve a
3
+ * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's
4
+ * native file picker.
5
+ *
6
+ * Currently Apps-SDK-only — calling any of these from MCP Apps throws.
7
+ * `selectFiles` additionally requires a ChatGPT host version that exposes the
8
+ * picker; it throws if the capability is unavailable.
9
+ *
10
+ * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).
11
+ * To pass an uploaded file to a tool whose input uses {@link FileRef}, first
12
+ * call `getDownloadUrl` and then build the ref yourself — field names differ
13
+ * (camelCase on the client, snake_case in the schema) and `download_url` is
14
+ * required.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const { upload, getDownloadUrl } = useFiles();
19
+ * const meta = await upload(file);
20
+ * const { downloadUrl } = await getDownloadUrl(meta);
21
+ * callTool({
22
+ * document: {
23
+ * file_id: meta.fileId,
24
+ * download_url: downloadUrl,
25
+ * file_name: meta.fileName,
26
+ * mime_type: meta.mimeType,
27
+ * },
28
+ * });
29
+ * ```
30
+ *
31
+ * @see https://docs.skybridge.tech/api-reference/use-files
32
+ */
1
33
  export declare function useFiles(): {
2
- upload: (file: File) => Promise<import("../index.js").FileMetadata>;
34
+ upload: (file: File, options?: import("../index.js").UploadFileOptions) => Promise<import("../index.js").FileMetadata>;
3
35
  getDownloadUrl: (file: import("../index.js").FileMetadata) => Promise<{
4
36
  downloadUrl: string;
5
37
  }>;
38
+ selectFiles: () => Promise<import("../index.js").FileMetadata[]>;
6
39
  };
@@ -1,9 +1,42 @@
1
1
  import { getAdaptor } from "../bridges/index.js";
2
+ /**
3
+ * File operations bound to the current host: `upload` a `File`, resolve a
4
+ * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's
5
+ * native file picker.
6
+ *
7
+ * Currently Apps-SDK-only — calling any of these from MCP Apps throws.
8
+ * `selectFiles` additionally requires a ChatGPT host version that exposes the
9
+ * picker; it throws if the capability is unavailable.
10
+ *
11
+ * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).
12
+ * To pass an uploaded file to a tool whose input uses {@link FileRef}, first
13
+ * call `getDownloadUrl` and then build the ref yourself — field names differ
14
+ * (camelCase on the client, snake_case in the schema) and `download_url` is
15
+ * required.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const { upload, getDownloadUrl } = useFiles();
20
+ * const meta = await upload(file);
21
+ * const { downloadUrl } = await getDownloadUrl(meta);
22
+ * callTool({
23
+ * document: {
24
+ * file_id: meta.fileId,
25
+ * download_url: downloadUrl,
26
+ * file_name: meta.fileName,
27
+ * mime_type: meta.mimeType,
28
+ * },
29
+ * });
30
+ * ```
31
+ *
32
+ * @see https://docs.skybridge.tech/api-reference/use-files
33
+ */
2
34
  export function useFiles() {
3
35
  const adaptor = getAdaptor();
4
36
  return {
5
37
  upload: adaptor.uploadFile,
6
38
  getDownloadUrl: adaptor.getFileDownloadUrl,
39
+ selectFiles: adaptor.selectFiles,
7
40
  };
8
41
  }
9
42
  //# sourceMappingURL=use-files.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-files.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,UAAU,QAAQ;IACtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,UAAU;QAC1B,cAAc,EAAE,OAAO,CAAC,kBAAkB;KAC3C,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"use-files.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,UAAU;QAC1B,cAAc,EAAE,OAAO,CAAC,kBAAkB;QAC1C,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { getAdaptor } from \"../bridges/index.js\";\n\n/**\n * File operations bound to the current host: `upload` a `File`, resolve a\n * `downloadUrl` for an uploaded file, and `selectFiles` to open the host's\n * native file picker.\n *\n * Currently Apps-SDK-only — calling any of these from MCP Apps throws.\n * `selectFiles` additionally requires a ChatGPT host version that exposes the\n * picker; it throws if the capability is unavailable.\n *\n * `upload` returns `FileMetadata` (`fileId`, optional `fileName`, `mimeType`).\n * To pass an uploaded file to a tool whose input uses {@link FileRef}, first\n * call `getDownloadUrl` and then build the ref yourself — field names differ\n * (camelCase on the client, snake_case in the schema) and `download_url` is\n * required.\n *\n * @example\n * ```tsx\n * const { upload, getDownloadUrl } = useFiles();\n * const meta = await upload(file);\n * const { downloadUrl } = await getDownloadUrl(meta);\n * callTool({\n * document: {\n * file_id: meta.fileId,\n * download_url: downloadUrl,\n * file_name: meta.fileName,\n * mime_type: meta.mimeType,\n * },\n * });\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-files\n */\nexport function useFiles() {\n const adaptor = getAdaptor();\n return {\n upload: adaptor.uploadFile,\n getDownloadUrl: adaptor.getFileDownloadUrl,\n selectFiles: adaptor.selectFiles,\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { renderHook } from "@testing-library/react";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { AppsSdkAdaptor } from "../bridges/apps-sdk/adaptor.js";
3
4
  import { useFiles } from "./use-files.js";
4
5
  describe("useFiles", () => {
5
6
  const OpenaiMock = {
@@ -16,13 +17,32 @@ describe("useFiles", () => {
16
17
  });
17
18
  afterEach(() => {
18
19
  vi.unstubAllGlobals();
19
- vi.resetAllMocks();
20
+ vi.clearAllMocks();
21
+ AppsSdkAdaptor.resetInstance();
20
22
  });
21
23
  const dummyFile = new File([], "test.txt");
22
24
  it("should upload a file to ChatGPT", () => {
23
25
  const { result } = renderHook(() => useFiles());
24
26
  result.current.upload(dummyFile);
25
- expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile);
27
+ expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, undefined);
28
+ });
29
+ it("should upload a file with library option", () => {
30
+ const { result } = renderHook(() => useFiles());
31
+ result.current.upload(dummyFile, { library: true });
32
+ expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, {
33
+ library: true,
34
+ });
35
+ });
36
+ it("should select files from ChatGPT", async () => {
37
+ const selectedFiles = [
38
+ { fileId: "file_1", fileName: "doc.pdf", mimeType: "application/pdf" },
39
+ ];
40
+ OpenaiMock.selectFiles = vi.fn().mockResolvedValue(selectedFiles);
41
+ const { result } = renderHook(() => useFiles());
42
+ const files = await result.current.selectFiles();
43
+ expect(OpenaiMock.selectFiles).toHaveBeenCalled();
44
+ expect(files).toEqual(selectedFiles);
45
+ delete OpenaiMock.selectFiles;
26
46
  });
27
47
  it("should download a file from ChatGPT", () => {
28
48
  const fileId = "123";
@@ -1 +1 @@
1
- {"version":3,"file":"use-files.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,MAAM,UAAU,GAAG;QACjB,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YACpC,MAAM,EAAE,wBAAwB;SACjC,CAAC;QACF,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC3B,WAAW,EAAE,IAAI;QACjB,cAAc,EAAE,EAAE,CAAC,EAAE,EAAE;KACxB,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAE3C,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,MAAM,GAAG,KAAK,CAAC;QACrB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"use-files.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-files.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { AppsSdkAdaptor } from \"../bridges/apps-sdk/adaptor.js\";\nimport { useFiles } from \"./use-files.js\";\n\ndescribe(\"useFiles\", () => {\n const OpenaiMock: Record<string, unknown> = {\n uploadFile: vi.fn().mockResolvedValue({\n fileId: `sediment://file_abc123`,\n }),\n getFileDownloadUrl: vi.fn(),\n widgetState: null,\n setWidgetState: vi.fn(),\n };\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"openai\", OpenaiMock);\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.clearAllMocks();\n AppsSdkAdaptor.resetInstance();\n });\n\n const dummyFile = new File([], \"test.txt\");\n\n it(\"should upload a file to ChatGPT\", () => {\n const { result } = renderHook(() => useFiles());\n\n result.current.upload(dummyFile);\n expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, undefined);\n });\n\n it(\"should upload a file with library option\", () => {\n const { result } = renderHook(() => useFiles());\n\n result.current.upload(dummyFile, { library: true });\n expect(OpenaiMock.uploadFile).toHaveBeenCalledWith(dummyFile, {\n library: true,\n });\n });\n\n it(\"should select files from ChatGPT\", async () => {\n const selectedFiles = [\n { fileId: \"file_1\", fileName: \"doc.pdf\", mimeType: \"application/pdf\" },\n ];\n OpenaiMock.selectFiles = vi.fn().mockResolvedValue(selectedFiles);\n\n const { result } = renderHook(() => useFiles());\n\n const files = await result.current.selectFiles();\n expect(OpenaiMock.selectFiles).toHaveBeenCalled();\n expect(files).toEqual(selectedFiles);\n\n delete OpenaiMock.selectFiles;\n });\n\n it(\"should download a file from ChatGPT\", () => {\n const fileId = \"123\";\n const { result } = renderHook(() => useFiles());\n\n result.current.getDownloadUrl({ fileId });\n expect(OpenaiMock.getFileDownloadUrl).toHaveBeenCalledWith({ fileId });\n });\n});\n"]}
@@ -18,5 +18,7 @@ export type LayoutState = {
18
18
  * // Respect safe area insets
19
19
  * const paddingTop = safeArea.insets.top;
20
20
  * ```
21
+ *
22
+ * @see https://docs.skybridge.tech/api-reference/use-layout
21
23
  */
22
24
  export declare function useLayout(): LayoutState;
@@ -13,6 +13,8 @@ import { useHostContext } from "../bridges/index.js";
13
13
  * // Respect safe area insets
14
14
  * const paddingTop = safeArea.insets.top;
15
15
  * ```
16
+ *
17
+ * @see https://docs.skybridge.tech/api-reference/use-layout
16
18
  */
17
19
  export function useLayout() {
18
20
  const theme = useHostContext("theme");
@@ -1 +1 @@
1
- {"version":3,"file":"use-layout.js","sourceRoot":"","sources":["../../../src/web/hooks/use-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQhF;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAE5C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"use-layout.js","sourceRoot":"","sources":["../../../src/web/hooks/use-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQhF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAE5C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC","sourcesContent":["import { type SafeArea, type Theme, useHostContext } from \"../bridges/index.js\";\n\nexport type LayoutState = {\n theme: Theme;\n maxHeight: number | undefined;\n safeArea: SafeArea;\n};\n\n/**\n * Hook for accessing layout and visual environment information.\n * These values may change on resize or theme toggle.\n *\n * @example\n * ```tsx\n * const { theme, maxHeight, safeArea } = useLayout();\n *\n * // Apply theme-aware styling\n * const backgroundColor = theme === \"dark\" ? \"#1a1a1a\" : \"#ffffff\";\n *\n * // Respect safe area insets\n * const paddingTop = safeArea.insets.top;\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-layout\n */\nexport function useLayout(): LayoutState {\n const theme = useHostContext(\"theme\");\n const maxHeight = useHostContext(\"maxHeight\");\n const safeArea = useHostContext(\"safeArea\");\n\n return { theme, maxHeight, safeArea };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-layout.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-layout.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-layout.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-layout.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { McpAppAdaptor } from \"../bridges/mcp-app/adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport type { SafeArea, Theme } from \"../bridges/types.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useLayout } from \"./use-layout.js\";\n\ndescribe(\"useLayout\", () => {\n describe(\"apps-sdk host type\", () => {\n let OpenaiMock: {\n theme: Theme;\n maxHeight: number;\n safeArea: SafeArea;\n };\n\n beforeEach(() => {\n OpenaiMock = {\n theme: \"light\",\n maxHeight: 500,\n safeArea: { insets: { top: 0, bottom: 0, left: 0, right: 0 } },\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should return theme, maxHeight, and safeArea from window.openai\", () => {\n const { result } = renderHook(() => useLayout());\n\n expect(result.current.theme).toBe(\"light\");\n expect(result.current.maxHeight).toBe(500);\n expect(result.current.safeArea).toEqual({\n insets: { top: 0, bottom: 0, left: 0, right: 0 },\n });\n });\n\n it(\"should return dark theme when set to dark\", () => {\n OpenaiMock.theme = \"dark\";\n const { result } = renderHook(() => useLayout());\n\n expect(result.current.theme).toBe(\"dark\");\n });\n\n it(\"should return different maxHeight when set\", () => {\n OpenaiMock.maxHeight = 800;\n const { result } = renderHook(() => useLayout());\n\n expect(result.current.maxHeight).toBe(800);\n });\n\n it(\"should return safeArea with insets when set\", () => {\n OpenaiMock.safeArea = {\n insets: { top: 44, bottom: 34, left: 0, right: 0 },\n };\n const { result } = renderHook(() => useLayout());\n\n expect(result.current.safeArea.insets.top).toBe(44);\n expect(result.current.safeArea.insets.bottom).toBe(34);\n });\n });\n\n describe(\"mcp-app host type\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n McpAppAdaptor.resetInstance();\n });\n\n it(\"should return theme, maxHeight, and safeArea from mcp host context\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({\n theme: \"dark\",\n containerDimensions: { maxHeight: 800, width: 400 },\n safeAreaInsets: { top: 20, right: 0, bottom: 34, left: 0 },\n }),\n });\n const { result } = renderHook(() => useLayout());\n\n await waitFor(() => {\n expect(result.current.theme).toBe(\"dark\");\n expect(result.current.maxHeight).toBe(800);\n expect(result.current.safeArea).toEqual({\n insets: { top: 20, right: 0, bottom: 34, left: 0 },\n });\n });\n });\n\n it(\"should maintain safeArea referential stability when data has not changed\", async () => {\n vi.stubGlobal(\"parent\", {\n postMessage: getMcpAppHostPostMessageMock({\n theme: \"light\",\n containerDimensions: { maxHeight: 500, width: 400 },\n safeAreaInsets: { top: 44, right: 0, bottom: 34, left: 0 },\n }),\n });\n const { result, rerender } = renderHook(() => useLayout());\n\n await waitFor(() => {\n expect(result.current.safeArea).toBeDefined();\n });\n\n const initialSafeArea = result.current.safeArea;\n\n rerender();\n\n expect(result.current.safeArea).toBe(initialSafeArea);\n });\n });\n});\n"]}
@@ -1,3 +1,20 @@
1
1
  import type { OpenExternalOptions } from "../bridges/types.js";
2
+ /** Function that opens a URL outside the view's iframe, returned by {@link useOpenExternal}. */
2
3
  export type OpenExternalFn = (href: string, options?: OpenExternalOptions) => void;
4
+ /**
5
+ * Open an external URL through the host (e.g. in the user's browser).
6
+ *
7
+ * Use this instead of `window.open` or anchor `target="_blank"`, which are
8
+ * unreliable inside a sandboxed iframe. Hosts may transform the URL (e.g.
9
+ * ChatGPT appends a `?redirectUrl=…` parameter for allowlisted targets — pass
10
+ * `redirectUrl: false` to suppress it).
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const openExternal = useOpenExternal();
15
+ * <button onClick={() => openExternal("https://example.com")}>Open docs</button>
16
+ * ```
17
+ *
18
+ * @see https://docs.skybridge.tech/api-reference/use-open-external
19
+ */
3
20
  export declare function useOpenExternal(): OpenExternalFn;
@@ -1,5 +1,21 @@
1
1
  import { useCallback } from "react";
2
2
  import { getAdaptor } from "../bridges/index.js";
3
+ /**
4
+ * Open an external URL through the host (e.g. in the user's browser).
5
+ *
6
+ * Use this instead of `window.open` or anchor `target="_blank"`, which are
7
+ * unreliable inside a sandboxed iframe. Hosts may transform the URL (e.g.
8
+ * ChatGPT appends a `?redirectUrl=…` parameter for allowlisted targets — pass
9
+ * `redirectUrl: false` to suppress it).
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const openExternal = useOpenExternal();
14
+ * <button onClick={() => openExternal("https://example.com")}>Open docs</button>
15
+ * ```
16
+ *
17
+ * @see https://docs.skybridge.tech/api-reference/use-open-external
18
+ */
3
19
  export function useOpenExternal() {
4
20
  const adaptor = getAdaptor();
5
21
  const openExternal = useCallback((href, options) => adaptor.openExternal(href, options), [adaptor]);
@@ -1 +1 @@
1
- {"version":3,"file":"use-open-external.js","sourceRoot":"","sources":["../../../src/web/hooks/use-open-external.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQjD,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,IAAY,EAAE,OAA6B,EAAE,EAAE,CAC9C,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EACrC,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,YAAY,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"use-open-external.js","sourceRoot":"","sources":["../../../src/web/hooks/use-open-external.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AASjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,IAAY,EAAE,OAA6B,EAAE,EAAE,CAC9C,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EACrC,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\nimport type { OpenExternalOptions } from \"../bridges/types.js\";\n\n/** Function that opens a URL outside the view's iframe, returned by {@link useOpenExternal}. */\nexport type OpenExternalFn = (\n href: string,\n options?: OpenExternalOptions,\n) => void;\n\n/**\n * Open an external URL through the host (e.g. in the user's browser).\n *\n * Use this instead of `window.open` or anchor `target=\"_blank\"`, which are\n * unreliable inside a sandboxed iframe. Hosts may transform the URL (e.g.\n * ChatGPT appends a `?redirectUrl=…` parameter for allowlisted targets — pass\n * `redirectUrl: false` to suppress it).\n *\n * @example\n * ```tsx\n * const openExternal = useOpenExternal();\n * <button onClick={() => openExternal(\"https://example.com\")}>Open docs</button>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-open-external\n */\nexport function useOpenExternal(): OpenExternalFn {\n const adaptor = getAdaptor();\n const openExternal = useCallback(\n (href: string, options?: OpenExternalOptions) =>\n adaptor.openExternal(href, options),\n [adaptor],\n );\n\n return openExternal;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-open-external.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-open-external.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-open-external.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-open-external.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useOpenExternal } from \"./use-open-external.js\";\n\ndescribe(\"useOpenExternal\", () => {\n describe(\"apps-sdk host\", () => {\n let openExternalMock: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n openExternalMock = vi.fn();\n vi.stubGlobal(\"openai\", {\n openExternal: openExternalMock,\n });\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should return a function that calls window.openai.openExternal with the href\", () => {\n const { result } = renderHook(() => useOpenExternal());\n\n const href = \"https://example.com\";\n result.current(href);\n\n expect(openExternalMock).toHaveBeenCalledTimes(1);\n expect(openExternalMock).toHaveBeenCalledWith({ href });\n });\n\n it(\"should forward redirectUrl false option to window.openai.openExternal\", () => {\n const { result } = renderHook(() => useOpenExternal());\n\n const href = \"https://example.com\";\n result.current(href, { redirectUrl: false });\n\n expect(openExternalMock).toHaveBeenCalledTimes(1);\n expect(openExternalMock).toHaveBeenCalledWith({\n href,\n redirectUrl: false,\n });\n });\n });\n\n describe(\"mcp-app host\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n });\n\n it(\"should return a function that sends ui/open-link request to the MCP host\", async () => {\n const { result } = renderHook(() => useOpenExternal());\n\n const href = \"https://example.com\";\n result.current(href, { redirectUrl: false });\n\n await waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({\n jsonrpc: \"2.0\",\n method: \"ui/open-link\",\n params: { url: href },\n }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
@@ -0,0 +1,16 @@
1
+ /** Function that asks the host to close the current view, returned by {@link useRequestClose}. */
2
+ export type RequestCloseFn = () => Promise<void>;
3
+ /**
4
+ * Ask the host to close (dismiss) the current view. The host decides whether
5
+ * to honor the request. Useful from modal views or after a terminal action
6
+ * like "Done".
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const close = useRequestClose();
11
+ * <button onClick={() => close()}>Done</button>
12
+ * ```
13
+ *
14
+ * @see https://docs.skybridge.tech/api-reference/use-request-close
15
+ */
16
+ export declare function useRequestClose(): RequestCloseFn;
@@ -0,0 +1,21 @@
1
+ import { useCallback } from "react";
2
+ import { getAdaptor } from "../bridges/index.js";
3
+ /**
4
+ * Ask the host to close (dismiss) the current view. The host decides whether
5
+ * to honor the request. Useful from modal views or after a terminal action
6
+ * like "Done".
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const close = useRequestClose();
11
+ * <button onClick={() => close()}>Done</button>
12
+ * ```
13
+ *
14
+ * @see https://docs.skybridge.tech/api-reference/use-request-close
15
+ */
16
+ export function useRequestClose() {
17
+ const adaptor = getAdaptor();
18
+ const requestClose = useCallback(() => adaptor.requestClose(), [adaptor]);
19
+ return requestClose;
20
+ }
21
+ //# sourceMappingURL=use-request-close.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-request-close.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-close.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKjD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAE1E,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\n\n/** Function that asks the host to close the current view, returned by {@link useRequestClose}. */\nexport type RequestCloseFn = () => Promise<void>;\n\n/**\n * Ask the host to close (dismiss) the current view. The host decides whether\n * to honor the request. Useful from modal views or after a terminal action\n * like \"Done\".\n *\n * @example\n * ```tsx\n * const close = useRequestClose();\n * <button onClick={() => close()}>Done</button>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-request-close\n */\nexport function useRequestClose(): RequestCloseFn {\n const adaptor = getAdaptor();\n const requestClose = useCallback(() => adaptor.requestClose(), [adaptor]);\n\n return requestClose;\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
4
+ import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
5
+ import { useRequestClose } from "./use-request-close.js";
6
+ describe("useRequestClose", () => {
7
+ describe("apps-sdk host", () => {
8
+ let requestCloseMock;
9
+ beforeEach(() => {
10
+ requestCloseMock = vi.fn().mockResolvedValue(undefined);
11
+ vi.stubGlobal("openai", {
12
+ requestClose: requestCloseMock,
13
+ });
14
+ vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
15
+ });
16
+ afterEach(() => {
17
+ vi.unstubAllGlobals();
18
+ vi.resetAllMocks();
19
+ });
20
+ it("should return a function that calls window.openai.requestClose", async () => {
21
+ const { result } = renderHook(() => useRequestClose());
22
+ await result.current();
23
+ expect(requestCloseMock).toHaveBeenCalledTimes(1);
24
+ expect(requestCloseMock).toHaveBeenCalledWith();
25
+ });
26
+ });
27
+ describe("mcp-app host", () => {
28
+ let postMessageMock;
29
+ beforeEach(() => {
30
+ vi.stubGlobal("skybridge", { hostType: "mcp-app" });
31
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
32
+ postMessageMock = getMcpAppHostPostMessageMock();
33
+ vi.stubGlobal("parent", { postMessage: postMessageMock });
34
+ });
35
+ afterEach(async () => {
36
+ vi.unstubAllGlobals();
37
+ vi.resetAllMocks();
38
+ McpAppBridge.resetInstance();
39
+ });
40
+ it("should send a ui/notifications/request-teardown notification to the MCP host", async () => {
41
+ const { result } = renderHook(() => useRequestClose());
42
+ await result.current();
43
+ await waitFor(() => {
44
+ expect(postMessageMock).toHaveBeenCalledWith(expect.objectContaining({
45
+ jsonrpc: "2.0",
46
+ method: "ui/notifications/request-teardown",
47
+ }), "*");
48
+ });
49
+ });
50
+ });
51
+ });
52
+ //# sourceMappingURL=use-request-close.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-request-close.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-close.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,IAAI,gBAA0C,CAAC;QAE/C,UAAU,CAAC,GAAG,EAAE;YACd,gBAAgB,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;YACxD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE;gBACtB,YAAY,EAAE,gBAAgB;aAC/B,CAAC,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,GAAG,EAAE;YACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;YACtB,EAAE,CAAC,aAAa,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;YAC9E,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;YAEvD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YAEvB,MAAM,CAAC,gBAAgB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,CAAC,gBAAgB,CAAC,CAAC,oBAAoB,EAAE,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,IAAI,eAAgE,CAAC;QAErE,UAAU,CAAC,GAAG,EAAE;YACd,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;YACpD,EAAE,CAAC,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,CAAC;YACpD,eAAe,GAAG,4BAA4B,EAAE,CAAC;YACjD,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,SAAS,CAAC,KAAK,IAAI,EAAE;YACnB,EAAE,CAAC,gBAAgB,EAAE,CAAC;YACtB,EAAE,CAAC,aAAa,EAAE,CAAC;YACnB,YAAY,CAAC,aAAa,EAAE,CAAC;QAC/B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;YAC5F,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;YAEvD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YAEvB,MAAM,OAAO,CAAC,GAAG,EAAE;gBACjB,MAAM,CAAC,eAAe,CAAC,CAAC,oBAAoB,CAC1C,MAAM,CAAC,gBAAgB,CAAC;oBACtB,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,mCAAmC;iBAC5C,CAAC,EACF,GAAG,CACJ,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useRequestClose } from \"./use-request-close.js\";\n\ndescribe(\"useRequestClose\", () => {\n describe(\"apps-sdk host\", () => {\n let requestCloseMock: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n requestCloseMock = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", {\n requestClose: requestCloseMock,\n });\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should return a function that calls window.openai.requestClose\", async () => {\n const { result } = renderHook(() => useRequestClose());\n\n await result.current();\n\n expect(requestCloseMock).toHaveBeenCalledTimes(1);\n expect(requestCloseMock).toHaveBeenCalledWith();\n });\n });\n\n describe(\"mcp-app host\", () => {\n let postMessageMock: ReturnType<typeof getMcpAppHostPostMessageMock>;\n\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n postMessageMock = getMcpAppHostPostMessageMock();\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n });\n\n it(\"should send a ui/notifications/request-teardown notification to the MCP host\", async () => {\n const { result } = renderHook(() => useRequestClose());\n\n await result.current();\n\n await waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({\n jsonrpc: \"2.0\",\n method: \"ui/notifications/request-teardown\",\n }),\n \"*\",\n );\n });\n });\n });\n});\n"]}
@@ -1,6 +1,21 @@
1
1
  import { type RequestModalOptions } from "../bridges/index.js";
2
2
  /**
3
- * Triggers a modal containing the widget rendered in display mode "modal"
3
+ * Open the current view in a modal overlay (`displayMode === "modal"`).
4
+ *
5
+ * Returns `{ isOpen, params, open }`: `open(opts)` triggers the host to render
6
+ * the view in a modal, optionally passing `params` that are surfaced back via
7
+ * `params` here. Useful for confirmation flows, detail panels, or any modal
8
+ * lifecycle owned by the host.
9
+ *
10
+ * Use {@link useDisplayMode} for non-modal display modes.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { isOpen, open } = useRequestModal();
15
+ * <button onClick={() => open({ params: { id: 42 } })}>Show details</button>
16
+ * ```
17
+ *
18
+ * @see https://docs.skybridge.tech/api-reference/use-request-modal
4
19
  */
5
20
  export declare function useRequestModal(): {
6
21
  isOpen: boolean;
@@ -1,15 +1,30 @@
1
1
  import { useCallback } from "react";
2
2
  import { getAdaptor, useHostContext, } from "../bridges/index.js";
3
3
  /**
4
- * Triggers a modal containing the widget rendered in display mode "modal"
4
+ * Open the current view in a modal overlay (`displayMode === "modal"`).
5
+ *
6
+ * Returns `{ isOpen, params, open }`: `open(opts)` triggers the host to render
7
+ * the view in a modal, optionally passing `params` that are surfaced back via
8
+ * `params` here. Useful for confirmation flows, detail panels, or any modal
9
+ * lifecycle owned by the host.
10
+ *
11
+ * Use {@link useDisplayMode} for non-modal display modes.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const { isOpen, open } = useRequestModal();
16
+ * <button onClick={() => open({ params: { id: 42 } })}>Show details</button>
17
+ * ```
18
+ *
19
+ * @see https://docs.skybridge.tech/api-reference/use-request-modal
5
20
  */
6
21
  export function useRequestModal() {
7
22
  const adaptor = getAdaptor();
8
- const view = useHostContext("view");
23
+ const display = useHostContext("display");
9
24
  const open = useCallback((opts) => adaptor.openModal(opts), [adaptor]);
10
25
  return {
11
- isOpen: view.mode === "modal",
12
- params: view.params,
26
+ isOpen: display.mode === "modal",
27
+ params: display.params,
13
28
  open,
14
29
  };
15
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-request-modal.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-modal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,UAAU,EAEV,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,WAAW,CACtB,CAAC,IAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EACtD,CAAC,OAAO,CAAC,CACV,CAAC;IACF,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,IAAI,KAAK,OAAO;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI;KACL,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"use-request-modal.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-modal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,UAAU,EAEV,cAAc,GACf,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,WAAW,CACtB,CAAC,IAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EACtD,CAAC,OAAO,CAAC,CACV,CAAC;IACF,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,IAAI,KAAK,OAAO;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,IAAI;KACL,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport {\n getAdaptor,\n type RequestModalOptions,\n useHostContext,\n} from \"../bridges/index.js\";\n\n/**\n * Open the current view in a modal overlay (`displayMode === \"modal\"`).\n *\n * Returns `{ isOpen, params, open }`: `open(opts)` triggers the host to render\n * the view in a modal, optionally passing `params` that are surfaced back via\n * `params` here. Useful for confirmation flows, detail panels, or any modal\n * lifecycle owned by the host.\n *\n * Use {@link useDisplayMode} for non-modal display modes.\n *\n * @example\n * ```tsx\n * const { isOpen, open } = useRequestModal();\n * <button onClick={() => open({ params: { id: 42 } })}>Show details</button>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-request-modal\n */\nexport function useRequestModal() {\n const adaptor = getAdaptor();\n const display = useHostContext(\"display\");\n const open = useCallback(\n (opts: RequestModalOptions) => adaptor.openModal(opts),\n [adaptor],\n );\n return {\n isOpen: display.mode === \"modal\",\n params: display.params,\n open,\n };\n}\n"]}
@@ -51,7 +51,7 @@ describe("useRequestModal", () => {
51
51
  const options = {
52
52
  title: "Test Modal",
53
53
  params: { foo: "bar" },
54
- template: "ui://widget/modal-template.html",
54
+ template: "ui://view/modal-template.html",
55
55
  };
56
56
  open(options);
57
57
  expect(requestModalMock).toHaveBeenCalledTimes(1);
@@ -1 +1 @@
1
- {"version":3,"file":"use-request-modal.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-modal.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-request-modal.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-modal.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderHook } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { useRequestModal } from \"./use-request-modal.js\";\n\ndescribe(\"useRequestModal\", () => {\n let requestModalMock: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n requestModalMock = vi.fn();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"openai\", {\n requestModal: requestModalMock,\n view: { mode: \"inline\" },\n });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should return an object with open, isOpen, and params properties where isOpen is false when mode is not modal\", () => {\n const { result } = renderHook(() => useRequestModal());\n\n expect(typeof result.current).toBe(\"object\");\n expect(result.current).toHaveProperty(\"open\");\n expect(result.current).toHaveProperty(\"isOpen\");\n expect(result.current).toHaveProperty(\"params\");\n\n const { open, isOpen, params } = result.current;\n expect(typeof open).toBe(\"function\");\n expect(isOpen).toBe(false);\n expect(params).toBeUndefined();\n });\n\n it(\"should return isOpen as true when mode is modal\", () => {\n vi.stubGlobal(\"openai\", {\n requestModal: requestModalMock,\n view: { mode: \"modal\" },\n });\n\n const { result } = renderHook(() => useRequestModal());\n const { isOpen } = result.current;\n\n expect(isOpen).toBe(true);\n });\n\n it(\"should return params from view when available\", () => {\n const testParams = { foo: \"bar\", baz: 42 };\n vi.stubGlobal(\"openai\", {\n requestModal: requestModalMock,\n view: { mode: \"modal\", params: testParams },\n });\n\n const { result } = renderHook(() => useRequestModal());\n const { params } = result.current;\n\n expect(params).toEqual(testParams);\n });\n\n it(\"should call window.openai.requestModal with the options when open is called\", () => {\n const { result } = renderHook(() => useRequestModal());\n const { open } = result.current;\n\n const options = {\n title: \"Test Modal\",\n params: { foo: \"bar\" },\n template: \"ui://view/modal-template.html\",\n };\n open(options);\n\n expect(requestModalMock).toHaveBeenCalledTimes(1);\n expect(requestModalMock).toHaveBeenCalledWith(options);\n });\n});\n"]}
@@ -0,0 +1,20 @@
1
+ import type { RequestSizeOptions } from "../bridges/types.js";
2
+ /** Function that asks the host to resize the view, returned by {@link useRequestSize}. */
3
+ export type RequestSizeFn = (size: RequestSizeOptions) => Promise<void>;
4
+ /**
5
+ * Ask the host to resize the view iframe. The applied size is host-driven —
6
+ * the host decides whether and how to honor the request, and {@link useLayout}
7
+ * still reports the final `maxHeight` it allows.
8
+ *
9
+ * Pair with a `ResizeObserver` on your root element to react to content size
10
+ * changes without hard-coded values.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const requestSize = useRequestSize();
15
+ * useEffect(() => { requestSize({ height: rootRef.current!.scrollHeight }); }, [content]);
16
+ * ```
17
+ *
18
+ * @see https://docs.skybridge.tech/api-reference/use-request-size
19
+ */
20
+ export declare function useRequestSize(): RequestSizeFn;
@@ -0,0 +1,24 @@
1
+ import { useCallback } from "react";
2
+ import { getAdaptor } from "../bridges/index.js";
3
+ /**
4
+ * Ask the host to resize the view iframe. The applied size is host-driven —
5
+ * the host decides whether and how to honor the request, and {@link useLayout}
6
+ * still reports the final `maxHeight` it allows.
7
+ *
8
+ * Pair with a `ResizeObserver` on your root element to react to content size
9
+ * changes without hard-coded values.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const requestSize = useRequestSize();
14
+ * useEffect(() => { requestSize({ height: rootRef.current!.scrollHeight }); }, [content]);
15
+ * ```
16
+ *
17
+ * @see https://docs.skybridge.tech/api-reference/use-request-size
18
+ */
19
+ export function useRequestSize() {
20
+ const adaptor = getAdaptor();
21
+ const requestSize = useCallback((size) => adaptor.requestSize(size), [adaptor]);
22
+ return requestSize;
23
+ }
24
+ //# sourceMappingURL=use-request-size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-request-size.js","sourceRoot":"","sources":["../../../src/web/hooks/use-request-size.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMjD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,IAAwB,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EACvD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\nimport type { RequestSizeOptions } from \"../bridges/types.js\";\n\n/** Function that asks the host to resize the view, returned by {@link useRequestSize}. */\nexport type RequestSizeFn = (size: RequestSizeOptions) => Promise<void>;\n\n/**\n * Ask the host to resize the view iframe. The applied size is host-driven —\n * the host decides whether and how to honor the request, and {@link useLayout}\n * still reports the final `maxHeight` it allows.\n *\n * Pair with a `ResizeObserver` on your root element to react to content size\n * changes without hard-coded values.\n *\n * @example\n * ```tsx\n * const requestSize = useRequestSize();\n * useEffect(() => { requestSize({ height: rootRef.current!.scrollHeight }); }, [content]);\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-request-size\n */\nexport function useRequestSize(): RequestSizeFn {\n const adaptor = getAdaptor();\n const requestSize = useCallback(\n (size: RequestSizeOptions) => adaptor.requestSize(size),\n [adaptor],\n );\n\n return requestSize;\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { renderHook, waitFor } from "@testing-library/react";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { AppsSdkAdaptor } from "../bridges/apps-sdk/adaptor.js";
4
+ import { McpAppBridge } from "../bridges/mcp-app/bridge.js";
5
+ import { getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
6
+ import { useRequestSize } from "./use-request-size.js";
7
+ describe("useRequestSize", () => {
8
+ describe("apps-sdk host", () => {
9
+ let consoleWarnSpy;
10
+ beforeEach(() => {
11
+ AppsSdkAdaptor.resetInstance();
12
+ vi.stubGlobal("openai", {});
13
+ vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
14
+ consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
15
+ });
16
+ afterEach(() => {
17
+ AppsSdkAdaptor.resetInstance();
18
+ vi.unstubAllGlobals();
19
+ vi.resetAllMocks();
20
+ });
21
+ it("warns that requestSize is not supported on Apps SDK", async () => {
22
+ const { result } = renderHook(() => useRequestSize());
23
+ await result.current({ height: 400 });
24
+ expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
25
+ expect(consoleWarnSpy.mock.calls[0]?.[0]).toContain("not supported");
26
+ });
27
+ });
28
+ describe("mcp-app host", () => {
29
+ let postMessageMock;
30
+ beforeEach(() => {
31
+ vi.stubGlobal("skybridge", { hostType: "mcp-app" });
32
+ vi.stubGlobal("ResizeObserver", MockResizeObserver);
33
+ postMessageMock = getMcpAppHostPostMessageMock();
34
+ vi.stubGlobal("parent", { postMessage: postMessageMock });
35
+ });
36
+ afterEach(async () => {
37
+ vi.unstubAllGlobals();
38
+ vi.resetAllMocks();
39
+ McpAppBridge.resetInstance();
40
+ });
41
+ it("sends a ui/notifications/size-changed notification with width and height", async () => {
42
+ const { result } = renderHook(() => useRequestSize());
43
+ await result.current({ width: 800, height: 400 });
44
+ await waitFor(() => {
45
+ expect(postMessageMock).toHaveBeenCalledWith(expect.objectContaining({
46
+ jsonrpc: "2.0",
47
+ method: "ui/notifications/size-changed",
48
+ params: { width: 800, height: 400 },
49
+ }), "*");
50
+ });
51
+ });
52
+ it("forwards height-only payloads as-is", async () => {
53
+ const { result } = renderHook(() => useRequestSize());
54
+ await result.current({ height: 400 });
55
+ await waitFor(() => {
56
+ expect(postMessageMock).toHaveBeenCalledWith(expect.objectContaining({
57
+ jsonrpc: "2.0",
58
+ method: "ui/notifications/size-changed",
59
+ params: { height: 400 },
60
+ }), "*");
61
+ });
62
+ });
63
+ });
64
+ });
65
+ //# sourceMappingURL=use-request-size.test.js.map