@mcp-b/react-components 0.40.0 → 0.42.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 (228) hide show
  1. package/dist/{AgentChat-CVO7xyLC.d.ts → AgentChat-DVr2giwD.d.ts} +1 -1
  2. package/dist/{AgentToolRunsContext-BCVqCtJ1.d.ts → AgentToolRunsContext-daY67hFM.d.ts} +12 -0
  3. package/dist/{CodeBlock-CWk1FVtm.js → CodeBlock-BjELA-_s.js} +1 -1
  4. package/dist/{FileTree-xqHYBw4g.js → FileTree-DMYRdT6R.js} +1 -1
  5. package/dist/McpConnectorForm-MRm1r59e.d.ts +39 -0
  6. package/dist/{Message-DBsHHhcM.d.ts → Message-D_vCVEGB.d.ts} +20 -1
  7. package/dist/{ThinkChatActivity-D6-_5Phw.d.ts → ThinkChatActivity-CsDql-ER.d.ts} +4 -1
  8. package/dist/{ThinkExecutionRun-lYYgyeE1.d.ts → ThinkExecutionRun-k8bpyXqt.d.ts} +3 -3
  9. package/dist/{WorkspaceFileView-Lab3om3F.js → WorkspaceFileView-DwdXiKku.js} +17 -22
  10. package/dist/{WorkspaceMessageResponse-Cy28WGSz.d.ts → WorkspaceMessageResponse-D66ty4FO.d.ts} +1 -1
  11. package/dist/components/agents-sdk/AgentActivity.js +4 -3
  12. package/dist/components/agents-sdk/AgentChat.d.ts +1 -1
  13. package/dist/components/agents-sdk/AgentChat.js +42 -18
  14. package/dist/components/agents-sdk/AgentToolRunsContext.d.ts +1 -1
  15. package/dist/components/agents-sdk/AgentToolRunsContext.js +12 -0
  16. package/dist/components/agents-sdk/BrowserLiveScreen.d.ts +135 -0
  17. package/dist/components/agents-sdk/BrowserLiveScreen.js +637 -0
  18. package/dist/components/agents-sdk/BrowserLiveView.d.ts +2 -2
  19. package/dist/components/agents-sdk/BrowserLiveView.js +6 -9
  20. package/dist/components/agents-sdk/BrowserReplay.js +1 -1
  21. package/dist/components/agents-sdk/BrowserRunPanel.d.ts +1 -1
  22. package/dist/components/agents-sdk/BrowserRunPanel.js +8 -13
  23. package/dist/components/agents-sdk/ManagerOffice.d.ts +168 -0
  24. package/dist/components/agents-sdk/ManagerOffice.js +698 -0
  25. package/dist/components/agents-sdk/McpConnectionRequest.js +3 -3
  26. package/dist/components/agents-sdk/McpConnectorForm.d.ts +1 -38
  27. package/dist/components/agents-sdk/McpPluginCatalog.d.ts +8 -2
  28. package/dist/components/agents-sdk/McpPluginCatalog.js +82 -62
  29. package/dist/components/agents-sdk/McpServerPicker.d.ts +1 -1
  30. package/dist/components/agents-sdk/McpServerPicker.js +2 -2
  31. package/dist/components/agents-sdk/ModelSelector.js +1 -1
  32. package/dist/components/agents-sdk/PromptInputCommands.js +1 -1
  33. package/dist/components/agents-sdk/SubagentRunDetails.d.ts +1 -9
  34. package/dist/components/agents-sdk/SubagentRunDetails.js +1 -9
  35. package/dist/components/agents-sdk/SubagentRunIndicator.js +1 -1
  36. package/dist/components/agents-sdk/SubagentRunSelector.d.ts +4 -2
  37. package/dist/components/agents-sdk/SubagentRunSelector.js +7 -6
  38. package/dist/components/agents-sdk/ThinkChat.d.ts +4 -4
  39. package/dist/components/agents-sdk/ThinkChatActivity.d.ts +1 -1
  40. package/dist/components/agents-sdk/ThinkChatActivity.js +18 -4
  41. package/dist/components/agents-sdk/ThinkChatToolPanels.js +7 -3
  42. package/dist/components/agents-sdk/ThinkExecutionDetail.d.ts +1 -1
  43. package/dist/components/agents-sdk/ThinkExecutionDetail.js +45 -43
  44. package/dist/components/agents-sdk/ThinkExecutionRun.d.ts +2 -2
  45. package/dist/components/agents-sdk/ThinkExecutionRun.js +5 -6
  46. package/dist/components/agents-sdk/ThinkExecutionStatus.d.ts +1 -1
  47. package/dist/components/agents-sdk/ThinkExecutionStatus.js +8 -12
  48. package/dist/components/agents-sdk/ThinkExecutionWorkbench.d.ts +8 -1
  49. package/dist/components/agents-sdk/ThinkExecutionWorkbench.js +43 -37
  50. package/dist/components/agents-sdk/ThinkFullApp.d.ts +42 -78
  51. package/dist/components/agents-sdk/ThinkFullApp.js +121 -202
  52. package/dist/components/agents-sdk/ThinkFullAppViewSwitch.d.ts +1 -1
  53. package/dist/components/agents-sdk/ThinkRoutine.d.ts +1 -1
  54. package/dist/components/agents-sdk/ThinkRoutineBrowser.js +1 -1
  55. package/dist/components/agents-sdk/ThinkRoutineDetail.js +4 -15
  56. package/dist/components/agents-sdk/ThinkRoutineForm.js +1 -1
  57. package/dist/components/agents-sdk/ThinkRoutineLibrary.d.ts +7 -6
  58. package/dist/components/agents-sdk/ThinkRoutineLibrary.js +112 -140
  59. package/dist/components/agents-sdk/ThinkRoutineRuns.d.ts +1 -1
  60. package/dist/components/agents-sdk/ThinkRoutineRuns.js +3 -10
  61. package/dist/components/agents-sdk/ThinkThreadPicker.js +2 -1
  62. package/dist/components/agents-sdk/WorkspaceBrowser.js +1 -1
  63. package/dist/components/agents-sdk/WorkspaceExplorer.js +2 -1
  64. package/dist/components/agents-sdk/WorkspaceFileView.js +1 -1
  65. package/dist/components/agents-sdk/WorkspaceHeader.js +2 -1
  66. package/dist/components/agents-sdk/WorkspaceMessageResponse.d.ts +1 -1
  67. package/dist/components/agents-sdk/WorkspaceMountManager.js +11 -11
  68. package/dist/components/ai-sdk/Activity.js +1 -1
  69. package/dist/components/ai-sdk/AgentAttention.d.ts +1 -1
  70. package/dist/components/ai-sdk/AgentChatStartScreen.js +1 -1
  71. package/dist/components/ai-sdk/Approval.js +1 -1
  72. package/dist/components/ai-sdk/Artifact.js +1 -1
  73. package/dist/components/ai-sdk/ChainOfThought.d.ts +1 -1
  74. package/dist/components/ai-sdk/ChainOfThought.js +1 -1
  75. package/dist/components/ai-sdk/Checkpoint.js +1 -1
  76. package/dist/components/ai-sdk/CodeBlock.js +1 -1
  77. package/dist/components/ai-sdk/Conversation.js +1 -1
  78. package/dist/components/ai-sdk/Goal.js +1 -1
  79. package/dist/components/ai-sdk/MediaPreviewCard.js +1 -1
  80. package/dist/components/ai-sdk/Message.d.ts +2 -2
  81. package/dist/components/ai-sdk/Message.js +25 -3
  82. package/dist/components/ai-sdk/MessageScroller.js +3 -2
  83. package/dist/components/ai-sdk/MessageScrollerVirtual.js +2 -1
  84. package/dist/components/ai-sdk/Plan.js +1 -1
  85. package/dist/components/ai-sdk/PromptInput.js +21 -15
  86. package/dist/components/ai-sdk/Queue.d.ts +4 -4
  87. package/dist/components/ai-sdk/Queue.js +6 -15
  88. package/dist/components/ai-sdk/Reasoning.js +1 -1
  89. package/dist/components/ai-sdk/Sources.js +1 -1
  90. package/dist/components/ai-sdk/Task.d.ts +2 -2
  91. package/dist/components/ai-sdk/Task.js +2 -2
  92. package/dist/components/extension/ChatGptSignInScreen.js +2 -1
  93. package/dist/components/extension/CodexModelSelector.js +1 -1
  94. package/dist/components/extension/ConversationTabPicker.js +1 -1
  95. package/dist/components/foundations/Avatar.d.ts +1 -10
  96. package/dist/components/foundations/Avatar.js +1 -10
  97. package/dist/components/foundations/Checkbox.js +1 -1
  98. package/dist/components/foundations/CheckboxGroup.js +1 -0
  99. package/dist/components/foundations/ContextMenu.js +1 -1
  100. package/dist/components/foundations/LinkButton.d.ts +12 -10
  101. package/dist/components/foundations/LinkButton.js +15 -11
  102. package/dist/components/foundations/Menubar.js +1 -1
  103. package/dist/components/foundations/SearchField.js +1 -1
  104. package/dist/components/foundations/Select.js +1 -1
  105. package/dist/components/foundations/Stepper.js +1 -1
  106. package/dist/components/foundations/Toast.js +1 -1
  107. package/dist/components/foundations/ToggleGroup.d.ts +79 -1
  108. package/dist/components/foundations/icons.d.ts +2 -2
  109. package/dist/components/foundations/icons.js +2 -2
  110. package/dist/components/general-purpose/AppFrame.js +1 -0
  111. package/dist/components/general-purpose/AppTopBar.d.ts +6 -0
  112. package/dist/components/general-purpose/AppTopBar.js +2 -1
  113. package/dist/components/general-purpose/Commit.d.ts +2 -2
  114. package/dist/components/general-purpose/Commit.js +5 -13
  115. package/dist/components/general-purpose/EnvironmentVariables.d.ts +2 -2
  116. package/dist/components/general-purpose/EnvironmentVariables.js +4 -7
  117. package/dist/components/general-purpose/FileTree.js +1 -1
  118. package/dist/components/general-purpose/PricingBillingToggle.js +4 -9
  119. package/dist/components/general-purpose/PricingPlanCard.js +15 -14
  120. package/dist/components/general-purpose/ResizableSidePanel.d.ts +8 -0
  121. package/dist/components/general-purpose/ResizableSidePanel.js +3 -2
  122. package/dist/components/general-purpose/ResponsiveSidebarShell.d.ts +105 -2
  123. package/dist/components/general-purpose/ResponsiveSidebarShell.js +112 -56
  124. package/dist/components/general-purpose/SetupChecklist.js +1 -1
  125. package/dist/components/general-purpose/Terminal.d.ts +1 -1
  126. package/dist/components/general-purpose/Terminal.js +4 -12
  127. package/dist/components/general-purpose/TestResults.d.ts +1 -1
  128. package/dist/components/general-purpose/TestResults.js +8 -21
  129. package/dist/components/general-purpose/ThemeModeControl.js +1 -1
  130. package/dist/components/general-purpose/WebPreview.d.ts +4 -1
  131. package/dist/components/general-purpose/WebPreview.js +6 -14
  132. package/dist/components/nanites/AgentConnection.js +2 -2
  133. package/dist/components/nanites/AgentToolDetail.d.ts +1 -1
  134. package/dist/components/nanites/AgentToolDetail.js +6 -5
  135. package/dist/components/nanites/NaniteScene.js +11 -11
  136. package/dist/{copy-button-Bc7jsYv_.js → copy-button-lys9N_aI.js} +1 -1
  137. package/dist/{icons-DwgNir1Q.d.ts → icons-0corjfxR.d.ts} +1 -16
  138. package/dist/{icons-MmIXciMO.js → icons-CVFSeTJn.js} +2 -5
  139. package/dist/styles/accordion.css +1 -0
  140. package/dist/styles/action-guide.css +8 -7
  141. package/dist/styles/activity-header.css +13 -39
  142. package/dist/styles/activity.css +40 -55
  143. package/dist/styles/agent-attention.css +19 -19
  144. package/dist/styles/agent-chat.css +94 -42
  145. package/dist/styles/agent-connection.css +10 -4
  146. package/dist/styles/agent-tool-detail.css +5 -15
  147. package/dist/styles/alert-dialog.css +4 -4
  148. package/dist/styles/app-frame.css +1 -3
  149. package/dist/styles/app-icon.css +12 -18
  150. package/dist/styles/app-top-bar.css +31 -9
  151. package/dist/styles/approval.css +1 -0
  152. package/dist/styles/artifact.css +6 -6
  153. package/dist/styles/attachment-preview.css +5 -4
  154. package/dist/styles/avatar.css +6 -22
  155. package/dist/styles/base.css +2 -8
  156. package/dist/styles/browser-live-screen.css +38 -0
  157. package/dist/styles/browser-live-view.css +12 -4
  158. package/dist/styles/browser-replay.css +6 -6
  159. package/dist/styles/browser-run-panel.css +21 -18
  160. package/dist/styles/button.css +15 -6
  161. package/dist/styles/card.css +5 -5
  162. package/dist/styles/chatgpt-sign-in-screen.css +6 -6
  163. package/dist/styles/code-block.css +14 -13
  164. package/dist/styles/codex-model-selector.css +2 -0
  165. package/dist/styles/collapsible.css +1 -0
  166. package/dist/styles/commit.css +30 -7
  167. package/dist/styles/context.css +9 -9
  168. package/dist/styles/conversation-tab-picker.css +1 -1
  169. package/dist/styles/conversation.css +6 -8
  170. package/dist/styles/dialog.css +4 -4
  171. package/dist/styles/empty-state.css +3 -3
  172. package/dist/styles/environment-variables.css +12 -9
  173. package/dist/styles/field.css +2 -1
  174. package/dist/styles/fieldset.css +2 -2
  175. package/dist/styles/index.css +8 -7
  176. package/dist/styles/manager-office.css +480 -0
  177. package/dist/styles/mcp-plugin-catalog.css +44 -39
  178. package/dist/styles/mcp-server-picker.css +4 -4
  179. package/dist/styles/message-scroller.css +9 -11
  180. package/dist/styles/message.css +74 -28
  181. package/dist/styles/nanite-scene.css +9 -9
  182. package/dist/styles/navigation-menu.css +1 -0
  183. package/dist/styles/plan.css +13 -12
  184. package/dist/styles/pricing-page.css +37 -30
  185. package/dist/styles/prompt-input.css +26 -18
  186. package/dist/styles/queue.css +1 -0
  187. package/dist/styles/reasoning.css +1 -9
  188. package/dist/styles/recipes.css +29 -58
  189. package/dist/styles/responsive-sidebar-shell.css +107 -29
  190. package/dist/styles/separator.css +5 -5
  191. package/dist/styles/sources.css +4 -4
  192. package/dist/styles/stepper.css +1 -0
  193. package/dist/styles/streamdown.css +31 -21
  194. package/dist/styles/subagent-run.css +20 -3
  195. package/dist/styles/tabs.css +1 -0
  196. package/dist/styles/task.css +5 -17
  197. package/dist/styles/terminal.css +29 -10
  198. package/dist/styles/test-results.css +16 -7
  199. package/dist/styles/think-execution-status.css +10 -26
  200. package/dist/styles/think-execution-workbench.css +98 -108
  201. package/dist/styles/think-full-app.css +75 -225
  202. package/dist/styles/think-routine-library.css +26 -95
  203. package/dist/styles/think-thread-picker.css +17 -12
  204. package/dist/styles/transition-surface.css +7 -8
  205. package/dist/styles/utilities.css +75 -0
  206. package/dist/styles/web-preview.css +46 -22
  207. package/dist/styles/workspace-explorer.css +22 -44
  208. package/dist/styles/workspace-mount-manager.css +24 -2
  209. package/dist/utils/favicon.d.ts +6 -1
  210. package/dist/utils/favicon.js +18 -1
  211. package/package.json +37 -35
  212. package/dist/ResponsiveSidebarShell-DWjULskY.d.ts +0 -68
  213. package/dist/ToggleGroup-_pQMyHGM.d.ts +0 -80
  214. package/dist/components/agents-sdk/AgentActivityPreview.d.ts +0 -46
  215. package/dist/components/agents-sdk/AgentActivityPreview.js +0 -93
  216. package/dist/components/agents-sdk/ThinkThreadShell.d.ts +0 -38
  217. package/dist/components/agents-sdk/ThinkThreadShell.js +0 -27
  218. package/dist/components/foundations/Badge.d.ts +0 -62
  219. package/dist/components/foundations/Badge.js +0 -55
  220. package/dist/components/foundations/StatusPill.d.ts +0 -43
  221. package/dist/components/foundations/StatusPill.js +0 -35
  222. package/dist/components/general-purpose/Shell.d.ts +0 -149
  223. package/dist/components/general-purpose/Shell.js +0 -116
  224. package/dist/styles/agent-activity-preview-overlay.css +0 -169
  225. package/dist/styles/agent-activity-preview.css +0 -209
  226. package/dist/styles/badge.css +0 -120
  227. package/dist/styles/shell.css +0 -122
  228. package/dist/styles/status-pill.css +0 -39
