@mcp-b/react-components 0.10.0 → 0.12.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.
Files changed (84) hide show
  1. package/dist/{AgentChat-B9LD_WF1.d.ts → AgentChat-CRmsKg9B.d.ts} +10 -6
  2. package/dist/{BrowserLiveView-ZktFZiMD.d.ts → BrowserLiveView-BV5qgFgI.d.ts} +11 -10
  3. package/dist/{BrowserReplay-6G-mnwcn.d.ts → BrowserReplay-xWvRjRAC.d.ts} +2 -1
  4. package/dist/{Menu-B5vDqZe_.d.ts → Menu-BLhnNnRK.d.ts} +3 -0
  5. package/dist/{ModelSelector-DXtpcRmT.js → ModelSelector-BB-xB-Es.js} +16 -13
  6. package/dist/{PromptInput-D6-G24p7.d.ts → PromptInput-IyM6ZETj.d.ts} +13 -45
  7. package/dist/ThinkChat-B3wYahor.js +190 -0
  8. package/dist/{ThinkChatActivity-B2UD95My.d.ts → ThinkChatActivity-F4KFPcJA.d.ts} +4 -4
  9. package/dist/ThinkChatContext-C3Eb1Xpi.d.ts +16 -0
  10. package/dist/{ThinkChatToolParts-DnUiZgCC.d.ts → ThinkChatToolParts-Cv4o7j6F.d.ts} +13 -5
  11. package/dist/components/AgentChat.d.ts +1 -1
  12. package/dist/components/AgentChat.js +57 -23
  13. package/dist/components/AgentConnection.d.ts +3 -14
  14. package/dist/components/AgentConnection.js +7 -111
  15. package/dist/components/Badge.js +1 -15
  16. package/dist/components/BrowserLiveView.d.ts +1 -1
  17. package/dist/components/BrowserLiveView.js +14 -10
  18. package/dist/components/BrowserReplay.d.ts +1 -1
  19. package/dist/components/BrowserRunPanel.d.ts +46 -0
  20. package/dist/components/BrowserRunPanel.js +159 -0
  21. package/dist/components/ChatGptSignInScreen.js +1 -1
  22. package/dist/components/CodexModelSelector.d.ts +63 -0
  23. package/dist/components/CodexModelSelector.js +148 -0
  24. package/dist/components/Commit.d.ts +2 -2
  25. package/dist/components/Commit.js +10 -22
  26. package/dist/components/Conversation.d.ts +2 -6
  27. package/dist/components/Conversation.js +2 -3
  28. package/dist/components/EnvironmentVariables.d.ts +2 -7
  29. package/dist/components/EnvironmentVariables.js +10 -33
  30. package/dist/components/Marker.js +1 -15
  31. package/dist/components/McpServerPicker.d.ts +31 -6
  32. package/dist/components/McpServerPicker.js +129 -67
  33. package/dist/components/Menu.d.ts +1 -1
  34. package/dist/components/Menu.js +2 -2
  35. package/dist/components/Message.d.ts +3 -2
  36. package/dist/components/Message.js +10 -3
  37. package/dist/components/ModelSelector.d.ts +11 -8
  38. package/dist/components/ModelSelector.js +1 -1
  39. package/dist/components/PromptInput.d.ts +2 -2
  40. package/dist/components/PromptInput.js +49 -98
  41. package/dist/components/Queue.d.ts +1 -12
  42. package/dist/components/Reasoning.js +2 -15
  43. package/dist/components/Stepper.js +1 -7
  44. package/dist/components/ThinkChat.d.ts +30 -30
  45. package/dist/components/ThinkChat.js +1 -152
  46. package/dist/components/ThinkChatActivity.d.ts +2 -2
  47. package/dist/components/ThinkChatActivity.js +3 -10
  48. package/dist/components/ThinkChatContext.d.ts +2 -2
  49. package/dist/components/ThinkChatContext.js +9 -2
  50. package/dist/components/ThinkChatToolPanels.d.ts +1 -1
  51. package/dist/components/ThinkChatToolPanels.js +19 -7
  52. package/dist/components/ThinkChatToolParts.d.ts +1 -1
  53. package/dist/components/ThinkExecutionWorkbench.d.ts +10 -7
  54. package/dist/components/ThinkExecutionWorkbench.js +382 -359
  55. package/dist/components/ThinkFullApp.d.ts +16 -13
  56. package/dist/components/ThinkFullApp.js +28 -18
  57. package/dist/components/ThinkThreadPicker.d.ts +1 -31
  58. package/dist/components/ThinkThreadPicker.js +32 -171
  59. package/dist/components/ThinkThreadShell.d.ts +35 -0
  60. package/dist/components/ThinkThreadShell.js +146 -0
  61. package/dist/components/WorkspaceExplorer.js +3 -1
  62. package/dist/merge-refs-DF_7w2x_.js +18 -0
  63. package/dist/styles/agent-chat.css +4 -1
  64. package/dist/styles/agent-connection.css +0 -166
  65. package/dist/styles/browser-run-panel.css +125 -0
  66. package/dist/styles/codex-model-selector.css +197 -0
  67. package/dist/styles/index.css +5 -0
  68. package/dist/styles/mcp-server-picker.css +62 -15
  69. package/dist/styles/message.css +1 -1
  70. package/dist/styles/model-selector.css +20 -8
  71. package/dist/styles/prompt-input.css +5 -0
  72. package/dist/styles/streamdown.css +2 -48
  73. package/dist/styles/think-execution-workbench.css +337 -0
  74. package/dist/styles/think-full-app.css +15 -390
  75. package/dist/styles/think-resize-handle.css +33 -0
  76. package/dist/styles/think-thread-picker.css +0 -238
  77. package/dist/styles/think-thread-shell.css +219 -0
  78. package/dist/styles/workspace-explorer.css +8 -2
  79. package/dist/summarize-tool-input-BUJsENSn.js +12 -0
  80. package/dist/utils/mcp-server-catalog.d.ts +2 -3
  81. package/package.json +2 -2
  82. package/dist/ThinkChatContext-BLAAb8J5.d.ts +0 -28
  83. package/dist/components/useStickToBottom.d.ts +0 -33
  84. package/dist/components/useStickToBottom.js +0 -107
