import { useState, useEffect, useRef } from "react"; import { Routes, Route, useNavigate, useParams, useLocation, } from "react-router-dom"; import { ChevronLeft } from "lucide-react"; import { Header } from "@/components/header"; import { Footer } from "@/components/footer"; import { EditRobotDialog } from "@/components/edit-robot-dialog"; import { DeviceDashboard } from "@/components/device-dashboard"; import { CalibrationView } from "@/components/calibration-view"; import { TeleoperationView } from "@/components/teleoperation-view"; import { SetupCards } from "@/components/setup-cards"; import { DocsSection } from "@/components/docs-section"; import { RoadmapSection } from "@/components/roadmap-section"; import { HardwareSupportSection } from "@/components/hardware-support-section"; import { useToast } from "@/hooks/use-toast"; import { Toaster } from "@/components/ui/toaster"; import { findPort, isWebSerialSupported, type RobotConnection, type RobotConfig, } from "@lerobot/web"; import { getAllSavedRobots, getUnifiedRobotData, saveDeviceInfo, removeRobotData, type DeviceInfo, } from "@/lib/unified-storage"; function App() { const [robots, setRobots] = useState([]); const [editingRobot, setEditingRobot] = useState( null ); const [isConnecting, setIsConnecting] = useState(false); const hardwareSectionRef = useRef(null); const { toast } = useToast(); const navigate = useNavigate(); const location = useLocation(); // Check browser support const isSupported = isWebSerialSupported(); useEffect(() => { if (!isSupported) { toast({ title: "Browser Not Supported", description: "WebSerial API is not supported. Please use Chrome, Edge, or another Chromium-based browser.", variant: "destructive", }); } }, [isSupported, toast]); useEffect(() => { const loadSavedRobots = async () => { if (!isSupported) return; try { setIsConnecting(true); // Get saved robot configurations const savedRobots = getAllSavedRobots(); if (savedRobots.length > 0) { const robotConfigs: RobotConfig[] = savedRobots.map((device) => ({ robotType: device.robotType as "so100_follower" | "so100_leader", robotId: device.robotId, serialNumber: device.serialNumber, })); // Auto-connect to saved robots const findPortProcess = await findPort({ robotConfigs, onMessage: (msg: string) => { console.log("Connection message:", msg); }, }); const reconnectedRobots = await findPortProcess.result; // Merge saved device info (names, etc.) with fresh connection data const robotsWithSavedInfo = reconnectedRobots.map((robot) => { const savedData = getUnifiedRobotData(robot.serialNumber || ""); if (savedData?.device_info) { return { ...robot, robotId: savedData.device_info.robotId, name: savedData.device_info.robotId, // Use the saved custom name robotType: savedData.device_info.robotType as | "so100_follower" | "so100_leader", }; } return robot; }); setRobots(robotsWithSavedInfo); } } catch (error) { console.error("Failed to load saved robots:", error); toast({ title: "Connection Error", description: "Failed to reconnect to saved robots", variant: "destructive", }); } finally { setIsConnecting(false); } }; loadSavedRobots(); }, [isSupported, toast]); const handleFindNewRobots = async () => { if (!isSupported) { toast({ title: "Browser Not Supported", description: "WebSerial API is required for robot connection", variant: "destructive", }); return; } try { setIsConnecting(true); // Interactive mode - show browser dialog const findPortProcess = await findPort({ onMessage: (msg: string) => { console.log("Find port message:", msg); }, }); const newRobots = await findPortProcess.result; if (newRobots.length > 0) { setRobots((prev: RobotConnection[]) => { const existingSerialNumbers = new Set( prev.map((r: RobotConnection) => r.serialNumber) ); const uniqueNewRobots = newRobots.filter( (r: RobotConnection) => !existingSerialNumbers.has(r.serialNumber) ); // Auto-edit first new robot for configuration if (uniqueNewRobots.length > 0) { setEditingRobot(uniqueNewRobots[0]); } return [...prev, ...uniqueNewRobots]; }); toast({ title: "Robots Found", description: `Found ${newRobots.length} robot(s)`, }); } else { toast({ title: "No Robots Found", description: "No compatible devices detected", }); } } catch (error) { console.error("Failed to find robots:", error); toast({ title: "Connection Error", description: "Failed to find robots. Please try again.", variant: "destructive", }); } finally { setIsConnecting(false); } }; const handleUpdateRobot = (updatedRobot: RobotConnection) => { // Save device info to unified storage if (updatedRobot.serialNumber && updatedRobot.robotId) { const deviceInfo: DeviceInfo = { serialNumber: updatedRobot.serialNumber, robotType: updatedRobot.robotType || "so100_follower", robotId: updatedRobot.robotId, usbMetadata: updatedRobot.usbMetadata ? { vendorId: parseInt(updatedRobot.usbMetadata.vendorId || "0", 16), productId: parseInt( updatedRobot.usbMetadata.productId || "0", 16 ), serialNumber: updatedRobot.usbMetadata.serialNumber, manufacturer: updatedRobot.usbMetadata.manufacturerName, product: updatedRobot.usbMetadata.productName, } : undefined, }; saveDeviceInfo(updatedRobot.serialNumber, deviceInfo); } setRobots((prev) => prev.map((r) => r.serialNumber === updatedRobot.serialNumber ? updatedRobot : r ) ); setEditingRobot(null); }; const handleRemoveRobot = (robotId: string) => { const robot = robots.find((r) => r.robotId === robotId); if (robot?.serialNumber) { removeRobotData(robot.serialNumber); } setRobots((prev) => prev.filter((r) => r.robotId !== robotId)); toast({ title: "Robot Removed", description: `${robotId} has been removed from the registry`, }); }; const handleCalibrate = (robot: RobotConnection) => { if (!robot.isConnected) { toast({ title: "Robot Not Connected", description: "Please connect the robot before calibrating", variant: "destructive", }); return; } navigate(`/device/${robot.serialNumber}/calibrate`); }; const handleTeleoperate = (robot: RobotConnection) => { if (!robot.isConnected) { toast({ title: "Robot Not Connected", description: "Please connect the robot before teleoperating", variant: "destructive", }); return; } navigate(`/device/${robot.serialNumber}/control`); }; const handleBackToDashboard = () => { navigate("/"); }; const scrollToHardware = () => { hardwareSectionRef.current?.scrollIntoView({ behavior: "smooth" }); }; return (
} /> } /> } /> !open && setEditingRobot(null)} onSave={handleUpdateRobot} />
); } // Dashboard Page Component function DashboardPage({ robots, onCalibrate, onTeleoperate, onRemove, onEdit, onFindNew, isConnecting, onScrollToHardware, hardwareSectionRef, }: { robots: RobotConnection[]; onCalibrate: (robot: RobotConnection) => void; onTeleoperate: (robot: RobotConnection) => void; onRemove: (robot: RobotConnection) => void; onEdit: (robot: RobotConnection | null) => void; onFindNew: () => void; isConnecting: boolean; onScrollToHardware: () => void; hardwareSectionRef: React.RefObject; }) { return (

