EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
CanvasDesktopViewer.jsx
Go to the documentation of this file.
1/**
2 * Canvas Desktop Viewer - Direct MeshCentral integration
3 * This component replaces the old RDS viewer with a cleaner MeshCentral connection
4 * Can be used standalone (with URL params) or embedded (with props)
5 */
6
7import React from 'react';
8import { useParams } from 'react-router-dom';
9import MainLayout from '../components/Layout/MainLayout';
10import { useCanvasDesktop } from '../hooks/useCanvasDesktop';
11
12export default function CanvasDesktopViewer({ nodeId: propNodeId, embedded = false }) {
13 const { nodeId: urlNodeId } = useParams();
14 const nodeId = propNodeId || urlNodeId; // Use prop if provided, otherwise use URL param
15 const {
16 status,
17 error,
18 capabilities,
19 phase,
20 canvasRef,
21 sendPing,
22 sendMouseEvent,
23 sendKeyEvent,
24 disconnect
25 } = useCanvasDesktop(nodeId);
26
27 const getStatusColor = () => {
28 switch (status) {
29 case 'connected': return 'text-green-600';
30 case 'connecting': return 'text-yellow-600';
31 case 'error': return 'text-red-600';
32 default: return 'text-gray-600';
33 }
34 };
35
36 const getStatusIcon = () => {
37 switch (status) {
38 case 'connected': return '✅';
39 case 'connecting': return '🔄';
40 case 'error': return '❌';
41 default: return '⚪';
42 }
43 };
44
45 const content = (
46 <div className={embedded ? "h-full" : "p-6"}>
47 <div className="bg-white rounded-lg shadow-lg h-full flex flex-col">
48 {/* Header */}
49 <div className="border-b border-gray-200 px-6 py-4">
50 <div className="flex items-center justify-between">
51 <div>
52 <h1 className={embedded ? "text-xl font-bold text-gray-900" : "text-2xl font-bold text-gray-900"}>
53 Remote Desktop - Canvas View
54 </h1>
55 <p className="text-sm text-gray-500 mt-1">
56 Node ID: {nodeId || 'Unknown'}
57 </p>
58 </div>
59 <div className="flex items-center gap-4">
60 {/* Status indicator */}
61 <div className="flex items-center gap-2">
62 <span className="text-2xl">{getStatusIcon()}</span>
63 <span className={`font-semibold ${getStatusColor()}`}>
64 {status.charAt(0).toUpperCase() + status.slice(1)}
65 </span>
66 </div>
67
68 {/* Actions */}
69 <button
70 onClick={sendPing}
71 disabled={status !== 'connected'}
72 className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
73 >
74 🏓 Ping
75 </button>
76
77 <button
78 onClick={disconnect}
79 className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
80 >
81 Disconnect
82 </button>
83 </div>
84 </div>
85 </div>
86
87 {/* Error alert */}
88 {error && (
89 <div className="mx-6 mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
90 <div className="flex items-start">
91 <span className="text-red-600 text-xl mr-3">⚠️</span>
92 <div>
93 <h3 className="text-red-800 font-semibold">Connection Error</h3>
94 <p className="text-red-700 text-sm mt-1">{error}</p>
95 </div>
96 </div>
97 </div>
98 )}
99
100 {/* Info panel */}
101 <div className="mx-6 mt-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
102 <div className="grid grid-cols-3 gap-4 text-sm">
103 <div>
104 <span className="font-semibold">Phase:</span>
105 <span className="ml-2">{phase || 1}</span>
106 </div>
107 <div>
108 <span className="font-semibold">Capabilities:</span>
109 <span className="ml-2">
110 {capabilities.length > 0 ? capabilities.join(', ') : 'None'}
111 </span>
112 </div>
113 <div>
114 <span className="font-semibold">Protocol:</span>
115 <span className="ml-2">MeshCentral WebSocket</span>
116 </div>
117 </div>
118 </div>
119
120 {/* Canvas container */}
121 <div className="p-6 flex-1">
122 <div className="bg-gray-900 rounded-lg overflow-hidden h-full" style={{ minHeight: '600px' }}>
123 {status === 'connected' ? (
124 <canvas
125 ref={canvasRef}
126 width={1920}
127 height={1080}
128 className="w-full h-auto cursor-pointer"
129 onMouseDown={sendMouseEvent}
130 onMouseUp={sendMouseEvent}
131 onMouseMove={sendMouseEvent}
132 onKeyDown={sendKeyEvent}
133 onKeyUp={sendKeyEvent}
134 tabIndex={0}
135 style={{ imageRendering: 'auto' }}
136 />
137 ) : (
138 <div className="flex items-center justify-center h-full min-h-[600px]">
139 <div className="text-center">
140 {status === 'connecting' && (
141 <>
142 <div className="animate-spin text-6xl mb-4">🔄</div>
143 <p className="text-white text-lg">Connecting to remote desktop...</p>
144 </>
145 )}
146 {status === 'disconnected' && (
147 <>
148 <div className="text-6xl mb-4">⚪</div>
149 <p className="text-white text-lg">Not connected</p>
150 </>
151 )}
152 {status === 'error' && (
153 <>
154 <div className="text-6xl mb-4">❌</div>
155 <p className="text-white text-lg">Connection failed</p>
156 <p className="text-gray-400 text-sm mt-2">{error}</p>
157 </>
158 )}
159 </div>
160 </div>
161 )}
162 </div>
163 </div>
164
165 {/* Phase info */}
166 <div className="border-t border-gray-200 px-6 py-4 bg-gray-50">
167 <div className="text-sm text-gray-600">
168 {phase === 1 && (
169 <p>
170 <strong>Phase 1:</strong> Authentication and ping/pong testing.
171 Screen streaming coming in Phase 2.
172 </p>
173 )}
174 {phase === 2 && (
175 <p>
176 <strong>Phase 2:</strong> Screen streaming active.
177 Mouse and keyboard input functional.
178 </p>
179 )}
180 {phase === 3 && (
181 <p>
182 <strong>Phase 3:</strong> Quality controls, compression, clipboard sync, and file transfer.
183 </p>
184 )}
185 {phase === 4 && (
186 <p>
187 <strong>Phase 4:</strong> Production-ready with full optimization and polish.
188 </p>
189 )}
190 </div>
191 </div>
192 </div>
193 </div>
194 );
195
196 return embedded ? content : <MainLayout>{content}</MainLayout>;
197}