import React, { useState } from "react"; import { Button } from "./ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"; import { Badge } from "./ui/badge"; import { Alert, AlertDescription } from "./ui/alert"; import { Progress } from "./ui/progress"; import { useTeleoperation } from "../hooks/useTeleoperation"; import type { RobotConnection } from "../../lerobot/web/find_port.js"; import { KEYBOARD_CONTROLS } from "../../lerobot/web/teleoperate"; interface TeleoperationPanelProps { robot: RobotConnection; onClose: () => void; } export function TeleoperationPanel({ robot, onClose, }: TeleoperationPanelProps) { const [enabled, setEnabled] = useState(false); const { isConnected, isActive, motorConfigs, keyStates, error, start, stop, simulateKeyPress, simulateKeyRelease, moveMotorToPosition, } = useTeleoperation({ robot, enabled, onError: (err: string) => console.error("Teleoperation error:", err), }); const handleStart = async () => { setEnabled(true); await start(); }; const handleStop = () => { stop(); setEnabled(false); }; const handleClose = () => { stop(); setEnabled(false); onClose(); }; // Virtual keyboard component const VirtualKeyboard = () => { const isKeyPressed = (key: string) => { return keyStates[key]?.pressed || false; }; const KeyButton = ({ keyCode, children, className = "", size = "default" as "default" | "sm" | "lg" | "icon", }: { keyCode: string; children: React.ReactNode; className?: string; size?: "default" | "sm" | "lg" | "icon"; }) => { const control = KEYBOARD_CONTROLS[keyCode as keyof typeof KEYBOARD_CONTROLS]; const pressed = isKeyPressed(keyCode); return ( ); }; return (
{/* Arrow Keys */}

Shoulder

↑
← ↓ →
{/* WASD Keys */}

Elbow/Wrist

W
A S D
{/* Q/E and Gripper */}

Roll

Q E

Gripper

O C
{/* Emergency Stop */}
ESC
); }; return (
{/* Header */}

🎮 Robot Teleoperation

{robot.robotId || robot.name} - {robot.serialNumber}

{/* Error Alert */} {error && ( {error} )}
{/* Status Panel */} Status {isConnected ? "Connected" : "Disconnected"}
Teleoperation {isActive ? "Active" : "Stopped"}
Active Keys { Object.values(keyStates).filter((state) => state.pressed) .length }
{isActive ? ( ) : ( )}
{/* Virtual Keyboard */} Virtual Keyboard {/* Motor Status */} Motor Positions {motorConfigs.map((motor, index) => { return (
{motor.name.replace("_", " ")} {motor.currentPosition}
{ if (!isActive) return; const newPosition = parseInt(e.target.value); try { await moveMotorToPosition(index, newPosition); } catch (error) { console.warn( "Failed to move motor via slider:", error ); } }} />
{motor.minPosition} {motor.maxPosition}
); })}
{/* Help Card */} Control Instructions

Arrow Keys

  • ↑ ↓ Shoulder lift
  • ← → Shoulder pan

WASD Keys

  • W S Elbow flex
  • A D Wrist flex

Other Keys

  • Q E Wrist roll
  • O Open gripper
  • C Close gripper

Emergency

  • ESC Emergency stop

💡 Pro tip: Use your physical keyboard for faster control, or click the virtual keys below. Hold keys down for continuous movement.

); }