skybridge 0.0.0-dev.b0f5d5c → 0.0.0-dev.b0f7f92
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 +123 -124
- package/dist/cli/build-helpers.d.ts +7 -0
- package/dist/cli/build-helpers.js +82 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.d.ts +1 -0
- package/dist/cli/build-helpers.test.js +64 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +2 -2
- package/dist/cli/detect-port.js +9 -20
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/use-nodemon.js +2 -1
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +18 -30
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.js +16 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +7 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.js +16 -1
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +51 -1
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
- package/dist/server/auth/index.d.ts +16 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/setup.d.ts +4 -0
- package/dist/server/auth/setup.js +28 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +161 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +11 -0
- package/dist/server/auth/verify.js +37 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +87 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/express.test.js +61 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +5 -2
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/server.d.ts +213 -6
- package/dist/server/server.js +254 -62
- package/dist/server/server.js.map +1 -1
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +88 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/test/view.test.js +45 -0
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +7 -2
- package/dist/web/bridges/apps-sdk/adaptor.js +27 -3
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +8 -1
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +7 -0
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +7 -2
- package/dist/web/bridges/mcp-app/adaptor.js +21 -6
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +4 -2
- package/dist/web/bridges/mcp-app/bridge.js +23 -1
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +98 -3
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +26 -0
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/data-llm.d.ts +33 -0
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -0
- package/dist/web/generate-helpers.js +2 -0
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +4 -2
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/hooks/index.d.ts +4 -0
- package/dist/web/hooks/index.js +4 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +6 -2
- package/dist/web/hooks/test/utils.js +13 -2
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +27 -6
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +95 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +52 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +16 -1
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +65 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +19 -1
- package/dist/web/hooks/use-send-follow-up-message.js +19 -2
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +53 -2
- package/dist/web/hooks/use-tool-info.js +30 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -29
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +5 -5
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/mount-view.d.ts +19 -0
- package/dist/web/mount-view.js +19 -0
- package/dist/web/mount-view.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +28 -0
- package/dist/web/plugin/plugin.js +33 -0
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-download.js","sourceRoot":"","sources":["../../../src/web/hooks/use-download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKjD,MAAM,UAAU,WAAW;IACzB,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EACpC,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\nimport type { DownloadParams, DownloadResult } from \"../bridges/types.js\";\n\nexport type DownloadFn = (params: DownloadParams) => Promise<DownloadResult>;\n\nexport function useDownload(): { download: DownloadFn } {\n const adaptor = getAdaptor();\n const download = useCallback<DownloadFn>(\n (params) => adaptor.download(params),\n [adaptor],\n );\n\n return { download };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
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 { useDownload } from "./use-download.js";
|
|
7
|
+
const params = {
|
|
8
|
+
contents: [
|
|
9
|
+
{
|
|
10
|
+
type: "resource",
|
|
11
|
+
resource: {
|
|
12
|
+
uri: "file:///export.json",
|
|
13
|
+
mimeType: "application/json",
|
|
14
|
+
text: '{"hello":"world"}',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
describe("useDownload", () => {
|
|
20
|
+
describe("apps-sdk host", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.stubGlobal("openai", {});
|
|
23
|
+
vi.stubGlobal("skybridge", { hostType: "apps-sdk" });
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
vi.unstubAllGlobals();
|
|
27
|
+
vi.resetAllMocks();
|
|
28
|
+
AppsSdkAdaptor.resetInstance();
|
|
29
|
+
});
|
|
30
|
+
it("returns { isError: true } and logs an error", async () => {
|
|
31
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
32
|
+
const { result } = renderHook(() => useDownload());
|
|
33
|
+
const res = await result.current.download(params);
|
|
34
|
+
expect(res).toEqual({ isError: true });
|
|
35
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("not supported on Apps SDK"));
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe("mcp-app host without downloadFile capability", () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
41
|
+
vi.stubGlobal("ResizeObserver", MockResizeObserver);
|
|
42
|
+
vi.stubGlobal("parent", { postMessage: getMcpAppHostPostMessageMock() });
|
|
43
|
+
});
|
|
44
|
+
afterEach(async () => {
|
|
45
|
+
vi.unstubAllGlobals();
|
|
46
|
+
vi.resetAllMocks();
|
|
47
|
+
McpAppBridge.resetInstance();
|
|
48
|
+
});
|
|
49
|
+
it("returns { isError: true } and logs an error", async () => {
|
|
50
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { });
|
|
51
|
+
const { result } = renderHook(() => useDownload());
|
|
52
|
+
const res = await result.current.download(params);
|
|
53
|
+
expect(res).toEqual({ isError: true });
|
|
54
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining("does not support ui/download-file"));
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe("mcp-app host with downloadFile capability", () => {
|
|
58
|
+
let postMessageMock;
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
vi.stubGlobal("skybridge", { hostType: "mcp-app" });
|
|
61
|
+
vi.stubGlobal("ResizeObserver", MockResizeObserver);
|
|
62
|
+
postMessageMock = getMcpAppHostPostMessageMock({}, { hostCapabilities: { downloadFile: {} } });
|
|
63
|
+
vi.stubGlobal("parent", { postMessage: postMessageMock });
|
|
64
|
+
});
|
|
65
|
+
afterEach(async () => {
|
|
66
|
+
vi.unstubAllGlobals();
|
|
67
|
+
vi.resetAllMocks();
|
|
68
|
+
McpAppBridge.resetInstance();
|
|
69
|
+
});
|
|
70
|
+
it("sends ui/download-file with the provided contents", async () => {
|
|
71
|
+
const { result } = renderHook(() => useDownload());
|
|
72
|
+
const res = await result.current.download(params);
|
|
73
|
+
expect(res).toEqual({});
|
|
74
|
+
await waitFor(() => {
|
|
75
|
+
expect(postMessageMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
76
|
+
jsonrpc: "2.0",
|
|
77
|
+
method: "ui/download-file",
|
|
78
|
+
params,
|
|
79
|
+
}), "*");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
it("returns { isError: true } when the host denies the request", async () => {
|
|
83
|
+
McpAppBridge.resetInstance();
|
|
84
|
+
postMessageMock = getMcpAppHostPostMessageMock({}, {
|
|
85
|
+
hostCapabilities: { downloadFile: {} },
|
|
86
|
+
downloadFileResult: { isError: true },
|
|
87
|
+
});
|
|
88
|
+
vi.stubGlobal("parent", { postMessage: postMessageMock });
|
|
89
|
+
const { result } = renderHook(() => useDownload());
|
|
90
|
+
const res = await result.current.download(params);
|
|
91
|
+
expect(res).toEqual({ isError: true });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=use-download.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-download.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-download.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 { AppsSdkAdaptor } from \"../bridges/apps-sdk/adaptor.js\";\nimport { McpAppBridge } from \"../bridges/mcp-app/bridge.js\";\nimport type { DownloadParams } from \"../bridges/types.js\";\nimport {\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useDownload } from \"./use-download.js\";\n\nconst params: DownloadParams = {\n contents: [\n {\n type: \"resource\",\n resource: {\n uri: \"file:///export.json\",\n mimeType: \"application/json\",\n text: '{\"hello\":\"world\"}',\n },\n },\n ],\n};\n\ndescribe(\"useDownload\", () => {\n describe(\"apps-sdk host\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"openai\", {});\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n AppsSdkAdaptor.resetInstance();\n });\n\n it(\"returns { isError: true } and logs an error\", async () => {\n const errorSpy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current.download(params);\n\n expect(res).toEqual({ isError: true });\n expect(errorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"not supported on Apps SDK\"),\n );\n });\n });\n\n describe(\"mcp-app host without downloadFile capability\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n vi.stubGlobal(\"parent\", { postMessage: getMcpAppHostPostMessageMock() });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n });\n\n it(\"returns { isError: true } and logs an error\", async () => {\n const errorSpy = vi.spyOn(console, \"error\").mockImplementation(() => {});\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current.download(params);\n\n expect(res).toEqual({ isError: true });\n expect(errorSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"does not support ui/download-file\"),\n );\n });\n });\n\n describe(\"mcp-app host with downloadFile capability\", () => {\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 {},\n { hostCapabilities: { downloadFile: {} } },\n );\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n });\n\n it(\"sends ui/download-file with the provided contents\", async () => {\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current.download(params);\n\n expect(res).toEqual({});\n await waitFor(() => {\n expect(postMessageMock).toHaveBeenCalledWith(\n expect.objectContaining({\n jsonrpc: \"2.0\",\n method: \"ui/download-file\",\n params,\n }),\n \"*\",\n );\n });\n });\n\n it(\"returns { isError: true } when the host denies the request\", async () => {\n McpAppBridge.resetInstance();\n postMessageMock = getMcpAppHostPostMessageMock(\n {},\n {\n hostCapabilities: { downloadFile: {} },\n downloadFileResult: { isError: true },\n },\n );\n vi.stubGlobal(\"parent\", { postMessage: postMessageMock });\n\n const { result } = renderHook(() => useDownload());\n\n const res = await result.current.download(params);\n\n expect(res).toEqual({ isError: true });\n });\n });\n});\n"]}
|
|
@@ -1,3 +1,35 @@
|
|
|
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
34
|
upload: (file: File, options?: import("../index.js").UploadFileOptions) => Promise<import("../index.js").FileMetadata>;
|
|
3
35
|
getDownloadUrl: (file: import("../index.js").FileMetadata) => Promise<{
|
|
@@ -1,4 +1,36 @@
|
|
|
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 {
|
|
@@ -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;QAC1C,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { getAdaptor } from \"../bridges/index.js\";\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
|
+
{"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"]}
|
|
@@ -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
|
|
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,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;
|
|
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"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ZodRawShapeCompat } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
2
|
+
import type { ViewToolConfig, ViewToolHandler } from "../bridges/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register a tool the view exposes to the host and model — the MCP Apps
|
|
5
|
+
* "app-provided tools" feature. A view tool runs *inside the view*: the host
|
|
6
|
+
* discovers it via `tools/list` and invokes it via `tools/call`, and the
|
|
7
|
+
* handler executes against the view's live state. It is the inverse of
|
|
8
|
+
* {@link useCallTool} (which calls a server tool). Registered on mount, removed
|
|
9
|
+
* on unmount; re-registered when `config.name` changes.
|
|
10
|
+
*
|
|
11
|
+
* MCP Apps only — on the Apps SDK (`window.openai`) runtime it is a no-op.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import * as z from "zod";
|
|
16
|
+
* import { useRegisterViewTool } from "skybridge/web";
|
|
17
|
+
*
|
|
18
|
+
* useRegisterViewTool(
|
|
19
|
+
* {
|
|
20
|
+
* name: "chess_make_move",
|
|
21
|
+
* description: "Play a move in algebraic notation, e.g. 'e4' or 'Nf3'.",
|
|
22
|
+
* inputSchema: { san: z.string() },
|
|
23
|
+
* annotations: { readOnlyHint: false },
|
|
24
|
+
* },
|
|
25
|
+
* ({ san }) => {
|
|
26
|
+
* const move = game.move(san);
|
|
27
|
+
* return {
|
|
28
|
+
* content: [{ type: "text", text: move ? `Played ${move.san}` : "Illegal move" }],
|
|
29
|
+
* structuredContent: { fen: game.fen() },
|
|
30
|
+
* isError: !move,
|
|
31
|
+
* };
|
|
32
|
+
* },
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see https://docs.skybridge.tech/api-reference/use-register-view-tool
|
|
37
|
+
*/
|
|
38
|
+
export declare const useRegisterViewTool: <TInput extends ZodRawShapeCompat = ZodRawShapeCompat>(config: ViewToolConfig<TInput>, handler: ViewToolHandler<TInput>) => void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { getAdaptor } from "../bridges/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Register a tool the view exposes to the host and model — the MCP Apps
|
|
5
|
+
* "app-provided tools" feature. A view tool runs *inside the view*: the host
|
|
6
|
+
* discovers it via `tools/list` and invokes it via `tools/call`, and the
|
|
7
|
+
* handler executes against the view's live state. It is the inverse of
|
|
8
|
+
* {@link useCallTool} (which calls a server tool). Registered on mount, removed
|
|
9
|
+
* on unmount; re-registered when `config.name` changes.
|
|
10
|
+
*
|
|
11
|
+
* MCP Apps only — on the Apps SDK (`window.openai`) runtime it is a no-op.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* import * as z from "zod";
|
|
16
|
+
* import { useRegisterViewTool } from "skybridge/web";
|
|
17
|
+
*
|
|
18
|
+
* useRegisterViewTool(
|
|
19
|
+
* {
|
|
20
|
+
* name: "chess_make_move",
|
|
21
|
+
* description: "Play a move in algebraic notation, e.g. 'e4' or 'Nf3'.",
|
|
22
|
+
* inputSchema: { san: z.string() },
|
|
23
|
+
* annotations: { readOnlyHint: false },
|
|
24
|
+
* },
|
|
25
|
+
* ({ san }) => {
|
|
26
|
+
* const move = game.move(san);
|
|
27
|
+
* return {
|
|
28
|
+
* content: [{ type: "text", text: move ? `Played ${move.san}` : "Illegal move" }],
|
|
29
|
+
* structuredContent: { fen: game.fen() },
|
|
30
|
+
* isError: !move,
|
|
31
|
+
* };
|
|
32
|
+
* },
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @see https://docs.skybridge.tech/api-reference/use-register-view-tool
|
|
37
|
+
*/
|
|
38
|
+
export const useRegisterViewTool = (config, handler) => {
|
|
39
|
+
const { name } = config;
|
|
40
|
+
const configRef = useRef(config);
|
|
41
|
+
configRef.current = config;
|
|
42
|
+
const handlerRef = useRef(handler);
|
|
43
|
+
handlerRef.current = handler;
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const adaptor = getAdaptor();
|
|
46
|
+
const wrappedHandler = (args) => handlerRef.current(args);
|
|
47
|
+
return adaptor.registerViewTool({ ...configRef.current, name }, wrappedHandler);
|
|
48
|
+
}, [name]);
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=use-register-view-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-register-view-tool.js","sourceRoot":"","sources":["../../../src/web/hooks/use-register-view-tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAOjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAGjC,MAA8B,EAC9B,OAAgC,EAChC,EAAE;IACF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACxB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,cAAc,GAAuB,CAAC,IAAI,EAAE,EAAE,CAClD,UAAU,CAAC,OAAO,CAAC,IAA8C,CAAC,CAAC;QAErE,OAAO,OAAO,CAAC,gBAAgB,CAC7B,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAC9B,cAAc,CACf,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACb,CAAC,CAAC","sourcesContent":["import type { ZodRawShapeCompat } from \"@modelcontextprotocol/sdk/server/zod-compat.js\";\nimport { useEffect, useRef } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\nimport type {\n AnyViewToolHandler,\n ViewToolConfig,\n ViewToolHandler,\n} from \"../bridges/types.js\";\n\n/**\n * Register a tool the view exposes to the host and model — the MCP Apps\n * \"app-provided tools\" feature. A view tool runs *inside the view*: the host\n * discovers it via `tools/list` and invokes it via `tools/call`, and the\n * handler executes against the view's live state. It is the inverse of\n * {@link useCallTool} (which calls a server tool). Registered on mount, removed\n * on unmount; re-registered when `config.name` changes.\n *\n * MCP Apps only — on the Apps SDK (`window.openai`) runtime it is a no-op.\n *\n * @example\n * ```tsx\n * import * as z from \"zod\";\n * import { useRegisterViewTool } from \"skybridge/web\";\n *\n * useRegisterViewTool(\n * {\n * name: \"chess_make_move\",\n * description: \"Play a move in algebraic notation, e.g. 'e4' or 'Nf3'.\",\n * inputSchema: { san: z.string() },\n * annotations: { readOnlyHint: false },\n * },\n * ({ san }) => {\n * const move = game.move(san);\n * return {\n * content: [{ type: \"text\", text: move ? `Played ${move.san}` : \"Illegal move\" }],\n * structuredContent: { fen: game.fen() },\n * isError: !move,\n * };\n * },\n * );\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-register-view-tool\n */\nexport const useRegisterViewTool = <\n TInput extends ZodRawShapeCompat = ZodRawShapeCompat,\n>(\n config: ViewToolConfig<TInput>,\n handler: ViewToolHandler<TInput>,\n) => {\n const { name } = config;\n const configRef = useRef(config);\n configRef.current = config;\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n\n useEffect(() => {\n const adaptor = getAdaptor();\n const wrappedHandler: AnyViewToolHandler = (args) =>\n handlerRef.current(args as Parameters<ViewToolHandler<TInput>>[0]);\n\n return adaptor.registerViewTool(\n { ...configRef.current, name },\n wrappedHandler,\n );\n }, [name]);\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
|
-
*
|
|
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,7 +1,22 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { getAdaptor, useHostContext, } from "../bridges/index.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
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();
|
|
@@ -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
|
|
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"]}
|
|
@@ -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 {};
|