@mcp-b/react-components 0.38.0 → 0.40.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 (89) hide show
  1. package/dist/{CodeBlock-DO4Hdu1R.js → CodeBlock-CWk1FVtm.js} +1 -1
  2. package/dist/{FileTree-DbQMpvxV.js → FileTree-xqHYBw4g.js} +1 -1
  3. package/dist/{WorkspaceFileView-DeGzWwhK.js → WorkspaceFileView-Lab3om3F.js} +2 -2
  4. package/dist/components/agents-sdk/AgentActivity.js +1 -1
  5. package/dist/components/agents-sdk/AgentActivityPreview.js +1 -1
  6. package/dist/components/agents-sdk/AgentChat.js +7 -7
  7. package/dist/components/agents-sdk/BrowserLiveView.d.ts +73 -25
  8. package/dist/components/agents-sdk/BrowserLiveView.js +95 -65
  9. package/dist/components/agents-sdk/BrowserReplay.js +1 -1
  10. package/dist/components/agents-sdk/BrowserRunPanel.d.ts +72 -25
  11. package/dist/components/agents-sdk/BrowserRunPanel.js +109 -62
  12. package/dist/components/agents-sdk/McpConnectionRequest.js +1 -1
  13. package/dist/components/agents-sdk/McpPluginCatalog.js +1 -1
  14. package/dist/components/agents-sdk/McpServerPicker.js +1 -1
  15. package/dist/components/agents-sdk/ModelSelector.js +1 -1
  16. package/dist/components/agents-sdk/PromptInputCommands.js +1 -1
  17. package/dist/components/agents-sdk/SubagentRunIndicator.js +1 -1
  18. package/dist/components/agents-sdk/SubagentRunSelector.js +1 -1
  19. package/dist/components/agents-sdk/ThinkChatToolPanels.js +1 -1
  20. package/dist/components/agents-sdk/ThinkExecutionDetail.js +1 -1
  21. package/dist/components/agents-sdk/ThinkExecutionStatus.js +1 -1
  22. package/dist/components/agents-sdk/ThinkExecutionWorkbench.js +1 -1
  23. package/dist/components/agents-sdk/ThinkFullApp.js +8 -7
  24. package/dist/components/agents-sdk/ThinkRoutineBrowser.js +1 -1
  25. package/dist/components/agents-sdk/ThinkRoutineForm.js +1 -1
  26. package/dist/components/agents-sdk/ThinkRoutineLibrary.js +1 -1
  27. package/dist/components/agents-sdk/ThinkThreadPicker.js +1 -1
  28. package/dist/components/agents-sdk/WorkspaceBrowser.js +1 -1
  29. package/dist/components/agents-sdk/WorkspaceExplorer.js +1 -1
  30. package/dist/components/agents-sdk/WorkspaceFileView.js +1 -1
  31. package/dist/components/agents-sdk/WorkspaceHeader.js +1 -1
  32. package/dist/components/agents-sdk/WorkspaceMountManager.js +1 -1
  33. package/dist/components/ai-sdk/Activity.js +1 -1
  34. package/dist/components/ai-sdk/AgentChatStartScreen.js +1 -1
  35. package/dist/components/ai-sdk/Approval.js +1 -1
  36. package/dist/components/ai-sdk/Artifact.js +1 -1
  37. package/dist/components/ai-sdk/ChainOfThought.d.ts +1 -1
  38. package/dist/components/ai-sdk/ChainOfThought.js +1 -1
  39. package/dist/components/ai-sdk/Checkpoint.js +1 -1
  40. package/dist/components/ai-sdk/CodeBlock.js +1 -1
  41. package/dist/components/ai-sdk/Conversation.js +1 -1
  42. package/dist/components/ai-sdk/Goal.js +1 -1
  43. package/dist/components/ai-sdk/MediaPreviewCard.js +1 -1
  44. package/dist/components/ai-sdk/Message.js +1 -1
  45. package/dist/components/ai-sdk/MessageScroller.js +1 -1
  46. package/dist/components/ai-sdk/MessageScrollerVirtual.js +1 -1
  47. package/dist/components/ai-sdk/Plan.js +1 -1
  48. package/dist/components/ai-sdk/PromptInput.js +1 -1
  49. package/dist/components/ai-sdk/Queue.js +1 -1
  50. package/dist/components/ai-sdk/Reasoning.js +1 -1
  51. package/dist/components/ai-sdk/Sources.js +1 -1
  52. package/dist/components/ai-sdk/Task.js +1 -1
  53. package/dist/components/extension/ChatGptSignInScreen.d.ts +21 -10
  54. package/dist/components/extension/ChatGptSignInScreen.js +42 -34
  55. package/dist/components/extension/CodexModelSelector.js +1 -1
  56. package/dist/components/extension/ConversationTabPicker.js +1 -1
  57. package/dist/components/foundations/Checkbox.js +1 -1
  58. package/dist/components/foundations/ContextMenu.js +1 -1
  59. package/dist/components/foundations/EmptyState.d.ts +10 -0
  60. package/dist/components/foundations/EmptyState.js +10 -0
  61. package/dist/components/foundations/Menubar.js +1 -1
  62. package/dist/components/foundations/SearchField.js +1 -1
  63. package/dist/components/foundations/Select.js +1 -1
  64. package/dist/components/foundations/Stepper.js +1 -1
  65. package/dist/components/foundations/Toast.js +1 -1
  66. package/dist/components/foundations/icons.d.ts +2 -2
  67. package/dist/components/foundations/icons.js +2 -2
  68. package/dist/components/general-purpose/Commit.js +2 -2
  69. package/dist/components/general-purpose/EnvironmentVariables.js +2 -2
  70. package/dist/components/general-purpose/FileTree.js +1 -1
  71. package/dist/components/general-purpose/PricingPlanCard.js +1 -1
  72. package/dist/components/general-purpose/ResponsiveSidebarShell.js +1 -1
  73. package/dist/components/general-purpose/SetupChecklist.js +1 -1
  74. package/dist/components/general-purpose/Terminal.js +2 -2
  75. package/dist/components/general-purpose/TestResults.js +1 -1
  76. package/dist/components/general-purpose/ThemeModeControl.js +1 -1
  77. package/dist/components/general-purpose/WebPreview.js +1 -1
  78. package/dist/components/nanites/AgentConnection.js +2 -2
  79. package/dist/components/nanites/AgentToolDetail.js +1 -1
  80. package/dist/{copy-button-4YvddK4e.js → copy-button-Bc7jsYv_.js} +1 -1
  81. package/dist/{icons-BGZ916hb.d.ts → icons-DwgNir1Q.d.ts} +6 -1
  82. package/dist/{icons-B4mGvCvc.js → icons-MmIXciMO.js} +3 -2
  83. package/dist/styles/agent-chat.css +2 -2
  84. package/dist/styles/base.css +4 -0
  85. package/dist/styles/browser-live-view.css +45 -0
  86. package/dist/styles/browser-run-panel.css +3 -1
  87. package/dist/styles/chatgpt-sign-in-screen.css +6 -0
  88. package/dist/styles/think-full-app.css +14 -0
  89. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { t as cx } from "./class-names-BiMDVpUo.js";