@@ -0,0 +1,637 @@
1
+ import { E as KeyboardIcon, H as RefreshIcon, r as ArrowLeftIcon } from "../../icons-CVFSeTJn.js";
2
+ import { Button } from "../foundations/Button.js";
3
+ import { WebPreviewNavigationButton } from "../general-purpose/WebPreview.js";
4
+ import { BrowserLiveView } from "./BrowserLiveView.js";
5
+ import { BrowserRunPanel } from "./BrowserRunPanel.js";
6
+ import { useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/components/agents-sdk/BrowserLiveScreen.tsx
9
+ const BROWSER_LIVE_SCREEN_NEGOTIATION_TIMEOUT_MS = 1e4;
10
+ /**
11
+ * A complete interactive viewer for an employee's claimed browser tabs.
12
+ *
13
+ * The host supplies only signaling transport, ICE configuration, and claimed
14
+ * tab summaries. This component owns the peer connection, remote-input
15
+ * mapping, reconnect lifecycle, synthesized browser chrome, and cleanup.
16
+ */
17
+ function BrowserLiveScreen({ sendSignal: sendHostSignal, subscribeSignals, protocol, getIceServers, label = "Employee browser", controlLabel = "Control employee browser", tabs = [], requestedTabId }) {
18
+ const video = useRef(null);
19
+ const track = useRef(null);
20
+ const attachVideo = useCallback((element) => {
21
+ video.current = element;
22
+ if (element) element.srcObject = track.current;
23
+ }, []);
24
+ const showTrack = useCallback((stream) => {
25
+ track.current = stream;
26
+ if (video.current) video.current.srcObject = stream;
27
+ }, []);
28
+ const channel = useRef(null);
29
+ const geometry = useRef(null);
30
+ const [phase, setPhase] = useState("connecting");
31
+ const [failure, setFailure] = useState(null);
32
+ const sendSignal = useEffectEvent((viewerId, payload) => sendHostSignal({
33
+ viewerId,
34
+ payload
35
+ }));
36
+ const loadIceServers = useEffectEvent(async () => await getIceServers?.() ?? []);
37
+ const subscribe = useEffectEvent((listener) => subscribeSignals(listener));
38
+ const parseStatus = useEffectEvent((value) => protocol.parseStatus(value));
39
+ const serializeInput = useEffectEvent((value) => protocol.serializeInput(value));
40
+ const serializeCommand = useEffectEvent((value) => protocol.serializeCommand(value));
41
+ const selectRequestedTab = useEffectEvent((data) => {
42
+ if (requestedTabId === void 0 || data?.readyState !== "open") return;
43
+ data.send(serializeCommand({
44
+ kind: "selectTab",
45
+ tabId: requestedTabId
46
+ }));
47
+ });
48
+ const dataChannelLabel = protocol.dataChannelLabel;
49
+ const input = useRef(null);
50
+ const handleWheel = useEffectEvent((event) => {
51
+ event.preventDefault();
52
+ const point = toPagePoint(event);
53
+ if (!point) return;
54
+ sendInput([{
55
+ kind: "mouse",
56
+ type: "mouseWheel",
57
+ ...point,
58
+ deltaX: event.deltaX,
59
+ deltaY: event.deltaY,
60
+ modifiers: modifiers(event)
61
+ }]);
62
+ });
63
+ const attachInput = useCallback((element) => {
64
+ input.current?.removeEventListener("wheel", handleWheel);
65
+ input.current = element;
66
+ element?.addEventListener("wheel", handleWheel, { passive: false });
67
+ }, []);
68
+ const [keyboardOpen, setKeyboardOpen] = useState(false);
69
+ const touch = useRef(null);
70
+ const mousePress = useRef(null);
71
+ const [streamed, setStreamed] = useState(null);
72
+ useEffect(() => {
73
+ let disposed = false;
74
+ let connection;
75
+ let viewerId = "";
76
+ let attempt = 0;
77
+ let pendingCandidates = [];
78
+ let retryTimer;
79
+ let negotiationTimer;
80
+ let signaling = Promise.resolve();
81
+ function clearRetry() {
82
+ if (retryTimer !== void 0) clearTimeout(retryTimer);
83
+ retryTimer = void 0;
84
+ }
85
+ function clearNegotiationTimeout() {
86
+ if (negotiationTimer !== void 0) clearTimeout(negotiationTimer);
87
+ negotiationTimer = void 0;
88
+ }
89
+ function closeCurrent(notify) {
90
+ const closing = connection;
91
+ const closingViewerId = viewerId;
92
+ connection = void 0;
93
+ channel.current = null;
94
+ geometry.current = null;
95
+ if (!disposed) setStreamed(null);
96
+ showTrack(null);
97
+ pendingCandidates = [];
98
+ signaling = Promise.resolve();
99
+ clearNegotiationTimeout();
100
+ closing?.close();
101
+ if (notify && closingViewerId) sendSignal(closingViewerId, { kind: "close" }).catch(() => void 0);
102
+ }
103
+ function scheduleReconnect(error) {
104
+ if (disposed) return;
105
+ clearNegotiationTimeout();
106
+ setPhase("reconnecting");
107
+ if (error !== void 0) setFailure(error instanceof Error ? error.message : "Live view connection failed.");
108
+ retryTimer ??= setTimeout(() => {
109
+ retryTimer = void 0;
110
+ restart();
111
+ }, RECONNECT_DELAY_MS);
112
+ }
113
+ const unsubscribe = subscribe((signal) => {
114
+ if (signal.viewerId !== viewerId) return;
115
+ const payload = signal.payload;
116
+ const signalViewerId = signal.viewerId;
117
+ signaling = signaling.then(async () => {
118
+ const peer = connection;
119
+ if (!peer || signalViewerId !== viewerId) return;
120
+ if (payload.kind === "answer") {
121
+ await peer.setRemoteDescription({
122
+ type: "answer",
123
+ sdp: payload.sdp
124
+ });
125
+ if (peer !== connection || signalViewerId !== viewerId) return;
126
+ for (const candidate of pendingCandidates.splice(0)) await peer.addIceCandidate(candidate);
127
+ return;
128
+ }
129
+ if (payload.kind === "candidate") {
130
+ if (!peer.remoteDescription) {
131
+ pendingCandidates.push(payload.candidate);
132
+ return;
133
+ }
134
+ await peer.addIceCandidate(payload.candidate);
135
+ }
136
+ }).catch((error) => {
137
+ if (signalViewerId === viewerId) scheduleReconnect(error);
138
+ });
139
+ });
140
+ async function connect() {
141
+ const currentAttempt = ++attempt;
142
+ try {
143
+ await connectAs(currentAttempt);
144
+ } catch (error) {
145
+ if (currentAttempt === attempt) scheduleReconnect(error);
146
+ }
147
+ }
148
+ async function connectAs(currentAttempt) {
149
+ const currentViewerId = crypto.randomUUID();
150
+ viewerId = currentViewerId;
151
+ setFailure(null);
152
+ clearNegotiationTimeout();
153
+ negotiationTimer = setTimeout(() => {
154
+ if (currentAttempt === attempt) scheduleReconnect(/* @__PURE__ */ new Error("The employee machine did not answer the live view."));
155
+ }, BROWSER_LIVE_SCREEN_NEGOTIATION_TIMEOUT_MS);
156
+ const iceServers = await loadIceServers();
157
+ if (disposed || currentAttempt !== attempt) return;
158
+ const peer = new RTCPeerConnection({ iceServers });
159
+ connection = peer;
160
+ pendingCandidates = [];
161
+ peer.ontrack = (event) => {
162
+ if (peer !== connection) return;
163
+ showTrack(event.streams[0] ?? new MediaStream([event.track]));
164
+ };
165
+ peer.onicecandidate = (event) => {
166
+ if (event.candidate && peer === connection) sendSignal(currentViewerId, {
167
+ kind: "candidate",
168
+ candidate: event.candidate.toJSON()
169
+ }).catch((error) => {
170
+ if (peer === connection && currentViewerId === viewerId) scheduleReconnect(error);
171
+ });
172
+ };
173
+ peer.onconnectionstatechange = () => {
174
+ if (peer !== connection) return;
175
+ if (peer.connectionState === "connected") {
176
+ clearRetry();
177
+ clearNegotiationTimeout();
178
+ setFailure(null);
179
+ setPhase((current) => current === "reconnecting" ? geometry.current ? "streaming" : "idle" : current);
180
+ }
181
+ if (peer.connectionState === "failed" || peer.connectionState === "disconnected") scheduleReconnect();
182
+ };
183
+ const data = peer.createDataChannel(dataChannelLabel);
184
+ channel.current = data;
185
+ data.onopen = () => selectRequestedTab(data);
186
+ selectRequestedTab(data);
187
+ data.onmessage = (event) => {
188
+ const status = parseStatus(event.data);
189
+ if (!status) return;
190
+ if (status.kind === "idle") {
191
+ geometry.current = null;
192
+ setFailure(null);
193
+ setPhase("idle");
194
+ setStreamed(null);
195
+ return;
196
+ }
197
+ if (status.kind === "streaming") {
198
+ geometry.current = status;
199
+ setFailure(null);
200
+ setPhase("streaming");
201
+ setStreamed(status);
202
+ return;
203
+ }
204
+ setFailure(status.message);
205
+ };
206
+ peer.addTransceiver("video", { direction: "recvonly" });
207
+ const offer = await peer.createOffer();
208
+ await peer.setLocalDescription(offer);
209
+ if (disposed || peer !== connection) return;
210
+ await sendSignal(currentViewerId, {
211
+ kind: "offer",
212
+ sdp: offer.sdp ?? ""
213
+ });
214
+ }
215
+ function restart() {
216
+ clearRetry();
217
+ closeCurrent(true);
218
+ if (disposed) return;
219
+ connect();
220
+ }
221
+ connect();
222
+ return () => {
223
+ disposed = true;
224
+ attempt += 1;
225
+ clearRetry();
226
+ unsubscribe();
227
+ closeCurrent(true);
228
+ };
229
+ }, [dataChannelLabel]);
230
+ useEffect(() => {
231
+ selectRequestedTab(channel.current);
232
+ }, [requestedTabId]);
233
+ function sendInput(events) {
234
+ const open = channel.current;
235
+ const status = geometry.current;
236
+ if (open?.readyState !== "open" || !status || events.length === 0) return;
237
+ open.send(serializeInput({
238
+ tabId: status.tabId,
239
+ streamGeneration: status.streamGeneration,
240
+ events
241
+ }));
242
+ }
243
+ /**
244
+ * A browser-chrome command needs only the channel, not a live stream:
245
+ * picking a tab is exactly what a viewer does while nothing is on screen.
246
+ */
247
+ function sendCommand(command) {
248
+ const open = channel.current;
249
+ if (open?.readyState !== "open") return;
250
+ open.send(serializeCommand(command));
251
+ }
252
+ /**
253
+ * How the displayed picture maps to page CSS pixels, computed from the
254
+ * video's current on-screen box. The machine never rescales: it sends the
255
+ * geometry with each change and the viewer, which is the side that knows how
256
+ * big it drew the picture, does the arithmetic.
257
+ *
258
+ * A touch gesture freezes one of these at its first contact, because the
259
+ * soft keyboard opening or closing mid-gesture moves the video and would
260
+ * otherwise put the release somewhere the press never was.
261
+ */
262
+ function currentViewGeometry() {
263
+ const element = video.current;
264
+ const status = geometry.current;
265
+ if (!element || !status) return null;
266
+ const bounds = element.getBoundingClientRect();
267
+ const sourceWidth = element.videoWidth || status.metadata.deviceWidth;
268
+ const sourceHeight = element.videoHeight || status.metadata.deviceHeight;
269
+ if (bounds.width === 0 || bounds.height === 0 || sourceWidth <= 0 || sourceHeight <= 0) return null;
270
+ const containScale = Math.min(bounds.width / sourceWidth, bounds.height / sourceHeight);
271
+ const renderedWidth = sourceWidth * containScale;
272
+ const renderedHeight = sourceHeight * containScale;
273
+ return {
274
+ renderedLeft: bounds.left + (bounds.width - renderedWidth) / 2,
275
+ renderedTop: bounds.top + (bounds.height - renderedHeight) / 2,
276
+ renderedWidth,
277
+ renderedHeight,
278
+ zoomX: renderedWidth / status.metadata.deviceWidth,
279
+ zoomY: renderedHeight / status.metadata.deviceHeight,
280
+ offsetTop: status.metadata.offsetTop
281
+ };
282
+ }
283
+ function toPagePoint(event) {
284
+ const view = currentViewGeometry();
285
+ return view ? pointWithin(view, event) : null;
286
+ }
287
+ function beginTouch(event) {
288
+ if (touch.current) return;
289
+ const view = currentViewGeometry();
290
+ const startPage = view ? pointWithin(view, event) : null;
291
+ if (!view || !startPage) return;
292
+ const client = {
293
+ x: event.clientX,
294
+ y: event.clientY
295
+ };
296
+ const gesture = {
297
+ pointerId: event.pointerId,
298
+ mode: "pending",
299
+ view,
300
+ startClient: client,
301
+ startPage,
302
+ lastClient: client,
303
+ lastPage: startPage,
304
+ holdTimer: setTimeout(() => {
305
+ gesture.mode = "drag";
306
+ sendInput([move(startPage, 0), press(startPage)]);
307
+ }, TOUCH_HOLD_MS)
308
+ };
309
+ touch.current = gesture;
310
+ event.currentTarget.setPointerCapture(event.pointerId);
311
+ }
312
+ function moveTouch(event) {
313
+ const gesture = touch.current;
314
+ if (!gesture || event.pointerId !== gesture.pointerId) return;
315
+ const client = {
316
+ x: event.clientX,
317
+ y: event.clientY
318
+ };
319
+ if (gesture.mode === "pending") {
320
+ if (Math.hypot(client.x - gesture.startClient.x, client.y - gesture.startClient.y) <= TOUCH_TAP_SLOP_PX) return;
321
+ clearTimeout(gesture.holdTimer);
322
+ gesture.mode = "scroll";
323
+ }
324
+ if (gesture.mode === "scroll") {
325
+ const deltaX = (gesture.lastClient.x - client.x) / gesture.view.zoomX;
326
+ const deltaY = (gesture.lastClient.y - client.y) / gesture.view.zoomY;
327
+ gesture.lastClient = client;
328
+ if (deltaX !== 0 || deltaY !== 0) sendInput([{
329
+ kind: "mouse",
330
+ type: "mouseWheel",
331
+ ...gesture.startPage,
332
+ deltaX,
333
+ deltaY
334
+ }]);
335
+ return;
336
+ }
337
+ const point = pointWithin(gesture.view, event);
338
+ if (!point) return;
339
+ gesture.lastPage = point;
340
+ sendInput([move(point, 1)]);
341
+ }
342
+ function endTouch(event, cancelled) {
343
+ const gesture = touch.current;
344
+ if (!gesture || event.pointerId !== gesture.pointerId) return;
345
+ touch.current = null;
346
+ clearTimeout(gesture.holdTimer);
347
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
348
+ if (gesture.mode === "pending") {
349
+ if (cancelled) return;
350
+ sendInput([
351
+ move(gesture.startPage, 0),
352
+ press(gesture.startPage),
353
+ release(gesture.startPage)
354
+ ]);
355
+ return;
356
+ }
357
+ if (gesture.mode === "drag") sendInput([release(gesture.lastPage)]);
358
+ }
359
+ const notice = failure ?? (phase === "streaming" ? void 0 : PHASE_TEXT[phase]);
360
+ const streamedTab = streamed ? tabs.find((tab) => tab.tabId === streamed.tabId) : void 0;
361
+ const displayUrl = streamed?.pageUrl ?? streamedTab?.url ?? "";
362
+ const view = /* @__PURE__ */ jsx(BrowserLiveView, {
363
+ expandable: false,
364
+ fill: true,
365
+ phase,
366
+ statusLabel: notice,
367
+ renderMedia: (_placement, statusId) => /* @__PURE__ */ jsxs(Fragment, { children: [
368
+ /* @__PURE__ */ jsx("video", {
369
+ ref: attachVideo,
370
+ autoPlay: true,
371
+ playsInline: true,
372
+ muted: true,
373
+ "aria-hidden": "true"
374
+ }),
375
+ /* @__PURE__ */ jsx("textarea", {
376
+ "aria-describedby": notice ? statusId : void 0,
377
+ ref: attachInput,
378
+ "aria-label": controlLabel,
379
+ autoCapitalize: "none",
380
+ autoComplete: "off",
381
+ className: "browser-live-screen__input",
382
+ inputMode: keyboardOpen ? "text" : "none",
383
+ spellCheck: false,
384
+ onBeforeInput: (event) => {
385
+ const nativeInput = event.nativeEvent;
386
+ if (nativeInput.isComposing) return;
387
+ if (nativeInput.inputType === "insertLineBreak" || nativeInput.inputType === "insertParagraph") {
388
+ event.preventDefault();
389
+ event.currentTarget.value = "";
390
+ sendInput([{
391
+ kind: "key",
392
+ type: "keyDown",
393
+ key: "Enter",
394
+ code: "Enter",
395
+ windowsVirtualKeyCode: 13,
396
+ text: "\r",
397
+ modifiers: 0
398
+ }, {
399
+ kind: "key",
400
+ type: "keyUp",
401
+ key: "Enter",
402
+ code: "Enter",
403
+ windowsVirtualKeyCode: 13,
404
+ modifiers: 0
405
+ }]);
406
+ return;
407
+ }
408
+ if (!nativeInput.data) return;
409
+ event.preventDefault();
410
+ event.currentTarget.value = "";
411
+ sendInput([{
412
+ kind: "text",
413
+ text: nativeInput.data
414
+ }]);
415
+ },
416
+ onInput: (event) => {
417
+ if ("isComposing" in event.nativeEvent && event.nativeEvent.isComposing) return;
418
+ const text = event.currentTarget.value;
419
+ event.currentTarget.value = "";
420
+ if (text) sendInput([{
421
+ kind: "text",
422
+ text
423
+ }]);
424
+ },
425
+ onKeyDown: (event) => {
426
+ if (event.nativeEvent.isComposing) return;
427
+ if (event.key === "Enter") event.preventDefault();
428
+ sendInput([{
429
+ kind: "key",
430
+ type: "keyDown",
431
+ key: event.key,
432
+ code: event.code,
433
+ windowsVirtualKeyCode: event.keyCode,
434
+ ...event.key === "Enter" ? { text: "\r" } : {},
435
+ modifiers: modifiers(event)
436
+ }]);
437
+ },
438
+ onKeyUp: (event) => {
439
+ if (event.nativeEvent.isComposing) return;
440
+ sendInput([{
441
+ kind: "key",
442
+ type: "keyUp",
443
+ key: event.key,
444
+ code: event.code,
445
+ windowsVirtualKeyCode: event.keyCode,
446
+ modifiers: modifiers(event)
447
+ }]);
448
+ },
449
+ onPaste: (event) => {
450
+ event.preventDefault();
451
+ const text = event.clipboardData.getData("text");
452
+ if (text) sendInput([{
453
+ kind: "text",
454
+ text
455
+ }]);
456
+ },
457
+ onContextMenu: (event) => {
458
+ event.preventDefault();
459
+ },
460
+ onPointerDown: (event) => {
461
+ if (event.pointerType === "touch") {
462
+ beginTouch(event);
463
+ return;
464
+ }
465
+ const point = toPagePoint(event);
466
+ if (point) {
467
+ event.currentTarget.setPointerCapture(event.pointerId);
468
+ mousePress.current = point;
469
+ sendInput([press(point)]);
470
+ }
471
+ },
472
+ onPointerMove: (event) => {
473
+ if (event.pointerType === "touch") {
474
+ moveTouch(event);
475
+ return;
476
+ }
477
+ const point = toPagePoint(event);
478
+ if (point) {
479
+ if (mousePress.current) mousePress.current = point;
480
+ sendInput([move(point, event.buttons)]);
481
+ }
482
+ },
483
+ onPointerUp: (event) => {
484
+ if (event.pointerType === "touch") {
485
+ endTouch(event, false);
486
+ return;
487
+ }
488
+ const point = toPagePoint(event) ?? mousePress.current;
489
+ mousePress.current = null;
490
+ if (point) sendInput([release(point)]);
491
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) event.currentTarget.releasePointerCapture(event.pointerId);
492
+ },
493
+ onPointerCancel: (event) => {
494
+ if (event.pointerType === "touch") {
495
+ endTouch(event, true);
496
+ return;
497
+ }
498
+ const point = toPagePoint(event) ?? mousePress.current;
499
+ mousePress.current = null;
500
+ if (point) sendInput([release(point)]);
501
+ }
502
+ }),
503
+ /* @__PURE__ */ jsx(Button, {
504
+ "aria-label": keyboardOpen ? "Hide keyboard" : "Show keyboard",
505
+ "aria-pressed": keyboardOpen,
506
+ className: "browser-live-screen__keyboard",
507
+ color: "neutral",
508
+ iconOnly: true,
509
+ size: "sm",
510
+ variant: "ghost",
511
+ onClick: () => {
512
+ const element = input.current;
513
+ const open = !keyboardOpen;
514
+ setKeyboardOpen(open);
515
+ if (!element) return;
516
+ element.inputMode = open ? "text" : "none";
517
+ if (open) element.focus();
518
+ else element.blur();
519
+ },
520
+ children: /* @__PURE__ */ jsx(KeyboardIcon, {})
521
+ })
522
+ ] })
523
+ });
524
+ return /* @__PURE__ */ jsx(BrowserRunPanel, {
525
+ label,
526
+ tabs: tabs.map((tab) => ({
527
+ id: String(tab.tabId),
528
+ title: tab.title || tab.url || `Tab ${String(tab.tabId)}`,
529
+ ...tab.url === void 0 ? {} : { url: tab.url }
530
+ })),
531
+ selectedTabId: streamed ? String(streamed.tabId) : null,
532
+ onSelectTab: (id) => sendCommand({
533
+ kind: "selectTab",
534
+ tabId: Number(id)
535
+ }),
536
+ url: displayUrl,
537
+ onOpenUrl: (next) => {
538
+ if (!streamed) return;
539
+ const url = normalizeOpenUrl(next);
540
+ if (url) sendCommand({
541
+ kind: "open",
542
+ tabId: streamed.tabId,
543
+ url
544
+ });
545
+ },
546
+ urlLabel: "Streamed page URL",
547
+ urlPlaceholder: streamed ? "Open a page…" : "Nothing is streaming",
548
+ urlDisabled: !streamed,
549
+ navigation: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(WebPreviewNavigationButton, {
550
+ label: "Back",
551
+ disabled: !streamed,
552
+ onClick: () => streamed && sendCommand({
553
+ kind: "navigate",
554
+ tabId: streamed.tabId,
555
+ action: "back"
556
+ }),
557
+ children: /* @__PURE__ */ jsx(ArrowLeftIcon, {})
558
+ }), /* @__PURE__ */ jsx(WebPreviewNavigationButton, {
559
+ label: "Reload",
560
+ disabled: !streamed,
561
+ onClick: () => streamed && sendCommand({
562
+ kind: "navigate",
563
+ tabId: streamed.tabId,
564
+ action: "reload"
565
+ }),
566
+ children: /* @__PURE__ */ jsx(RefreshIcon, {})
567
+ })] }),
568
+ children: view
569
+ });
570
+ }
571
+ /**
572
+ * What a thumb types is rarely a URL: absent scheme becomes https, and
573
+ * anything that still is not an http(s) page is refused here, mirroring the
574
+ * machine's own validation rather than surprising it.
575
+ */
576
+ function normalizeOpenUrl(raw) {
577
+ const value = raw.trim();
578
+ if (!value) return null;
579
+ for (const candidate of [value, `https://${value}`]) {
580
+ let url;
581
+ try {
582
+ url = new URL(candidate);
583
+ } catch {
584
+ continue;
585
+ }
586
+ if (url.protocol === "http:" || url.protocol === "https:") return url.href;
587
+ }
588
+ return null;
589
+ }
590
+ const press = (p) => ({
591
+ kind: "mouse",
592
+ type: "mousePressed",
593
+ ...p,
594
+ button: "left",
595
+ buttons: 1,
596
+ clickCount: 1
597
+ });
598
+ const release = (p) => ({
599
+ kind: "mouse",
600
+ type: "mouseReleased",
601
+ ...p,
602
+ button: "left",
603
+ buttons: 0,
604
+ clickCount: 1
605
+ });
606
+ const move = (p, buttons) => ({
607
+ kind: "mouse",
608
+ type: "mouseMoved",
609
+ ...p,
610
+ buttons
611
+ });
612
+ /** A client point in the picture's own page coordinates, or null if outside it. */
613
+ function pointWithin(view, event) {
614
+ const renderedX = event.clientX - view.renderedLeft;
615
+ const renderedY = event.clientY - view.renderedTop;
616
+ if (renderedX < 0 || renderedY < 0 || renderedX > view.renderedWidth || renderedY > view.renderedHeight) return null;
617
+ return {
618
+ x: renderedX / view.zoomX,
619
+ y: renderedY / view.zoomY - view.offsetTop
620
+ };
621
+ }
622
+ /** CDP's modifier bitmask: alt, ctrl, meta, shift. */
623
+ function modifiers(event) {
624
+ return Number(event.altKey) | Number(event.ctrlKey) << 1 | Number(event.metaKey) << 2 | Number(event.shiftKey) << 3;
625
+ }
626
+ /** Finger travel that still reads as a tap, in the viewer's own pixels. */
627
+ const TOUCH_TAP_SLOP_PX = 8;
628
+ /** A hold this long turns the touch into a mouse drag instead of a scroll. */
629
+ const TOUCH_HOLD_MS = 450;
630
+ const RECONNECT_DELAY_MS = 1e3;
631
+ const PHASE_TEXT = {
632
+ connecting: "Connecting to the employee browser…",
633
+ idle: "Nothing is on screen. The employee browser appears here while it works.",
634
+ reconnecting: "Reconnecting…"
635
+ };
636
+ //#endregion
637
+ export { BrowserLiveScreen };
@@ -47,7 +47,7 @@ type BrowserLiveViewProps = BrowserLiveViewCommonProps & ({
47
47
  * expects. This component owns the frame around them. An overlay drawn
48
48
  * here covers the picture and stays under the frame's own chrome, so a
49
49
  * control surface can take every pointer the picture gets without
50
- * swallowing the Live badge or the expand control.
50
+ * swallowing the live status or the expand control.
51
51
  *
52
52
  * `statusId` names whatever element currently states the phase, for a
53
53
  * control surface to point `aria-describedby` at.
@@ -64,7 +64,7 @@ type BrowserLiveViewProps = BrowserLiveViewCommonProps & ({
64
64
  * `BrowserLiveViewUrl` — and it embeds the hosted viewer, expiring the frame
65
65
  * on its own once `expiresInMs` passes. Give it `renderMedia` and a `phase`
66
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
67
+ * the frame around it. The live status, connecting overlay, ended
68
68
  * state, and the fullscreen expand are the same either way, because to the
69
69
  * person watching it is the same thing.
70
70
  *
@@ -1,8 +1,7 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
- import { Y as SpinnerIcon, j as MaximizeIcon } from "../../icons-MmIXciMO.js";
2
+ import { K as SpinnerIcon, O as MaximizeIcon, _ as DotIcon } from "../../icons-CVFSeTJn.js";
3
3
  import { Button } from "../foundations/Button.js";
4
4
  import { Dialog } from "../foundations/Dialog.js";
5
- import { StatusPill } from "../foundations/StatusPill.js";
6
5
  import { TransitionSurface } from "../foundations/TransitionSurface.js";
7
6
  import * as React from "react";
8
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -34,7 +33,7 @@ function EndedState({ title, detail }) {
34
33
  * `BrowserLiveViewUrl` — and it embeds the hosted viewer, expiring the frame
35
34
  * on its own once `expiresInMs` passes. Give it `renderMedia` and a `phase`
36
35
  * 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
36
+ * the frame around it. The live status, connecting overlay, ended
38
37
  * state, and the fullscreen expand are the same either way, because to the
39
38
  * person watching it is the same thing.
40
39
  *
@@ -105,12 +104,10 @@ function BrowserLiveView(props) {
105
104
  detail: pageTitle
106
105
  });
107
106
  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"
107
+ live && placement === "inline" ? /* @__PURE__ */ jsxs("span", {
108
+ className: "browser-live-view__status",
109
+ role: "status",
110
+ children: [/* @__PURE__ */ jsx(DotIcon, {}), "Live"]
114
111
  }) : null,
115
112
  source ? placement === "dialog" ? /* @__PURE__ */ jsx("iframe", {
116
113
  src: source.url,
@@ -1,5 +1,5 @@
1
1
  import { t as cx } from "../../class-names-BiMDVpUo.js";
2
- import { B as PauseIcon, H as PlayIcon } from "../../icons-MmIXciMO.js";
2
+ import { L as PauseIcon, z as PlayIcon } from "../../icons-CVFSeTJn.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";