install

Choose your preferred development environment

); } // Calibrate Page Component function CalibratePage({ robots, onBackToDashboard, }: { robots: RobotConnection[]; onBackToDashboard: () => void; }) { const { serialNumber } = useParams<{ serialNumber: string }>(); const selectedRobot = robots.find( (robot) => robot.serialNumber === serialNumber ); if (!selectedRobot) { return (

Device not found or not connected.

); } return (
); } // Control Page Component function ControlPage({ robots, onBackToDashboard, }: { robots: RobotConnection[]; onBackToDashboard: () => void; }) { const { serialNumber } = useParams<{ serialNumber: string }>(); const selectedRobot = robots.find( (robot) => robot.serialNumber === serialNumber ); if (!selectedRobot) { return (

Device not found or not connected.

); } return (
); } // Page Header Component function PageHeader({ onBackToDashboard, selectedRobot, }: { onBackToDashboard?: () => void; selectedRobot?: RobotConnection | null; }) { const location = useLocation(); const isDashboard = location.pathname === "/"; const isCalibrating = location.pathname.includes("/calibrate"); const isTeleoperating = location.pathname.includes("/control"); return (
{isCalibrating && selectedRobot ? (

calibrate: {" "} {selectedRobot.robotId?.toUpperCase()}

) : isTeleoperating && selectedRobot ? (

teleoperate: {" "} {selectedRobot.robotId?.toUpperCase()}

) : (

DASHBOARD

)}
{!isDashboard && onBackToDashboard ? ( ) : (

{""}

)}
); } export default App;