2
2
  import { Select } from "./components/foundations/Select.js";
3
- import { t as CopyButton } from "./copy-button-4YvddK4e.js";
3
+ import { t as CopyButton } from "./copy-button-Bc7jsYv_.js";
4
4
  import * as React from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { createHighlighter } from "shiki";
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "./class-names-BiMDVpUo.js";
2
- import { C as FileIcon, T as FolderOpenIcon, m as ChevronRightIcon, w as FolderIcon } from "./icons-B4mGvCvc.js";
2
+ import { C as FileIcon, T as FolderOpenIcon, m as ChevronRightIcon, w as FolderIcon } from "./icons-MmIXciMO.js";
3
3
  import { t as NavigationLink } from "./navigation-link-kqMa81v5.js";
4
4
  import * as React from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,6 +1,6 @@
1
- import { A as MaximizeIcon, d as CheckIcon, h as CodeIcon, r as ArrowLeftIcon, tt as WarningIcon, x as EyeIcon, y as DownloadIcon } from "./icons-B4mGvCvc.js";
1
+ import { d as CheckIcon, h as CodeIcon, j as MaximizeIcon, nt as WarningIcon, r as ArrowLeftIcon, x as EyeIcon, y as DownloadIcon } from "./icons-MmIXciMO.js";
2
2
  import { r as formatClock } from "./format-DB2mX9DA.js";
3
- import { a as CodeBlockCopyButton, i as CodeBlockContent, r as CodeBlockContainer, t as CodeBlock } from "./CodeBlock-DO4Hdu1R.js";
3
+ import { a as CodeBlockCopyButton, i as CodeBlockContent, r as CodeBlockContainer, t as CodeBlock } from "./CodeBlock-CWk1FVtm.js";
4
4
  import { AppTopBar } from "./components/general-purpose/AppTopBar.js";
5
5
  import { inferWorkspaceLanguage, inferWorkspaceMedia, isWorkspaceMarkdownPath, isWorkspaceSkillPath, parseWorkspaceSkill } from "./components/agents-sdk/WorkspaceFile.js";
