EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
useRemoteDesktop.js
Go to the documentation of this file.
1import { useRef, useState, useEffect } from 'react';
2
3export function useRemoteDesktop(agentId) {
4 const rdsWS = useRef(null);
5 const rdsImgRef = useRef(null);
6 const [rdsFrame, setRdsFrame] = useState(null);
7 const [scriptOutput, setScriptOutput] = useState('');
8 const [scriptRunning, setScriptRunning] = useState(false);
9
10 const connectRds = () => {
11 if (!agentId) return;
12 const loc = window.location;
13 const proto = loc.protocol === 'https:' ? 'wss' : 'ws';
14 const host = loc.host;
15 const token = localStorage.getItem('jwt') || '';
16 const ws = new WebSocket(`${proto}://${host}/api/rds/stream/${agentId}?token=${token}`);
17 rdsWS.current = ws;
18 ws.onmessage = (event) => {
19 try {
20 const parsed = JSON.parse(event.data);
21 if (parsed.type === 'script_output') {
22 setScriptOutput((prev) => prev + parsed.data);
23 } else if (parsed.type === 'script_done') {
24 setScriptRunning(false);
25 setScriptOutput((prev) => prev + `\n[Process exited with code ${parsed.code}]\n`);
26 } else if (parsed.type === 'frame') {
27 setRdsFrame(parsed.data);
28 }
29 } catch {
30 // ignore non-JSON output
31 }
32 };
33 ws.onclose = () => {
34 console.warn('WebSocket closed.');
35 setScriptRunning(false);
36 };
37 };
38
39 const handleMouseEvent = (e) => {
40 if (!rdsWS.current || rdsWS.current.readyState !== WebSocket.OPEN) return;
41 const rect = rdsImgRef.current.getBoundingClientRect();
42 const x = (e.clientX - rect.left) / rect.width;
43 const y = (e.clientY - rect.top) / rect.height;
44 const msg = {
45 type: "mouse",
46 event: e.type,
47 x,
48 y,
49 button: e.button,
50 buttons: e.buttons,
51 };
52 rdsWS.current.send(JSON.stringify(msg));
53 e.preventDefault();
54 };
55
56 const handleKeyEvent = (e, rdsOpen) => {
57 if (!rdsOpen) return;
58 rdsWS.current?.send(JSON.stringify({
59 type: "keyboard",
60 event: e.type,
61 key: e.key,
62 code: e.code,
63 ctrl: e.ctrlKey,
64 alt: e.altKey,
65 shift: e.shiftKey,
66 meta: e.metaKey,
67 }));
68 e.preventDefault();
69 };
70
71 useEffect(() => {
72 // Cleanup on unmount
73 return () => {
74 if (rdsWS.current) {
75 rdsWS.current.close();
76 }
77 };
78 }, []);
79
80 const sendClipboard = async () => {
81 try {
82 const txt = await navigator.clipboard.readText();
83 rdsWS.current?.send(JSON.stringify({ type: "clipboard", text: txt }));
84 } catch {}
85 };
86
87 const uploadFile = async (file) => {
88 const buf = await file.arrayBuffer();
89 const base64 = btoa(String.fromCharCode(...new Uint8Array(buf)));
90 rdsWS.current.send(JSON.stringify({
91 type: "file_upload",
92 name: file.name,
93 size: file.size,
94 mime: file.type,
95 data: base64,
96 }));
97 };
98
99 return {
100 rdsWS,
101 rdsImgRef,
102 rdsFrame,
103 scriptOutput,
104 scriptRunning,
105 connectRds,
106 handleMouseEvent,
107 handleKeyEvent,
108 sendClipboard,
109 uploadFile,
110 setScriptOutput,
111 setScriptRunning,
112 };
113}