"use client"; import { type RobotPlugin } from "~/lib/plugin-store/types"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { Bot, Download, Info, Zap, Battery, Scale, Ruler } from "lucide-react"; import Image from "next/image"; import { cn } from "~/lib/utils"; import { useState, useRef, useEffect } from "react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"; import { PageHeader } from "~/components/layout/page-header"; import { PageContent } from "~/components/layout/page-content"; import { api } from "~/trpc/react"; import { useToast } from "~/hooks/use-toast"; import { useRouter } from "next/navigation"; interface RobotListProps { plugins: RobotPlugin[]; } function RobotListItem({ plugin, isSelected, onSelect }: { plugin: RobotPlugin; isSelected: boolean; onSelect: () => void; }) { return (
{plugin.assets.logo ? ( {plugin.name} ) : plugin.assets.thumbnailUrl ? ( {plugin.name} ) : (
)}

{plugin.name}

{plugin.platform}

{plugin.description}

{plugin.specs.maxSpeed}m/s
{plugin.specs.batteryLife}h
); } function RobotHeader({ robot }: { robot: RobotPlugin }) { const router = useRouter(); const { toast } = useToast(); const [isInstalling, setIsInstalling] = useState(false); const utils = api.useUtils(); const installPlugin = api.pluginStore.installPlugin.useMutation({ onSuccess: () => { toast({ title: "Success", description: `${robot.name} installed successfully`, }); // Invalidate both queries to refresh the data utils.pluginStore.getInstalledPlugins.invalidate(); utils.pluginStore.getPlugins.invalidate(); }, onError: (error) => { console.error("Failed to install plugin:", error); toast({ title: "Error", description: error.message || "Failed to install plugin", variant: "destructive", }); }, }); const handleInstall = async () => { if (isInstalling) return; try { setIsInstalling(true); await installPlugin.mutateAsync({ robotId: robot.robotId, repositoryId: "hristudio-official", // TODO: Get from context }); } finally { setIsInstalling(false); } }; return (

{robot.name}

{robot.description}

{robot.specs.maxSpeed}m/s
{robot.specs.batteryLife}h
{robot.specs.dimensions.weight}kg
); } function RobotImages({ robot }: { robot: RobotPlugin }) { const [showLeftFade, setShowLeftFade] = useState(false); const [showRightFade, setShowRightFade] = useState(false); const scrollRef = useRef(null); useEffect(() => { const el = scrollRef.current; if (!el) return; const checkScroll = () => { const hasLeftScroll = el.scrollLeft > 0; const hasRightScroll = el.scrollLeft < (el.scrollWidth - el.clientWidth); setShowLeftFade(hasLeftScroll); setShowRightFade(hasRightScroll); }; // Check initial scroll checkScroll(); // Add scroll listener el.addEventListener('scroll', checkScroll); // Add resize listener to handle window changes window.addEventListener('resize', checkScroll); return () => { el.removeEventListener('scroll', checkScroll); window.removeEventListener('resize', checkScroll); }; }, []); return (
{/* Main Image */}
{robot.name}
{/* Angle Images */} {robot.assets.images.angles && (
{Object.entries(robot.assets.images.angles).map(([angle, url]) => url && (
{`${robot.name}
{angle} View
))}
)}
{/* Fade indicators */} {showLeftFade && (
)} {showRightFade && (
)}
); } function RobotSpecs({ robot }: { robot: RobotPlugin }) { return (

Physical Specifications

{robot.specs.dimensions.length}m × {robot.specs.dimensions.width}m × {robot.specs.dimensions.height}m
{robot.specs.dimensions.weight}kg
{robot.specs.maxSpeed}m/s
{robot.specs.batteryLife}h

Capabilities

{robot.specs.capabilities.map((capability) => ( {capability} ))}

ROS 2 Configuration

Namespace: {robot.ros2Config.namespace}
Node Prefix: {robot.ros2Config.nodePrefix}
Default Topics:
{Object.entries(robot.ros2Config.defaultTopics).map(([name, topic]) => (
{name}: {topic}
))}
); } function RobotActions({ robot }: { robot: RobotPlugin }) { return (
{robot.actions.map((action) => (

{action.title}

{action.type}

{action.description}

Parameters:
{Object.entries(action.parameters.properties).map(([name, prop]) => (
{prop.title} {prop.unit && ( ({prop.unit}) )} {prop.description && (

{prop.description}

)}
))}
))}
); } export function RobotList({ plugins }: RobotListProps) { const [selectedRobot, setSelectedRobot] = useState(plugins[0] ?? null); if (!plugins.length) { return (

No robots available

); } return (
{/* Left Pane - Robot List */}
{plugins.map((plugin) => ( setSelectedRobot(plugin)} /> ))}
{/* Right Pane - Robot Details */} {selectedRobot && (
Overview Specifications Actions

Documentation

User Manual {selectedRobot.documentation.apiReference && ( API Reference )}
)}
); }