@mcp-b/react-components 0.11.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 (83) 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.js +4 -20
  14. package/dist/components/Badge.js +1 -15
  15. package/dist/components/BrowserLiveView.d.ts +1 -1
  16. package/dist/components/BrowserLiveView.js +14 -10
  17. package/dist/components/BrowserReplay.d.ts +1 -1
  18. package/dist/components/BrowserRunPanel.js +91 -55
  19. package/dist/components/ChatGptSignInScreen.js +1 -1
  20. package/dist/components/CodexModelSelector.d.ts +63 -0
  21. package/dist/components/CodexModelSelector.js +148 -0
  22. package/dist/components/Commit.d.ts +2 -2
  23. package/dist/components/Commit.js +10 -22
  24. package/dist/components/Conversation.d.ts +2 -6
  25. package/dist/components/Conversation.js +2 -3
  26. package/dist/components/EnvironmentVariables.d.ts +2 -7
  27. package/dist/components/EnvironmentVariables.js +10 -33
  28. package/dist/components/Marker.js +1 -15
  29. package/dist/components/McpServerPicker.d.ts +31 -6
  30. package/dist/components/McpServerPicker.js +129 -67
  31. package/dist/components/Menu.d.ts +1 -1
  32. package/dist/components/Menu.js +2 -2
  33. package/dist/components/Message.d.ts +3 -2
  34. package/dist/components/Message.js +10 -3
  35. package/dist/components/ModelSelector.d.ts +11 -8
  36. package/dist/components/ModelSelector.js +1 -1
  37. package/dist/components/PromptInput.d.ts +2 -2
  38. package/dist/components/PromptInput.js +49 -98
  39. package/dist/components/Queue.d.ts +1 -12
  40. package/dist/components/Reasoning.js +2 -15
  41. package/dist/components/Stepper.js +1 -7
  42. package/dist/components/ThinkChat.d.ts +30 -30
  43. package/dist/components/ThinkChat.js +1 -152
  44. package/dist/components/ThinkChatActivity.d.ts +2 -2
  45. package/dist/components/ThinkChatActivity.js +3 -10
  46. package/dist/components/ThinkChatContext.d.ts +2 -2
  47. package/dist/components/ThinkChatContext.js +9 -2
  48. package/dist/components/ThinkChatToolPanels.d.ts +1 -1
  49. package/dist/components/ThinkChatToolPanels.js +19 -7
  50. package/dist/components/ThinkChatToolParts.d.ts +1 -1
  51. package/dist/components/ThinkExecutionWorkbench.d.ts +10 -7
  52. package/dist/components/ThinkExecutionWorkbench.js +382 -359
  53. package/dist/components/ThinkFullApp.d.ts +16 -13
  54. package/dist/components/ThinkFullApp.js +28 -18
  55. package/dist/components/ThinkThreadPicker.d.ts +1 -31
  56. package/dist/components/ThinkThreadPicker.js +32 -171
  57. package/dist/components/ThinkThreadShell.d.ts +35 -0
  58. package/dist/components/ThinkThreadShell.js +146 -0
  59. package/dist/components/WorkspaceExplorer.js +3 -1
  60. package/dist/merge-refs-DF_7w2x_.js +18 -0
  61. package/dist/styles/agent-chat.css +4 -1
  62. package/dist/styles/browser-run-panel.css +57 -110
  63. package/dist/styles/codex-model-selector.css +197 -0
  64. package/dist/styles/index.css +4 -1
  65. package/dist/styles/mcp-server-picker.css +62 -15
  66. package/dist/styles/message.css +1 -1
  67. package/dist/styles/model-selector.css +20 -8
  68. package/dist/styles/prompt-input.css +5 -0
  69. package/dist/styles/streamdown.css +2 -48
  70. package/dist/styles/think-execution-workbench.css +337 -0
  71. package/dist/styles/think-full-app.css +15 -390
  72. package/dist/styles/think-resize-handle.css +33 -0
  73. package/dist/styles/think-thread-picker.css +0 -238
  74. package/dist/styles/think-thread-shell.css +219 -0
  75. package/dist/styles/workspace-explorer.css +8 -2
  76. package/dist/summarize-tool-input-BUJsENSn.js +12 -0
  77. package/package.json +2 -2
  78. package/dist/ThinkChatContext-BLAAb8J5.d.ts +0 -28
  79. package/dist/components/McpServerConnections.d.ts +0 -41
  80. package/dist/components/McpServerConnections.js +0 -188
  81. package/dist/components/useStickToBottom.d.ts +0 -33
  82. package/dist/components/useStickToBottom.js +0 -107
  83. package/dist/styles/mcp-server-connections.css +0 -145
@@ -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, {}),
@@ -178,12 +178,8 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
178
178
  ...props,
