@mcp-b/react-components 0.9.0 → 0.11.0
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/{ModelSelector-9G_-GooI.js → ModelSelector-DXtpcRmT.js} +185 -2
- package/dist/components/AgentConnection.d.ts +3 -14
- package/dist/components/AgentConnection.js +3 -91
- package/dist/components/BrowserRunPanel.d.ts +46 -0
- package/dist/components/BrowserRunPanel.js +123 -0
- package/dist/components/ChatGptSignInScreen.js +1 -1
- package/dist/components/McpServerConnections.d.ts +41 -0
- package/dist/components/McpServerConnections.js +188 -0
- package/dist/components/ModelSelector.d.ts +47 -1
- package/dist/components/ModelSelector.js +2 -2
- package/dist/styles/agent-connection.css +0 -166
- package/dist/styles/browser-run-panel.css +178 -0
- package/dist/styles/index.css +2 -0
- package/dist/styles/mcp-server-connections.css +145 -0
- package/dist/utils/mcp-server-catalog.d.ts +2 -3
- package/package.json +4 -3
|
@@ -3,7 +3,7 @@ import { A as SearchIcon, o as ChevronDownIcon } from "./icons-D2Q5n5C1.js";
|
|
|
3
3
|
import { Button } from "./components/Button.js";
|
|
4
4
|
import { Dialog, DialogBackdrop, DialogPopup, DialogPortal, DialogTitle, DialogTrigger } from "./components/Dialog.js";
|
|
5
5
|
import * as React from "react";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/_internal/provider-logos.tsx
|
|
8
8
|
function Svg({ children, size = 16, ...props }) {
|
|
9
9
|
return /* @__PURE__ */ jsx("svg", {
|
|
@@ -77,6 +77,128 @@ function ProviderLogo({ provider, ...props }) {
|
|
|
77
77
|
//#endregion
|
|
78
78
|
//#region src/components/ModelSelector.tsx
|
|
79
79
|
const ModelSelectorContext = React.createContext(null);
|
|
80
|
+
const cloudflareModelProviderLabels = {
|
|
81
|
+
ai4bharat: "AI4Bharat",
|
|
82
|
+
aisingapore: "AI Singapore",
|
|
83
|
+
alibaba: "Alibaba",
|
|
84
|
+
anthropic: "Anthropic",
|
|
85
|
+
baai: "BAAI",
|
|
86
|
+
cohere: "Cohere",
|
|
87
|
+
deepseek: "DeepSeek",
|
|
88
|
+
"deepseek-ai": "DeepSeek",
|
|
89
|
+
google: "Google",
|
|
90
|
+
"google-vertex": "Google Vertex",
|
|
91
|
+
groq: "Groq",
|
|
92
|
+
ibm: "IBM",
|
|
93
|
+
"ibm-granite": "IBM",
|
|
94
|
+
meta: "Meta",
|
|
95
|
+
"meta-llama": "Meta",
|
|
96
|
+
microsoft: "Microsoft",
|
|
97
|
+
mistral: "Mistral AI",
|
|
98
|
+
mistralai: "Mistral AI",
|
|
99
|
+
minimax: "MiniMax",
|
|
100
|
+
moonshotai: "Moonshot AI",
|
|
101
|
+
nvidia: "NVIDIA",
|
|
102
|
+
openai: "OpenAI",
|
|
103
|
+
openrouter: "OpenRouter",
|
|
104
|
+
perplexity: "Perplexity",
|
|
105
|
+
qwen: "Qwen",
|
|
106
|
+
replicate: "Replicate",
|
|
107
|
+
together: "Together AI",
|
|
108
|
+
xai: "xAI",
|
|
109
|
+
"zai-org": "Zhipu AI"
|
|
110
|
+
};
|
|
111
|
+
function isCloudflareRunnableModelId(value) {
|
|
112
|
+
const modelId = value.trim();
|
|
113
|
+
return /^@(?:cf|hf)\/[^/\s]+\/\S+$/.test(modelId) || /^[a-z0-9][a-z0-9._-]*\/\S+$/i.test(modelId);
|
|
114
|
+
}
|
|
115
|
+
function readCatalogString(value) {
|
|
116
|
+
if (typeof value !== "string") return null;
|
|
117
|
+
const trimmed = value.trim();
|
|
118
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
119
|
+
}
|
|
120
|
+
function readCatalogField(entry, field) {
|
|
121
|
+
if (typeof entry !== "object" || entry === null || !Object.hasOwn(entry, field)) return null;
|
|
122
|
+
return readCatalogString(entry[field]);
|
|
123
|
+
}
|
|
124
|
+
function readCatalogEntries(entry) {
|
|
125
|
+
if (Array.isArray(entry)) return entry;
|
|
126
|
+
if (typeof entry !== "object" || entry === null || !Object.hasOwn(entry, "data")) return null;
|
|
127
|
+
const data = entry.data;
|
|
128
|
+
return Array.isArray(data) ? data : null;
|
|
129
|
+
}
|
|
130
|
+
function readCloudflareModelId(entry) {
|
|
131
|
+
if (typeof entry === "string") {
|
|
132
|
+
const modelId = readCatalogString(entry);
|
|
133
|
+
return modelId && isCloudflareRunnableModelId(modelId) ? modelId : null;
|
|
134
|
+
}
|
|
135
|
+
for (const field of ["name", "id"]) {
|
|
136
|
+
const modelId = readCatalogField(entry, field);
|
|
137
|
+
if (modelId && isCloudflareRunnableModelId(modelId)) return modelId;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function getCloudflareModelProvider(modelId) {
|
|
142
|
+
const [, hostedProvider] = /^@(?:cf|hf)\/([^/]+)/.exec(modelId) ?? [];
|
|
143
|
+
if (hostedProvider) return hostedProvider;
|
|
144
|
+
return modelId.split("/", 1)[0] || "other";
|
|
145
|
+
}
|
|
146
|
+
function formatCloudflareModelProvider(provider) {
|
|
147
|
+
const normalized = provider.trim().toLowerCase();
|
|
148
|
+
if (cloudflareModelProviderLabels[normalized]) return cloudflareModelProviderLabels[normalized];
|
|
149
|
+
return normalized.split(/[-_]/).filter(Boolean).map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
|
150
|
+
}
|
|
151
|
+
function normalizeCloudflareModelSelectorModels(models) {
|
|
152
|
+
const seen = /* @__PURE__ */ new Set();
|
|
153
|
+
const options = [];
|
|
154
|
+
const appendOption = (entry) => {
|
|
155
|
+
const entries = readCatalogEntries(entry);
|
|
156
|
+
if (entries) {
|
|
157
|
+
for (const nestedEntry of entries) appendOption(nestedEntry);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const id = readCloudflareModelId(entry);
|
|
161
|
+
if (!id || seen.has(id)) return;
|
|
162
|
+
seen.add(id);
|
|
163
|
+
const provider = getCloudflareModelProvider(id);
|
|
164
|
+
options.push({
|
|
165
|
+
id,
|
|
166
|
+
provider,
|
|
167
|
+
providerLabel: formatCloudflareModelProvider(provider),
|
|
168
|
+
source: id.startsWith("@") ? "workers-ai" : "ai-gateway",
|
|
169
|
+
description: readCatalogField(entry, "description")
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
for (const entry of models) appendOption(entry);
|
|
173
|
+
return options;
|
|
174
|
+
}
|
|
175
|
+
async function listCloudflareModelSelectorOptions(client, params) {
|
|
176
|
+
const models = [];
|
|
177
|
+
for await (const model of client.ai.models.list(params)) models.push(model);
|
|
178
|
+
if (params.format !== "openrouter") for await (const model of client.ai.models.list({
|
|
179
|
+
...params,
|
|
180
|
+
format: "openrouter"
|
|
181
|
+
})) models.push(model);
|
|
182
|
+
return normalizeCloudflareModelSelectorModels(models);
|
|
183
|
+
}
|
|
184
|
+
function groupCloudflareModelOptions(options) {
|
|
185
|
+
const groups = [];
|
|
186
|
+
const groupsByLabel = /* @__PURE__ */ new Map();
|
|
187
|
+
for (const option of options) {
|
|
188
|
+
const group = groupsByLabel.get(option.providerLabel);
|
|
189
|
+
if (group) {
|
|
190
|
+
group.push(option);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const nextGroup = [option];
|
|
194
|
+
groupsByLabel.set(option.providerLabel, nextGroup);
|
|
195
|
+
groups.push({
|
|
196
|
+
providerLabel: option.providerLabel,
|
|
197
|
+
options: nextGroup
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return groups;
|
|
201
|
+
}
|
|
80
202
|
function useModelSelectorContext() {
|
|
81
203
|
const ctx = React.useContext(ModelSelectorContext);
|
|
82
204
|
if (!ctx) throw new Error("ModelSelector subcomponents must be used inside <ModelSelector>.");
|
|
@@ -154,6 +276,67 @@ function ModelSelector({ open, defaultOpen = false, onOpenChange, children, clas
|
|
|
154
276
|
})
|
|
155
277
|
});
|
|
156
278
|
}
|
|
279
|
+
function CloudflareModelSelector({ models, selectedModelId = null, onSelectModel, disabled = false, placeholder = "Search or paste an AI Gateway model id...", triggerLabel = "Select a model", emptyLabel = "No models found", allowTypedModel = true, onOpenChange, ...props }) {
|
|
280
|
+
const [query, setQuery] = React.useState("");
|
|
281
|
+
const options = React.useMemo(() => normalizeCloudflareModelSelectorModels(models), [models]);
|
|
282
|
+
const groups = React.useMemo(() => groupCloudflareModelOptions(options), [options]);
|
|
283
|
+
const optionIds = React.useMemo(() => new Set(options.map((option) => option.id)), [options]);
|
|
284
|
+
const selectedOption = selectedModelId ? options.find((option) => option.id === selectedModelId) ?? {
|
|
285
|
+
id: selectedModelId,
|
|
286
|
+
provider: getCloudflareModelProvider(selectedModelId),
|
|
287
|
+
providerLabel: formatCloudflareModelProvider(getCloudflareModelProvider(selectedModelId)),
|
|
288
|
+
source: selectedModelId.startsWith("@") ? "workers-ai" : "ai-gateway",
|
|
289
|
+
description: null
|
|
290
|
+
} : null;
|
|
291
|
+
const typedModelId = query.trim();
|
|
292
|
+
const showTypedModel = allowTypedModel && isCloudflareRunnableModelId(typedModelId) && typedModelId !== selectedModelId && !optionIds.has(typedModelId);
|
|
293
|
+
return /* @__PURE__ */ jsxs(ModelSelector, {
|
|
294
|
+
onOpenChange: React.useCallback((nextOpen) => {
|
|
295
|
+
if (!nextOpen) setQuery("");
|
|
296
|
+
onOpenChange?.(nextOpen);
|
|
297
|
+
}, [onOpenChange]),
|
|
298
|
+
...props,
|
|
299
|
+
children: [/* @__PURE__ */ jsx(ModelSelectorTrigger, {
|
|
300
|
+
disabled,
|
|
301
|
+
children: selectedOption ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ModelSelectorLogo, { provider: selectedOption.provider }), /* @__PURE__ */ jsx(ModelSelectorName, { children: selectedOption.id })] }) : /* @__PURE__ */ jsx(ModelSelectorName, { children: triggerLabel })
|
|
302
|
+
}), /* @__PURE__ */ jsx(ModelSelectorContent, {
|
|
303
|
+
title: "Cloudflare model selector",
|
|
304
|
+
children: /* @__PURE__ */ jsxs(ModelSelectorDialog, { children: [/* @__PURE__ */ jsx(ModelSelectorInput, {
|
|
305
|
+
"aria-label": "Search Cloudflare AI models",
|
|
306
|
+
placeholder,
|
|
307
|
+
onInput: (event) => setQuery(event.currentTarget.value)
|
|
308
|
+
}), /* @__PURE__ */ jsxs(ModelSelectorList, { children: [
|
|
309
|
+
showTypedModel ? /* @__PURE__ */ jsx(ModelSelectorGroup, {
|
|
310
|
+
label: "AI Gateway",
|
|
311
|
+
children: /* @__PURE__ */ jsxs(ModelSelectorItem, {
|
|
312
|
+
value: typedModelId,
|
|
313
|
+
onSelect: onSelectModel,
|
|
314
|
+
children: [
|
|
315
|
+
/* @__PURE__ */ jsx(ModelSelectorLogo, { provider: getCloudflareModelProvider(typedModelId) }),
|
|
316
|
+
/* @__PURE__ */ jsx(ModelSelectorName, { children: typedModelId }),
|
|
317
|
+
/* @__PURE__ */ jsx(ModelSelectorShortcut, { children: "Use" })
|
|
318
|
+
]
|
|
319
|
+
})
|
|
320
|
+
}) : null,
|
|
321
|
+
groups.map((group) => /* @__PURE__ */ jsx(ModelSelectorGroup, {
|
|
322
|
+
label: group.providerLabel,
|
|
323
|
+
children: group.options.map((option) => /* @__PURE__ */ jsxs(ModelSelectorItem, {
|
|
324
|
+
value: option.id,
|
|
325
|
+
keywords: `${option.provider} ${option.providerLabel} ${option.source} ${option.description ?? ""}`,
|
|
326
|
+
selected: option.id === selectedModelId,
|
|
327
|
+
onSelect: onSelectModel,
|
|
328
|
+
children: [
|
|
329
|
+
/* @__PURE__ */ jsx(ModelSelectorLogo, { provider: option.provider }),
|
|
330
|
+
/* @__PURE__ */ jsx(ModelSelectorName, { children: option.id }),
|
|
331
|
+
/* @__PURE__ */ jsx(ModelSelectorShortcut, { children: option.source === "workers-ai" ? "Workers AI" : "Gateway" })
|
|
332
|
+
]
|
|
333
|
+
}, option.id))
|
|
334
|
+
}, group.providerLabel)),
|
|
335
|
+
/* @__PURE__ */ jsx(ModelSelectorEmpty, { children: emptyLabel })
|
|
336
|
+
] })] })
|
|
337
|
+
})]
|
|
338
|
+
});
|
|
339
|
+
}
|
|
157
340
|
function ModelSelectorTrigger({ className, children, ...props }) {
|
|
158
341
|
return /* @__PURE__ */ jsx(DialogTrigger, { render: /* @__PURE__ */ jsxs(Button, {
|
|
159
342
|
variant: "outline",
|
|
@@ -303,4 +486,4 @@ function ModelSelectorShortcut({ className, children, ref, ...props }) {
|
|
|
303
486
|
});
|
|
304
487
|
}
|
|
305
488
|
//#endregion
|
|
306
|
-
export {
|
|
489
|
+
export { getCloudflareModelProvider as _, ModelSelectorEmpty as a, normalizeCloudflareModelSelectorModels as b, ModelSelectorItem as c, ModelSelectorLogoGroup as d, ModelSelectorName as f, formatCloudflareModelProvider as g, ModelSelectorTrigger as h, ModelSelectorDialog as i, ModelSelectorList as l, ModelSelectorShortcut as m, ModelSelector as n, ModelSelectorGroup as o, ModelSelectorSeparator as p, ModelSelectorContent as r, ModelSelectorInput as s, CloudflareModelSelector as t, ModelSelectorLogo as u, isCloudflareRunnableModelId as v, readCloudflareModelId as x, listCloudflareModelSelectorOptions as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { McpServerSetupAuth } from "../utils/mcp-server-catalog.js";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/components/AgentConnection.d.ts
|
|
@@ -25,19 +25,14 @@ type NanitesAgentConnectionOptions = {
|
|
|
25
25
|
type AgentConnectionPanelProps = {
|
|
26
26
|
readonly className?: string;
|
|
27
27
|
readonly commands?: readonly AgentConnectionCommand[];
|
|
28
|
-
readonly dcrMcpServers?: readonly McpServerCard[];
|
|
29
28
|
readonly defaultTarget?: AgentConnectionTarget;
|
|
30
29
|
readonly headingLevel?: 2 | 3;
|
|
31
|
-
readonly manualMcpServers?: readonly McpServerCard[];
|
|
32
30
|
readonly origin?: string;
|
|
33
31
|
readonly section?: boolean;
|
|
34
32
|
};
|
|
35
33
|
type AgentConnectionPopoverProps = Omit<AgentConnectionPanelProps, "section"> & {
|
|
36
34
|
readonly className?: string;
|
|
37
35
|
};
|
|
38
|
-
type McpServerSetupProps = {
|
|
39
|
-
readonly setup: McpServerManualSetup;
|
|
40
|
-
};
|
|
41
36
|
type McpConnectorFormValues = {
|
|
42
37
|
readonly bearerToken?: string;
|
|
43
38
|
readonly clientId?: string;
|
|
@@ -46,11 +41,10 @@ type McpConnectorFormValues = {
|
|
|
46
41
|
readonly scope?: string;
|
|
47
42
|
readonly url: string;
|
|
48
43
|
};
|
|
49
|
-
type McpConnectorFormDefaults = Partial<Pick<McpConnectorFormValues, "name" | "url">>;
|
|
50
44
|
type McpConnectorFormProps = Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit" | "status"> & {
|
|
51
45
|
/** Authentication metadata that decides which credential fields render. */readonly auth?: McpServerSetupAuth; /** Label for the OAuth authorization button. */
|
|
52
46
|
readonly authorizeLabel?: string; /** Initial MCP name and URL values. */
|
|
53
|
-
readonly defaults?:
|
|
47
|
+
readonly defaults?: Partial<Pick<McpConnectorFormValues, "name" | "url">>; /** Pattern used to validate the MCP name field. */
|
|
54
48
|
readonly namePattern?: string; /** OAuth URL waiting for the user to authorize, when available. */
|
|
55
49
|
readonly pendingAuthUrl?: string | null; /** Read-only redirect URL shown for manual OAuth client setup. */
|
|
56
50
|
readonly redirectUrl?: string; /** Inline connection status announced politely to assistive tech. */
|
|
@@ -81,16 +75,11 @@ declare function buildNanitesAgentConnectionCommands(origin: string, {
|
|
|
81
75
|
declare function AgentConnectionPanel({
|
|
82
76
|
className,
|
|
83
77
|
commands: commandsProp,
|
|
84
|
-
dcrMcpServers,
|
|
85
78
|
defaultTarget,
|
|
86
79
|
headingLevel,
|
|
87
|
-
manualMcpServers,
|
|
88
80
|
origin,
|
|
89
81
|
section
|
|
90
82
|
}: AgentConnectionPanelProps): React.JSX.Element | null;
|
|
91
|
-
declare function McpServerSetup({
|
|
92
|
-
setup
|
|
93
|
-
}: McpServerSetupProps): React.JSX.Element;
|
|
94
83
|
/** Collects MCP connector settings and optional credentials with native form validation. */
|
|
95
84
|
declare function McpConnectorForm({
|
|
96
85
|
auth,
|
|
@@ -118,4 +107,4 @@ declare function AgentConnectionPopover({
|
|
|
118
107
|
...props
|
|
119
108
|
}: AgentConnectionPopoverProps): React.JSX.Element;
|
|
120
109
|
//#endregion
|
|
121
|
-
export { AgentConnectionCommand, AgentConnectionPanel, AgentConnectionPanelProps, AgentConnectionPopover, AgentConnectionPopoverProps, AgentConnectionTarget, Connection, ConnectionProps, McpConnectorForm,
|
|
110
|
+
export { AgentConnectionCommand, AgentConnectionPanel, AgentConnectionPanelProps, AgentConnectionPopover, AgentConnectionPopoverProps, AgentConnectionTarget, Connection, ConnectionProps, McpConnectorForm, McpConnectorFormProps, McpConnectorFormValues, NanitesAgentConnectionOptions, buildNanitesAgentConnectionCommands };
|
|
@@ -5,7 +5,6 @@ import { Button } from "./Button.js";
|
|
|
5
5
|
import { a as CodeBlockCopyButton, i as CodeBlockContent, n as CodeBlockActions, p as CodeBlockTitle, r as CodeBlockContainer, s as CodeBlockHeader, t as CodeBlock } from "../CodeBlock-XNQOupQ3.js";
|
|
6
6
|
import { Field } from "./Field.js";
|
|
7
7
|
import { Form } from "./Form.js";
|
|
8
|
-
import { DCR_MCP_SERVER_CARDS, MANUAL_MCP_SERVER_CARDS, getMcpServerIconSrc } from "../utils/mcp-server-catalog.js";
|
|
9
8
|
import * as React from "react";
|
|
10
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
10
|
//#region src/components/AgentConnection.tsx
|
|
@@ -104,7 +103,7 @@ function buildNanitesAgentConnectionCommands(origin, { mcpName = "nanites", plug
|
|
|
104
103
|
* Use inside setup screens where users need Codex, Claude Code, skill-only,
|
|
105
104
|
* or raw MCP JSON connection instructions.
|
|
106
105
|
*/
|
|
107
|
-
function AgentConnectionPanel({ className, commands: commandsProp,
|
|
106
|
+
function AgentConnectionPanel({ className, commands: commandsProp, defaultTarget = "codex", headingLevel = 2, origin, section = true }) {
|
|
108
107
|
const commands = React.useMemo(() => commandsProp ?? buildNanitesAgentConnectionCommands(origin ?? getBrowserOrigin()), [commandsProp, origin]);
|
|
109
108
|
const [target, setTarget] = React.useState(defaultTarget);
|
|
110
109
|
const selected = commands.find((command) => command.target === target) ?? commands[0];
|
|
@@ -150,72 +149,7 @@ function AgentConnectionPanel({ className, commands: commandsProp, dcrMcpServers
|
|
|
150
149
|
code: selected.code,
|
|
151
150
|
language: selected.language,
|
|
152
151
|
children: [/* @__PURE__ */ jsxs(CodeBlockHeader, { children: [/* @__PURE__ */ jsx(CodeBlockTitle, { children: selected.label }), /* @__PURE__ */ jsx(CodeBlockActions, { children: /* @__PURE__ */ jsx(CodeBlockCopyButton, {}) })] }), /* @__PURE__ */ jsx(CodeBlockContainer, { children: /* @__PURE__ */ jsx(CodeBlockContent, {}) })]
|
|
153
|
-
})
|
|
154
|
-
dcrMcpServers.length > 0 ? /* @__PURE__ */ jsx(McpServerList, {
|
|
155
|
-
heading: "Auto",
|
|
156
|
-
servers: dcrMcpServers
|
|
157
|
-
}) : null,
|
|
158
|
-
manualMcpServers.length > 0 ? /* @__PURE__ */ jsx(McpServerList, {
|
|
159
|
-
heading: "Manual",
|
|
160
|
-
servers: manualMcpServers
|
|
161
|
-
}) : null
|
|
162
|
-
]
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
function McpServerList({ heading, servers }) {
|
|
166
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
167
|
-
className: "agent-connect__mcp",
|
|
168
|
-
children: [/* @__PURE__ */ jsx("p", {
|
|
169
|
-
className: "agent-connect__mcp-heading",
|
|
170
|
-
children: heading
|
|
171
|
-
}), /* @__PURE__ */ jsx("ul", {
|
|
172
|
-
className: "agent-connect__mcp-list",
|
|
173
|
-
role: "list",
|
|
174
|
-
children: servers.map((server) => /* @__PURE__ */ jsxs("li", {
|
|
175
|
-
className: "agent-connect__mcp-item",
|
|
176
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
177
|
-
className: "agent-connect__mcp-icon",
|
|
178
|
-
children: renderMcpServerIcon(server)
|
|
179
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
180
|
-
className: "agent-connect__mcp-copy",
|
|
181
|
-
children: [
|
|
182
|
-
/* @__PURE__ */ jsx("a", {
|
|
183
|
-
className: "agent-connect__mcp-name",
|
|
184
|
-
href: server.url,
|
|
185
|
-
target: "_blank",
|
|
186
|
-
rel: "noreferrer",
|
|
187
|
-
children: server.name
|
|
188
|
-
}),
|
|
189
|
-
server.description ? /* @__PURE__ */ jsx("span", {
|
|
190
|
-
className: "agent-connect__mcp-description",
|
|
191
|
-
children: server.description
|
|
192
|
-
}) : null,
|
|
193
|
-
server.manualSetup ? /* @__PURE__ */ jsx(McpServerSetup, { setup: server.manualSetup }) : null
|
|
194
|
-
]
|
|
195
|
-
})]
|
|
196
|
-
}, server.id))
|
|
197
|
-
})]
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
function McpServerSetup({ setup }) {
|
|
201
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
202
|
-
className: "agent-connect__mcp-setup",
|
|
203
|
-
children: [
|
|
204
|
-
/* @__PURE__ */ jsx("p", { children: setup.description }),
|
|
205
|
-
/* @__PURE__ */ jsxs("p", {
|
|
206
|
-
className: "agent-connect__mcp-setup-auth",
|
|
207
|
-
children: [/* @__PURE__ */ jsx("span", { children: setup.auth.label }), setup.auth.fields.length > 0 ? /* @__PURE__ */ jsx("span", { children: setup.auth.fields.map((field) => field.label).join(", ") }) : null]
|
|
208
|
-
}),
|
|
209
|
-
setup.links.length > 0 ? /* @__PURE__ */ jsx("ul", {
|
|
210
|
-
className: "agent-connect__mcp-setup-links",
|
|
211
|
-
role: "list",
|
|
212
|
-
children: setup.links.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", {
|
|
213
|
-
href: link.url,
|
|
214
|
-
target: "_blank",
|
|
215
|
-
rel: "noreferrer",
|
|
216
|
-
children: link.label
|
|
217
|
-
}) }, `${link.label}:${link.url}`))
|
|
218
|
-
}) : null
|
|
152
|
+
})
|
|
219
153
|
]
|
|
220
154
|
});
|
|
221
155
|
}
|
|
@@ -362,28 +296,6 @@ function readFormString(data, name) {
|
|
|
362
296
|
function isCredentialAuthField(field) {
|
|
363
297
|
return field.name !== "redirectUrl";
|
|
364
298
|
}
|
|
365
|
-
function renderMcpServerIcon(server) {
|
|
366
|
-
const src = getMcpServerIconSrc(server);
|
|
367
|
-
if (src) return /* @__PURE__ */ jsx(McpServerIconImage, {
|
|
368
|
-
src,
|
|
369
|
-
name: server.name
|
|
370
|
-
});
|
|
371
|
-
return server.name.slice(0, 1);
|
|
372
|
-
}
|
|
373
|
-
function McpServerIconImage({ src, name }) {
|
|
374
|
-
const [failed, setFailed] = React.useState(false);
|
|
375
|
-
if (failed) return name.slice(0, 1);
|
|
376
|
-
return /* @__PURE__ */ jsx("img", {
|
|
377
|
-
src,
|
|
378
|
-
alt: "",
|
|
379
|
-
width: 16,
|
|
380
|
-
height: 16,
|
|
381
|
-
loading: "lazy",
|
|
382
|
-
decoding: "async",
|
|
383
|
-
referrerPolicy: "no-referrer",
|
|
384
|
-
onError: () => setFailed(true)
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
299
|
/**
|
|
388
300
|
* Compact popover trigger for showing AgentConnectionPanel inline.
|
|
389
301
|
*/
|
|
@@ -412,4 +324,4 @@ function AgentConnectionPopover({ className, ...props }) {
|
|
|
412
324
|
}) })] });
|
|
413
325
|
}
|
|
414
326
|
//#endregion
|
|
415
|
-
export { AgentConnectionPanel, AgentConnectionPopover, Connection, McpConnectorForm,
|
|
327
|
+
export { AgentConnectionPanel, AgentConnectionPopover, Connection, McpConnectorForm, buildNanitesAgentConnectionCommands };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { BrowserLiveView } from "agents/browser";
|
|
3
|
+
|
|
4
|
+
//#region src/components/BrowserRunPanel.d.ts
|
|
5
|
+
interface BrowserRunPanelProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
6
|
+
/** Host-side Browser Run Live View data from `connector.liveView()`. */
|
|
7
|
+
liveView?: BrowserLiveView;
|
|
8
|
+
/** Refreshes the Live View URL before it expires. */
|
|
9
|
+
onRefresh?: () => void | Promise<void>;
|
|
10
|
+
/** Closes the app-owned shared browser session. */
|
|
11
|
+
onCloseSession?: () => void | Promise<void>;
|
|
12
|
+
/** True while the app is fetching Live View data. */
|
|
13
|
+
loading?: boolean;
|
|
14
|
+
/** Error from the app-owned browser session fetch/refresh path. */
|
|
15
|
+
error?: React.ReactNode;
|
|
16
|
+
/** Panel heading. */
|
|
17
|
+
label?: React.ReactNode;
|
|
18
|
+
/** Empty state shown when there is no shared browser session. */
|
|
19
|
+
empty?: React.ReactNode;
|
|
20
|
+
/** Fallback passed through to `BrowserLiveView` after URL expiry/failure. */
|
|
21
|
+
fallback?: React.ReactNode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Right-side Browser Run panel for `ThinkFullApp.workbenchPanels`.
|
|
25
|
+
*
|
|
26
|
+
* The app owns `createBrowserRuntime()`, `connector.liveView()`, polling, and
|
|
27
|
+
* session cleanup. This component only renders upstream `BrowserLiveView`
|
|
28
|
+
* data and calls app-provided refresh/close callbacks.
|
|
29
|
+
*/
|
|
30
|
+
declare function BrowserRunPanel({
|
|
31
|
+
liveView,
|
|
32
|
+
onRefresh,
|
|
33
|
+
onCloseSession,
|
|
34
|
+
loading,
|
|
35
|
+
error,
|
|
36
|
+
label,
|
|
37
|
+
empty,
|
|
38
|
+
fallback,
|
|
39
|
+
className,
|
|
40
|
+
ref,
|
|
41
|
+
...props
|
|
42
|
+
}: BrowserRunPanelProps & {
|
|
43
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
44
|
+
}): React.JSX.Element;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { BrowserRunPanel, BrowserRunPanelProps };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
+
import { C as MonitorIcon, L as XIcon, M as SpinnerIcon, k as RefreshIcon } from "../icons-D2Q5n5C1.js";
|
|
3
|
+
import { Button } from "./Button.js";
|
|
4
|
+
import { BrowserLiveView } from "./BrowserLiveView.js";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/BrowserRunPanel.tsx
|
|
8
|
+
function isInspectablePage(target) {
|
|
9
|
+
const pageUrl = target.pageUrl;
|
|
10
|
+
return Boolean(pageUrl && !pageUrl.startsWith("about:") && !pageUrl.startsWith("chrome:") && !pageUrl.startsWith("devtools:"));
|
|
11
|
+
}
|
|
12
|
+
function targetLabel(target) {
|
|
13
|
+
return target.title || target.pageUrl || target.targetId;
|
|
14
|
+
}
|
|
15
|
+
function pickTarget(targets, targetId) {
|
|
16
|
+
return (targetId ? targets.find((target) => target.targetId === targetId) : void 0) ?? targets.find(isInspectablePage) ?? targets[0];
|
|
17
|
+
}
|
|
18
|
+
function BrowserRunPanelState({ children, role, tone = "neutral" }) {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: "browser-run-panel__state",
|
|
21
|
+
"data-tone": tone,
|
|
22
|
+
role: role ?? (tone === "danger" ? "alert" : void 0),
|
|
23
|
+
children
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Right-side Browser Run panel for `ThinkFullApp.workbenchPanels`.
|
|
28
|
+
*
|
|
29
|
+
* The app owns `createBrowserRuntime()`, `connector.liveView()`, polling, and
|
|
30
|
+
* session cleanup. This component only renders upstream `BrowserLiveView`
|
|
31
|
+
* data and calls app-provided refresh/close callbacks.
|
|
32
|
+
*/
|
|
33
|
+
function BrowserRunPanel({ liveView, onRefresh, onCloseSession, loading, error, label = "Browser", empty = "No live browser session", fallback, className, ref, ...props }) {
|
|
34
|
+
const [selectedTargetId, setSelectedTargetId] = React.useState();
|
|
35
|
+
const [receivedAt, setReceivedAt] = React.useState(() => Date.now());
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
if (liveView) setReceivedAt(Date.now());
|
|
38
|
+
}, [liveView]);
|
|
39
|
+
const targets = liveView?.targets ?? [];
|
|
40
|
+
const target = pickTarget(targets, selectedTargetId);
|
|
41
|
+
const pageLabel = target ? targetLabel(target) : "Waiting for a shared browser session";
|
|
42
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
43
|
+
ref,
|
|
44
|
+
className: cx("browser-run-panel", className),
|
|
45
|
+
"aria-label": typeof label === "string" ? label : "Browser",
|
|
46
|
+
...props,
|
|
47
|
+
children: [
|
|
48
|
+
/* @__PURE__ */ jsxs("header", {
|
|
49
|
+
className: "browser-run-panel__header",
|
|
50
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
51
|
+
className: "browser-run-panel__identity",
|
|
52
|
+
children: [/* @__PURE__ */ jsx(MonitorIcon, { className: "browser-run-panel__icon" }), /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "browser-run-panel__title-block",
|
|
54
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
55
|
+
className: "browser-run-panel__title",
|
|
56
|
+
children: label
|
|
57
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
58
|
+
className: "browser-run-panel__url",
|
|
59
|
+
title: pageLabel,
|
|
60
|
+
children: pageLabel
|
|
61
|
+
})]
|
|
62
|
+
})]
|
|
63
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
64
|
+
className: "browser-run-panel__actions",
|
|
65
|
+
children: [onRefresh ? /* @__PURE__ */ jsx(Button, {
|
|
66
|
+
type: "button",
|
|
67
|
+
variant: "ghost",
|
|
68
|
+
color: "neutral",
|
|
69
|
+
size: "icon-sm",
|
|
70
|
+
"aria-label": "Refresh browser view",
|
|
71
|
+
disabled: loading,
|
|
72
|
+
onClick: () => void onRefresh(),
|
|
73
|
+
children: /* @__PURE__ */ jsx(RefreshIcon, {})
|
|
74
|
+
}) : null, onCloseSession ? /* @__PURE__ */ jsx(Button, {
|
|
75
|
+
type: "button",
|
|
76
|
+
variant: "ghost",
|
|
77
|
+
color: "neutral",
|
|
78
|
+
size: "icon-sm",
|
|
79
|
+
"aria-label": "Close browser session",
|
|
80
|
+
onClick: () => void onCloseSession(),
|
|
81
|
+
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
82
|
+
}) : null]
|
|
83
|
+
})]
|
|
84
|
+
}),
|
|
85
|
+
targets.length > 1 ? /* @__PURE__ */ jsx("div", {
|
|
86
|
+
className: "browser-run-panel__targets",
|
|
87
|
+
"aria-label": "Browser tabs",
|
|
88
|
+
children: targets.map((candidate) => /* @__PURE__ */ jsxs("button", {
|
|
89
|
+
type: "button",
|
|
90
|
+
className: "browser-run-panel__target",
|
|
91
|
+
"aria-pressed": candidate.targetId === target?.targetId,
|
|
92
|
+
onClick: () => setSelectedTargetId(candidate.targetId),
|
|
93
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
94
|
+
className: "browser-run-panel__target-title",
|
|
95
|
+
children: targetLabel(candidate)
|
|
96
|
+
}), candidate.pageUrl ? /* @__PURE__ */ jsx("span", {
|
|
97
|
+
className: "browser-run-panel__target-url",
|
|
98
|
+
children: candidate.pageUrl
|
|
99
|
+
}) : null]
|
|
100
|
+
}, candidate.targetId))
|
|
101
|
+
}) : null,
|
|
102
|
+
error ? /* @__PURE__ */ jsx(BrowserRunPanelState, {
|
|
103
|
+
tone: "danger",
|
|
104
|
+
children: error
|
|
105
|
+
}) : null,
|
|
106
|
+
/* @__PURE__ */ jsx("div", {
|
|
107
|
+
className: "browser-run-panel__body",
|
|
108
|
+
"aria-busy": loading || void 0,
|
|
109
|
+
children: target && liveView ? /* @__PURE__ */ jsx(BrowserLiveView, {
|
|
110
|
+
url: target.url,
|
|
111
|
+
expiresAt: receivedAt + liveView.expiresInMs,
|
|
112
|
+
pageTitle: target.title ?? target.pageUrl,
|
|
113
|
+
fallback
|
|
114
|
+
}) : loading ? /* @__PURE__ */ jsxs(BrowserRunPanelState, {
|
|
115
|
+
role: "status",
|
|
116
|
+
children: [/* @__PURE__ */ jsx(SpinnerIcon, { className: "browser-run-panel__spinner" }), "Loading browser session"]
|
|
117
|
+
}) : /* @__PURE__ */ jsx(BrowserRunPanelState, { children: empty })
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { BrowserRunPanel };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
2
|
import { M as SpinnerIcon } from "../icons-D2Q5n5C1.js";
|
|
3
3
|
import { Button } from "./Button.js";
|
|
4
|
-
import {
|
|
4
|
+
import { u as ModelSelectorLogo } from "../ModelSelector-DXtpcRmT.js";
|
|
5
5
|
import "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/ChatGptSignInScreen.tsx
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { MCPServer, MCPServersState } from "agents";
|
|
3
|
+
|
|
4
|
+
//#region src/components/McpServerConnections.d.ts
|
|
5
|
+
type McpServerConnectionAction = (serverId: string, server: MCPServer) => void | Promise<void>;
|
|
6
|
+
interface McpServerConnectionsProps extends React.HTMLAttributes<HTMLElement> {
|
|
7
|
+
/** Upstream Agents SDK MCP state from `getMcpServers()` or `useAgent({ onMcpUpdate })`. */
|
|
8
|
+
mcp: MCPServersState;
|
|
9
|
+
/** Optional heading for the connected-server list. */
|
|
10
|
+
heading?: React.ReactNode;
|
|
11
|
+
/** Called when an OAuth server needs user authorization. */
|
|
12
|
+
onAuthorize?: McpServerConnectionAction;
|
|
13
|
+
/** Called when a failed server should retry through an app-owned Agent RPC. */
|
|
14
|
+
onRetry?: McpServerConnectionAction;
|
|
15
|
+
/** Called when a ready/connected server should refresh discovery through an app-owned Agent RPC. */
|
|
16
|
+
onRefresh?: McpServerConnectionAction;
|
|
17
|
+
/** Called when a server transport should close but the stored config should remain. */
|
|
18
|
+
onDisconnect?: McpServerConnectionAction;
|
|
19
|
+
/** Should call `removeMcpServer(serverId)`; Agents SDK disconnects and removes storage. */
|
|
20
|
+
onRemove?: McpServerConnectionAction;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Runtime MCP server list. Add servers with `McpServerPicker`; render connected
|
|
24
|
+
* state here from the upstream Agents SDK `MCPServersState`.
|
|
25
|
+
*/
|
|
26
|
+
declare function McpServerConnections({
|
|
27
|
+
className,
|
|
28
|
+
heading,
|
|
29
|
+
mcp,
|
|
30
|
+
onAuthorize,
|
|
31
|
+
onDisconnect,
|
|
32
|
+
onRefresh,
|
|
33
|
+
onRemove,
|
|
34
|
+
onRetry,
|
|
35
|
+
ref,
|
|
36
|
+
...props
|
|
37
|
+
}: McpServerConnectionsProps & {
|
|
38
|
+
ref?: React.Ref<HTMLElement>;
|
|
39
|
+
}): React.JSX.Element;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { McpServerConnections, McpServerConnectionsProps };
|