6
6
  import { c as useWorkspaceNavigationContext, s as useWorkspaceNavigationRestoration } from "./WorkspaceLink-DtH66bAY.js";
@@ -1,5 +1,5 @@
1
1
  import { inferFaviconUrl } from "../../utils/favicon.js";
2
- import { $ as TerminalIcon, B as PencilIcon, C as FileIcon, F as MonitorIcon, G as SearchIcon, O as InfoIcon, k as LinkIcon, nt as WrenchIcon, tt as WarningIcon } from "../../icons-B4mGvCvc.js";
2
+ import { A as LinkIcon, C as FileIcon, I as MonitorIcon, K as SearchIcon, O as InfoIcon, V as PencilIcon, et as TerminalIcon, nt as WarningIcon, rt as WrenchIcon } from "../../icons-MmIXciMO.js";
3
3
  import * as React from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/agents-sdk/AgentActivity.tsx
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
- import { C as FileIcon, J as SpinnerIcon, j as McpIcon, rt as XIcon, tt as WarningIcon } from "../../icons-B4mGvCvc.js";
2
+ import { C as FileIcon, M as McpIcon, Y as SpinnerIcon, it as XIcon, nt as WarningIcon } from "../../icons-MmIXciMO.js";
3
3
  import { ActivityHeader } from "../ai-sdk/Activity.js";
4
4
  import { Button } from "../foundations/Button.js";
5
5
  import { t as NavigationLink } from "../../navigation-link-kqMa81v5.js";
@@ -1,6 +1,6 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
2
  import { ExpansionStateProvider } from "../ai-sdk/ExpansionState.js";
3
- import { B as PencilIcon, H as PlusIcon, J as SpinnerIcon, L as MoveToFrontIcon, U as QueueIcon, W as RefreshIcon, et as TrashIcon, t as ArchiveIcon } from "../../icons-B4mGvCvc.js";
3
+ import { G as RefreshIcon, R as MoveToFrontIcon, U as PlusIcon, V as PencilIcon, W as QueueIcon, Y as SpinnerIcon, t as ArchiveIcon, tt as TrashIcon } from "../../icons-MmIXciMO.js";
4
4
  import { Button } from "../foundations/Button.js";
5
5
  import { AgentChatMessageEditor } from "../ai-sdk/AgentChatMessageEditor.js";
6
6
  import { IconButton } from "../foundations/IconButton.js";
@@ -10,7 +10,7 @@ import { AgentChatStartScreen } from "../ai-sdk/AgentChatStartScreen.js";
10
10
  import { Message, MessageAction, MessageActions } from "../ai-sdk/Message.js";
11
11
  import { AgentMessageParts } from "../ai-sdk/AgentMessageParts.js";
12
12
  import { Marker, MarkerContent } from "../foundations/Marker.js";
13
- import { t as CopyButton } from "../../copy-button-4YvddK4e.js";
13
+ import { t as CopyButton } from "../../copy-button-Bc7jsYv_.js";
14
14
  import { Context, ContextContent, ContextContentBody, ContextContentHeader, ContextTrigger, getContextUsage, getContextUsedTokens } from "../ai-sdk/Context.js";
15
15
  import { VirtualMessageScroller } from "../ai-sdk/MessageScrollerVirtual.js";
16
16
  import { QueueItem, QueueItemActions, QueueItemContent } from "../ai-sdk/Queue.js";
@@ -666,13 +666,13 @@ function AgentChat({ className, messages, status = "ready", onSubmit, showCompos
666
666
  "aria-atomic": "true",
667
667
  children: copyAnnouncement
668
668
  }),