179
179
  children: [
180
180
  /* @__PURE__ */ jsxs(Field.Root, { children: [
181
- /* @__PURE__ */ jsx(Field.Label, {
182
- htmlFor: "mcpName",
183
- children: "MCP name"
184
- }),
181
+ /* @__PURE__ */ jsx(Field.Label, { children: "MCP name" }),
185
182
  /* @__PURE__ */ jsx(Field.Control, {
186
- id: "mcpName",
187
183
  name: "name",
188
184
  pattern: namePattern,
189
185
  required: true,
@@ -200,12 +196,8 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
200
196
  })
201
197
  ] }),
202
198
  /* @__PURE__ */ jsxs(Field.Root, { children: [
203
- /* @__PURE__ */ jsx(Field.Label, {
204
- htmlFor: "mcpUrl",
205
- children: "MCP URL"
206
- }),
199
+ /* @__PURE__ */ jsx(Field.Label, { children: "MCP URL" }),
207
200
  /* @__PURE__ */ jsx(Field.Control, {
208
- id: "mcpUrl",
209
201
  name: "url",
210
202
  placeholder: "http://127.0.0.1:8787/mcp",
211
203
  required: true,
@@ -221,24 +213,16 @@ function McpConnectorForm({ auth, authorizeLabel = "Authorize", className, defau
221
213
  children: "MCP URL must use http or https."
222
214
  })
223
215
  ] }),
224
- redirectUrlField ? /* @__PURE__ */ jsxs(Field.Root, { children: [/* @__PURE__ */ jsx(Field.Label, {
225
- htmlFor: "mcpRedirectUrl",
226
- children: redirectUrlField.label
227
- }), /* @__PURE__ */ jsx(Field.Control, {
228
- id: "mcpRedirectUrl",
216
+ redirectUrlField ? /* @__PURE__ */ jsxs(Field.Root, { children: [/* @__PURE__ */ jsx(Field.Label, { children: redirectUrlField.label }), /* @__PURE__ */ jsx(Field.Control, {
229
217
  name: "redirectUrl",
230
218
  readOnly: true,
231
219
  type: "url",
232
220
  value: redirectUrl
233
221
  })] }) : null,
234
222
  credentialFields.map((authField) => /* @__PURE__ */ jsxs(Field.Root, { children: [
235
- /* @__PURE__ */ jsx(Field.Label, {
236
- htmlFor: `mcp-${authField.name}`,
237
- children: authField.label
238
- }),
223
+ /* @__PURE__ */ jsx(Field.Label, { children: authField.label }),
239
224
  /* @__PURE__ */ jsx(Field.Control, {
240
225
  autoComplete: "off",
241
- id: `mcp-${authField.name}`,
242
226
  name: authField.name,
243
227
  placeholder: authField.placeholder,
244
228
  required: authField.required,
@@ -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 };
@@ -1,7 +1,8 @@
1
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";
2
+ import { L as XIcon, M as SpinnerIcon } from "../icons-D2Q5n5C1.js";
3
3
  import { Button } from "./Button.js";
4
4
  import { BrowserLiveView } from "./BrowserLiveView.js";
5
+ import { WebPreview, WebPreviewNavigation, WebPreviewNavigationButton, WebPreviewUrl } from "./WebPreview.js";
5
6
  import * as React from "react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region src/components/BrowserRunPanel.tsx
@@ -12,12 +13,15 @@ function isInspectablePage(target) {
12
13
  function targetLabel(target) {
13
14
  return target.title || target.pageUrl || target.targetId;
14
15
  }
16
+ function targetGlyph(target) {
17
+ return (target.title || target.pageUrl || target.targetId).trim().charAt(0).toUpperCase() || "•";
18
+ }
15
19
  function pickTarget(targets, targetId) {
16
20
  return (targetId ? targets.find((target) => target.targetId === targetId) : void 0) ?? targets.find(isInspectablePage) ?? targets[0];
17
21
  }
18
22
  function BrowserRunPanelState({ children, role, tone = "neutral" }) {
19
23
  return /* @__PURE__ */ jsx("div", {
20
- className: "browser-run-panel__state",
24
+ className: "web-preview__body--empty browser-run-panel__state",
21
25
  "data-tone": tone,
22
26
  role: role ?? (tone === "danger" ? "alert" : void 0),
23
27
  children
@@ -33,84 +37,116 @@ function BrowserRunPanelState({ children, role, tone = "neutral" }) {
33
37
  function BrowserRunPanel({ liveView, onRefresh, onCloseSession, loading, error, label = "Browser", empty = "No live browser session", fallback, className, ref, ...props }) {
34
38
  const [selectedTargetId, setSelectedTargetId] = React.useState();
35
39
  const [receivedAt, setReceivedAt] = React.useState(() => Date.now());
40
+ const tabRefs = React.useRef(/* @__PURE__ */ new Map());
41
+ const tabListId = React.useId();
36
42
  React.useEffect(() => {
37
43
  if (liveView) setReceivedAt(Date.now());
38
44
  }, [liveView]);
39
45
  const targets = liveView?.targets ?? [];
40
46
  const target = pickTarget(targets, selectedTargetId);
41
- const pageLabel = target ? targetLabel(target) : "Waiting for a shared browser session";
42
- return /* @__PURE__ */ jsxs("section", {
47
+ const pageUrl = target?.pageUrl ?? "";
48
+ const selectTarget = React.useCallback((nextTarget) => {
49
+ setSelectedTargetId(nextTarget.targetId);
50
+ }, []);
51
+ const handleTabListKeyDown = (event) => {
52
+ const currentIndex = targets.findIndex((candidate) => candidate.targetId === target?.targetId);
53
+ if (currentIndex < 0) return;
54
+ let nextIndex;
55
+ if (event.key === "ArrowRight") nextIndex = (currentIndex + 1) % targets.length;
56
+ if (event.key === "ArrowLeft") nextIndex = (currentIndex - 1 + targets.length) % targets.length;
57
+ if (event.key === "Home") nextIndex = 0;
58
+ if (event.key === "End") nextIndex = targets.length - 1;
59
+ if (nextIndex === void 0) return;
60
+ event.preventDefault();
61
+ const nextTarget = targets[nextIndex];
62
+ if (!nextTarget) return;
63
+ selectTarget(nextTarget);
64
+ tabRefs.current.get(nextTarget.targetId)?.focus();
65
+ };
66
+ return /* @__PURE__ */ jsxs(WebPreview, {
43
67
  ref,
44
68
  className: cx("browser-run-panel", className),
69
+ url: pageUrl,
70
+ role: "region",
45
71
  "aria-label": typeof label === "string" ? label : "Browser",
46
72
  ...props,
47
73
  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, {
74
+ targets.length > 1 ? /* @__PURE__ */ jsx(WebPreviewNavigation, { children: /* @__PURE__ */ jsx("div", {
75
+ id: tabListId,
76
+ className: "browser-run-panel__tabs",
77
+ role: "tablist",
78
+ "aria-label": "Open browser pages",
79
+ onKeyDown: handleTabListKeyDown,
80
+ children: targets.map((candidate) => {
81
+ const selected = candidate.targetId === target?.targetId;
82
+ return /* @__PURE__ */ jsxs(Button, {
83
+ ref: (node) => {
84
+ if (node) tabRefs.current.set(candidate.targetId, node);
85
+ else tabRefs.current.delete(candidate.targetId);
86
+ },
66
87
  type: "button",
67
88
  variant: "ghost",
68
89
  color: "neutral",
69
- size: "icon-sm",
70
- "aria-label": "Refresh browser view",
90
+ size: "sm",
91
+ className: "browser-run-panel__tab",
92
+ role: "tab",
93
+ id: `${tabListId}-${candidate.targetId}`,
94
+ "aria-selected": selected,
95
+ "aria-controls": `${tabListId}-panel`,
96
+ tabIndex: selected ? 0 : -1,
97
+ onClick: () => selectTarget(candidate),
98
+ children: [/* @__PURE__ */ jsx("span", {
99
+ className: "browser-run-panel__tab-glyph",
100
+ "aria-hidden": "true",
101
+ children: targetGlyph(candidate)
102
+ }), /* @__PURE__ */ jsx("span", {
103
+ className: "browser-run-panel__tab-title",
104
+ children: targetLabel(candidate)
105
+ })]
106
+ }, candidate.targetId);
107
+ })
108
+ }) }) : null,
109
+ /* @__PURE__ */ jsxs(WebPreviewNavigation, {
110
+ className: "browser-run-panel__address-bar",
111
+ children: [
112
+ /* @__PURE__ */ jsx(WebPreviewUrl, {
113
+ "aria-label": "Browser page URL",
114
+ placeholder: "Waiting for browser session",
115
+ readOnly: true
116
+ }),
117
+ onRefresh ? /* @__PURE__ */ jsx(WebPreviewNavigationButton, {
118
+ action: "reload",
119
+ label: "Refresh browser view",
71
120
  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",
121
+ onClick: () => void onRefresh()
122
+ }) : null,
123
+ onCloseSession ? /* @__PURE__ */ jsx(WebPreviewNavigationButton, {
124
+ label: "Close browser session",
80
125
  onClick: () => void onCloseSession(),
81
126
  children: /* @__PURE__ */ jsx(XIcon, {})
82
- }) : null]
83
- })]
127
+ }) : null
128
+ ]
84
129
  }),
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
130
  error ? /* @__PURE__ */ jsx(BrowserRunPanelState, {
103
131
  tone: "danger",
104
132
  children: error
105
133
  }) : null,
106
134
  /* @__PURE__ */ jsx("div", {
107
- className: "browser-run-panel__body",
135
+ id: `${tabListId}-panel`,
136
+ className: "web-preview__body browser-run-panel__body",
137
+ role: targets.length > 1 ? "tabpanel" : void 0,
138
+ "aria-labelledby": targets.length > 1 && target ? `${tabListId}-${target.targetId}` : void 0,
108
139
  "aria-busy": loading || void 0,
109
140
  children: target && liveView ? /* @__PURE__ */ jsx(BrowserLiveView, {
110
- url: target.url,
111
- expiresAt: receivedAt + liveView.expiresInMs,
141
+ source: {
142
+ url: target.url,
143
+ targetId: target.targetId,
144
+ expiresInMs: liveView.expiresInMs
145
+ },
146
+ receivedAt,
112
147
  pageTitle: target.title ?? target.pageUrl,
113
- fallback
148
+ fallback,
149
+ className: "browser-run-panel__live-view"
114
150
  }) : loading ? /* @__PURE__ */ jsxs(BrowserRunPanelState, {
115
151
  role: "status",
116
152
  children: [/* @__PURE__ */ jsx(SpinnerIcon, { className: "browser-run-panel__spinner" }), "Loading browser session"]
@@ -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 { u as ModelSelectorLogo } from "../ModelSelector-DXtpcRmT.js";
4
+ import { u as ModelSelectorLogo } from "../ModelSelector-BB-xB-Es.js";
5
5
  import "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/ChatGptSignInScreen.tsx
@@ -0,0 +1,63 @@
1
+ import * as React from "react";
2
+
3
+ //#region src/components/CodexModelSelector.d.ts
4
+ /** One server-supported value for a Codex response setting. */
5
+ interface CodexModelSelectorSetting {
6
+ readonly id: string;
7
+ readonly label: string;
8
+ /** Optional explanation shown below the option, such as its usage impact. */
9
+ readonly description?: string;
10
+ }
11
+ interface CodexModelSelectorProps {
12
+ /** Model options allowed by the consuming app. Keep authentication and entitlement checks server-side. */
13
+ readonly models: readonly CodexModelSelectorSetting[];
14
+ /** Current model id used by the consuming app's AI SDK provider. */
15
+ readonly selectedModelId?: string | null;
16
+ /** Called only with an id from the app-provided catalog. */
17
+ readonly onSelectModel: (modelId: string) => void;
18
+ /** Server-supported reasoning effort values for the selected model. */
19
+ readonly effortOptions?: readonly CodexModelSelectorSetting[];
20
+ readonly selectedEffortId?: string | null;
21
+ readonly onSelectEffort?: (effortId: string) => void;
22
+ /** Server-supported response-speed values for the selected model. */
23
+ readonly speedOptions?: readonly CodexModelSelectorSetting[];
24
+ readonly selectedSpeedId?: string | null;
25
+ readonly onSelectSpeed?: (speedId: string) => void;
26
+ /** Restores all values to the consuming app's preferred defaults. */
27
+ readonly onReset?: () => void;
28
+ readonly disabled?: boolean;
29
+ readonly className?: string;
30
+ /** Controlled menu open state. */
31
+ readonly open?: boolean;
32
+ /** Initial menu state for uncontrolled usage. */
33
+ readonly defaultOpen?: boolean;
34
+ readonly onOpenChange?: (open: boolean) => void;
35
+ readonly "aria-label"?: string;
36
+ }
37
+ /**
38
+ * A compact Codex-style response settings picker: model, reasoning effort,
39
+ * response speed, and reset are kept in one cascading menu.
40
+ *
41
+ * The consuming app supplies every selectable value. This component only
42
+ * renders those values and never guesses model ids or account entitlements.
43
+ */
44
+ declare function CodexModelSelector({
45
+ models,
46
+ selectedModelId,
47
+ onSelectModel,
48
+ effortOptions,
49
+ selectedEffortId,
50
+ onSelectEffort,
51
+ speedOptions,
52
+ selectedSpeedId,
53
+ onSelectSpeed,
54
+ onReset,
55
+ disabled,
56
+ className,
57
+ open,
58
+ defaultOpen,
59
+ onOpenChange,
60
+ "aria-label": ariaLabel
61
+ }: CodexModelSelectorProps): React.JSX.Element;
62
+ //#endregion
63
+ export { CodexModelSelector, CodexModelSelectorProps, CodexModelSelectorSetting };