@botiverse/porthole 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/LICENSE +217 -0
  2. package/dist/bridge/bridge.d.ts +39 -0
  3. package/dist/bridge/bridge.js +198 -0
  4. package/dist/bridge/types.d.ts +83 -0
  5. package/dist/bridge/types.js +1 -0
  6. package/dist/compile/compile.d.ts +35 -0
  7. package/dist/compile/compile.js +30 -0
  8. package/dist/compile/policy.d.ts +55 -0
  9. package/dist/compile/policy.js +103 -0
  10. package/dist/config/capabilities.d.ts +154 -0
  11. package/dist/config/capabilities.js +63 -0
  12. package/dist/config/schema.d.ts +152 -0
  13. package/dist/config/schema.js +11 -0
  14. package/dist/config/validate.d.ts +22 -0
  15. package/dist/config/validate.js +261 -0
  16. package/dist/conformance/behavior.d.ts +2 -0
  17. package/dist/conformance/behavior.js +426 -0
  18. package/dist/conformance/index.d.ts +101 -0
  19. package/dist/conformance/index.js +4 -0
  20. package/dist/conformance/kit.d.ts +42 -0
  21. package/dist/conformance/kit.js +24 -0
  22. package/dist/conformance/oracles.d.ts +35 -0
  23. package/dist/conformance/oracles.js +77 -0
  24. package/dist/conformance/probes.d.ts +33 -0
  25. package/dist/conformance/probes.js +876 -0
  26. package/dist/conformance/served.d.ts +75 -0
  27. package/dist/conformance/served.js +335 -0
  28. package/dist/guide/examples.d.ts +70 -0
  29. package/dist/guide/examples.js +69 -0
  30. package/dist/guide/guide.d.ts +16 -0
  31. package/dist/guide/guide.js +265 -0
  32. package/dist/host/create-host.d.ts +8 -0
  33. package/dist/host/create-host.js +292 -0
  34. package/dist/host/locate.d.ts +8 -0
  35. package/dist/host/locate.js +16 -0
  36. package/dist/host/outcome.d.ts +24 -0
  37. package/dist/host/outcome.js +134 -0
  38. package/dist/host/types.d.ts +127 -0
  39. package/dist/host/types.js +1 -0
  40. package/dist/index.d.ts +25 -0
  41. package/dist/index.js +21 -0
  42. package/dist/isolation/served.d.ts +35 -0
  43. package/dist/isolation/served.js +117 -0
  44. package/dist/isolation/types.d.ts +96 -0
  45. package/dist/isolation/types.js +2 -0
  46. package/dist/mcp/adapter.d.ts +43 -0
  47. package/dist/mcp/adapter.js +327 -0
  48. package/dist/mcp/index.d.ts +27 -0
  49. package/dist/mcp/index.js +25 -0
  50. package/dist/mcp/proxy.d.ts +25 -0
  51. package/dist/mcp/proxy.js +106 -0
  52. package/dist/mcp/view.d.ts +31 -0
  53. package/dist/mcp/view.js +72 -0
  54. package/dist/platform/types.d.ts +51 -0
  55. package/dist/platform/types.js +1 -0
  56. package/dist/platform/web.d.ts +8 -0
  57. package/dist/platform/web.js +85 -0
  58. package/dist/presets/presets.d.ts +71 -0
  59. package/dist/presets/presets.js +140 -0
  60. package/dist/protocol/adapter.d.ts +73 -0
  61. package/dist/protocol/adapter.js +1 -0
  62. package/dist/protocol/jsonrpc.d.ts +43 -0
  63. package/dist/protocol/jsonrpc.js +50 -0
  64. package/dist/protocol/mcp-apps.d.ts +79 -0
  65. package/dist/protocol/mcp-apps.js +99 -0
  66. package/dist/protocol/shell-adapter.d.ts +5 -0
  67. package/dist/protocol/shell-adapter.js +228 -0
  68. package/dist/react/dialog.d.ts +20 -0
  69. package/dist/react/dialog.js +98 -0
  70. package/dist/react/frame.d.ts +91 -0
  71. package/dist/react/frame.js +255 -0
  72. package/dist/react/index.d.ts +13 -0
  73. package/dist/react/index.js +13 -0
  74. package/dist/react/live.d.ts +24 -0
  75. package/dist/react/live.js +57 -0
  76. package/dist/server/index.d.ts +27 -0
  77. package/dist/server/index.js +26 -0
  78. package/dist/server/keys.d.ts +19 -0
  79. package/dist/server/keys.js +78 -0
  80. package/dist/server/pages.d.ts +11 -0
  81. package/dist/server/pages.js +86 -0
  82. package/dist/server/proxy.d.ts +21 -0
  83. package/dist/server/proxy.js +107 -0
  84. package/dist/server/server.d.ts +7 -0
  85. package/dist/server/server.js +542 -0
  86. package/dist/server/tickets.d.ts +8 -0
  87. package/dist/server/tickets.js +28 -0
  88. package/dist/server/types.d.ts +97 -0
  89. package/dist/server/types.js +1 -0
  90. package/dist/shell/document.d.ts +29 -0
  91. package/dist/shell/document.js +85 -0
  92. package/dist/shell/plan.d.ts +37 -0
  93. package/dist/shell/plan.js +35 -0
  94. package/dist/shell/runtime.d.ts +28 -0
  95. package/dist/shell/runtime.js +848 -0
  96. package/dist/theme/variables.d.ts +73 -0
  97. package/dist/theme/variables.js +171 -0
  98. package/dist/transport/post-message.d.ts +9 -0
  99. package/dist/transport/post-message.js +38 -0
  100. package/dist/transport/types.d.ts +15 -0
  101. package/dist/transport/types.js +1 -0
  102. package/package.json +63 -1
  103. package/src/bridge/bridge.ts +278 -0
  104. package/src/bridge/types.ts +95 -0
  105. package/src/compile/compile.ts +68 -0
  106. package/src/compile/policy.ts +130 -0
  107. package/src/config/capabilities.ts +138 -0
  108. package/src/config/schema.ts +168 -0
  109. package/src/config/validate.ts +318 -0
  110. package/src/conformance/behavior.ts +485 -0
  111. package/src/conformance/index.ts +116 -0
  112. package/src/conformance/kit.ts +63 -0
  113. package/src/conformance/oracles.ts +126 -0
  114. package/src/conformance/probes.ts +1129 -0
  115. package/src/conformance/served.ts +501 -0
  116. package/src/guide/examples.ts +74 -0
  117. package/src/guide/guide.ts +430 -0
  118. package/src/host/create-host.ts +335 -0
  119. package/src/host/locate.ts +25 -0
  120. package/src/host/outcome.ts +159 -0
  121. package/src/host/types.ts +126 -0
  122. package/src/index.ts +128 -0
  123. package/src/isolation/served.ts +151 -0
  124. package/src/isolation/types.ts +91 -0
  125. package/src/mcp/adapter.ts +441 -0
  126. package/src/mcp/index.ts +50 -0
  127. package/src/mcp/proxy.ts +117 -0
  128. package/src/mcp/view.ts +97 -0
  129. package/src/platform/types.ts +55 -0
  130. package/src/platform/web.ts +91 -0
  131. package/src/presets/presets.ts +217 -0
  132. package/src/protocol/adapter.ts +67 -0
  133. package/src/protocol/jsonrpc.ts +83 -0
  134. package/src/protocol/mcp-apps.ts +138 -0
  135. package/src/protocol/shell-adapter.ts +261 -0
  136. package/src/react/dialog.tsx +158 -0
  137. package/src/react/frame.tsx +486 -0
  138. package/src/react/index.ts +20 -0
  139. package/src/react/live.ts +76 -0
  140. package/src/server/index.ts +37 -0
  141. package/src/server/keys.ts +100 -0
  142. package/src/server/pages.ts +117 -0
  143. package/src/server/proxy.ts +131 -0
  144. package/src/server/server.ts +657 -0
  145. package/src/server/tickets.ts +30 -0
  146. package/src/server/types.ts +104 -0
  147. package/src/shell/document.ts +121 -0
  148. package/src/shell/plan.ts +69 -0
  149. package/src/shell/runtime.ts +848 -0
  150. package/src/theme/variables.ts +190 -0
  151. package/src/transport/post-message.ts +45 -0
  152. package/src/transport/types.ts +16 -0