669
- loading ? /* @__PURE__ */ jsxs("div", {
669
+ loading ? /* @__PURE__ */ jsx("div", {
670
670
  className: "agent-chat__transcript-loading",
671
671
  role: "status",
672
- children: [/* @__PURE__ */ jsx(SpinnerIcon, {}), /* @__PURE__ */ jsx("span", {
673
- className: "sigvelo-visually-hidden",
674
- children: "Loading conversation"
675
- })]
672
+ children: /* @__PURE__ */ jsx(EmptyState, {
673
+ icon: /* @__PURE__ */ jsx(SpinnerIcon, {}),
674
+ description: "Loading conversation"
675
+ })
676
676
  }) : showsStartScreen ? /* @__PURE__ */ jsx(AgentChatStartScreen, {
677
677
  ...startScreenProps,
678
678
  composer
@@ -2,27 +2,85 @@ import * as React from "react";
2
2
  import { BrowserLiveViewUrl } from "agents/browser";
3
3
 
4
4
  //#region src/components/agents-sdk/BrowserLiveView.d.ts
5
- interface BrowserLiveViewProps extends React.HTMLAttributes<HTMLDivElement> {
6
- /** Complete upstream value returned by `cdp.getLiveViewUrl()`. */
7
- source: BrowserLiveViewUrl;
8
- /** Epoch ms when `source` was received. Defaults to its first render. */
9
- receivedAt?: number;
10
- /** The page the session was showing — used for the ended-state text. */
5
+ /** Where a live view is in its lifecycle, however the pixels arrive. */
6
+ type BrowserLiveViewPhase = "connecting" | "streaming" | "reconnecting" | "idle" | "failed";
7
+ interface BrowserLiveViewCommonProps extends React.HTMLAttributes<HTMLDivElement> {
8
+ /** The page being shown names the surface and labels its ended state. */
11
9
  pageTitle?: string;
12
10
  /**
13
- * Shown once the link expires pass the last screenshot the agent took
11
+ * Replaces the built-in text for the current phase e.g. a failure reason.
12
+ * Set it while the view is live and it reads as a notice over the picture,
13
+ * which is where a fault that does not end the session belongs.
14
+ */
15
+ statusLabel?: string;
16
+ /**
17
+ * Shown once the view stops — pass the last screenshot the agent took
14
18
  * (`<img>`) to keep the final frame on screen. Defaults to a quiet
15
19
  * "Live view ended" state.
16
20
  */
17
21
  fallback?: React.ReactNode;
22
+ /** Omits the expand control for a view that already fills its viewport. */
23
+ expandable?: boolean;
24
+ /**
25
+ * Takes the whole container instead of standing in it as a 16/10 card.
26
+ * The card treatment — the aspect box, the hairline, the rounded corners —
27
+ * is there to separate the view from what sits beside it; a view that is
28
+ * the entire surface, as it is on a phone, has nothing to separate from and
29
+ * spends real pixels saying so. Usually paired with `expandable={false}`,
30
+ * since there is nowhere larger to expand into.
31
+ */
32
+ fill?: boolean;
18
33
  }
34
+ type BrowserLiveViewProps = BrowserLiveViewCommonProps & ({
35
+ /** Complete upstream value returned by `cdp.getLiveViewUrl()`. */source: BrowserLiveViewUrl; /** Epoch ms when `source` was received. Defaults to its first render. */
36
+ receivedAt?: number;
37
+ renderMedia?: never;
38
+ phase?: never;
39
+ } | {
40
+ /**
41
+ * The picture, drawn by the host — a `<video>` on its own transport, a
42
+ * canvas, whatever it carries. Called once per placement because
43
+ * neither a video nor an iframe can occupy two places at once.
44
+ *
45
+ * Remote-control surfaces belong in here too: coordinate mapping and
46
+ * key handling are the host's, since only it knows what its input API
47
+ * expects. This component owns the frame around them. An overlay drawn
48
+ * here covers the picture and stays under the frame's own chrome, so a
49
+ * control surface can take every pointer the picture gets without
50
+ * swallowing the Live badge or the expand control.
51
+ *
52
+ * `statusId` names whatever element currently states the phase, for a
53
+ * control surface to point `aria-describedby` at.
54
+ */
55
+ renderMedia: (placement: "inline" | "dialog", statusId: string) => React.ReactNode; /** The host draws, so the host owns the lifecycle. A URL derives its own. */
56
+ phase: BrowserLiveViewPhase;
57
+ source?: never;
58
+ receivedAt?: never;
59
+ });
19
60
  /**
20
- * Embeds a Browser Run Live View — a real-time, interactive view of the
21
- * agent's browser session. Links expire (~5 minutes), so the iframe swaps to
22
- * `fallback` (e.g. the last screenshot) once `source.expiresInMs` passes.
23
- * While the hosted viewer connects, a quiet loading overlay hides the frame; if it
24
- * never loads, the view degrades to `fallback` or an unavailable state. An
25
- * expand button opens the same view in a fullscreen dialog.
61
+ * A live, interactive view of an agent's browser session.
62
+ *
63
+ * It takes the session either way round. Give it `source` — a Browser Run
64
+ * `BrowserLiveViewUrl` — and it embeds the hosted viewer, expiring the frame
65
+ * on its own once `expiresInMs` passes. Give it `renderMedia` and a `phase`
66
+ * instead and the host draws the picture on its own transport while this keeps
67
+ * the frame around it. The Live badge, the connecting overlay, the ended
68
+ * state, and the fullscreen expand are the same either way, because to the
69
+ * person watching it is the same thing.
70
+ *
71
+ * `fill` drops the card treatment for a view that is the whole surface rather
72
+ * than a block on one — the phone's screen, or a panel that already sizes
73
+ * itself. Everything else about the frame is unchanged.
74
+ *
75
+ * A host-drawn view keeps its own input surface, because coordinate mapping
76
+ * and key handling depend on what the host's input API expects — a screencast
77
+ * wants CSS pixels offset by its viewport, and nothing generic here could know
78
+ * that. This component owns the frame; the host owns the picture and control.
79
+ *
80
+ * The two sources differ in what the host must trust. A hosted URL embeds a
81
+ * third-party origin and runs its script; the iframe is unsandboxed by design
82
+ * because the viewer needs scripts and its own origin to stream the session,
83
+ * so only pass trusted Live View URLs. Host-drawn media embeds no origin.
26
84
  *
27
85
  * Where URLs come from (upstream: `agents/browser` in
28
86
  * github.com/cloudflare/agents — `BrowserLiveViewUrl`): the model calls
@@ -32,17 +90,7 @@ interface BrowserLiveViewProps extends React.HTMLAttributes<HTMLDivElement> {
32
90
  *
33
91
  * Styling rides `@mcp-b/react-components/styles` (browser-live-view.css),
34
92
  * which think-chat consumers load anyway.
35
- *
36
- * The iframe is unsandboxed by design: the hosted Live View UI needs scripts
37
- * and its own origin to stream the session. Only pass trusted Live View URLs.
38
93
  */
39
- declare function BrowserLiveView({
40
- source,
41
- receivedAt,
42
- pageTitle,
43
- fallback,
44
- className,
45
- ...props
46
- }: BrowserLiveViewProps): React.JSX.Element;
94
+ declare function BrowserLiveView(props: BrowserLiveViewProps): React.JSX.Element;
47
95
  //#endregion
48
- export { BrowserLiveView, BrowserLiveViewProps };
96
+ export { BrowserLiveView, BrowserLiveViewPhase, BrowserLiveViewProps };
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
- import { A as MaximizeIcon, J as SpinnerIcon } from "../../icons-B4mGvCvc.js";
2
+ import { Y as SpinnerIcon, j as MaximizeIcon } from "../../icons-MmIXciMO.js";
3
3
  import { Button } from "../foundations/Button.js";
4
4
  import { Dialog } from "../foundations/Dialog.js";
5
5
  import { StatusPill } from "../foundations/StatusPill.js";
@@ -8,6 +8,13 @@ import * as React from "react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/components/agents-sdk/BrowserLiveView.tsx
10
10
  const FRAME_LOAD_TIMEOUT_MS = 15e3;
11
+ const PHASE_TEXT = {
12
+ connecting: "Connecting live view…",
13
+ streaming: "Live",
14
+ reconnecting: "Reconnecting live view…",
15
+ idle: "Live view ended",
16
+ failed: "Live view unavailable"
17
+ };
11
18
  function EndedState({ title, detail }) {
12
19
  return /* @__PURE__ */ jsxs("div", {
13
20
  className: "browser-live-view__ended",
@@ -21,12 +28,29 @@ function EndedState({ title, detail }) {
21
28
  });
22
29
  }
23
30
  /**
24
- * Embeds a Browser Run Live View — a real-time, interactive view of the
25
- * agent's browser session. Links expire (~5 minutes), so the iframe swaps to
26
- * `fallback` (e.g. the last screenshot) once `source.expiresInMs` passes.
27
- * While the hosted viewer connects, a quiet loading overlay hides the frame; if it
28
- * never loads, the view degrades to `fallback` or an unavailable state. An
29
- * expand button opens the same view in a fullscreen dialog.
31
+ * A live, interactive view of an agent's browser session.
32
+ *
33
+ * It takes the session either way round. Give it `source` — a Browser Run
34
+ * `BrowserLiveViewUrl` — and it embeds the hosted viewer, expiring the frame
35
+ * on its own once `expiresInMs` passes. Give it `renderMedia` and a `phase`
36
+ * instead and the host draws the picture on its own transport while this keeps
37
+ * the frame around it. The Live badge, the connecting overlay, the ended
38
+ * state, and the fullscreen expand are the same either way, because to the
39
+ * person watching it is the same thing.
40
+ *
41
+ * `fill` drops the card treatment for a view that is the whole surface rather
42
+ * than a block on one — the phone's screen, or a panel that already sizes
43
+ * itself. Everything else about the frame is unchanged.
44
+ *
45
+ * A host-drawn view keeps its own input surface, because coordinate mapping
46
+ * and key handling depend on what the host's input API expects — a screencast
47
+ * wants CSS pixels offset by its viewport, and nothing generic here could know
48
+ * that. This component owns the frame; the host owns the picture and control.
49
+ *
50
+ * The two sources differ in what the host must trust. A hosted URL embeds a
51
+ * third-party origin and runs its script; the iframe is unsandboxed by design
52
+ * because the viewer needs scripts and its own origin to stream the session,
53
+ * so only pass trusted Live View URLs. Host-drawn media embeds no origin.
30
54
  *
31
55
  * Where URLs come from (upstream: `agents/browser` in
32
56
  * github.com/cloudflare/agents — `BrowserLiveViewUrl`): the model calls
@@ -36,22 +60,21 @@ function EndedState({ title, detail }) {
36
60
  *
37
61
  * Styling rides `@mcp-b/react-components/styles` (browser-live-view.css),
38
62
  * which think-chat consumers load anyway.
39
- *
40
- * The iframe is unsandboxed by design: the hosted Live View UI needs scripts
41
- * and its own origin to stream the session. Only pass trusted Live View URLs.
42
63
  */
43
- function BrowserLiveView({ source, receivedAt, pageTitle, fallback, className, ...props }) {
44
- const [expired, setExpired] = React.useState(() => receivedAt !== void 0 && receivedAt + source.expiresInMs <= Date.now());
64
+ function BrowserLiveView(props) {
65
+ const { source, receivedAt, renderMedia, phase, pageTitle, statusLabel, fallback, expandable = true, fill, className, ...rest } = props;
66
+ const [expired, setExpired] = React.useState(() => source !== void 0 && receivedAt !== void 0 && receivedAt + source.expiresInMs <= Date.now());
45
67
  const [reportedFrameState, setReportedFrameState] = React.useState(() => ({
46
- url: source.url,
68
+ url: source?.url ?? "",
47
69
  value: "loading"
48
70
  }));
49
- if (reportedFrameState.url !== source.url) setReportedFrameState({
71
+ if (source && reportedFrameState.url !== source.url) setReportedFrameState({
50
72
  url: source.url,
51
73
  value: "loading"
52
74
  });
53
- const frameState = reportedFrameState.url === source.url ? reportedFrameState.value : "loading";
75
+ const frameState = source && reportedFrameState.url === source.url ? reportedFrameState.value : "loading";
54
76
  React.useEffect(() => {
77
+ if (!source) return;
55
78
  const remaining = (receivedAt ?? Date.now()) + source.expiresInMs - Date.now();
56
79
  if (remaining <= 0) {
57
80
  setExpired(true);
@@ -60,57 +83,71 @@ function BrowserLiveView({ source, receivedAt, pageTitle, fallback, className, .
60
83
  setExpired(false);
61
84
  const timer = setTimeout(() => setExpired(true), remaining);
62
85
  return () => clearTimeout(timer);
63
- }, [
64
- receivedAt,
65
- source.expiresInMs,
66
- source.targetId,
67
- source.url
68
- ]);
86
+ }, [receivedAt, source]);
69
87
  React.useEffect(() => {
70
- if (frameState !== "loading") return;
88
+ if (!source || frameState !== "loading") return;
71
89
  const url = source.url;
72
90
  const timer = setTimeout(() => setReportedFrameState({
73
91
  url,
74
92
  value: "failed"
75
93
  }), FRAME_LOAD_TIMEOUT_MS);
76
94
  return () => clearTimeout(timer);
77
- }, [frameState, source.url]);
78
- const failed = frameState === "failed";
79
- const live = !expired && !failed;
95
+ }, [frameState, source]);
96
+ const activePhase = source ? expired ? "idle" : frameState === "failed" ? "failed" : frameState === "loading" ? "connecting" : "streaming" : phase ?? "connecting";
97
+ const live = activePhase === "streaming";
98
+ const connecting = activePhase === "connecting" || activePhase === "reconnecting";
80
99
  const frameTitle = pageTitle ? `Live browser view — ${pageTitle}` : "Live browser view";
81
- const view = live ? /* @__PURE__ */ jsxs(Fragment, { children: [
82
- /* @__PURE__ */ jsx(StatusPill, {
83
- busy: true,
84
- size: "sm",
85
- color: "destructive",
86
- className: "browser-live-view__badge",
87
- children: "Live"
88
- }),
89
- /* @__PURE__ */ jsx("iframe", {
90
- className: "browser-live-view__frame",
91
- src: source.url,
92
- title: frameTitle,
93
- allow: "clipboard-read; clipboard-write",
94
- onLoad: () => setReportedFrameState({
95
- url: source.url,
96
- value: "ready"
97
- })
98
- }, source.url),
99
- frameState === "loading" ? /* @__PURE__ */ jsxs(TransitionSurface, {
100
- className: "browser-live-view__state",
101
- role: "status",
102
- children: [/* @__PURE__ */ jsx(SpinnerIcon, {}), "Connecting live view…"]
103
- }) : null
104
- ] }) : fallback ?? /* @__PURE__ */ jsx(EndedState, {
105
- title: failed ? "Live view unavailable" : "Live view ended",
106
- detail: pageTitle
107
- });
100
+ const statusId = React.useId();
101
+ const renderView = (placement) => {
102
+ const placementStatusId = `${statusId}-${placement}`;
103
+ if (!live && !connecting) return fallback ?? /* @__PURE__ */ jsx(EndedState, {
104
+ title: statusLabel ?? PHASE_TEXT[activePhase],
105
+ detail: pageTitle
106
+ });
107
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ live && placement === "inline" ? /* @__PURE__ */ jsx(StatusPill, {
109
+ busy: true,
110
+ size: "sm",
111
+ color: "destructive",
112
+ className: "browser-live-view__badge",
113
+ children: "Live"
114
+ }) : null,
115
+ source ? placement === "dialog" ? /* @__PURE__ */ jsx("iframe", {
116
+ src: source.url,
117
+ title: frameTitle,
118
+ allow: "clipboard-read; clipboard-write"
119
+ }) : /* @__PURE__ */ jsx("iframe", {
120
+ className: "browser-live-view__frame",
121
+ src: source.url,
122
+ title: frameTitle,
123
+ allow: "clipboard-read; clipboard-write",
124
+ onLoad: () => setReportedFrameState({
125
+ url: source.url,
126
+ value: "ready"
127
+ })
128
+ }, source.url) : renderMedia?.(placement, placementStatusId),
129
+ connecting ? /* @__PURE__ */ jsxs(TransitionSurface, {
130
+ className: "browser-live-view__state",
131
+ id: placementStatusId,
132
+ role: "status",
133
+ children: [/* @__PURE__ */ jsx(SpinnerIcon, {}), statusLabel ?? PHASE_TEXT[activePhase]]
134
+ }) : null,
135
+ live && statusLabel ? /* @__PURE__ */ jsx("p", {
136
+ className: "browser-live-view__notice",
137
+ id: placementStatusId,
138
+ role: "status",
139
+ children: statusLabel
140
+ }) : null
141
+ ] });
142
+ };
108
143
  return /* @__PURE__ */ jsxs("div", {
109
- ...props,
144
+ ...rest,
110
145
  className: cx("browser-live-view", className),
111
- "data-expired": expired || failed || void 0,
112
- "data-frame-state": frameState,
113
- children: [view, /* @__PURE__ */ jsxs(Dialog.Root, { children: [/* @__PURE__ */ jsx(Dialog.Trigger, { render: /* @__PURE__ */ jsx(Button, {
146
+ "data-expired": activePhase === "idle" || activePhase === "failed" || void 0,
147
+ "data-fill": fill || void 0,
148
+ "data-frame-state": source ? frameState : void 0,
149
+ "data-phase": activePhase,
150
+ children: [renderView("inline"), expandable ? /* @__PURE__ */ jsxs(Dialog.Root, { children: [/* @__PURE__ */ jsx(Dialog.Trigger, { render: /* @__PURE__ */ jsx(Button, {
114
151
  variant: "ghost",
115
152
  color: "neutral",
116
153
  size: "sm",
@@ -126,16 +163,9 @@ function BrowserLiveView({ source, receivedAt, pageTitle, fallback, className, .
126
163
  children: frameTitle
127
164
  }), /* @__PURE__ */ jsx("div", {
128
165
  className: "browser-live-view__dialog-body",
129
- children: live ? /* @__PURE__ */ jsx("iframe", {
130
- src: source.url,
131
- title: frameTitle,
132
- allow: "clipboard-read; clipboard-write"
133
- }) : fallback ?? /* @__PURE__ */ jsx(EndedState, {
134
- title: failed ? "Live view unavailable" : "Live view ended",
135
- detail: pageTitle
136
- })
166
+ children: renderView("dialog")
137
167
  })]
138
- }) })] })] })]
168
+ }) })] })] }) : null]
139
169
  });
