@mcp-b/react-components 0.2.2 → 0.3.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/{CodeBlock-BrCoypnc.js → CodeBlock-C2Xjxu9M.js} +1 -1
- package/dist/FileTree-D0c9Yj9U.js +310 -0
- package/dist/PromptInput-Dzn-fOPc.d.ts +444 -0
- package/dist/components/Activity.js +1 -1
- package/dist/components/AgentChat.d.ts +19 -9
- package/dist/components/AgentChat.js +27 -24
- package/dist/components/AgentConnection.js +2 -2
- package/dist/components/Artifact.js +1 -1
- package/dist/components/BrowserLiveView.d.ts +47 -0
- package/dist/components/BrowserLiveView.js +135 -0
- package/dist/components/BrowserReplay.d.ts +40 -0
- package/dist/components/BrowserReplay.js +187 -0
- package/dist/components/ChainOfThought.d.ts +1 -1
- package/dist/components/ChainOfThought.js +1 -1
- package/dist/components/CodeBlock.js +1 -1
- package/dist/components/Commit.js +1 -1
- package/dist/components/ContextMenu.js +1 -1
- package/dist/components/Conversation.js +1 -1
- package/dist/components/Dialog.js +1 -1
- package/dist/components/EnvironmentVariables.js +1 -1
- package/dist/components/FileTree.js +1 -309
- package/dist/components/Menubar.js +1 -1
- package/dist/components/Message.d.ts +2 -1
- package/dist/components/Message.js +1 -1
- package/dist/components/ModelSelector.js +1 -1
- package/dist/components/NavigationMenu.js +1 -1
- package/dist/components/PricingPage.js +1 -1
- package/dist/components/PromptInput.d.ts +2 -399
- package/dist/components/PromptInput.js +177 -2
- package/dist/components/Queue.js +1 -1
- package/dist/components/Reasoning.js +1 -1
- package/dist/components/Select.js +1 -1
- package/dist/components/Sources.js +1 -1
- package/dist/components/Stepper.js +1 -1
- package/dist/components/Task.js +1 -1
- package/dist/components/Terminal.js +2 -2
- package/dist/components/TestResults.js +1 -1
- package/dist/components/ThinkChat.d.ts +186 -0
- package/dist/components/ThinkChat.js +730 -0
- package/dist/components/Tool.d.ts +32 -28
- package/dist/components/Tool.js +29 -16
- package/dist/components/WebPreview.js +1 -1
- package/dist/components/WorkspaceExplorer.d.ts +102 -0
- package/dist/components/WorkspaceExplorer.js +352 -0
- package/dist/components/icons.d.ts +2 -2
- package/dist/components/icons.js +2 -2
- package/dist/{copy-button-DAPlB8Z0.js → copy-button-Dd6DEhOm.js} +1 -1
- package/dist/{icons-Bf7JMKk-.js → icons-DQC4Zw5R.js} +5 -1
- package/dist/{icons-BklFyZbx.d.ts → icons-ctdVxng0.d.ts} +2 -1
- package/dist/styles/prompt-input.css +149 -0
- package/package.json +26 -3
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/BrowserLiveView.d.ts
|
|
4
|
+
interface BrowserLiveViewProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/** Live View URL (Browser Run `devtoolsFrontendUrl`, e.g. live.browser.run). */
|
|
6
|
+
url: string;
|
|
7
|
+
/** Epoch ms after which the URL is dead (Browser Run mints ~5-minute links). */
|
|
8
|
+
expiresAt?: number;
|
|
9
|
+
/** The page the session was showing — used for the ended-state text. */
|
|
10
|
+
pageTitle?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Shown once the link expires — pass the last screenshot the agent took
|
|
13
|
+
* (`<img>`) to keep the final frame on screen. Defaults to a quiet
|
|
14
|
+
* "Live view ended" state.
|
|
15
|
+
*/
|
|
16
|
+
fallback?: React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Embeds a Browser Run Live View — a real-time, interactive view of the
|
|
20
|
+
* agent's browser session. Links expire (~5 minutes), so the iframe swaps to
|
|
21
|
+
* `fallback` (e.g. the last screenshot) once `expiresAt` passes. While the
|
|
22
|
+
* hosted viewer connects, a quiet loading overlay hides the frame; if it
|
|
23
|
+
* never loads, the view degrades to `fallback` or an unavailable state. An
|
|
24
|
+
* expand button opens the same view in a fullscreen dialog.
|
|
25
|
+
*
|
|
26
|
+
* Where URLs come from (upstream: `agents/browser` in
|
|
27
|
+
* github.com/cloudflare/agents — `BrowserLiveViewUrl`): the model calls
|
|
28
|
+
* `cdp.getLiveViewUrl()` inside a `browser_execute` tool run, or the host
|
|
29
|
+
* calls `connector.liveView()`/`sessionInfo()`. This package's chat rendering
|
|
30
|
+
* derives the component automatically from links found in tool results.
|
|
31
|
+
*
|
|
32
|
+
* Styling rides `@mcp-b/react-components/styles` (browser-live-view.css),
|
|
33
|
+
* which think-chat consumers load anyway.
|
|
34
|
+
*
|
|
35
|
+
* The iframe is unsandboxed by design: the hosted Live View UI needs scripts
|
|
36
|
+
* and its own origin to stream the session. Only pass trusted Live View URLs.
|
|
37
|
+
*/
|
|
38
|
+
declare function BrowserLiveView({
|
|
39
|
+
url,
|
|
40
|
+
expiresAt,
|
|
41
|
+
pageTitle,
|
|
42
|
+
fallback,
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}: BrowserLiveViewProps): React.JSX.Element;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { BrowserLiveView, BrowserLiveViewProps };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
+
import { A as SpinnerIcon, x as MaximizeIcon } from "../icons-DQC4Zw5R.js";
|
|
3
|
+
import { Button } from "./Button.js";
|
|
4
|
+
import { Badge } from "./Badge.js";
|
|
5
|
+
import { Dialog, DialogContent, DialogTitle } from "./Dialog.js";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/components/BrowserLiveView.tsx
|
|
9
|
+
const FRAME_LOAD_TIMEOUT_MS = 15e3;
|
|
10
|
+
function EndedState({ title, detail }) {
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
12
|
+
className: "browser-live-view__ended",
|
|
13
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
14
|
+
className: "browser-live-view__ended-title",
|
|
15
|
+
children: title
|
|
16
|
+
}), detail ? /* @__PURE__ */ jsx("span", {
|
|
17
|
+
className: "browser-live-view__ended-detail",
|
|
18
|
+
children: detail
|
|
19
|
+
}) : null]
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Embeds a Browser Run Live View — a real-time, interactive view of the
|
|
24
|
+
* agent's browser session. Links expire (~5 minutes), so the iframe swaps to
|
|
25
|
+
* `fallback` (e.g. the last screenshot) once `expiresAt` passes. While the
|
|
26
|
+
* hosted viewer connects, a quiet loading overlay hides the frame; if it
|
|
27
|
+
* never loads, the view degrades to `fallback` or an unavailable state. An
|
|
28
|
+
* expand button opens the same view in a fullscreen dialog.
|
|
29
|
+
*
|
|
30
|
+
* Where URLs come from (upstream: `agents/browser` in
|
|
31
|
+
* github.com/cloudflare/agents — `BrowserLiveViewUrl`): the model calls
|
|
32
|
+
* `cdp.getLiveViewUrl()` inside a `browser_execute` tool run, or the host
|
|
33
|
+
* calls `connector.liveView()`/`sessionInfo()`. This package's chat rendering
|
|
34
|
+
* derives the component automatically from links found in tool results.
|
|
35
|
+
*
|
|
36
|
+
* Styling rides `@mcp-b/react-components/styles` (browser-live-view.css),
|
|
37
|
+
* which think-chat consumers load anyway.
|
|
38
|
+
*
|
|
39
|
+
* The iframe is unsandboxed by design: the hosted Live View UI needs scripts
|
|
40
|
+
* and its own origin to stream the session. Only pass trusted Live View URLs.
|
|
41
|
+
*/
|
|
42
|
+
function BrowserLiveView({ url, expiresAt, pageTitle, fallback, className, ...props }) {
|
|
43
|
+
const [expired, setExpired] = React.useState(() => expiresAt !== void 0 && expiresAt <= Date.now());
|
|
44
|
+
const [frameState, setFrameState] = React.useState("loading");
|
|
45
|
+
const [expandedOpen, setExpandedOpen] = React.useState(false);
|
|
46
|
+
React.useEffect(() => {
|
|
47
|
+
if (expiresAt === void 0) return;
|
|
48
|
+
const remaining = expiresAt - Date.now();
|
|
49
|
+
if (remaining <= 0) {
|
|
50
|
+
setExpired(true);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
setExpired(false);
|
|
54
|
+
const timer = setTimeout(() => setExpired(true), remaining);
|
|
55
|
+
return () => clearTimeout(timer);
|
|
56
|
+
}, [expiresAt]);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
setFrameState("loading");
|
|
59
|
+
}, [url]);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (frameState !== "loading") return;
|
|
62
|
+
const timer = setTimeout(() => setFrameState("failed"), FRAME_LOAD_TIMEOUT_MS);
|
|
63
|
+
return () => clearTimeout(timer);
|
|
64
|
+
}, [frameState]);
|
|
65
|
+
const failed = frameState === "failed";
|
|
66
|
+
const live = !expired && !failed;
|
|
67
|
+
const frameTitle = pageTitle ? `Live browser view — ${pageTitle}` : "Live browser view";
|
|
68
|
+
const view = live ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
/* @__PURE__ */ jsxs(Badge, {
|
|
70
|
+
size: "sm",
|
|
71
|
+
color: "destructive",
|
|
72
|
+
className: "browser-live-view__badge",
|
|
73
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
74
|
+
className: "browser-live-view__badge-dot",
|
|
75
|
+
"aria-hidden": true
|
|
76
|
+
}), "Live"]
|
|
77
|
+
}),
|
|
78
|
+
/* @__PURE__ */ jsx("iframe", {
|
|
79
|
+
className: "browser-live-view__frame",
|
|
80
|
+
src: url,
|
|
81
|
+
title: frameTitle,
|
|
82
|
+
allow: "clipboard-read; clipboard-write",
|
|
83
|
+
onLoad: () => setFrameState("ready")
|
|
84
|
+
}),
|
|
85
|
+
frameState === "loading" ? /* @__PURE__ */ jsxs("div", {
|
|
86
|
+
className: "browser-live-view__state",
|
|
87
|
+
role: "status",
|
|
88
|
+
children: [/* @__PURE__ */ jsx(SpinnerIcon, { className: "browser-live-view__state-spinner" }), "Connecting live view…"]
|
|
89
|
+
}) : null
|
|
90
|
+
] }) : fallback ?? /* @__PURE__ */ jsx(EndedState, {
|
|
91
|
+
title: failed ? "Live view unavailable" : "Live view ended",
|
|
92
|
+
detail: pageTitle
|
|
93
|
+
});
|
|
94
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
95
|
+
...props,
|
|
96
|
+
className: cx("browser-live-view", className),
|
|
97
|
+
"data-expired": expired || failed || void 0,
|
|
98
|
+
children: [
|
|
99
|
+
view,
|
|
100
|
+
/* @__PURE__ */ jsx(Button, {
|
|
101
|
+
variant: "ghost",
|
|
102
|
+
color: "neutral",
|
|
103
|
+
size: "icon-xs",
|
|
104
|
+
className: "browser-live-view__expand",
|
|
105
|
+
"aria-label": "Expand browser view",
|
|
106
|
+
onClick: () => setExpandedOpen(true),
|
|
107
|
+
children: /* @__PURE__ */ jsx(MaximizeIcon, {})
|
|
108
|
+
}),
|
|
109
|
+
/* @__PURE__ */ jsx(Dialog, {
|
|
110
|
+
open: expandedOpen,
|
|
111
|
+
onOpenChange: setExpandedOpen,
|
|
112
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
113
|
+
size: "fullscreen",
|
|
114
|
+
className: "browser-live-view__dialog",
|
|
115
|
+
children: [/* @__PURE__ */ jsx(DialogTitle, {
|
|
116
|
+
className: "browser-live-view__dialog-title",
|
|
117
|
+
children: frameTitle
|
|
118
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
119
|
+
className: "browser-live-view__dialog-body",
|
|
120
|
+
children: live ? /* @__PURE__ */ jsx("iframe", {
|
|
121
|
+
src: url,
|
|
122
|
+
title: frameTitle,
|
|
123
|
+
allow: "clipboard-read; clipboard-write"
|
|
124
|
+
}) : fallback ?? /* @__PURE__ */ jsx(EndedState, {
|
|
125
|
+
title: failed ? "Live view unavailable" : "Live view ended",
|
|
126
|
+
detail: pageTitle
|
|
127
|
+
})
|
|
128
|
+
})]
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
]
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
export { BrowserLiveView };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/BrowserReplay.d.ts
|
|
4
|
+
interface BrowserReplayProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
5
|
+
/**
|
|
6
|
+
* rrweb events for one tab, e.g. one
|
|
7
|
+
* `BrowserRecording.events[targetId]` entry from Cloudflare Browser Run.
|
|
8
|
+
*/
|
|
9
|
+
events: unknown[];
|
|
10
|
+
/** Start playing on mount. */
|
|
11
|
+
autoPlay?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Replays a completed Cloudflare Browser Run recording locally with rrweb.
|
|
15
|
+
*
|
|
16
|
+
* This is intentionally not derived from Think chat message parts. Live view
|
|
17
|
+
* links and screenshots ride the `browser_execute` stream and belong to
|
|
18
|
+
* `BrowserLiveView`; replay events are fetched after a session closes with
|
|
19
|
+
* Browser Run's `getBrowserRecording()`. Upstream currently shapes those
|
|
20
|
+
* recordings as `Record<targetId, unknown[]>`, where each array is plain rrweb
|
|
21
|
+
* JSON for one tab. Playback happens in the viewer's browser through
|
|
22
|
+
* `@rrweb/replay`; no replay service is contacted at playback time.
|
|
23
|
+
*
|
|
24
|
+
* Styling rides `@mcp-b/react-components/styles` (browser-replay.css), which
|
|
25
|
+
* think-chat consumers load anyway.
|
|
26
|
+
*
|
|
27
|
+
* @see https://github.com/cloudflare/agents/tree/main/packages/agents/src/browser
|
|
28
|
+
* @see https://github.com/rrweb-io/rrweb/tree/main/packages/%40rrweb/replay
|
|
29
|
+
*/
|
|
30
|
+
declare function BrowserReplay({
|
|
31
|
+
className,
|
|
32
|
+
events,
|
|
33
|
+
autoPlay,
|
|
34
|
+
ref,
|
|
35
|
+
...props
|
|
36
|
+
}: BrowserReplayProps & {
|
|
37
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
38
|
+
}): React.JSX.Element;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { BrowserReplay, BrowserReplayProps };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
+
import { E as PlayIcon, w as PauseIcon } from "../icons-DQC4Zw5R.js";
|
|
3
|
+
import { Button } from "./Button.js";
|
|
4
|
+
import { Slider, SliderControl, SliderIndicator, SliderThumb, SliderTrack } from "./Slider.js";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { Replayer } from "@rrweb/replay";
|
|
8
|
+
//#region src/components/BrowserReplay.tsx
|
|
9
|
+
function formatReplayTime(ms) {
|
|
10
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
11
|
+
return `${Math.floor(totalSeconds / 60)}:${String(totalSeconds % 60).padStart(2, "0")}`;
|
|
12
|
+
}
|
|
13
|
+
function labelReplayFrame(stage) {
|
|
14
|
+
const frame = stage.querySelector("iframe");
|
|
15
|
+
if (frame) frame.title = "Browser session replay";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Replays a completed Cloudflare Browser Run recording locally with rrweb.
|
|
19
|
+
*
|
|
20
|
+
* This is intentionally not derived from Think chat message parts. Live view
|
|
21
|
+
* links and screenshots ride the `browser_execute` stream and belong to
|
|
22
|
+
* `BrowserLiveView`; replay events are fetched after a session closes with
|
|
23
|
+
* Browser Run's `getBrowserRecording()`. Upstream currently shapes those
|
|
24
|
+
* recordings as `Record<targetId, unknown[]>`, where each array is plain rrweb
|
|
25
|
+
* JSON for one tab. Playback happens in the viewer's browser through
|
|
26
|
+
* `@rrweb/replay`; no replay service is contacted at playback time.
|
|
27
|
+
*
|
|
28
|
+
* Styling rides `@mcp-b/react-components/styles` (browser-replay.css), which
|
|
29
|
+
* think-chat consumers load anyway.
|
|
30
|
+
*
|
|
31
|
+
* @see https://github.com/cloudflare/agents/tree/main/packages/agents/src/browser
|
|
32
|
+
* @see https://github.com/rrweb-io/rrweb/tree/main/packages/%40rrweb/replay
|
|
33
|
+
*/
|
|
34
|
+
function BrowserReplay({ className, events, autoPlay, ref, ...props }) {
|
|
35
|
+
const stageRef = React.useRef(null);
|
|
36
|
+
const replayerRef = React.useRef(null);
|
|
37
|
+
const rafRef = React.useRef(0);
|
|
38
|
+
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
39
|
+
const [currentTime, setCurrentTime] = React.useState(0);
|
|
40
|
+
const [duration, setDuration] = React.useState(0);
|
|
41
|
+
const [hasError, setHasError] = React.useState(false);
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
const stage = stageRef.current;
|
|
44
|
+
if (!stage) return;
|
|
45
|
+
stage.replaceChildren();
|
|
46
|
+
cancelAnimationFrame(rafRef.current);
|
|
47
|
+
setIsPlaying(false);
|
|
48
|
+
setCurrentTime(0);
|
|
49
|
+
setDuration(0);
|
|
50
|
+
setHasError(false);
|
|
51
|
+
if (events.length < 2) return;
|
|
52
|
+
let frameLabelRaf = 0;
|
|
53
|
+
const handleFinish = () => {
|
|
54
|
+
const replayer = replayerRef.current;
|
|
55
|
+
cancelAnimationFrame(rafRef.current);
|
|
56
|
+
setIsPlaying(false);
|
|
57
|
+
setCurrentTime(replayer?.getMetaData().totalTime ?? 0);
|
|
58
|
+
};
|
|
59
|
+
const fit = ({ width, height }) => {
|
|
60
|
+
const scale = Math.min(stage.clientWidth / width, stage.clientHeight / height, 1);
|
|
61
|
+
const replayer = replayerRef.current;
|
|
62
|
+
if (!replayer) return;
|
|
63
|
+
replayer.wrapper.style.position = "absolute";
|
|
64
|
+
replayer.wrapper.style.transform = `scale(${scale})`;
|
|
65
|
+
replayer.wrapper.style.transformOrigin = "top left";
|
|
66
|
+
replayer.wrapper.style.left = `${Math.max(0, (stage.clientWidth - width * scale) / 2)}px`;
|
|
67
|
+
replayer.wrapper.style.top = `${Math.max(0, (stage.clientHeight - height * scale) / 2)}px`;
|
|
68
|
+
labelReplayFrame(stage);
|
|
69
|
+
};
|
|
70
|
+
try {
|
|
71
|
+
const replayer = new Replayer(events, {
|
|
72
|
+
root: stage,
|
|
73
|
+
mouseTail: false,
|
|
74
|
+
speed: 1
|
|
75
|
+
});
|
|
76
|
+
replayerRef.current = replayer;
|
|
77
|
+
setDuration(replayer.getMetaData().totalTime);
|
|
78
|
+
replayer.on("finish", handleFinish);
|
|
79
|
+
replayer.on("resize", fit);
|
|
80
|
+
replayer.pause(0);
|
|
81
|
+
labelReplayFrame(stage);
|
|
82
|
+
frameLabelRaf = requestAnimationFrame(() => labelReplayFrame(stage));
|
|
83
|
+
if (autoPlay) {
|
|
84
|
+
replayer.play(0);
|
|
85
|
+
setIsPlaying(true);
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
setHasError(true);
|
|
89
|
+
}
|
|
90
|
+
return () => {
|
|
91
|
+
cancelAnimationFrame(rafRef.current);
|
|
92
|
+
cancelAnimationFrame(frameLabelRaf);
|
|
93
|
+
replayerRef.current?.destroy();
|
|
94
|
+
replayerRef.current = null;
|
|
95
|
+
stage.replaceChildren();
|
|
96
|
+
};
|
|
97
|
+
}, [autoPlay, events]);
|
|
98
|
+
React.useEffect(() => {
|
|
99
|
+
if (!isPlaying) return;
|
|
100
|
+
const tick = () => {
|
|
101
|
+
const replayer = replayerRef.current;
|
|
102
|
+
if (replayer) setCurrentTime(Math.min(replayer.getCurrentTime(), duration));
|
|
103
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
104
|
+
};
|
|
105
|
+
rafRef.current = requestAnimationFrame(tick);
|
|
106
|
+
return () => cancelAnimationFrame(rafRef.current);
|
|
107
|
+
}, [duration, isPlaying]);
|
|
108
|
+
const disabled = events.length < 2 || hasError || duration <= 0;
|
|
109
|
+
const handlePlayPause = () => {
|
|
110
|
+
const replayer = replayerRef.current;
|
|
111
|
+
if (!replayer || disabled) return;
|
|
112
|
+
if (isPlaying) {
|
|
113
|
+
replayer.pause(currentTime);
|
|
114
|
+
setIsPlaying(false);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const startAt = currentTime >= duration ? 0 : currentTime;
|
|
118
|
+
replayer.play(startAt);
|
|
119
|
+
setCurrentTime(startAt);
|
|
120
|
+
setIsPlaying(true);
|
|
121
|
+
};
|
|
122
|
+
const handleScrub = (value) => {
|
|
123
|
+
const next = typeof value === "number" ? value : value[0] ?? 0;
|
|
124
|
+
setCurrentTime(next);
|
|
125
|
+
const replayer = replayerRef.current;
|
|
126
|
+
if (!replayer) return;
|
|
127
|
+
if (isPlaying) replayer.play(next);
|
|
128
|
+
else replayer.pause(next);
|
|
129
|
+
};
|
|
130
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
131
|
+
ref,
|
|
132
|
+
className: cx("browser-replay", className),
|
|
133
|
+
...props,
|
|
134
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
135
|
+
className: "browser-replay__stage-wrap",
|
|
136
|
+
children: [
|
|
137
|
+
/* @__PURE__ */ jsx("div", {
|
|
138
|
+
ref: stageRef,
|
|
139
|
+
className: "browser-replay__stage"
|
|
140
|
+
}),
|
|
141
|
+
events.length < 2 ? /* @__PURE__ */ jsx("div", {
|
|
142
|
+
className: "browser-replay__empty",
|
|
143
|
+
children: "No browser recording"
|
|
144
|
+
}) : null,
|
|
145
|
+
hasError ? /* @__PURE__ */ jsx("div", {
|
|
146
|
+
className: "browser-replay__empty",
|
|
147
|
+
role: "alert",
|
|
148
|
+
children: "Browser replay could not be loaded"
|
|
149
|
+
}) : null
|
|
150
|
+
]
|
|
151
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
152
|
+
className: "browser-replay__controls",
|
|
153
|
+
children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Button, {
|
|
155
|
+
variant: "ghost",
|
|
156
|
+
color: "neutral",
|
|
157
|
+
size: "icon-sm",
|
|
158
|
+
className: "browser-replay__toggle",
|
|
159
|
+
"aria-label": isPlaying ? "Pause replay" : "Play replay",
|
|
160
|
+
disabled,
|
|
161
|
+
onClick: handlePlayPause,
|
|
162
|
+
children: isPlaying ? /* @__PURE__ */ jsx(PauseIcon, {}) : /* @__PURE__ */ jsx(PlayIcon, {})
|
|
163
|
+
}),
|
|
164
|
+
/* @__PURE__ */ jsx(Slider, {
|
|
165
|
+
className: "browser-replay__scrubber",
|
|
166
|
+
min: 0,
|
|
167
|
+
max: Math.max(duration, 1),
|
|
168
|
+
step: 100,
|
|
169
|
+
value: Math.min(currentTime, duration),
|
|
170
|
+
disabled,
|
|
171
|
+
onValueChange: handleScrub,
|
|
172
|
+
children: /* @__PURE__ */ jsx(SliderControl, { children: /* @__PURE__ */ jsxs(SliderTrack, { children: [/* @__PURE__ */ jsx(SliderIndicator, {}), /* @__PURE__ */ jsx(SliderThumb, { "aria-label": "Replay position" })] }) })
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ jsxs("span", {
|
|
175
|
+
className: "browser-replay__time",
|
|
176
|
+
children: [
|
|
177
|
+
formatReplayTime(currentTime),
|
|
178
|
+
" / ",
|
|
179
|
+
formatReplayTime(duration)
|
|
180
|
+
]
|
|
181
|
+
})
|
|
182
|
+
]
|
|
183
|
+
})]
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
export { BrowserReplay };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CollapsiblePanelProps, CollapsibleRootProps, CollapsibleTriggerProps } from "./Collapsible.js";
|
|
2
|
-
import { v as IconProps } from "../icons-
|
|
2
|
+
import { v as IconProps } from "../icons-ctdVxng0.js";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/components/ChainOfThought.d.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CollapsiblePanel, CollapsibleRoot, CollapsibleTrigger } from "./Collapsible.js";
|
|
2
2
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
3
|
-
import {
|
|
3
|
+
import { A as SpinnerIcon, d as DotIcon, l as ChevronRightIcon, o as CheckIcon } from "../icons-DQC4Zw5R.js";
|
|
4
4
|
import "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/ChainOfThought.tsx
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as CodeBlockCopyButton, c as CodeBlockLanguageSelector, d as CodeBlockLanguageSelectorTrigger, f as CodeBlockLanguageSelectorValue, h as CODE_BLOCK_LANGUAGES, i as CodeBlockContent, l as CodeBlockLanguageSelectorContent, m as highlightCode, n as CodeBlockActions, o as CodeBlockFilename, p as CodeBlockTitle, r as CodeBlockContainer, s as CodeBlockHeader, t as CodeBlock, u as CodeBlockLanguageSelectorItem } from "../CodeBlock-
|
|
1
|
+
import { a as CodeBlockCopyButton, c as CodeBlockLanguageSelector, d as CodeBlockLanguageSelectorTrigger, f as CodeBlockLanguageSelectorValue, h as CODE_BLOCK_LANGUAGES, i as CodeBlockContent, l as CodeBlockLanguageSelectorContent, m as highlightCode, n as CodeBlockActions, o as CodeBlockFilename, p as CodeBlockTitle, r as CodeBlockContainer, s as CodeBlockHeader, t as CodeBlock, u as CodeBlockLanguageSelectorItem } from "../CodeBlock-C2Xjxu9M.js";
|
|
2
2
|
export { CODE_BLOCK_LANGUAGES, CodeBlock, CodeBlockActions, CodeBlockContainer, CodeBlockContent, CodeBlockCopyButton, CodeBlockFilename, CodeBlockHeader, CodeBlockLanguageSelector, CodeBlockLanguageSelectorContent, CodeBlockLanguageSelectorItem, CodeBlockLanguageSelectorTrigger, CodeBlockLanguageSelectorValue, CodeBlockTitle, highlightCode };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CollapsiblePanel, CollapsibleRoot, CollapsibleTrigger } from "./Collapsible.js";
|
|
2
2
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
3
|
-
import { h as FileIcon, l as ChevronRightIcon, o as CheckIcon, u as CopyIcon } from "../icons-
|
|
3
|
+
import { h as FileIcon, l as ChevronRightIcon, o as CheckIcon, u as CopyIcon } from "../icons-DQC4Zw5R.js";
|
|
4
4
|
import { Button } from "./Button.js";
|
|
5
5
|
import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
|
|
6
6
|
import { Badge } from "./Badge.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as DotIcon, l as ChevronRightIcon, o as CheckIcon } from "../icons-
|
|
1
|
+
import { d as DotIcon, l as ChevronRightIcon, o as CheckIcon } from "../icons-DQC4Zw5R.js";
|
|
2
2
|
import "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import { ContextMenu as ContextMenu$1 } from "@base-ui/react/context-menu";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
-
import { f as DownloadIcon, t as ArrowDownIcon } from "../icons-
|
|
2
|
+
import { f as DownloadIcon, t as ArrowDownIcon } from "../icons-DQC4Zw5R.js";
|
|
3
3
|
import { Button } from "./Button.js";
|
|
4
4
|
import { useStickToBottom } from "./useStickToBottom.js";
|
|
5
5
|
import * as React from "react";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as cx } from "../class-names-9UtZwb0J.js";
|
|
2
|
-
import { m as EyeOffIcon, o as CheckIcon, p as EyeIcon, u as CopyIcon } from "../icons-
|
|
2
|
+
import { m as EyeOffIcon, o as CheckIcon, p as EyeIcon, u as CopyIcon } from "../icons-DQC4Zw5R.js";
|
|
3
3
|
import { Button } from "./Button.js";
|
|
4
4
|
import { Tooltip, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipTrigger } from "./Tooltip.js";
|
|
5
5
|
import { Badge } from "./Badge.js";
|