@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.
- package/dist/{AgentChat-B9LD_WF1.d.ts → AgentChat-CRmsKg9B.d.ts} +10 -6
- package/dist/{BrowserLiveView-ZktFZiMD.d.ts → BrowserLiveView-BV5qgFgI.d.ts} +11 -10
- package/dist/{BrowserReplay-6G-mnwcn.d.ts → BrowserReplay-xWvRjRAC.d.ts} +2 -1
- package/dist/{Menu-B5vDqZe_.d.ts → Menu-BLhnNnRK.d.ts} +3 -0
- package/dist/{ModelSelector-DXtpcRmT.js → ModelSelector-BB-xB-Es.js} +16 -13
- package/dist/{PromptInput-D6-G24p7.d.ts → PromptInput-IyM6ZETj.d.ts} +13 -45
- package/dist/ThinkChat-B3wYahor.js +190 -0
- package/dist/{ThinkChatActivity-B2UD95My.d.ts → ThinkChatActivity-F4KFPcJA.d.ts} +4 -4
- package/dist/ThinkChatContext-C3Eb1Xpi.d.ts +16 -0
- package/dist/{ThinkChatToolParts-DnUiZgCC.d.ts → ThinkChatToolParts-Cv4o7j6F.d.ts} +13 -5
- package/dist/components/AgentChat.d.ts +1 -1
- package/dist/components/AgentChat.js +57 -23
- package/dist/components/AgentConnection.js +4 -20
- package/dist/components/Badge.js +1 -15
- package/dist/components/BrowserLiveView.d.ts +1 -1
- package/dist/components/BrowserLiveView.js +14 -10
- package/dist/components/BrowserReplay.d.ts +1 -1
- package/dist/components/BrowserRunPanel.js +91 -55
- package/dist/components/ChatGptSignInScreen.js +1 -1
- package/dist/components/CodexModelSelector.d.ts +63 -0
- package/dist/components/CodexModelSelector.js +148 -0
- package/dist/components/Commit.d.ts +2 -2
- package/dist/components/Commit.js +10 -22
- package/dist/components/Conversation.d.ts +2 -6
- package/dist/components/Conversation.js +2 -3
- package/dist/components/EnvironmentVariables.d.ts +2 -7
- package/dist/components/EnvironmentVariables.js +10 -33
- package/dist/components/Marker.js +1 -15
- package/dist/components/McpServerPicker.d.ts +31 -6
- package/dist/components/McpServerPicker.js +129 -67
- package/dist/components/Menu.d.ts +1 -1
- package/dist/components/Menu.js +2 -2
- package/dist/components/Message.d.ts +3 -2
- package/dist/components/Message.js +10 -3
- package/dist/components/ModelSelector.d.ts +11 -8
- package/dist/components/ModelSelector.js +1 -1
- package/dist/components/PromptInput.d.ts +2 -2
- package/dist/components/PromptInput.js +49 -98
- package/dist/components/Queue.d.ts +1 -12
- package/dist/components/Reasoning.js +2 -15
- package/dist/components/Stepper.js +1 -7
- package/dist/components/ThinkChat.d.ts +30 -30
- package/dist/components/ThinkChat.js +1 -152
- package/dist/components/ThinkChatActivity.d.ts +2 -2
- package/dist/components/ThinkChatActivity.js +3 -10
- package/dist/components/ThinkChatContext.d.ts +2 -2
- package/dist/components/ThinkChatContext.js +9 -2
- package/dist/components/ThinkChatToolPanels.d.ts +1 -1
- package/dist/components/ThinkChatToolPanels.js +19 -7
- package/dist/components/ThinkChatToolParts.d.ts +1 -1
- package/dist/components/ThinkExecutionWorkbench.d.ts +10 -7
- package/dist/components/ThinkExecutionWorkbench.js +382 -359
- package/dist/components/ThinkFullApp.d.ts +16 -13
- package/dist/components/ThinkFullApp.js +28 -18
- package/dist/components/ThinkThreadPicker.d.ts +1 -31
- package/dist/components/ThinkThreadPicker.js +32 -171
- package/dist/components/ThinkThreadShell.d.ts +35 -0
- package/dist/components/ThinkThreadShell.js +146 -0
- package/dist/components/WorkspaceExplorer.js +3 -1
- package/dist/merge-refs-DF_7w2x_.js +18 -0
- package/dist/styles/agent-chat.css +4 -1
- package/dist/styles/browser-run-panel.css +57 -110
- package/dist/styles/codex-model-selector.css +197 -0
- package/dist/styles/index.css +4 -1
- package/dist/styles/mcp-server-picker.css +62 -15
- package/dist/styles/message.css +1 -1
- package/dist/styles/model-selector.css +20 -8
- package/dist/styles/prompt-input.css +5 -0
- package/dist/styles/streamdown.css +2 -48
- package/dist/styles/think-execution-workbench.css +337 -0
- package/dist/styles/think-full-app.css +15 -390
- package/dist/styles/think-resize-handle.css +33 -0
- package/dist/styles/think-thread-picker.css +0 -238
- package/dist/styles/think-thread-shell.css +219 -0
- package/dist/styles/workspace-explorer.css +8 -2
- package/dist/summarize-tool-input-BUJsENSn.js +12 -0
- package/package.json +2 -2
- package/dist/ThinkChatContext-BLAAb8J5.d.ts +0 -28
- package/dist/components/McpServerConnections.d.ts +0 -41
- package/dist/components/McpServerConnections.js +0 -188
- package/dist/components/useStickToBottom.d.ts +0 -33
- package/dist/components/useStickToBottom.js +0 -107
- 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__ */
|
|
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,
|
|
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 [
|
|
196
|
-
const
|
|
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 })), ...
|
|
201
|
-
const approvalsWired = Boolean(onApprovalResponse ||
|
|
202
|
-
const
|
|
203
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
345
|
+
thinkStub,
|
|
346
|
+
onExecutionSettled,
|
|
316
347
|
onSkip: () => {
|
|
317
348
|
const id = "part" in pendingApproval ? getApprovalStateKey(pendingApproval.part) : pendingApproval.approval.executionId;
|
|
318
|
-
if (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,
|
package/dist/components/Badge.js
CHANGED
|
@@ -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-
|
|
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 `
|
|
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({
|
|
43
|
-
const [expired, setExpired] = React.useState(() =>
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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-
|
|
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 {
|
|
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
|
|
42
|
-
|
|
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__ */
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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: "
|
|
70
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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-
|
|
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 };
|