@@ -0,0 +1,228 @@
1
+ import { JSON_RPC_ERRORS, parseJsonRpc, } from "./jsonrpc.js";
2
+ import { MCP_APPS_METHODS as M, MCP_APPS_PROTOCOL_VERSION } from "./mcp-apps.js";
3
+ /**
4
+ * The core's adapter: the host side of the MCP Apps subset the shell speaks, which content using
5
+ * the official App SDK speaks too. Every message is checked before use; every power goes through
6
+ * the bridge. Content goes as `tool-input-partial` while it is written and `tool-input` once,
7
+ * after the view has initialized.
8
+ */
9
+ const LOG_ERRORS = new Set(["error", "critical", "alert", "emergency"]);
10
+ /** Requests that are capability calls, and how their params become the capability's args. */
11
+ const REQUESTS = {
12
+ [M.message]: {
13
+ capability: "sendPrompt",
14
+ args: (params) => ({
15
+ text: Array.isArray(params.content)
16
+ ? params.content
17
+ .map((part) => {
18
+ const block = part;
19
+ return block !== null &&
20
+ typeof block === "object" &&
21
+ block.type === "text" &&
22
+ typeof block.text === "string"
23
+ ? block.text
24
+ : "";
25
+ })
26
+ .join("")
27
+ : undefined,
28
+ }),
29
+ },
30
+ [M.openLink]: { capability: "openLink", args: (params) => ({ url: params.url }) },
31
+ [M.downloadFile]: {
32
+ capability: "downloadFile",
33
+ args: (params) => ({ contents: params.contents }),
34
+ },
35
+ [M.callTool]: {
36
+ capability: "callTool",
37
+ args: (params) => ({ name: params.name, arguments: params.arguments }),
38
+ },
39
+ [M.readResource]: { capability: "readData", args: (params) => ({ uri: params.uri }) },
40
+ [M.updateModelContext]: {
41
+ capability: "setModelContext",
42
+ args: (params) => ({ content: params.content, structuredContent: params.structuredContent }),
43
+ },
44
+ };
45
+ /** What a capability's outcome looks like to the view. */
46
+ function answer(capability, outcome) {
47
+ const flagged = capability === "sendPrompt" || capability === "openLink" || capability === "downloadFile";
48
+ if (!outcome.ok) {
49
+ return flagged
50
+ ? { result: { isError: true } }
51
+ : { error: { code: -32000, message: `Refused (${outcome.refusal})` } };
52
+ }
53
+ if (flagged) {
54
+ return { result: outcome.value === true ? {} : { isError: true } };
55
+ }
56
+ return { result: outcome.value ?? {} };
57
+ }
58
+ /** What the host offers, from the declared capabilities (MCP Apps `hostCapabilities`). */
59
+ export function hostCapabilities(declared) {
60
+ const offered = {};
61
+ if (declared("openLink")) {
62
+ offered.openLinks = {};
63
+ }
64
+ if (declared("sendPrompt")) {
65
+ offered.message = { text: {} };
66
+ }
67
+ if (declared("downloadFile")) {
68
+ offered.downloadFile = {};
69
+ }
70
+ if (declared("callTool")) {
71
+ offered.serverTools = {};
72
+ }
73
+ if (declared("readData")) {
74
+ offered.serverResources = {};
75
+ }
76
+ if (declared("setModelContext")) {
77
+ offered.updateModelContext = { text: {} };
78
+ }
79
+ if (declared("reportError")) {
80
+ offered.logging = {};
81
+ }
82
+ return offered;
83
+ }
84
+ export const shellAdapter = {
85
+ name: "porthole-shell",
86
+ connect(session) {
87
+ const { transport, bridge } = session;
88
+ let initialized = false;
89
+ let closed = false;
90
+ let drafted = null;
91
+ let content = null;
92
+ let sentWhole = false;
93
+ let nextId = 1;
94
+ const pending = new Map();
95
+ const respond = (id, body) => transport.send({ jsonrpc: "2.0", id, ...body });
96
+ const notify = (method, params) => transport.send({ jsonrpc: "2.0", method, params });
97
+ const flush = () => {
98
+ if (!initialized || closed || content === null || sentWhole) {
99
+ return;
100
+ }
101
+ if (content.partial) {
102
+ if (drafted !== content.html) {
103
+ drafted = content.html;
104
+ notify(M.toolInputPartial, { arguments: { html: content.html } });
105
+ }
106
+ return;
107
+ }
108
+ sentWhole = true;
109
+ notify(M.toolInput, { arguments: { html: content.html } });
110
+ };
111
+ const onRequest = async (request) => {
112
+ const params = request.params ?? {};
113
+ if (request.method === M.initialize) {
114
+ respond(request.id, {
115
+ result: {
116
+ protocolVersion: MCP_APPS_PROTOCOL_VERSION,
117
+ hostInfo: session.hostInfo,
118
+ hostCapabilities: hostCapabilities((name) => bridge.declared(name)),
119
+ hostContext: session.hostContext(),
120
+ },
121
+ });
122
+ return;
123
+ }
124
+ if (request.method === "ping") {
125
+ respond(request.id, { result: {} });
126
+ return;
127
+ }
128
+ if (request.method === M.requestDisplayMode) {
129
+ respond(request.id, { result: { mode: "inline" } });
130
+ return;
131
+ }
132
+ const mapped = REQUESTS[request.method];
133
+ if (mapped === undefined) {
134
+ respond(request.id, {
135
+ error: {
136
+ code: JSON_RPC_ERRORS.methodNotFound,
137
+ message: `${request.method} isn't offered here`,
138
+ },
139
+ });
140
+ return;
141
+ }
142
+ const outcome = await bridge.call(mapped.capability, mapped.args(params));
143
+ if (!closed) {
144
+ respond(request.id, answer(mapped.capability, outcome));
145
+ }
146
+ };
147
+ const onNotification = (method, params) => {
148
+ switch (method) {
149
+ case M.initialized:
150
+ if (!initialized) {
151
+ initialized = true;
152
+ session.emit({ type: "initialized" });
153
+ flush();
154
+ }
155
+ return;
156
+ case M.sizeChanged:
157
+ void bridge.call("resize", { height: params.height, width: params.width });
158
+ return;
159
+ case M.log:
160
+ if (LOG_ERRORS.has(String(params.level))) {
161
+ const data = params.data;
162
+ void bridge.call("reportError", {
163
+ message: typeof data === "string" ? data : JSON.stringify(data ?? null).slice(0, 2_000),
164
+ });
165
+ }
166
+ return;
167
+ case M.requestTeardown:
168
+ session.emit({ type: "teardown-requested" });
169
+ return;
170
+ default:
171
+ return;
172
+ }
173
+ };
174
+ const stop = transport.listen((raw) => {
175
+ const message = parseJsonRpc(raw);
176
+ if (message === null || closed) {
177
+ return;
178
+ }
179
+ if ("method" in message) {
180
+ if ("id" in message) {
181
+ void onRequest(message);
182
+ }
183
+ else {
184
+ onNotification(message.method, message.params ?? {});
185
+ }
186
+ return;
187
+ }
188
+ pending.get(message.id)?.(message);
189
+ pending.delete(message.id);
190
+ });
191
+ return {
192
+ setContent(html, options) {
193
+ if (sentWhole) {
194
+ return;
195
+ }
196
+ content = { html, partial: options?.partial === true };
197
+ flush();
198
+ },
199
+ hostContextChanged(context) {
200
+ if (initialized && !closed) {
201
+ notify(M.hostContextChanged, { ...context });
202
+ }
203
+ },
204
+ teardown() {
205
+ if (!initialized || closed) {
206
+ return Promise.resolve();
207
+ }
208
+ const id = `teardown-${nextId++}`;
209
+ return new Promise((resolve) => {
210
+ const timer = setTimeout(() => {
211
+ pending.delete(id);
212
+ resolve();
213
+ }, 500);
214
+ pending.set(id, () => {
215
+ clearTimeout(timer);
216
+ resolve();
217
+ });
218
+ transport.send({ jsonrpc: "2.0", id, method: M.resourceTeardown, params: {} });
219
+ });
220
+ },
221
+ close() {
222
+ closed = true;
223
+ stop();
224
+ pending.clear();
225
+ },
226
+ };
227
+ },
228
+ };
@@ -0,0 +1,20 @@
1
+ import { type ReactElement } from "react";
2
+ import type { ConfirmRequest } from "../bridge/types.ts";
3
+ /**
4
+ * The default confirmation dialog (P019): drawn by the host's page, outside every frame, as a
5
+ * modal (the top layer covers the frames, so content can't draw over it or click it). It shows
6
+ * the call's whole effect (the prompt's full text, the link's full URL), the display's title as
7
+ * the host named it, Cancel focused, and ignores presses on its accept button for a moment after
8
+ * it opens (a click meant for the content mustn't land on it). Escape cancels. A host replaces it
9
+ * by passing its own `confirm`; it styles it through the `porthole-confirm` class and data
10
+ * attributes.
11
+ */
12
+ /** How long the accept button ignores presses after the dialog opens. */
13
+ export declare const ARM_DELAY_MS = 500;
14
+ export interface ConfirmDialogProps {
15
+ /** The call to confirm, or null when nothing is asked. */
16
+ readonly request: ConfirmRequest | null;
17
+ readonly onAnswer: (accepted: boolean) => void;
18
+ readonly className?: string;
19
+ }
20
+ export declare function ConfirmDialog({ request, onAnswer, className }: ConfirmDialogProps): ReactElement;
@@ -0,0 +1,98 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useRef, useState } from "react";
3
+ /**
4
+ * The default confirmation dialog (P019): drawn by the host's page, outside every frame, as a
5
+ * modal (the top layer covers the frames, so content can't draw over it or click it). It shows
6
+ * the call's whole effect (the prompt's full text, the link's full URL), the display's title as
7
+ * the host named it, Cancel focused, and ignores presses on its accept button for a moment after
8
+ * it opens (a click meant for the content mustn't land on it). Escape cancels. A host replaces it
9
+ * by passing its own `confirm`; it styles it through the `porthole-confirm` class and data
10
+ * attributes.
11
+ */
12
+ /** How long the accept button ignores presses after the dialog opens. */
13
+ export const ARM_DELAY_MS = 500;
14
+ const json = (value) => JSON.stringify(value, null, 2) ?? "";
15
+ function words(request) {
16
+ switch (request.capability) {
17
+ case "sendPrompt":
18
+ return { heading: "Send this message?", accept: "Send", detail: request.args.text };
19
+ case "openLink": {
20
+ let host = "";
21
+ try {
22
+ host = new URL(request.args.url).host;
23
+ }
24
+ catch {
25
+ host = "";
26
+ }
27
+ return {
28
+ heading: "Open this link?",
29
+ accept: "Open link",
30
+ detail: request.args.url,
31
+ ...(host === "" ? {} : { note: `It opens ${host}.` }),
32
+ };
33
+ }
34
+ case "downloadFile":
35
+ return {
36
+ heading: "Download this file?",
37
+ accept: "Download",
38
+ detail: json(request.args.contents),
39
+ };
40
+ case "callTool":
41
+ return {
42
+ heading: "Run this tool?",
43
+ accept: "Run",
44
+ detail: `${request.args.name}\n${json(request.args.arguments)}`,
45
+ };
46
+ case "setModelContext":
47
+ return { heading: "Share this with the agent?", accept: "Share", detail: json(request.args) };
48
+ case "readData":
49
+ return { heading: "Let it read this?", accept: "Allow", detail: request.args.uri };
50
+ default:
51
+ return { heading: "Allow this?", accept: "Allow", detail: json(request.args) };
52
+ }
53
+ }
54
+ const STYLE = `.porthole-confirm{color-scheme:light dark;color:CanvasText;background:Canvas;border:1px solid color-mix(in srgb,CanvasText 18%,transparent);border-radius:12px;padding:20px;width:min(440px,calc(100vw - 32px));font:inherit;box-shadow:0 12px 40px rgb(0 0 0 / .28)}
55
+ .porthole-confirm::backdrop{background:rgb(0 0 0 / .4)}
56
+ .porthole-confirm h2{font-size:1.05em;margin:0 0 4px}
57
+ .porthole-confirm [data-porthole-source],.porthole-confirm [data-porthole-note]{opacity:.72;font-size:.9em;margin:0 0 12px}
58
+ .porthole-confirm [data-porthole-detail]{white-space:pre-wrap;overflow-wrap:anywhere;max-height:40vh;overflow:auto;padding:10px 12px;border-radius:8px;background:color-mix(in srgb,CanvasText 7%,transparent);margin:0 0 12px;font:inherit}
59
+ .porthole-confirm menu{display:flex;justify-content:flex-end;gap:8px;margin:16px 0 0;padding:0}
60
+ .porthole-confirm button{font:inherit;min-height:36px;padding:0 14px;border-radius:8px;border:1px solid color-mix(in srgb,CanvasText 25%,transparent);background:transparent;color:inherit;cursor:pointer}
61
+ .porthole-confirm button[data-porthole-accept]{background:Highlight;background:AccentColor;color:HighlightText;color:AccentColorText;border-color:transparent}`;
62
+ export function ConfirmDialog({ request, onAnswer, className }) {
63
+ const dialog = useRef(null);
64
+ const cancel = useRef(null);
65
+ // The request the accept button is armed for: a new one starts unarmed.
66
+ const [armedFor, setArmedFor] = useState(null);
67
+ const armed = request !== null && armedFor === request;
68
+ const heading = useId();
69
+ useEffect(() => {
70
+ const element = dialog.current;
71
+ if (element === null) {
72
+ return;
73
+ }
74
+ if (request === null) {
75
+ if (element.open) {
76
+ element.close();
77
+ }
78
+ return;
79
+ }
80
+ if (!element.open) {
81
+ element.showModal();
82
+ }
83
+ cancel.current?.focus();
84
+ const timer = setTimeout(() => setArmedFor(request), ARM_DELAY_MS);
85
+ return () => clearTimeout(timer);
86
+ }, [request]);
87
+ const said = request === null ? null : words(request);
88
+ return (_jsxs("dialog", { ref: dialog, className: className === undefined ? "porthole-confirm" : `porthole-confirm ${className}`, "aria-labelledby": heading, "data-porthole-confirm": request?.capability ?? "", onCancel: (event) => {
89
+ event.preventDefault();
90
+ if (request !== null) {
91
+ onAnswer(false);
92
+ }
93
+ }, children: [_jsx("style", { children: STYLE }), said === null || request === null ? null : (_jsxs(_Fragment, { children: [_jsx("h2", { id: heading, children: said.heading }), _jsx("p", { "data-porthole-source": "", children: `From “${request.display.title}”` }), _jsx("div", { "data-porthole-detail": "", children: said.detail }), said.note === undefined ? null : _jsx("p", { "data-porthole-note": "", children: said.note }), _jsxs("menu", { children: [_jsx("button", { type: "button", ref: cancel, autoFocus: true, "data-porthole-cancel": "", onClick: () => onAnswer(false), children: "Cancel" }), _jsx("button", { type: "button", "data-porthole-accept": "", "data-armed": String(armed), onClick: () => {
94
+ if (armed) {
95
+ onAnswer(true);
96
+ }
97
+ }, children: said.accept })] })] }))] }));
98
+ }
@@ -0,0 +1,91 @@
1
+ import { type CSSProperties, type ReactElement, type ReactNode } from "react";
2
+ import type { CapabilityHandlers, Confirm, Refusal } from "../bridge/types.ts";
3
+ import type { CapabilityName } from "../config/capabilities.ts";
4
+ import type { ColorScheme, PortholeConfig } from "../config/schema.ts";
5
+ import type { FrameOutcome } from "../host/types.ts";
6
+ import type { InstanceLocator, InstanceRef } from "../isolation/types.ts";
7
+ /** The height a display holds before it has reported its own. */
8
+ export declare const PLACEHOLDER_HEIGHT_PX = 96;
9
+ export interface PortholeProviderProps {
10
+ /**
11
+ * Who confirms gated calls for every frame below. Omitted: the default dialog (`ConfirmDialog`).
12
+ * Pass your own function to replace it.
13
+ */
14
+ readonly confirm?: Confirm;
15
+ /** How the host names itself in the handshake. */
16
+ readonly hostInfo?: {
17
+ readonly name: string;
18
+ readonly version: string;
19
+ };
20
+ /** Where domain and external frames load from (see `InstanceLocator`). */
21
+ readonly locator?: InstanceLocator;
22
+ /** A class for the default dialog, to restyle it. */
23
+ readonly dialogClassName?: string;
24
+ readonly children?: ReactNode;
25
+ }
26
+ export declare function PortholeProvider(props: PortholeProviderProps): ReactElement;
27
+ /** What a placeholder shows: a display not yet running, loading, or stopped for others. */
28
+ export interface PlaceholderProps {
29
+ readonly state: "waiting" | "loading" | "stopped";
30
+ readonly title: string;
31
+ /** Runs the display again (a stopped one), stopping another if the page is at its most. */
32
+ readonly run: () => void;
33
+ }
34
+ export interface PortholeFrameProps {
35
+ /** A preset's config. Keep it stable (module scope or memoized): a new one remounts the frame. */
36
+ readonly config: PortholeConfig;
37
+ /** The host's id for this display: what its height is remembered by, and calls name. */
38
+ readonly displayId: string;
39
+ /** The frame's accessible name, and the title its confirmations show (the host's words). */
40
+ readonly title: string;
41
+ /** The content so far (inline and artifact modes). */
42
+ readonly content?: string;
43
+ /** The content is still being written: drawn inert as it grows, run once when whole. */
44
+ readonly partial?: boolean;
45
+ /** A whole document to start with (an MCP Apps view). */
46
+ readonly document?: string;
47
+ /** Domain and external isolation: the instance to show. */
48
+ readonly instance?: InstanceRef;
49
+ readonly scheme: ColorScheme;
50
+ /** The host's live values for the config's theme variables (from its design tokens). */
51
+ readonly theme?: (scheme: ColorScheme) => Readonly<Record<string, string | null | undefined>>;
52
+ /** Handlers for the declared capabilities (the latest ones are called). */
53
+ readonly handlers?: CapabilityHandlers;
54
+ /** Who confirms this frame's gated calls, over the provider's. */
55
+ readonly confirm?: Confirm;
56
+ /** Bump to run the content again from the start. */
57
+ readonly generation?: number;
58
+ /** Run at once, without waiting to near the screen (it still counts toward the page's most). */
59
+ readonly eager?: boolean;
60
+ readonly onInitialized?: () => void;
61
+ readonly onResize?: (size: {
62
+ readonly height: number;
63
+ readonly width?: number;
64
+ }) => void;
65
+ readonly onError?: (message: string) => void;
66
+ /** The content navigated its frame away; it was started again (or stopped, after a few). */
67
+ readonly onNavigated?: () => void;
68
+ readonly onRefused?: (capability: CapabilityName, refusal: Refusal) => void;
69
+ /** Too many declines: the display stops asking until it runs again. */
70
+ readonly onMuted?: () => void;
71
+ /**
72
+ * How the frame's first document went: loaded, blocked (and why), or a timeout. A host shows its
73
+ * own fallback (a url-mode app "Open in a new tab") on anything but loaded.
74
+ */
75
+ readonly onOutcome?: (outcome: FrameOutcome) => void;
76
+ /**
77
+ * Other ids this display had (the call's id, before the display's own settled): its remembered
78
+ * height is found under them, for a frame that mounts again under its new id.
79
+ */
80
+ readonly aliases?: readonly string[];
81
+ /**
82
+ * Change it when the host's theme values change without a scheme flip (or after an async
83
+ * restyle): the running frame reads them again.
84
+ */
85
+ readonly themeKey?: string | number;
86
+ /** Draws the placeholder instead of the default one. */
87
+ readonly renderPlaceholder?: (props: PlaceholderProps) => ReactNode;
88
+ readonly className?: string;
89
+ readonly style?: CSSProperties;
90
+ }
91
+ export declare function PortholeFrame(props: PortholeFrameProps): ReactElement;