140
170
  }
141
171
  //#endregion
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
- import { V as PlayIcon, z as PauseIcon } from "../../icons-B4mGvCvc.js";
2
+ import { B as PauseIcon, H as PlayIcon } from "../../icons-MmIXciMO.js";
3
3
  import { IconButton } from "../foundations/IconButton.js";
4
4
  import { r as formatClock } from "../../format-DB2mX9DA.js";
5
5
  import { EmptyState } from "../foundations/EmptyState.js";
@@ -2,7 +2,26 @@ import * as React from "react";
2
2
  import { BrowserLiveView } from "agents/browser";
3
3
 
4
4
  //#region src/components/agents-sdk/BrowserRunPanel.d.ts
5
- interface BrowserRunPanelProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
5
+ /**
6
+ * One entry of the drawn tab strip. The panel synthesizes browser chrome
7
+ * around content Chrome itself never paints, so a tab is whatever record of a
8
+ * page the host holds — a Browser Run target, a claimed extension tab — cut
9
+ * down to the identity and words the strip needs.
10
+ */
11
+ interface BrowserRunPanelTab {
12
+ /** Stable identity; the strip's selection value. Hosts stringify their own ids. */
13
+ id: string;
14
+ title?: string;
15
+ url?: string;
16
+ }
17
+ interface BrowserRunPanelCommonProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
18
+ /** Error from the host's browser session or transport. */
19
+ error?: React.ReactNode;
20
+ /** Panel heading. */
21
+ label?: React.ReactNode;
22
+ ref?: React.Ref<HTMLDivElement>;
23
+ }
24
+ interface BrowserRunPanelHostedProps extends BrowserRunPanelCommonProps {
6
25
  /** Host-side Browser Run Live View data from `connector.liveView()`. */
7
26
  liveView?: BrowserLiveView;
8
27
  /** Refreshes the Live View URL before it expires. */
@@ -11,36 +30,64 @@ interface BrowserRunPanelProps extends Omit<React.HTMLAttributes<HTMLDivElement>
11
30
  onCloseSession?: () => void | Promise<void>;
12
31
  /** True while the app is fetching Live View data. */
13
32
  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
33
  /** Empty state shown when there is no shared browser session. */
19
34
  empty?: React.ReactNode;
20
35
  /** Fallback passed through to `BrowserLiveView` after URL expiry/failure. */
21
36
  fallback?: React.ReactNode;
37
+ children?: never;
38
+ tabs?: never;
39
+ selectedTabId?: never;
40
+ onSelectTab?: never;
41
+ url?: never;
42
+ onOpenUrl?: never;
43
+ urlPlaceholder?: never;
44
+ urlDisabled?: never;
45
+ urlLabel?: never;
46
+ navigation?: never;
47
+ }
48
+ interface BrowserRunPanelStreamedProps extends BrowserRunPanelCommonProps {
49
+ /**
50
+ * The host's own browser surface — typically a `BrowserLiveView` carrying a
51
+ * host-fed stream. The panel frames it with the drawn tab strip and address
52
+ * bar; the host keeps everything transport: media, input, reconnection.
53
+ */
54
+ children: React.ReactNode;
55
+ /** The drawn tab strip; it renders only with more than one entry. */
56
+ tabs?: readonly BrowserRunPanelTab[];
57
+ /** Controlled selection over `tabs`; null selects nothing. */
58
+ selectedTabId?: string | null;
59
+ /** A viewer picked a tab it is not watching; the host grants or ignores it. */
60
+ onSelectTab?: (tabId: string) => void;
61
+ /** Address bar value: the streamed page's current URL. */
62
+ url?: string;
63
+ /** Commits an address-bar entry. Absent, the address bar is read-only. */
64
+ onOpenUrl?: (url: string) => void;
65
+ urlPlaceholder?: string;
66
+ urlDisabled?: boolean;
67
+ /** Accessible name for the address input. */
68
+ urlLabel?: string;
69
+ /** Navigation controls before the address input, e.g. Back and Reload. */
70
+ navigation?: React.ReactNode;
71
+ liveView?: never;
72
+ onRefresh?: never;
73
+ onCloseSession?: never;
74
+ loading?: never;
75
+ empty?: never;
76
+ fallback?: never;
22
77
  }
78
+ type BrowserRunPanelProps = BrowserRunPanelHostedProps | BrowserRunPanelStreamedProps;
23
79
  /**
24
- * Right-side Browser Run panel for `ThinkFullApp.workbenchPanels`.
80
+ * Browser panel chrome — drawn tab strip, address bar, framed body — for
81
+ * browser surfaces Chrome itself never paints.
25
82
  *
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.
83
+ * Two hosts fill it in. A Browser Run host passes upstream `BrowserLiveView`
84
+ * data from `connector.liveView()`, and the panel derives tabs, address, and
85
+ * an embedded live view itself, calling app-provided refresh/close callbacks.
86
+ * A streaming host instead passes `children` — its own live surface — with
87
+ * the tab strip, address bar, and navigation controlled from the host's own
88
+ * records, because a remote screencast carries only page pixels and every
89
+ * piece of chrome around it has to be synthesized from host state.
29
90
  */
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;
91
+ declare function BrowserRunPanel(props: BrowserRunPanelProps): React.JSX.Element;
45
92
  //#endregion
46
- export { BrowserRunPanel, BrowserRunPanelProps };
93
+ export { BrowserRunPanel, BrowserRunPanelProps, BrowserRunPanelTab };