@@ -6,10 +6,10 @@ import { MessageScroller, MessageScrollerButton, MessageScrollerContent, Message
6
6
  import { ConversationEmptyState } from "./Conversation.js";
7
7
  import { Message, MessageAction, MessageActions, MessageAttachment, MessageContent, MessageResponse } from "./Message.js";
8
8
  import { Marker, MarkerContent } from "./Marker.js";
9
- import { MenuGroup, MenuGroupLabel } from "./Menu.js";
10
9
  import { PromptInput, PromptInputActionAddAttachments, PromptInputActionMenu, PromptInputActionMenuContent, PromptInputActionMenuTrigger, PromptInputApproval, PromptInputAttachments, PromptInputBody, PromptInputButton, PromptInputFooter, PromptInputPermissionSelector, PromptInputSubmit, PromptInputTextarea, PromptInputTools, getApprovalRequestId, getApprovalStateKey, getPendingToolApprovals, usePromptInputAttachments } from "./PromptInput.js";
11
10
  import { Reasoning, ReasoningContent, ReasoningTrigger } from "./Reasoning.js";
12
11
  import { Source, Sources, SourcesContent, SourcesTrigger } from "./Sources.js";
12
+ import { t as summarizeToolInput } from "../summarize-tool-input-BUJsENSn.js";
13
13
  import * as React from "react";
14
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
15
  import { isCompactionMessage } from "agents/experimental/memory/utils";
@@ -17,6 +17,17 @@ import { getToolInput, getToolOutput, getToolPartState } from "@cloudflare/think
17
17
  import { getToolName, isFileUIPart, isReasoningUIPart, isTextUIPart, isToolUIPart } from "ai";
18
18
  //#region src/components/AgentChat.tsx
19
19
  const EMPTY_PENDING_APPROVALS = [];
20
+ const EMPTY_SKIPPED_APPROVAL_IDS = [];
21
+ function createApprovalSessionState(thinkStub) {
22
+ return {
23
+ thinkStub,
24
+ skippedApprovalIds: EMPTY_SKIPPED_APPROVAL_IDS,
25
+ permissionMode: "ask"
26
+ };
27
+ }
28
+ function isCurrentApprovalSession(state, thinkStub) {
29
+ return state.thinkStub === thinkStub;
30
+ }
20
31
  function AgentChatAttachButton() {
21
32
  const attachments = usePromptInputAttachments();
22
33
  return /* @__PURE__ */ jsx(PromptInputButton, {
@@ -33,7 +44,7 @@ function AgentChatAddMenu({ children }) {
33
44
  sideOffset: 12,
34
45
  collisionPadding: 8,
35
46
  className: "agent-chat__add-menu",
36
- children: [/* @__PURE__ */ jsxs(MenuGroup, { children: [/* @__PURE__ */ jsx(MenuGroupLabel, { children: "Files" }), /* @__PURE__ */ jsx(PromptInputActionAddAttachments, {})] }), children]
47
+ children: [/* @__PURE__ */ jsx(PromptInputActionAddAttachments, {}), children]
37
48
  })] });
