"use client"; import React, { memo, useMemo } from "react"; import { NonIndexedLeRobotDatasetRow } from "@lerobot/web"; import { TeleoperatorJointGraph } from "./teleoperator-joint-graph"; interface TeleoperatorFramesViewProps { frames: NonIndexedLeRobotDatasetRow[]; isRecording?: boolean; refreshTick?: number; } export const TeleoperatorFramesView = memo(function TeleoperatorFramesView({ frames, isRecording, refreshTick, }: TeleoperatorFramesViewProps) { // Joint names in the order they appear in the arrays const jointNames = [ "shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper", ]; // Helper function to format an object as a column of key-value pairs with joint names const formatArrayAsColumn = (obj: Record): string => { return Object.entries(obj) .map(([key, value]) => { // Convert numeric key to joint name if possible const index = parseInt(key); const jointName = !isNaN(index) && index < jointNames.length ? jointNames[index] : key; const formatted = Number.isFinite(value) ? value.toFixed(2) : String(value); return `${jointName}: ${formatted}`; }) .join("\n"); }; const visibleFrames = useMemo( () => frames || ([] as NonIndexedLeRobotDatasetRow[]), [frames, refreshTick] ); return (
{/* Joint visualization graph */} {/* Frames container with horizontal scroll */}
{/* Frames header */} {/* Frame rows */} {visibleFrames.map( (frame: NonIndexedLeRobotDatasetRow, frameIndex: number) => ( ) )}
Frame Timestamp Action State
{frameIndex} {Number.isFinite(frame.timestamp) ? frame.timestamp.toFixed(3) : String(frame.timestamp)} {Object.keys(frame.action).length > 0 ? formatArrayAsColumn(frame.action) : "-"} {Object.keys(frame["observation.state"]).length > 0 ? formatArrayAsColumn(frame["observation.state"]) : "-"}
); });