Skip to content

Commit 182ee1a

Browse files
committed
Support clipboard operations on RemoteBrowser
1 parent b8f2b63 commit 182ee1a

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

llmstack/client/src/components/connections/RemoteBrowser.jsx

+40-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ import {
55
DialogActions,
66
DialogContent,
77
DialogTitle,
8+
IconButton,
9+
Tooltip,
810
Typography,
911
} from "@mui/material";
12+
import ContentPasteGoIcon from "@mui/icons-material/ContentPasteGo";
13+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
1014
import RFB from "@novnc/novnc/core/rfb";
1115
import React from "react";
1216
import { isMobileState } from "../../data/atoms";
@@ -16,10 +20,35 @@ import { useCallback, useEffect, useRef, useState } from "react";
1620
export function RemoteBrowser({ wsUrl, timeout, onClose }) {
1721
const screenRef = useRef(null);
1822
const rfbRef = useRef(null);
23+
const [selectedText, setSelectedText] = useState("");
1924
const [connected, setConnected] = useState(false);
2025
const [open, setOpen] = useState(false);
2126
const [timeLeft, setTimeLeft] = useState(timeout);
2227

28+
const onPaste = useCallback(() => {
29+
if (!rfbRef.current) {
30+
return;
31+
}
32+
33+
navigator.permissions.query({ name: "clipboard-read" }).then((status) => {
34+
if (status.state === "granted" || status.state === "prompt") {
35+
navigator.clipboard.readText().then((text) => {
36+
for (const char of text) {
37+
rfbRef.current.sendKey(char.charCodeAt(0), char);
38+
}
39+
});
40+
}
41+
});
42+
}, [rfbRef]);
43+
44+
const onCopy = useCallback(() => {
45+
if (!selectedText) {
46+
return;
47+
}
48+
49+
navigator.clipboard.writeText(selectedText);
50+
}, [selectedText]);
51+
2352
const setupRFB = useCallback(() => {
2453
if (screenRef.current && !rfbRef.current) {
2554
const credentials = wsUrl.split("@")[0].split("://")[1];
@@ -57,7 +86,7 @@ export function RemoteBrowser({ wsUrl, timeout, onClose }) {
5786

5887
rfbRef.current.addEventListener("clipboard", (e) => {
5988
if (e.detail.text) {
60-
navigator.clipboard.writeText(e.detail.text);
89+
setSelectedText(e.detail.text);
6190
}
6291
});
6392

@@ -147,6 +176,16 @@ export function RemoteBrowser({ wsUrl, timeout, onClose }) {
147176
{!connected && "Connecting..."}
148177
</DialogContent>
149178
<DialogActions>
179+
<Tooltip title="Copy selected text to clipboard">
180+
<IconButton onClick={onCopy} sx={{ pr: 0 }}>
181+
<ContentCopyIcon />
182+
</IconButton>
183+
</Tooltip>
184+
<Tooltip title="Paste text from clipboard">
185+
<IconButton onClick={onPaste}>
186+
<ContentPasteGoIcon />
187+
</IconButton>
188+
</Tooltip>
150189
<Button onClick={() => onClose()} variant="contained">
151190
Done
152191
</Button>

0 commit comments

Comments
 (0)