38
49
  }
39
50
  /**
@@ -62,14 +73,6 @@ function getPartKey(part, partIndex) {
62
73
  if ("id" in part && typeof part.id === "string") return `${part.type}-${part.id}`;
63
74
  return `${part.type}-${partIndex}`;
64
75
  }
65
- function summarizeToolInput(input) {
66
- if (!input || typeof input !== "object") return null;
67
- for (const value of Object.values(input)) if (typeof value === "string" && value.trim()) {
68
- const line = value.trim().split("\n", 1)[0] ?? "";
69
- return line.length > 64 ? `${line.slice(0, 64)}…` : line;
70
- }
71
- return null;
72
- }
73
76
  function isToolPartWorking(part) {
74
77
  const state = getToolPartState(part);
75
78
  return state === "loading" || state === "streaming" || state === "waiting-approval";
@@ -188,38 +191,65 @@ function renderDefaultPart(part) {
188
191
  * transport. Product code that talks to a Cloudflare Think agent should render
189
192
  * `ThinkChat`; use this directly only for plain AI SDK or read-only messages.
190
193
  */
191
- function AgentChat({ className, messages, status = "ready", onSubmit, onStop, placeholder = "Ask anything...", disabled, emptyState, composerTools, composerAddMenuItems, notice, onRegenerate, onApprovalResponse, executionApprovals, pendingApprovals: durableApprovals = EMPTY_PENDING_APPROVALS, contextWindow, renderPart, renderMessageAfter, ref }) {
194
+ function AgentChat({ className, messages, status = "ready", onSubmit, onStop, placeholder = "Ask anything...", disabled, emptyState, composerTools, composerAddMenuItems, notice, onRegenerate, onApprovalResponse, thinkStub, onExecutionSettled, pendingApprovals: durableApprovals = EMPTY_PENDING_APPROVALS, contextWindow, renderPart, renderMessageAfter, ref }) {
192
195
  const handleSubmit = React.useCallback((message) => {
193
196
  onSubmit?.(message);
194
197
  }, [onSubmit]);
195
- const [skippedApprovalIds, setSkippedApprovalIds] = React.useState([]);
196
- const pendingApprovalItems = [...getPendingToolApprovals(messages, { pausedExecutions: Boolean(executionApprovals) }).filter(({ part }) => {
198
+ const [approvalSession, setApprovalSession] = React.useState(() => createApprovalSessionState(thinkStub));
199
+ const approvalSessionIsCurrent = isCurrentApprovalSession(approvalSession, thinkStub);
200
+ const skippedApprovalIds = approvalSessionIsCurrent ? approvalSession.skippedApprovalIds : EMPTY_SKIPPED_APPROVAL_IDS;
201
+ const permissionMode = approvalSessionIsCurrent ? approvalSession.permissionMode : "ask";
202
+ const updateApprovalSession = React.useCallback((update) => {
203
+ setApprovalSession((previous) => update(isCurrentApprovalSession(previous, thinkStub) ? previous : createApprovalSessionState(thinkStub)));
204
+ }, [thinkStub]);
205
+ const setPermissionMode = React.useCallback((next) => {
206
+ updateApprovalSession((current) => ({
207
+ ...current,
208
+ permissionMode: next
209
+ }));
210
+ }, [updateApprovalSession]);
211
+ React.useLayoutEffect(() => {
212
+ setApprovalSession((previous) => isCurrentApprovalSession(previous, thinkStub) ? previous : createApprovalSessionState(thinkStub));
213
+ }, [thinkStub]);
214
+ const pendingApprovalItems = [...getPendingToolApprovals(messages, { pausedExecutions: Boolean(thinkStub) }).filter(({ part }) => {
197
215
  if (part.state === "approval-requested" && !onApprovalResponse) return false;
198
216
  const id = getApprovalStateKey(part);
199
217
  return Boolean(id) && !skippedApprovalIds.includes(id);
200
- }).map((approval) => ({ part: approval.part })), ...executionApprovals ? durableApprovals.filter((approval) => approval.source === "action" && !skippedApprovalIds.includes(approval.executionId)).map((approval) => ({ approval })) : []];
201
- const approvalsWired = Boolean(onApprovalResponse || executionApprovals);
202
- const [permissionMode, setPermissionMode] = React.useState("ask");
203
- const autoApprovedIds = React.useRef(/* @__PURE__ */ new Set());
218
+ }).map((approval) => ({ part: approval.part })), ...thinkStub ? durableApprovals.filter((approval) => approval.source === "action" && !skippedApprovalIds.includes(approval.executionId)).map((approval) => ({ approval })) : []];
219
+ const approvalsWired = Boolean(onApprovalResponse || thinkStub);
220
+ const autoApprovedIds = React.useRef({
221
+ thinkStub,
222
+ ids: /* @__PURE__ */ new Set()
223
+ });
224
+ React.useLayoutEffect(() => {
225
+ if (isCurrentApprovalSession(autoApprovedIds.current, thinkStub)) return;
226
+ autoApprovedIds.current = {
227
+ thinkStub,
228
+ ids: /* @__PURE__ */ new Set()
229
+ };
230
+ }, [thinkStub]);
204
231
  React.useEffect(() => {
205
232
  if (permissionMode !== "bypass") return;
206
233
  for (const item of pendingApprovalItems) {
207
234
  const id = "part" in item ? getApprovalRequestId(item.part) : item.approval.executionId;
208
235
  const key = "part" in item ? getApprovalStateKey(item.part) : item.approval.executionId;
209
- if (!id || !key || autoApprovedIds.current.has(key)) continue;
210
- autoApprovedIds.current.add(key);
236
+ if (!id || !key || autoApprovedIds.current.ids.has(key)) continue;
237
+ autoApprovedIds.current.ids.add(key);
211
238
  if ("part" in item && item.part.state === "approval-requested") onApprovalResponse?.({
212
239
  id,
213
240
  approved: true
214
241
  });
215
- else executionApprovals?.approve(id);
242
+ else if (thinkStub) thinkStub.approveExecution(id).finally(() => {
243
+ onExecutionSettled?.();
244
+ });
216
245
  }
217
246
  }, [
218
247
  permissionMode,
219
248
  messages,
220
249
  durableApprovals,
221
250
  onApprovalResponse,
222
- executionApprovals
251
+ thinkStub,
252
+ onExecutionSettled
223
253
  ]);
224
254
  const pendingApproval = permissionMode === "bypass" ? void 0 : pendingApprovalItems[0];
225
255
  const contextUsage = contextWindow ? getContextUsage(messages) : void 0;
@@ -312,10 +342,14 @@ function AgentChat({ className, messages, status = "ready", onSubmit, onStop, pl
312
342
  approval: "approval" in pendingApproval ? pendingApproval.approval : void 0,
313
343
  count: pendingApprovalItems.length,
314
344
  onRespond: onApprovalResponse,
315
- executionApprovals,
345
+ thinkStub,
346
+ onExecutionSettled,
316
347
  onSkip: () => {
317
348
  const id = "part" in pendingApproval ? getApprovalStateKey(pendingApproval.part) : pendingApproval.approval.executionId;
318
- if (id) setSkippedApprovalIds((previous) => [...previous, id]);
349
+ if (id) updateApprovalSession((current) => ({
350
+ ...current,
351
+ skippedApprovalIds: [...current.skippedApprovalIds, id]
352
+ }));
319
353
  }
320
354
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [
321
355
  /* @__PURE__ */ jsx(PromptInputAttachments, {}),
@@ -1,4 +1,4 @@
1
- import { McpServerCard, McpServerManualSetup, McpServerSetupAuth } from "../utils/mcp-server-catalog.js";
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?: McpConnectorFormDefaults; /** Pattern used to validate the MCP name field. */
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, McpConnectorFormDefaults, McpConnectorFormProps, McpConnectorFormValues, McpServerSetup, McpServerSetupProps, NanitesAgentConnectionOptions, buildNanitesAgentConnectionCommands };
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, dcrMcpServers = DCR_MCP_SERVER_CARDS, defaultTarget = "codex", headingLevel = 2, manualMcpServers = MANUAL_MCP_SERVER_CARDS, origin, section = true }) {
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
  }
@@ -244,12 +178,8 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
244
178
  ...props,
245
179
  children: [
246
180
  /* @__PURE__ */ jsxs(Field.Root, { children: [
247
- /* @__PURE__ */ jsx(Field.Label, {
248
- htmlFor: "mcpName",
249
- children: "MCP name"
250
- }),
181
+ /* @__PURE__ */ jsx(Field.Label, { children: "MCP name" }),
251
182
  /* @__PURE__ */ jsx(Field.Control, {
252
- id: "mcpName",
253
183
  name: "name",
254
184
  pattern: namePattern,
255
185
  required: true,
@@ -266,12 +196,8 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
266
196
  })
267
197
  ] }),
268
198
  /* @__PURE__ */ jsxs(Field.Root, { children: [
269
- /* @__PURE__ */ jsx(Field.Label, {
270
- htmlFor: "mcpUrl",
271
- children: "MCP URL"
272
- }),
199
+ /* @__PURE__ */ jsx(Field.Label, { children: "MCP URL" }),
273
200
  /* @__PURE__ */ jsx(Field.Control, {
274
- id: "mcpUrl",
275
201
  name: "url",
276
202
  placeholder: "http://127.0.0.1:8787/mcp",
277
203
  required: true,
@@ -287,24 +213,16 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
287
213
  children: "MCP URL must use http or https."
288
214
  })
289
215
  ] }),
290
- redirectUrlField ? /* @__PURE__ */ jsxs(Field.Root, { children: [/* @__PURE__ */ jsx(Field.Label, {
291
- htmlFor: "mcpRedirectUrl",
292
- children: redirectUrlField.label
293
- }), /* @__PURE__ */ jsx(Field.Control, {
294
- id: "mcpRedirectUrl",
216
+ redirectUrlField ? /* @__PURE__ */ jsxs(Field.Root, { children: [/* @__PURE__ */ jsx(Field.Label, { children: redirectUrlField.label }), /* @__PURE__ */ jsx(Field.Control, {
295
217
  name: "redirectUrl",
296
218
  readOnly: true,
297
219
  type: "url",
298
220
  value: redirectUrl
299
221
  })] }) : null,
300
222
  credentialFields.map((authField) => /* @__PURE__ */ jsxs(Field.Root, { children: [
301
- /* @__PURE__ */ jsx(Field.Label, {
302
- htmlFor: `mcp-${authField.name}`,
303
- children: authField.label
304
- }),
223
+ /* @__PURE__ */ jsx(Field.Label, { children: authField.label }),
305
224
  /* @__PURE__ */ jsx(Field.Control, {
306
225
  autoComplete: "off",
307
- id: `mcp-${authField.name}`,
308
226
  name: authField.name,
309
227
  placeholder: authField.placeholder,
310
228
  required: authField.required,
@@ -362,28 +280,6 @@ function readFormString(data, name) {
362
280
  function isCredentialAuthField(field) {
363
281
  return field.name !== "redirectUrl";
364
282
  }
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
283
  /**
388
284
  * Compact popover trigger for showing AgentConnectionPanel inline.
389
285
  */
@@ -412,4 +308,4 @@ function AgentConnectionPopover({ className, ...props }) {
412
308
  }) })] });
413
309
  }
414
310
  //#endregion
415
- export { AgentConnectionPanel, AgentConnectionPopover, Connection, McpConnectorForm, McpServerSetup, buildNanitesAgentConnectionCommands };
311
+ export { AgentConnectionPanel, AgentConnectionPopover, Connection, McpConnectorForm, buildNanitesAgentConnectionCommands };
@@ -1,3 +1,4 @@
1
+ import { n as mergeRefs } from "../merge-refs-DF_7w2x_.js";
1
2
  import * as React from "react";
2
3
  import { jsx } from "react/jsx-runtime";
3
4
  //#region src/components/Badge.tsx
@@ -26,21 +27,6 @@ function badgeVariants({ variant = "default", color, size = "md", className } =
26
27
  className
27
28
  ].filter(Boolean).join(" ");
28
29
  }
29
- function assignRef(ref, value) {
30
- if (!ref) return;
31
- if (typeof ref === "function") {
32
- ref(value);
33
- return;
34
- }
35
- ref.current = value;
36
- }
37
- function mergeRefs(...refs) {
38
- const activeRefs = refs.filter(Boolean);
39
- if (activeRefs.length === 0) return;
40
- return (value) => {
41
- for (const activeRef of activeRefs) assignRef(activeRef, value);
42
- };
43
- }
44
30
  function isEventHandler(propName, value) {
45
31
  return /^on[A-Z]/.test(propName) && typeof value === "function";
46
32
  }
@@ -1,2 +1,2 @@
1
- import { n as BrowserLiveViewProps, t as BrowserLiveView } from "../BrowserLiveView-ZktFZiMD.js";
1
+ import { n as BrowserLiveViewProps, t as BrowserLiveView } from "../BrowserLiveView-BV5qgFgI.js";
2
2
  export { BrowserLiveView, BrowserLiveViewProps };
@@ -22,8 +22,8 @@ function EndedState({ title, detail }) {
22
22
  /**
23
23
  * Embeds a Browser Run Live View — a real-time, interactive view of the
24
24
  * agent's browser session. Links expire (~5 minutes), so the iframe swaps to
25
- * `fallback` (e.g. the last screenshot) once `expiresAt` passes. While the
26
- * hosted viewer connects, a quiet loading overlay hides the frame; if it
25
+ * `fallback` (e.g. the last screenshot) once `source.expiresInMs` passes.
26
+ * While the hosted viewer connects, a quiet loading overlay hides the frame; if it
27
27
  * never loads, the view degrades to `fallback` or an unavailable state. An
28
28
  * expand button opens the same view in a fullscreen dialog.
29
29
  *
@@ -39,13 +39,12 @@ function EndedState({ title, detail }) {
39
39
  * The iframe is unsandboxed by design: the hosted Live View UI needs scripts
40
40
  * and its own origin to stream the session. Only pass trusted Live View URLs.
41
41
  */
42
- function BrowserLiveView({ url, expiresAt, pageTitle, fallback, className, ...props }) {
43
- const [expired, setExpired] = React.useState(() => expiresAt !== void 0 && expiresAt <= Date.now());
42
+ function BrowserLiveView({ source, receivedAt, pageTitle, fallback, className, ...props }) {
43
+ const [expired, setExpired] = React.useState(() => receivedAt !== void 0 && receivedAt + source.expiresInMs <= Date.now());
44
44
  const [frameState, setFrameState] = React.useState("loading");
45
45
  const [expandedOpen, setExpandedOpen] = React.useState(false);
46
46
  React.useEffect(() => {
47
- if (expiresAt === void 0) return;
48
- const remaining = expiresAt - Date.now();
47
+ const remaining = (receivedAt ?? Date.now()) + source.expiresInMs - Date.now();
49
48
  if (remaining <= 0) {
50
49
  setExpired(true);
51
50
  return;
@@ -53,10 +52,15 @@ function BrowserLiveView({ url, expiresAt, pageTitle, fallback, className, ...pr
53
52
  setExpired(false);
54
53
  const timer = setTimeout(() => setExpired(true), remaining);
55
54
  return () => clearTimeout(timer);
56
- }, [expiresAt]);
55
+ }, [
56
+ receivedAt,
57
+ source.expiresInMs,
58
+ source.targetId,
59
+ source.url
60
+ ]);
57
61
  React.useEffect(() => {
58
62
  setFrameState("loading");
59
- }, [url]);
63
+ }, [source.url]);
60
64
  React.useEffect(() => {
61
65
  if (frameState !== "loading") return;
62
66
  const timer = setTimeout(() => setFrameState("failed"), FRAME_LOAD_TIMEOUT_MS);
@@ -77,7 +81,7 @@ function BrowserLiveView({ url, expiresAt, pageTitle, fallback, className, ...pr
77
81
  }),
78
82
  /* @__PURE__ */ jsx("iframe", {
79
83
  className: "browser-live-view__frame",
80
- src: url,
84
+ src: source.url,
81
85
  title: frameTitle,
82
86
  allow: "clipboard-read; clipboard-write",
83
87
  onLoad: () => setFrameState("ready")
@@ -118,7 +122,7 @@ function BrowserLiveView({ url, expiresAt, pageTitle, fallback, className, ...pr
118
122
  }), /* @__PURE__ */ jsx("div", {
119
123
  className: "browser-live-view__dialog-body",
120
124
  children: live ? /* @__PURE__ */ jsx("iframe", {
121
- src: url,
125
+ src: source.url,
122
126
  title: frameTitle,
123
127
  allow: "clipboard-read; clipboard-write"
124
128
  }) : fallback ?? /* @__PURE__ */ jsx(EndedState, {
@@ -1,2 +1,2 @@
1
- import { n as BrowserReplayProps, t as BrowserReplay } from "../BrowserReplay-6G-mnwcn.js";
1
+ import { n as BrowserReplayProps, t as BrowserReplay } from "../BrowserReplay-xWvRjRAC.js";
2
2
  export { BrowserReplay, BrowserReplayProps };
@@ -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 };