skybridge 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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-open-tunnel-browser.d.ts +6 -0
- package/dist/cli/use-open-tunnel-browser.js +19 -0
- package/dist/cli/use-open-tunnel-browser.js.map +1 -0
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.js +1 -16
- 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 +19 -1
- package/dist/commands/dev.js.map +1 -1
- 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/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/file-ref.d.ts +20 -0
- package/dist/server/file-ref.js +19 -0
- package/dist/server/file-ref.js.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.js +1 -0
- 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 +167 -0
- package/dist/server/server.js +151 -58
- package/dist/server/server.js.map +1 -1
- 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 +3 -1
- package/dist/web/bridges/apps-sdk/adaptor.js +5 -0
- 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/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 +3 -1
- package/dist/web/bridges/mcp-app/adaptor.js +9 -0
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.js +1 -0
- 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/types.d.ts +55 -1
- 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/hooks/index.d.ts +1 -0
- package/dist/web/hooks/index.js +1 -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-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-request-close.d.ts +14 -0
- package/dist/web/hooks/use-request-close.js +13 -0
- package/dist/web/hooks/use-request-close.js.map +1 -1
- 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 +17 -0
- package/dist/web/hooks/use-request-size.js +16 -0
- package/dist/web/hooks/use-request-size.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js +17 -0
- 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 +33 -0
- package/dist/web/hooks/use-tool-info.js +26 -0
- package/dist/web/hooks/use-tool-info.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 +26 -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 +5 -1
|
@@ -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"]}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
import type { RequestSizeOptions } from "../bridges/types.js";
|
|
2
|
+
/** Function that asks the host to resize the view, returned by {@link useRequestSize}. */
|
|
2
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
|
+
*/
|
|
3
20
|
export declare function useRequestSize(): RequestSizeFn;
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
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
|
+
*/
|
|
3
19
|
export function useRequestSize() {
|
|
4
20
|
const adaptor = getAdaptor();
|
|
5
21
|
const requestSize = useCallback((size) => adaptor.requestSize(size), [adaptor]);
|
|
@@ -1 +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;
|
|
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"]}
|
|
@@ -1,2 +1,19 @@
|
|
|
1
1
|
import { type SendFollowUpMessageOptions } from "../bridges/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Send a follow-up message to the LLM on behalf of the view, as if the user
|
|
4
|
+
* had sent it. Use to chain interactions from view UI (e.g. a button that
|
|
5
|
+
* triggers the next assistant turn).
|
|
6
|
+
*
|
|
7
|
+
* Pass `scrollToBottom: false` to keep the chat scroll position when the host
|
|
8
|
+
* posts the message. This option is Apps-SDK-only; it is silently ignored in
|
|
9
|
+
* the MCP Apps runtime.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const send = useSendFollowUpMessage();
|
|
14
|
+
* <button onClick={() => send("Summarize the last 5 results")}>Summarize</button>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @see https://docs.skybridge.tech/api-reference/use-send-follow-up-message
|
|
18
|
+
*/
|
|
2
19
|
export declare function useSendFollowUpMessage(): (prompt: string, options?: SendFollowUpMessageOptions) => Promise<void>;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { getAdaptor, } from "../bridges/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Send a follow-up message to the LLM on behalf of the view, as if the user
|
|
5
|
+
* had sent it. Use to chain interactions from view UI (e.g. a button that
|
|
6
|
+
* triggers the next assistant turn).
|
|
7
|
+
*
|
|
8
|
+
* Pass `scrollToBottom: false` to keep the chat scroll position when the host
|
|
9
|
+
* posts the message. This option is Apps-SDK-only; it is silently ignored in
|
|
10
|
+
* the MCP Apps runtime.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const send = useSendFollowUpMessage();
|
|
15
|
+
* <button onClick={() => send("Summarize the last 5 results")}>Summarize</button>
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @see https://docs.skybridge.tech/api-reference/use-send-follow-up-message
|
|
19
|
+
*/
|
|
3
20
|
export function useSendFollowUpMessage() {
|
|
4
21
|
const adaptor = getAdaptor();
|
|
5
22
|
const sendFollowUpMessage = useCallback((prompt, options) => adaptor.sendFollowUpMessage(prompt, options), [adaptor]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-send-follow-up-message.js","sourceRoot":"","sources":["../../../src/web/hooks/use-send-follow-up-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,UAAU,GAEX,MAAM,qBAAqB,CAAC;AAE7B,MAAM,UAAU,sBAAsB;IACpC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,MAAc,EAAE,OAAoC,EAAE,EAAE,CACvD,OAAO,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9C,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,mBAAmB,CAAC;AAC7B,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport {\n getAdaptor,\n type SendFollowUpMessageOptions,\n} from \"../bridges/index.js\";\n\nexport function useSendFollowUpMessage() {\n const adaptor = getAdaptor();\n const sendFollowUpMessage = useCallback(\n (prompt: string, options?: SendFollowUpMessageOptions) =>\n adaptor.sendFollowUpMessage(prompt, options),\n [adaptor],\n );\n\n return sendFollowUpMessage;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-send-follow-up-message.js","sourceRoot":"","sources":["../../../src/web/hooks/use-send-follow-up-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EACL,UAAU,GAEX,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB;IACpC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,MAAc,EAAE,OAAoC,EAAE,EAAE,CACvD,OAAO,CAAC,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9C,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,mBAAmB,CAAC;AAC7B,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport {\n getAdaptor,\n type SendFollowUpMessageOptions,\n} from \"../bridges/index.js\";\n\n/**\n * Send a follow-up message to the LLM on behalf of the view, as if the user\n * had sent it. Use to chain interactions from view UI (e.g. a button that\n * triggers the next assistant turn).\n *\n * Pass `scrollToBottom: false` to keep the chat scroll position when the host\n * posts the message. This option is Apps-SDK-only; it is silently ignored in\n * the MCP Apps runtime.\n *\n * @example\n * ```tsx\n * const send = useSendFollowUpMessage();\n * <button onClick={() => send(\"Summarize the last 5 results\")}>Summarize</button>\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-send-follow-up-message\n */\nexport function useSendFollowUpMessage() {\n const adaptor = getAdaptor();\n const sendFollowUpMessage = useCallback(\n (prompt: string, options?: SendFollowUpMessageOptions) =>\n adaptor.sendFollowUpMessage(prompt, options),\n [adaptor],\n );\n\n return sendFollowUpMessage;\n}\n"]}
|
|
@@ -1 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Override the target URL the host opens from its fullscreen "Open in <App>"
|
|
3
|
+
* affordance. If unset, the host opens the view's current iframe path.
|
|
4
|
+
*
|
|
5
|
+
* Currently Apps-SDK-only — calling this from MCP Apps throws.
|
|
6
|
+
*
|
|
7
|
+
* Call this once your view has enough context to construct the canonical URL
|
|
8
|
+
* (e.g. a permalink to the entity the user is currently viewing).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const setOpenInAppUrl = useSetOpenInAppUrl();
|
|
13
|
+
* useEffect(() => { setOpenInAppUrl(`https://example.com/orders/${orderId}`); }, [orderId]);
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @see https://docs.skybridge.tech/api-reference/use-set-open-in-app-url
|
|
17
|
+
*/
|
|
1
18
|
export declare function useSetOpenInAppUrl(): (href: string) => Promise<void>;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { getAdaptor } from "../bridges/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Override the target URL the host opens from its fullscreen "Open in <App>"
|
|
5
|
+
* affordance. If unset, the host opens the view's current iframe path.
|
|
6
|
+
*
|
|
7
|
+
* Currently Apps-SDK-only — calling this from MCP Apps throws.
|
|
8
|
+
*
|
|
9
|
+
* Call this once your view has enough context to construct the canonical URL
|
|
10
|
+
* (e.g. a permalink to the entity the user is currently viewing).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const setOpenInAppUrl = useSetOpenInAppUrl();
|
|
15
|
+
* useEffect(() => { setOpenInAppUrl(`https://example.com/orders/${orderId}`); }, [orderId]);
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @see https://docs.skybridge.tech/api-reference/use-set-open-in-app-url
|
|
19
|
+
*/
|
|
3
20
|
export function useSetOpenInAppUrl() {
|
|
4
21
|
const adaptor = getAdaptor();
|
|
5
22
|
const setOpenInAppUrl = useCallback((href) => adaptor.setOpenInAppUrl(href), [adaptor]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-set-open-in-app-url.js","sourceRoot":"","sources":["../../../src/web/hooks/use-set-open-in-app-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,EAC/C,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,eAAe,CAAC;AACzB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\n\nexport function useSetOpenInAppUrl() {\n const adaptor = getAdaptor();\n const setOpenInAppUrl = useCallback(\n (href: string) => adaptor.setOpenInAppUrl(href),\n [adaptor],\n );\n\n return setOpenInAppUrl;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-set-open-in-app-url.js","sourceRoot":"","sources":["../../../src/web/hooks/use-set-open-in-app-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,EAC/C,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,eAAe,CAAC;AACzB,CAAC","sourcesContent":["import { useCallback } from \"react\";\nimport { getAdaptor } from \"../bridges/index.js\";\n\n/**\n * Override the target URL the host opens from its fullscreen \"Open in <App>\"\n * affordance. If unset, the host opens the view's current iframe path.\n *\n * Currently Apps-SDK-only — calling this from MCP Apps throws.\n *\n * Call this once your view has enough context to construct the canonical URL\n * (e.g. a permalink to the entity the user is currently viewing).\n *\n * @example\n * ```tsx\n * const setOpenInAppUrl = useSetOpenInAppUrl();\n * useEffect(() => { setOpenInAppUrl(`https://example.com/orders/${orderId}`); }, [orderId]);\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-set-open-in-app-url\n */\nexport function useSetOpenInAppUrl() {\n const adaptor = getAdaptor();\n const setOpenInAppUrl = useCallback(\n (href: string) => adaptor.setOpenInAppUrl(href),\n [adaptor],\n );\n\n return setOpenInAppUrl;\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { UnknownObject } from "../types.js";
|
|
2
|
+
/** {@link useToolInfo} state before the tool has been invoked. */
|
|
2
3
|
export type ToolIdleState = {
|
|
3
4
|
status: "idle";
|
|
4
5
|
isIdle: true;
|
|
@@ -8,6 +9,7 @@ export type ToolIdleState = {
|
|
|
8
9
|
output: undefined;
|
|
9
10
|
responseMetadata: undefined;
|
|
10
11
|
};
|
|
12
|
+
/** {@link useToolInfo} state while the tool is executing — `input` is available, output is not yet. */
|
|
11
13
|
export type ToolPendingState<ToolInput extends UnknownObject> = {
|
|
12
14
|
status: "pending";
|
|
13
15
|
isIdle: false;
|
|
@@ -17,6 +19,7 @@ export type ToolPendingState<ToolInput extends UnknownObject> = {
|
|
|
17
19
|
output: undefined;
|
|
18
20
|
responseMetadata: undefined;
|
|
19
21
|
};
|
|
22
|
+
/** {@link useToolInfo} state once the tool returned — `input`, `output`, and `responseMetadata` are all available. */
|
|
20
23
|
export type ToolSuccessState<ToolInput extends UnknownObject, ToolOutput extends UnknownObject, ToolResponseMetadata extends UnknownObject> = {
|
|
21
24
|
status: "success";
|
|
22
25
|
isIdle: false;
|
|
@@ -26,11 +29,41 @@ export type ToolSuccessState<ToolInput extends UnknownObject, ToolOutput extends
|
|
|
26
29
|
output: ToolOutput;
|
|
27
30
|
responseMetadata: ToolResponseMetadata;
|
|
28
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Discriminated union describing the tool invocation that triggered the
|
|
34
|
+
* current view render. Use `isIdle` / `isPending` / `isSuccess` to narrow.
|
|
35
|
+
*/
|
|
29
36
|
export type ToolState<ToolInput extends UnknownObject, ToolOutput extends UnknownObject, ToolResponseMetadata extends UnknownObject> = ToolIdleState | ToolPendingState<ToolInput> | ToolSuccessState<ToolInput, ToolOutput, ToolResponseMetadata>;
|
|
30
37
|
type ToolSignature = {
|
|
31
38
|
input: UnknownObject;
|
|
32
39
|
output: UnknownObject;
|
|
33
40
|
responseMetadata: UnknownObject;
|
|
34
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* Access the tool invocation that produced the current view: its `input`,
|
|
44
|
+
* resulting `output`, and `responseMetadata`. The shape evolves as the tool
|
|
45
|
+
* runs (idle → pending → success), exposed through {@link ToolState}.
|
|
46
|
+
*
|
|
47
|
+
* For full input/output typing per tool name, prefer the typed `useToolInfo`
|
|
48
|
+
* returned by {@link generateHelpers} over the generic form.
|
|
49
|
+
*
|
|
50
|
+
* @typeParam TS - Optional partial shape `{ input, output, responseMetadata }`
|
|
51
|
+
* to refine each field's type. When omitted, each typed field resolves to
|
|
52
|
+
* `never` — pass an explicit shape or use the typed helper from
|
|
53
|
+
* {@link generateHelpers} to get usable types.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* const { isSuccess, input, output } = useToolInfo<{
|
|
58
|
+
* input: { query: string };
|
|
59
|
+
* output: { results: Result[] };
|
|
60
|
+
* }>();
|
|
61
|
+
*
|
|
62
|
+
* if (!isSuccess || !output) return <Skeleton />;
|
|
63
|
+
* return <Results items={output.results} />;
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @see https://docs.skybridge.tech/api-reference/use-tool-info
|
|
67
|
+
*/
|
|
35
68
|
export declare function useToolInfo<TS extends Partial<ToolSignature> = Record<string, never>>(): ToolState<UnknownObject & TS["input"], UnknownObject & TS["output"], UnknownObject & TS["responseMetadata"]>;
|
|
36
69
|
export {};
|
|
@@ -8,6 +8,32 @@ function deriveStatus(input, output, responseMetadata) {
|
|
|
8
8
|
}
|
|
9
9
|
return "success";
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Access the tool invocation that produced the current view: its `input`,
|
|
13
|
+
* resulting `output`, and `responseMetadata`. The shape evolves as the tool
|
|
14
|
+
* runs (idle → pending → success), exposed through {@link ToolState}.
|
|
15
|
+
*
|
|
16
|
+
* For full input/output typing per tool name, prefer the typed `useToolInfo`
|
|
17
|
+
* returned by {@link generateHelpers} over the generic form.
|
|
18
|
+
*
|
|
19
|
+
* @typeParam TS - Optional partial shape `{ input, output, responseMetadata }`
|
|
20
|
+
* to refine each field's type. When omitted, each typed field resolves to
|
|
21
|
+
* `never` — pass an explicit shape or use the typed helper from
|
|
22
|
+
* {@link generateHelpers} to get usable types.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const { isSuccess, input, output } = useToolInfo<{
|
|
27
|
+
* input: { query: string };
|
|
28
|
+
* output: { results: Result[] };
|
|
29
|
+
* }>();
|
|
30
|
+
*
|
|
31
|
+
* if (!isSuccess || !output) return <Skeleton />;
|
|
32
|
+
* return <Results items={output.results} />;
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @see https://docs.skybridge.tech/api-reference/use-tool-info
|
|
36
|
+
*/
|
|
11
37
|
export function useToolInfo() {
|
|
12
38
|
const input = useHostContext("toolInput");
|
|
13
39
|
const output = useHostContext("toolOutput");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-info.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-tool-info.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA2DrD,SAAS,YAAY,CACnB,KAAqC,EACrC,MAAsC,EACtC,gBAAgD;IAEhD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QACjD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,WAAW;IAGzB,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,cAAc,CAAC,sBAAsB,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAM7D,OAAO;QACL,KAAK;QACL,MAAM;QACN,MAAM,EAAE,MAAM,KAAK,MAAM;QACzB,SAAS,EAAE,MAAM,KAAK,SAAS;QAC/B,SAAS,EAAE,MAAM,KAAK,SAAS;QAC/B,MAAM;QACN,gBAAgB;KACqB,CAAC;AAC1C,CAAC","sourcesContent":["import { useHostContext } from \"../bridges/index.js\";\nimport type { UnknownObject } from \"../types.js\";\n\n/** {@link useToolInfo} state before the tool has been invoked. */\nexport type ToolIdleState = {\n status: \"idle\";\n isIdle: true;\n isPending: false;\n isSuccess: false;\n input: undefined;\n output: undefined;\n responseMetadata: undefined;\n};\n\n/** {@link useToolInfo} state while the tool is executing — `input` is available, output is not yet. */\nexport type ToolPendingState<ToolInput extends UnknownObject> = {\n status: \"pending\";\n isIdle: false;\n isPending: true;\n isSuccess: false;\n input: ToolInput;\n output: undefined;\n responseMetadata: undefined;\n};\n\n/** {@link useToolInfo} state once the tool returned — `input`, `output`, and `responseMetadata` are all available. */\nexport type ToolSuccessState<\n ToolInput extends UnknownObject,\n ToolOutput extends UnknownObject,\n ToolResponseMetadata extends UnknownObject,\n> = {\n status: \"success\";\n isIdle: false;\n isPending: false;\n isSuccess: true;\n input: ToolInput;\n output: ToolOutput;\n responseMetadata: ToolResponseMetadata;\n};\n\n/**\n * Discriminated union describing the tool invocation that triggered the\n * current view render. Use `isIdle` / `isPending` / `isSuccess` to narrow.\n */\nexport type ToolState<\n ToolInput extends UnknownObject,\n ToolOutput extends UnknownObject,\n ToolResponseMetadata extends UnknownObject,\n> =\n | ToolIdleState\n | ToolPendingState<ToolInput>\n | ToolSuccessState<ToolInput, ToolOutput, ToolResponseMetadata>;\n\ntype ToolSignature = {\n input: UnknownObject;\n output: UnknownObject;\n responseMetadata: UnknownObject;\n};\n\nfunction deriveStatus(\n input: Record<string, unknown> | null,\n output: Record<string, unknown> | null,\n responseMetadata: Record<string, unknown> | null,\n): \"idle\" | \"pending\" | \"success\" {\n if (input === null) {\n return \"idle\";\n }\n if (output === null && responseMetadata === null) {\n return \"pending\";\n }\n return \"success\";\n}\n\n/**\n * Access the tool invocation that produced the current view: its `input`,\n * resulting `output`, and `responseMetadata`. The shape evolves as the tool\n * runs (idle → pending → success), exposed through {@link ToolState}.\n *\n * For full input/output typing per tool name, prefer the typed `useToolInfo`\n * returned by {@link generateHelpers} over the generic form.\n *\n * @typeParam TS - Optional partial shape `{ input, output, responseMetadata }`\n * to refine each field's type. When omitted, each typed field resolves to\n * `never` — pass an explicit shape or use the typed helper from\n * {@link generateHelpers} to get usable types.\n *\n * @example\n * ```tsx\n * const { isSuccess, input, output } = useToolInfo<{\n * input: { query: string };\n * output: { results: Result[] };\n * }>();\n *\n * if (!isSuccess || !output) return <Skeleton />;\n * return <Results items={output.results} />;\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-tool-info\n */\nexport function useToolInfo<\n TS extends Partial<ToolSignature> = Record<string, never>,\n>() {\n const input = useHostContext(\"toolInput\");\n const output = useHostContext(\"toolOutput\");\n const responseMetadata = useHostContext(\"toolResponseMetadata\");\n\n const status = deriveStatus(input, output, responseMetadata);\n\n type Input = UnknownObject & TS[\"input\"];\n type Output = UnknownObject & TS[\"output\"];\n type Metadata = UnknownObject & TS[\"responseMetadata\"];\n\n return {\n input,\n status,\n isIdle: status === \"idle\",\n isPending: status === \"pending\",\n isSuccess: status === \"success\",\n output,\n responseMetadata,\n } as ToolState<Input, Output, Metadata>;\n}\n"]}
|
|
@@ -26,6 +26,8 @@ function normalizeLocale(locale) {
|
|
|
26
26
|
* // Access device type
|
|
27
27
|
* const isMobile = userAgent.device.type === "mobile";
|
|
28
28
|
* ```
|
|
29
|
+
*
|
|
30
|
+
* @see https://docs.skybridge.tech/api-reference/use-user
|
|
29
31
|
*/
|
|
30
32
|
export function useUser() {
|
|
31
33
|
const rawLocale = useHostContext("locale");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-user.js","sourceRoot":"","sources":["../../../src/web/hooks/use-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAOrE,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"use-user.js","sourceRoot":"","sources":["../../../src/web/hooks/use-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAOrE,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAE9C,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import { type UserAgent, useHostContext } from \"../bridges/index.js\";\n\nexport type UserState = {\n locale: string;\n userAgent: UserAgent;\n};\n\nconst DEFAULT_LOCALE = \"en-US\";\n\n/**\n * Normalizes a locale string to canonical BCP 47 format using {@link Intl.Locale}.\n *\n * Handles underscored identifiers returned by the ChatGPT mobile app (e.g. \"fr_FR\" → \"fr-FR\"),\n * incorrect casing (e.g. \"en-us\" → \"en-US\"), and complex subtags (e.g. \"zh_Hans_CN\" → \"zh-Hans-CN\").\n * Falls back to \"en-US\" if the locale is invalid.\n */\nfunction normalizeLocale(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, \"-\")).toString();\n } catch {\n return DEFAULT_LOCALE;\n }\n}\n\n/**\n * Hook for accessing session-stable user information.\n * These values are set once at initialization and do not change during the session.\n *\n * @example\n * ```tsx\n * const { locale, userAgent } = useUser();\n *\n * // Access device type\n * const isMobile = userAgent.device.type === \"mobile\";\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-user\n */\nexport function useUser(): UserState {\n const rawLocale = useHostContext(\"locale\");\n const userAgent = useHostContext(\"userAgent\");\n\n return { locale: normalizeLocale(rawLocale), userAgent };\n}\n"]}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { type SetStateAction } from "react";
|
|
2
2
|
import type { UnknownObject } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Persist a piece of UI state on the host, so it survives view remounts and
|
|
5
|
+
* is restored on subsequent renders of the same tool invocation.
|
|
6
|
+
*
|
|
7
|
+
* Returns a `[state, setState]` pair with the same ergonomics as
|
|
8
|
+
* `useState`. State is filtered to strip Skybridge-internal context fields
|
|
9
|
+
* (see {@link DataLLM}) before being returned to your component.
|
|
10
|
+
*
|
|
11
|
+
* Provide a `defaultState` (value or lazy initializer) to get a non-nullable
|
|
12
|
+
* tuple; omit it for `T | null`.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T - Shape of the persisted state. Must be a plain object.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const [filters, setFilters] = useViewState({ sort: "newest", page: 1 });
|
|
19
|
+
* setFilters((f) => ({ ...f, page: f.page + 1 }));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see https://docs.skybridge.tech/api-reference/use-view-state
|
|
23
|
+
*/
|
|
3
24
|
export declare function useViewState<T extends UnknownObject>(defaultState: T | (() => T)): readonly [T, (state: SetStateAction<T>) => void];
|
|
4
25
|
export declare function useViewState<T extends UnknownObject>(defaultState?: T | (() => T | null) | null): readonly [T | null, (state: SetStateAction<T | null>) => void];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-view-state.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-view-state.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA8B3E,MAAM,UAAU,YAAY,CAC1B,YAA0C;IAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAa,CAAC;IAEpE,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE;QACzD,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;YACjC,OAAO,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,OAAO,YAAY,KAAK,UAAU;YACvC,CAAC,CAAC,YAAY,EAAE;YAChB,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAE1B,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAA+B,EAAE,EAAE;QAClC,aAAa,CAAC,CAAC,SAAS,EAAE,EAAE;YAC1B,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACxE,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAE/C,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;YAED,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,CAAC,SAAS,EAAE,YAAY,CAAU,CAAC;AAC5C,CAAC","sourcesContent":["import { type SetStateAction, useCallback, useEffect, useState } from \"react\";\nimport { getAdaptor, useHostContext } from \"../bridges/index.js\";\nimport { filterViewContext, injectViewContext } from \"../helpers/state.js\";\nimport type { UnknownObject } from \"../types.js\";\n\n/**\n * Persist a piece of UI state on the host, so it survives view remounts and\n * is restored on subsequent renders of the same tool invocation.\n *\n * Returns a `[state, setState]` pair with the same ergonomics as\n * `useState`. State is filtered to strip Skybridge-internal context fields\n * (see {@link DataLLM}) before being returned to your component.\n *\n * Provide a `defaultState` (value or lazy initializer) to get a non-nullable\n * tuple; omit it for `T | null`.\n *\n * @typeParam T - Shape of the persisted state. Must be a plain object.\n *\n * @example\n * ```tsx\n * const [filters, setFilters] = useViewState({ sort: \"newest\", page: 1 });\n * setFilters((f) => ({ ...f, page: f.page + 1 }));\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-view-state\n */\nexport function useViewState<T extends UnknownObject>(\n defaultState: T | (() => T),\n): readonly [T, (state: SetStateAction<T>) => void];\nexport function useViewState<T extends UnknownObject>(\n defaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void];\nexport function useViewState<T extends UnknownObject>(\n defaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void] {\n const adaptor = getAdaptor();\n const viewStateFromBridge = useHostContext(\"viewState\") as T | null;\n\n const [viewState, _setViewState] = useState<T | null>(() => {\n if (viewStateFromBridge !== null) {\n return filterViewContext(viewStateFromBridge);\n }\n\n return typeof defaultState === \"function\"\n ? defaultState()\n : (defaultState ?? null);\n });\n\n useEffect(() => {\n if (viewStateFromBridge !== null) {\n _setViewState(filterViewContext(viewStateFromBridge));\n }\n }, [viewStateFromBridge]);\n\n const setViewState = useCallback(\n (state: SetStateAction<T | null>) => {\n _setViewState((prevState) => {\n const newState = typeof state === \"function\" ? state(prevState) : state;\n const stateToSet = injectViewContext(newState);\n\n if (stateToSet !== null) {\n adaptor.setViewState(stateToSet);\n }\n\n return filterViewContext(stateToSet);\n });\n },\n [adaptor],\n );\n\n return [viewState, setViewState] as const;\n}\n"]}
|
package/dist/web/mount-view.d.ts
CHANGED
|
@@ -1 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a view's root React component into `#root`. Each view file's entry
|
|
3
|
+
* point should call this exactly once.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the component in `StrictMode`, applies host-specific providers
|
|
6
|
+
* automatically (e.g. modal support for MCP Apps), and installs the dev-mode
|
|
7
|
+
* logging proxy for `window.openai` calls.
|
|
8
|
+
*
|
|
9
|
+
* @param component - Your root React element (already constructed, e.g. `<App />`).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* // src/views/search.tsx
|
|
14
|
+
* import { mountView } from "skybridge/web";
|
|
15
|
+
* import { App } from "./App";
|
|
16
|
+
*
|
|
17
|
+
* mountView(<App />);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
1
20
|
export declare const mountView: (component: React.ReactNode) => void;
|
package/dist/web/mount-view.js
CHANGED
|
@@ -3,6 +3,25 @@ import { createElement, StrictMode } from "react";
|
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
import { installOpenAILoggingProxy } from "./proxy.js";
|
|
5
5
|
let rootInstance = null;
|
|
6
|
+
/**
|
|
7
|
+
* Mount a view's root React component into `#root`. Each view file's entry
|
|
8
|
+
* point should call this exactly once.
|
|
9
|
+
*
|
|
10
|
+
* Wraps the component in `StrictMode`, applies host-specific providers
|
|
11
|
+
* automatically (e.g. modal support for MCP Apps), and installs the dev-mode
|
|
12
|
+
* logging proxy for `window.openai` calls.
|
|
13
|
+
*
|
|
14
|
+
* @param component - Your root React element (already constructed, e.g. `<App />`).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* // src/views/search.tsx
|
|
19
|
+
* import { mountView } from "skybridge/web";
|
|
20
|
+
* import { App } from "./App";
|
|
21
|
+
*
|
|
22
|
+
* mountView(<App />);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
6
25
|
export const mountView = (component) => {
|
|
7
26
|
const rootElement = document.getElementById("root");
|
|
8
27
|
if (!rootElement) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-view.js","sourceRoot":"","sources":["../../src/web/mount-view.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,IAAI,YAAY,GAAgB,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAA0B,EAAE,EAAE;IACtD,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACxB,yBAAyB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;IAE5C,CAAC,KAAK,IAAI,EAAE;QACV,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;YACzE,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,EAAE,CAAC;AACP,CAAC,CAAC","sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { createElement, StrictMode } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { installOpenAILoggingProxy } from \"./proxy.js\";\n\nlet rootInstance: Root | null = null;\n\nexport const mountView = (component: React.ReactNode) => {\n const rootElement = document.getElementById(\"root\");\n if (!rootElement) {\n throw new Error(\"Root element not found\");\n }\n\n if (!rootInstance) {\n rootInstance = createRoot(rootElement);\n }\n\n if (import.meta.env.DEV) {\n installOpenAILoggingProxy();\n }\n\n const hostType = window.skybridge?.hostType;\n\n (async () => {\n let app = component;\n if (hostType === \"mcp-app\") {\n const { ModalProvider } = await import(\"./components/modal-provider.js\");\n app = createElement(ModalProvider, null, component);\n }\n rootInstance.render(createElement(StrictMode, null, app));\n })();\n};\n"]}
|
|
1
|
+
{"version":3,"file":"mount-view.js","sourceRoot":"","sources":["../../src/web/mount-view.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,IAAI,YAAY,GAAgB,IAAI,CAAC;AAErC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAA0B,EAAE,EAAE;IACtD,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACxB,yBAAyB,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;IAE5C,CAAC,KAAK,IAAI,EAAE;QACV,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;YACzE,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,EAAE,CAAC;AACP,CAAC,CAAC","sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { createElement, StrictMode } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { installOpenAILoggingProxy } from \"./proxy.js\";\n\nlet rootInstance: Root | null = null;\n\n/**\n * Mount a view's root React component into `#root`. Each view file's entry\n * point should call this exactly once.\n *\n * Wraps the component in `StrictMode`, applies host-specific providers\n * automatically (e.g. modal support for MCP Apps), and installs the dev-mode\n * logging proxy for `window.openai` calls.\n *\n * @param component - Your root React element (already constructed, e.g. `<App />`).\n *\n * @example\n * ```tsx\n * // src/views/search.tsx\n * import { mountView } from \"skybridge/web\";\n * import { App } from \"./App\";\n *\n * mountView(<App />);\n * ```\n */\nexport const mountView = (component: React.ReactNode) => {\n const rootElement = document.getElementById(\"root\");\n if (!rootElement) {\n throw new Error(\"Root element not found\");\n }\n\n if (!rootInstance) {\n rootInstance = createRoot(rootElement);\n }\n\n if (import.meta.env.DEV) {\n installOpenAILoggingProxy();\n }\n\n const hostType = window.skybridge?.hostType;\n\n (async () => {\n let app = component;\n if (hostType === \"mcp-app\") {\n const { ModalProvider } = await import(\"./components/modal-provider.js\");\n app = createElement(ModalProvider, null, component);\n }\n rootInstance.render(createElement(StrictMode, null, app));\n })();\n};\n"]}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
/** Options for the {@link skybridge} Vite plugin. */
|
|
2
3
|
export interface SkybridgePluginOptions {
|
|
4
|
+
/** Directory scanned for view modules. Defaults to `"src/views"`. */
|
|
3
5
|
viewsDir?: string;
|
|
4
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Vite plugin that wires a Skybridge project's view files into Vite.
|
|
9
|
+
*
|
|
10
|
+
* For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the
|
|
11
|
+
* plugin:
|
|
12
|
+
* - exposes a virtual entry that calls {@link mountView} with the view's
|
|
13
|
+
* default export,
|
|
14
|
+
* - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so
|
|
15
|
+
* {@link ViewName} narrows to the actual view names,
|
|
16
|
+
* - rewrites `<DataLLM>` JSX so the host can extract its content,
|
|
17
|
+
* - warns in dev if a view file is missing a default export.
|
|
18
|
+
*
|
|
19
|
+
* Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* // vite.config.ts
|
|
24
|
+
* import { defineConfig } from "vite";
|
|
25
|
+
* import react from "@vitejs/plugin-react";
|
|
26
|
+
* import { skybridge } from "skybridge/vite";
|
|
27
|
+
*
|
|
28
|
+
* export default defineConfig({
|
|
29
|
+
* plugins: [react(), skybridge({ viewsDir: "src/views" })],
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
5
33
|
export declare function skybridge(options?: SkybridgePluginOptions): Plugin;
|
|
@@ -17,6 +17,32 @@ function getViewEntryPattern(viewsDir) {
|
|
|
17
17
|
const escaped = viewsDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18
18
|
return new RegExp(`${escaped}\\/(?:[^/]+\\.(?:jsx|tsx)|[^/]+\\/index\\.(?:tsx|jsx))(?:\\?.*)?$`);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Vite plugin that wires a Skybridge project's view files into Vite.
|
|
22
|
+
*
|
|
23
|
+
* For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the
|
|
24
|
+
* plugin:
|
|
25
|
+
* - exposes a virtual entry that calls {@link mountView} with the view's
|
|
26
|
+
* default export,
|
|
27
|
+
* - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so
|
|
28
|
+
* {@link ViewName} narrows to the actual view names,
|
|
29
|
+
* - rewrites `<DataLLM>` JSX so the host can extract its content,
|
|
30
|
+
* - warns in dev if a view file is missing a default export.
|
|
31
|
+
*
|
|
32
|
+
* Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* // vite.config.ts
|
|
37
|
+
* import { defineConfig } from "vite";
|
|
38
|
+
* import react from "@vitejs/plugin-react";
|
|
39
|
+
* import { skybridge } from "skybridge/vite";
|
|
40
|
+
*
|
|
41
|
+
* export default defineConfig({
|
|
42
|
+
* plugins: [react(), skybridge({ viewsDir: "src/views" })],
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
20
46
|
export function skybridge(options) {
|
|
21
47
|
const rawViewsDir = options?.viewsDir ?? "src/views";
|
|
22
48
|
let resolvedViewsDir;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n assertUniqueViewNames,\n type DiscoveredView,\n discoverViewsSync,\n scanViewsSync,\n writeViewsDts,\n} from \"./scan-views.js\";\nimport { transform as dataLlmTransform } from \"./transform-data-llm.js\";\nimport { hasDefaultExport } from \"./validate-view.js\";\n\nconst VIRTUAL_PREFIX = \"/_skybridge/view/\";\nconst VIRTUAL_MODULE_PREFIX = \"\\0skybridge:view:\";\n\nexport interface SkybridgePluginOptions {\n viewsDir?: string;\n}\n\nfunction buildVirtualEntry(viewFilePath: string): string {\n const normalized = viewFilePath.replace(/\\\\/g, \"/\");\n return [\n `import { mountView } from \"skybridge/web\";`,\n `import Component from \"${normalized}\";`,\n `import { createElement } from \"react\";`,\n `mountView(createElement(Component));`,\n ].join(\"\\n\");\n}\n\nfunction getViewEntryPattern(viewsDir: string): RegExp {\n const escaped = viewsDir.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(\n `${escaped}\\\\/(?:[^/]+\\\\.(?:jsx|tsx)|[^/]+\\\\/index\\\\.(?:tsx|jsx))(?:\\\\?.*)?$`,\n );\n}\n\nexport function skybridge(options?: SkybridgePluginOptions): Plugin {\n const rawViewsDir = options?.viewsDir ?? \"src/views\";\n let resolvedViewsDir: string;\n let projectRoot: string;\n let viewMap = new Map<string, DiscoveredView>();\n let viewEntryPattern: RegExp;\n\n return {\n name: \"skybridge\",\n enforce: \"pre\",\n // Read by `skybridge build` to resolve viewsDir before `tsc -b` runs.\n api: { viewsDir: rawViewsDir },\n\n config(config) {\n projectRoot = config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(projectRoot, rawViewsDir);\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n\n const views = discoverViewsSync(resolvedViewsDir);\n viewMap = new Map(views.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, views);\n\n const input: Record<string, string> = {};\n for (const view of views) {\n input[view.name] = `${VIRTUAL_PREFIX}${view.name}`;\n }\n\n return {\n base: \"/assets\",\n // Fixes \"Invalid hook call\" on createStore by forcing a single\n // copy of React. Under pnpm's isolated node_modules, zustand\n // inside `skybridge` resolves React from skybridge's own\n // dependencies while the host app loads its own copy\n resolve: {\n dedupe: [\"react\", \"react-dom\"],\n },\n build: {\n outDir: \"dist/assets\",\n emptyOutDir: true,\n manifest: true,\n minify: true,\n cssCodeSplit: false,\n rollupOptions: {\n input,\n },\n },\n // Pre-bundle view deps at startup so the first tool invocation\n // doesn't hit Vite's on-demand re-optimization path (which sends\n // `full-reload` over HMR — in our iframe flow the parent host\n // can't honour a reload, and the view silently never mounts).\n optimizeDeps: {\n // Scan view files so transitive user deps (zod, tailwind, etc.)\n // get pre-bundled at startup.\n entries: [\n `${resolvedViewsDir}/*.{tsx,jsx}`,\n `${resolvedViewsDir}/*/index.{tsx,jsx}`,\n ],\n include: [\"react\", \"react-dom/client\", \"react/jsx-runtime\"],\n exclude: [\"skybridge/web\"],\n },\n experimental: {\n renderBuiltUrl: (filename) => {\n return {\n runtime: `window.skybridge.serverUrl + \"/assets/${filename}\"`,\n };\n },\n },\n };\n },\n\n resolveId(id) {\n if (id.startsWith(VIRTUAL_PREFIX)) {\n const name = id.slice(VIRTUAL_PREFIX.length);\n if (viewMap.has(name)) {\n return `${VIRTUAL_MODULE_PREFIX}${name}`;\n }\n }\n return null;\n },\n\n load(id) {\n if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {\n const name = id.slice(VIRTUAL_MODULE_PREFIX.length);\n const view = viewMap.get(name);\n if (view) {\n return buildVirtualEntry(view.filePath);\n }\n }\n return null;\n },\n\n configureServer(server: ViteDevServer) {\n if (!resolvedViewsDir) {\n const root = server.config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(root, rawViewsDir);\n projectRoot = root;\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n }\n\n server.watcher.add(resolvedViewsDir);\n // Track which view files we've already warned about so a rescan\n // triggered by an unrelated edit doesn't re-emit the same warning.\n let knownInvalid = new Set<string>();\n const rescan = () => {\n try {\n // Surface broken view files. Without this, files lacking a\n // default export are silently dropped from the input and the\n // user has no idea why their widget never mounts.\n const { valid, invalid } = scanViewsSync(resolvedViewsDir);\n const nextInvalid = new Set(invalid.map((v) => v.filePath));\n\n for (const filePath of nextInvalid) {\n if (!knownInvalid.has(filePath)) {\n server.config.logger.warn(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" is missing a default export — it won't be served until fixed.`,\n );\n }\n }\n for (const filePath of knownInvalid) {\n if (!nextInvalid.has(filePath)) {\n server.config.logger.info(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" resolved.`,\n );\n }\n }\n knownInvalid = nextInvalid;\n\n assertUniqueViewNames(valid);\n viewMap = new Map(valid.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, valid);\n } catch (err) {\n // assertUniqueViewNames throws on duplicate view names. Catch so\n // chokidar's listener chain doesn't surface it as unhandled and\n // crash the dev server — previous viewMap stays active until\n // the user fixes the conflict.\n const message = err instanceof Error ? err.message : String(err);\n server.config.logger.error(\n `[skybridge] view rescan failed: ${message}`,\n );\n }\n };\n\n // Initial scan emits warnings for broken files that exist at startup.\n rescan();\n server.watcher.on(\"add\", rescan);\n server.watcher.on(\"change\", rescan);\n server.watcher.on(\"unlink\", rescan);\n },\n\n async transform(code, id) {\n if (viewEntryPattern?.test(id) && !hasDefaultExport(code, id)) {\n this.warn(\n `View file \"${id.split(\"/\").pop()}\" is missing a default export.`,\n );\n }\n\n return await dataLlmTransform(code, id);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n assertUniqueViewNames,\n type DiscoveredView,\n discoverViewsSync,\n scanViewsSync,\n writeViewsDts,\n} from \"./scan-views.js\";\nimport { transform as dataLlmTransform } from \"./transform-data-llm.js\";\nimport { hasDefaultExport } from \"./validate-view.js\";\n\nconst VIRTUAL_PREFIX = \"/_skybridge/view/\";\nconst VIRTUAL_MODULE_PREFIX = \"\\0skybridge:view:\";\n\n/** Options for the {@link skybridge} Vite plugin. */\nexport interface SkybridgePluginOptions {\n /** Directory scanned for view modules. Defaults to `\"src/views\"`. */\n viewsDir?: string;\n}\n\nfunction buildVirtualEntry(viewFilePath: string): string {\n const normalized = viewFilePath.replace(/\\\\/g, \"/\");\n return [\n `import { mountView } from \"skybridge/web\";`,\n `import Component from \"${normalized}\";`,\n `import { createElement } from \"react\";`,\n `mountView(createElement(Component));`,\n ].join(\"\\n\");\n}\n\nfunction getViewEntryPattern(viewsDir: string): RegExp {\n const escaped = viewsDir.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(\n `${escaped}\\\\/(?:[^/]+\\\\.(?:jsx|tsx)|[^/]+\\\\/index\\\\.(?:tsx|jsx))(?:\\\\?.*)?$`,\n );\n}\n\n/**\n * Vite plugin that wires a Skybridge project's view files into Vite.\n *\n * For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the\n * plugin:\n * - exposes a virtual entry that calls {@link mountView} with the view's\n * default export,\n * - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so\n * {@link ViewName} narrows to the actual view names,\n * - rewrites `<DataLLM>` JSX so the host can extract its content,\n * - warns in dev if a view file is missing a default export.\n *\n * Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from \"vite\";\n * import react from \"@vitejs/plugin-react\";\n * import { skybridge } from \"skybridge/vite\";\n *\n * export default defineConfig({\n * plugins: [react(), skybridge({ viewsDir: \"src/views\" })],\n * });\n * ```\n */\nexport function skybridge(options?: SkybridgePluginOptions): Plugin {\n const rawViewsDir = options?.viewsDir ?? \"src/views\";\n let resolvedViewsDir: string;\n let projectRoot: string;\n let viewMap = new Map<string, DiscoveredView>();\n let viewEntryPattern: RegExp;\n\n return {\n name: \"skybridge\",\n enforce: \"pre\",\n // Read by `skybridge build` to resolve viewsDir before `tsc -b` runs.\n api: { viewsDir: rawViewsDir },\n\n config(config) {\n projectRoot = config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(projectRoot, rawViewsDir);\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n\n const views = discoverViewsSync(resolvedViewsDir);\n viewMap = new Map(views.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, views);\n\n const input: Record<string, string> = {};\n for (const view of views) {\n input[view.name] = `${VIRTUAL_PREFIX}${view.name}`;\n }\n\n return {\n base: \"/assets\",\n // Fixes \"Invalid hook call\" on createStore by forcing a single\n // copy of React. Under pnpm's isolated node_modules, zustand\n // inside `skybridge` resolves React from skybridge's own\n // dependencies while the host app loads its own copy\n resolve: {\n dedupe: [\"react\", \"react-dom\"],\n },\n build: {\n outDir: \"dist/assets\",\n emptyOutDir: true,\n manifest: true,\n minify: true,\n cssCodeSplit: false,\n rollupOptions: {\n input,\n },\n },\n // Pre-bundle view deps at startup so the first tool invocation\n // doesn't hit Vite's on-demand re-optimization path (which sends\n // `full-reload` over HMR — in our iframe flow the parent host\n // can't honour a reload, and the view silently never mounts).\n optimizeDeps: {\n // Scan view files so transitive user deps (zod, tailwind, etc.)\n // get pre-bundled at startup.\n entries: [\n `${resolvedViewsDir}/*.{tsx,jsx}`,\n `${resolvedViewsDir}/*/index.{tsx,jsx}`,\n ],\n include: [\"react\", \"react-dom/client\", \"react/jsx-runtime\"],\n exclude: [\"skybridge/web\"],\n },\n experimental: {\n renderBuiltUrl: (filename) => {\n return {\n runtime: `window.skybridge.serverUrl + \"/assets/${filename}\"`,\n };\n },\n },\n };\n },\n\n resolveId(id) {\n if (id.startsWith(VIRTUAL_PREFIX)) {\n const name = id.slice(VIRTUAL_PREFIX.length);\n if (viewMap.has(name)) {\n return `${VIRTUAL_MODULE_PREFIX}${name}`;\n }\n }\n return null;\n },\n\n load(id) {\n if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {\n const name = id.slice(VIRTUAL_MODULE_PREFIX.length);\n const view = viewMap.get(name);\n if (view) {\n return buildVirtualEntry(view.filePath);\n }\n }\n return null;\n },\n\n configureServer(server: ViteDevServer) {\n if (!resolvedViewsDir) {\n const root = server.config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(root, rawViewsDir);\n projectRoot = root;\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n }\n\n server.watcher.add(resolvedViewsDir);\n // Track which view files we've already warned about so a rescan\n // triggered by an unrelated edit doesn't re-emit the same warning.\n let knownInvalid = new Set<string>();\n const rescan = () => {\n try {\n // Surface broken view files. Without this, files lacking a\n // default export are silently dropped from the input and the\n // user has no idea why their widget never mounts.\n const { valid, invalid } = scanViewsSync(resolvedViewsDir);\n const nextInvalid = new Set(invalid.map((v) => v.filePath));\n\n for (const filePath of nextInvalid) {\n if (!knownInvalid.has(filePath)) {\n server.config.logger.warn(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" is missing a default export — it won't be served until fixed.`,\n );\n }\n }\n for (const filePath of knownInvalid) {\n if (!nextInvalid.has(filePath)) {\n server.config.logger.info(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" resolved.`,\n );\n }\n }\n knownInvalid = nextInvalid;\n\n assertUniqueViewNames(valid);\n viewMap = new Map(valid.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, valid);\n } catch (err) {\n // assertUniqueViewNames throws on duplicate view names. Catch so\n // chokidar's listener chain doesn't surface it as unhandled and\n // crash the dev server — previous viewMap stays active until\n // the user fixes the conflict.\n const message = err instanceof Error ? err.message : String(err);\n server.config.logger.error(\n `[skybridge] view rescan failed: ${message}`,\n );\n }\n };\n\n // Initial scan emits warnings for broken files that exist at startup.\n rescan();\n server.watcher.on(\"add\", rescan);\n server.watcher.on(\"change\", rescan);\n server.watcher.on(\"unlink\", rescan);\n },\n\n async transform(code, id) {\n if (viewEntryPattern?.test(id) && !hasDefaultExport(code, id)) {\n this.warn(\n `View file \"${id.split(\"/\").pop()}\" is missing a default export.`,\n );\n }\n\n return await dataLlmTransform(code, id);\n },\n };\n}\n"]}
|