Spaces:
Running
Running
feat(web): update demo to use latest lib
Browse files- docs/conventions.md +9 -0
- docs/hf_config.yml +2 -2
- src/demo/App.tsx +1 -2
- src/demo/components/CalibrationModal.tsx +0 -1
- src/demo/components/CalibrationPanel.tsx +1 -1
- src/demo/components/ErrorBoundary.tsx +1 -1
- src/demo/components/PortManager.tsx +1 -41
- src/demo/components/TeleoperationPanel.tsx +2 -3
- src/demo/hooks/useRobotConnection.ts +0 -91
- src/demo/main.tsx +0 -1
- src/demo/pages/Home.tsx +3 -19
- src/lerobot/web/calibrate.ts +7 -0
- src/lerobot/web/find_port.ts +0 -2
- src/lerobot/web/teleoperate.ts +7 -0
docs/conventions.md
CHANGED
@@ -20,6 +20,15 @@
|
|
20 |
- **Direct Library Usage**: End users call library functions directly (e.g., `calibrate()`, `teleoperate()`) - avoid unnecessary abstraction layers
|
21 |
- **Comments**: Write about the functionality, not what you did. We only need to know what the code is doing to make it more easy to understand, not a history of the changes
|
22 |
- **No Reference Comments**: Never write comments like "same pattern as calibrate.ts", "matches Node.js", "copied from X", etc. Comments should explain what the code does, not where it came from or what it's similar to
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
## Project Goals
|
25 |
|
|
|
20 |
- **Direct Library Usage**: End users call library functions directly (e.g., `calibrate()`, `teleoperate()`) - avoid unnecessary abstraction layers
|
21 |
- **Comments**: Write about the functionality, not what you did. We only need to know what the code is doing to make it more easy to understand, not a history of the changes
|
22 |
- **No Reference Comments**: Never write comments like "same pattern as calibrate.ts", "matches Node.js", "copied from X", etc. Comments should explain what the code does, not where it came from or what it's similar to
|
23 |
+
- **No Change Explanation Comments**: NEVER add comments explaining what you changed or why you removed something. Examples of forbidden comments:
|
24 |
+
|
25 |
+
- `// React import not needed with modern JSX transform`
|
26 |
+
- `// Removed unused import`
|
27 |
+
- `// Note: Connection manager registration removed for build compatibility`
|
28 |
+
- `// Card components not needed in this file`
|
29 |
+
- `// SerialPortRequestOptions unused in current implementation`
|
30 |
+
|
31 |
+
Just make the change cleanly without explaining it in comments.
|
32 |
|
33 |
## Project Goals
|
34 |
|
docs/hf_config.yml
CHANGED
@@ -4,8 +4,8 @@ emoji: 🤖
|
|
4 |
colorFrom: blue
|
5 |
colorTo: green
|
6 |
sdk: static
|
7 |
-
app_build_command:
|
8 |
-
app_file: dist/index.html
|
9 |
pinned: true
|
10 |
---
|
11 |
|
|
|
4 |
colorFrom: blue
|
5 |
colorTo: green
|
6 |
sdk: static
|
7 |
+
app_build_command: npx vite build --mode demo
|
8 |
+
app_file: dist/demo/index.html
|
9 |
pinned: true
|
10 |
---
|
11 |
|
src/demo/App.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import
|
2 |
import { Home } from "./pages/Home";
|
3 |
import { ErrorBoundary } from "./components/ErrorBoundary";
|
4 |
import type { RobotConnection } from "../lerobot/web/types/robot-connection.js";
|
@@ -10,7 +10,6 @@ export function App() {
|
|
10 |
<ErrorBoundary>
|
11 |
<div className="min-h-screen bg-background">
|
12 |
<Home
|
13 |
-
onGetStarted={() => {}} // No longer needed
|
14 |
connectedRobots={connectedRobots}
|
15 |
onConnectedRobotsChange={setConnectedRobots}
|
16 |
/>
|
|
|
1 |
+
import { useState } from "react";
|
2 |
import { Home } from "./pages/Home";
|
3 |
import { ErrorBoundary } from "./components/ErrorBoundary";
|
4 |
import type { RobotConnection } from "../lerobot/web/types/robot-connection.js";
|
|
|
10 |
<ErrorBoundary>
|
11 |
<div className="min-h-screen bg-background">
|
12 |
<Home
|
|
|
13 |
connectedRobots={connectedRobots}
|
14 |
onConnectedRobotsChange={setConnectedRobots}
|
15 |
/>
|
src/demo/components/CalibrationModal.tsx
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
import React from "react";
|
2 |
import {
|
3 |
Dialog,
|
4 |
DialogContent,
|
|
|
|
|
1 |
import {
|
2 |
Dialog,
|
3 |
DialogContent,
|
src/demo/components/CalibrationPanel.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import
|
2 |
import { Button } from "./ui/button";
|
3 |
import {
|
4 |
Card,
|
|
|
1 |
+
import { useState, useCallback, useMemo } from "react";
|
2 |
import { Button } from "./ui/button";
|
3 |
import {
|
4 |
Card,
|
src/demo/components/ErrorBoundary.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import
|
2 |
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
3 |
import { Button } from "./ui/button";
|
4 |
|
|
|
1 |
+
import { Component, type ErrorInfo, type ReactNode } from "react";
|
2 |
import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
|
3 |
import { Button } from "./ui/button";
|
4 |
|
src/demo/components/PortManager.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import
|
2 |
import { Button } from "./ui/button";
|
3 |
import {
|
4 |
Card,
|
@@ -165,25 +165,6 @@ export function PortManager({
|
|
165 |
);
|
166 |
await port.open({ baudRate: 1000000 });
|
167 |
isConnected = true;
|
168 |
-
|
169 |
-
// Register with singleton connection manager
|
170 |
-
try {
|
171 |
-
const { getRobotConnectionManager } = await import(
|
172 |
-
"../../lerobot/web/robot-connection"
|
173 |
-
);
|
174 |
-
const connectionManager = getRobotConnectionManager();
|
175 |
-
await connectionManager.connect(
|
176 |
-
port,
|
177 |
-
robotType,
|
178 |
-
robotId,
|
179 |
-
serialNumber!
|
180 |
-
);
|
181 |
-
} catch (error) {
|
182 |
-
console.warn(
|
183 |
-
"Failed to register with connection manager:",
|
184 |
-
error
|
185 |
-
);
|
186 |
-
}
|
187 |
} else {
|
188 |
console.log(
|
189 |
"Port found but no saved robot configuration, skipping auto-connect"
|
@@ -338,27 +319,6 @@ export function PortManager({
|
|
338 |
|
339 |
onConnectedRobotsChange([...connectedRobots, newRobot]);
|
340 |
console.log("🤖 New robot connected with ID:", serialNumber);
|
341 |
-
|
342 |
-
// Register with singleton connection manager if robot is configured
|
343 |
-
if (newRobot.robotType && newRobot.robotId) {
|
344 |
-
try {
|
345 |
-
const { getRobotConnectionManager } = await import(
|
346 |
-
"../../lerobot/web/robot-connection"
|
347 |
-
);
|
348 |
-
const connectionManager = getRobotConnectionManager();
|
349 |
-
await connectionManager.connect(
|
350 |
-
port,
|
351 |
-
newRobot.robotType,
|
352 |
-
newRobot.robotId,
|
353 |
-
serialNumber!
|
354 |
-
);
|
355 |
-
} catch (error) {
|
356 |
-
console.warn(
|
357 |
-
"Failed to register new connection with manager:",
|
358 |
-
error
|
359 |
-
);
|
360 |
-
}
|
361 |
-
}
|
362 |
} else {
|
363 |
// Existing robot - update port and connection status
|
364 |
const updatedRobots = connectedRobots.map((robot, index) =>
|
|
|
1 |
+
import { useState, useEffect } from "react";
|
2 |
import { Button } from "./ui/button";
|
3 |
import {
|
4 |
Card,
|
|
|
165 |
);
|
166 |
await port.open({ baudRate: 1000000 });
|
167 |
isConnected = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
} else {
|
169 |
console.log(
|
170 |
"Port found but no saved robot configuration, skipping auto-connect"
|
|
|
319 |
|
320 |
onConnectedRobotsChange([...connectedRobots, newRobot]);
|
321 |
console.log("🤖 New robot connected with ID:", serialNumber);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
} else {
|
323 |
// Existing robot - update port and connection status
|
324 |
const updatedRobots = connectedRobots.map((robot, index) =>
|
src/demo/components/TeleoperationPanel.tsx
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
-
import
|
2 |
import { Button } from "./ui/button";
|
3 |
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
|
4 |
import { Badge } from "./ui/badge";
|
5 |
import { Alert, AlertDescription } from "./ui/alert";
|
6 |
-
import { Progress } from "./ui/progress";
|
7 |
import {
|
8 |
teleoperate,
|
9 |
type TeleoperationProcess,
|
@@ -30,7 +29,7 @@ export function TeleoperationPanel({
|
|
30 |
keyStates: {},
|
31 |
});
|
32 |
const [error, setError] = useState<string | null>(null);
|
33 |
-
const [
|
34 |
|
35 |
const teleoperationProcessRef = useRef<TeleoperationProcess | null>(null);
|
36 |
|
|
|
1 |
+
import { useState, useEffect, useRef, useCallback } from "react";
|
2 |
import { Button } from "./ui/button";
|
3 |
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
|
4 |
import { Badge } from "./ui/badge";
|
5 |
import { Alert, AlertDescription } from "./ui/alert";
|
|
|
6 |
import {
|
7 |
teleoperate,
|
8 |
type TeleoperationProcess,
|
|
|
29 |
keyStates: {},
|
30 |
});
|
31 |
const [error, setError] = useState<string | null>(null);
|
32 |
+
const [, setIsInitialized] = useState(false);
|
33 |
|
34 |
const teleoperationProcessRef = useRef<TeleoperationProcess | null>(null);
|
35 |
|
src/demo/hooks/useRobotConnection.ts
DELETED
@@ -1,91 +0,0 @@
|
|
1 |
-
import { useState, useEffect, useCallback } from "react";
|
2 |
-
import {
|
3 |
-
getRobotConnectionManager,
|
4 |
-
type RobotConnectionState,
|
5 |
-
writeMotorPosition,
|
6 |
-
readMotorPosition,
|
7 |
-
readAllMotorPositions,
|
8 |
-
} from "../../lerobot/web/robot-connection";
|
9 |
-
|
10 |
-
export interface UseRobotConnectionResult {
|
11 |
-
// Connection state
|
12 |
-
isConnected: boolean;
|
13 |
-
robotType?: "so100_follower" | "so100_leader";
|
14 |
-
robotId?: string;
|
15 |
-
serialNumber?: string;
|
16 |
-
lastError?: string;
|
17 |
-
|
18 |
-
// Connection management
|
19 |
-
connect: (
|
20 |
-
port: SerialPort,
|
21 |
-
robotType: string,
|
22 |
-
robotId: string,
|
23 |
-
serialNumber: string
|
24 |
-
) => Promise<void>;
|
25 |
-
disconnect: () => Promise<void>;
|
26 |
-
|
27 |
-
// Robot operations
|
28 |
-
writeMotorPosition: (motorId: number, position: number) => Promise<void>;
|
29 |
-
readMotorPosition: (motorId: number) => Promise<number | null>;
|
30 |
-
readAllMotorPositions: (motorIds: number[]) => Promise<number[]>;
|
31 |
-
|
32 |
-
// Raw port access (for advanced use cases)
|
33 |
-
getPort: () => SerialPort | null;
|
34 |
-
}
|
35 |
-
|
36 |
-
/**
|
37 |
-
* React hook for robot connection management
|
38 |
-
* Uses the singleton connection manager as single source of truth
|
39 |
-
*/
|
40 |
-
export function useRobotConnection(): UseRobotConnectionResult {
|
41 |
-
const manager = getRobotConnectionManager();
|
42 |
-
const [state, setState] = useState<RobotConnectionState>(manager.getState());
|
43 |
-
|
44 |
-
// Subscribe to connection state changes
|
45 |
-
useEffect(() => {
|
46 |
-
const unsubscribe = manager.onStateChange(setState);
|
47 |
-
|
48 |
-
// Set initial state
|
49 |
-
setState(manager.getState());
|
50 |
-
|
51 |
-
return unsubscribe;
|
52 |
-
}, [manager]);
|
53 |
-
|
54 |
-
// Connection management functions
|
55 |
-
const connect = useCallback(
|
56 |
-
async (
|
57 |
-
port: SerialPort,
|
58 |
-
robotType: string,
|
59 |
-
robotId: string,
|
60 |
-
serialNumber: string
|
61 |
-
) => {
|
62 |
-
await manager.connect(port, robotType, robotId, serialNumber);
|
63 |
-
},
|
64 |
-
[manager]
|
65 |
-
);
|
66 |
-
|
67 |
-
const disconnect = useCallback(async () => {
|
68 |
-
await manager.disconnect();
|
69 |
-
}, [manager]);
|
70 |
-
|
71 |
-
const getPort = useCallback(() => {
|
72 |
-
return manager.getPort();
|
73 |
-
}, [manager]);
|
74 |
-
|
75 |
-
return {
|
76 |
-
// State
|
77 |
-
isConnected: state.isConnected,
|
78 |
-
robotType: state.robotType,
|
79 |
-
robotId: state.robotId,
|
80 |
-
serialNumber: state.serialNumber,
|
81 |
-
lastError: state.lastError,
|
82 |
-
|
83 |
-
// Methods
|
84 |
-
connect,
|
85 |
-
disconnect,
|
86 |
-
writeMotorPosition,
|
87 |
-
readMotorPosition,
|
88 |
-
readAllMotorPositions,
|
89 |
-
getPort,
|
90 |
-
};
|
91 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/demo/main.tsx
CHANGED
@@ -1,4 +1,3 @@
|
|
1 |
-
import React from "react";
|
2 |
import ReactDOM from "react-dom/client";
|
3 |
import { App } from "./App";
|
4 |
import "./index.css";
|
|
|
|
|
1 |
import ReactDOM from "react-dom/client";
|
2 |
import { App } from "./App";
|
3 |
import "./index.css";
|
src/demo/pages/Home.tsx
CHANGED
@@ -1,12 +1,5 @@
|
|
1 |
-
import
|
2 |
import { Button } from "../components/ui/button";
|
3 |
-
import {
|
4 |
-
Card,
|
5 |
-
CardContent,
|
6 |
-
CardDescription,
|
7 |
-
CardHeader,
|
8 |
-
CardTitle,
|
9 |
-
} from "../components/ui/card";
|
10 |
import { Alert, AlertDescription } from "../components/ui/alert";
|
11 |
import { PortManager } from "../components/PortManager";
|
12 |
import { CalibrationPanel } from "../components/CalibrationPanel";
|
@@ -15,27 +8,18 @@ import { isWebSerialSupported } from "../../lerobot/web/calibrate";
|
|
15 |
import type { RobotConnection } from "../../lerobot/web/types/robot-connection.js";
|
16 |
|
17 |
interface HomeProps {
|
18 |
-
onGetStarted: () => void;
|
19 |
connectedRobots: RobotConnection[];
|
20 |
onConnectedRobotsChange: (robots: RobotConnection[]) => void;
|
21 |
}
|
22 |
|
23 |
-
export function Home({
|
24 |
-
onGetStarted,
|
25 |
-
connectedRobots,
|
26 |
-
onConnectedRobotsChange,
|
27 |
-
}: HomeProps) {
|
28 |
const [calibratingRobot, setCalibratingRobot] =
|
29 |
useState<RobotConnection | null>(null);
|
30 |
const [teleoperatingRobot, setTeleoperatingRobot] =
|
31 |
useState<RobotConnection | null>(null);
|
32 |
const isSupported = isWebSerialSupported();
|
33 |
|
34 |
-
const handleCalibrate = (
|
35 |
-
port: SerialPort,
|
36 |
-
robotType: "so100_follower" | "so100_leader",
|
37 |
-
robotId: string
|
38 |
-
) => {
|
39 |
// Find the robot from connectedRobots
|
40 |
const robot = connectedRobots.find((r) => r.port === port);
|
41 |
if (robot) {
|
|
|
1 |
+
import { useState } from "react";
|
2 |
import { Button } from "../components/ui/button";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
import { Alert, AlertDescription } from "../components/ui/alert";
|
4 |
import { PortManager } from "../components/PortManager";
|
5 |
import { CalibrationPanel } from "../components/CalibrationPanel";
|
|
|
8 |
import type { RobotConnection } from "../../lerobot/web/types/robot-connection.js";
|
9 |
|
10 |
interface HomeProps {
|
|
|
11 |
connectedRobots: RobotConnection[];
|
12 |
onConnectedRobotsChange: (robots: RobotConnection[]) => void;
|
13 |
}
|
14 |
|
15 |
+
export function Home({ connectedRobots, onConnectedRobotsChange }: HomeProps) {
|
|
|
|
|
|
|
|
|
16 |
const [calibratingRobot, setCalibratingRobot] =
|
17 |
useState<RobotConnection | null>(null);
|
18 |
const [teleoperatingRobot, setTeleoperatingRobot] =
|
19 |
useState<RobotConnection | null>(null);
|
20 |
const isSupported = isWebSerialSupported();
|
21 |
|
22 |
+
const handleCalibrate = (port: SerialPort) => {
|
|
|
|
|
|
|
|
|
23 |
// Find the robot from connectedRobots
|
24 |
const robot = connectedRobots.find((r) => r.port === port);
|
25 |
if (robot) {
|
src/lerobot/web/calibrate.ts
CHANGED
@@ -23,6 +23,13 @@ import type {
|
|
23 |
CalibrationProcess,
|
24 |
} from "./types/calibration.js";
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
/**
|
27 |
* Record ranges of motion with live updates
|
28 |
*/
|
|
|
23 |
CalibrationProcess,
|
24 |
} from "./types/calibration.js";
|
25 |
|
26 |
+
// Re-export types for external use
|
27 |
+
export type {
|
28 |
+
WebCalibrationResults,
|
29 |
+
LiveCalibrationData,
|
30 |
+
CalibrationProcess,
|
31 |
+
} from "./types/calibration.js";
|
32 |
+
|
33 |
/**
|
34 |
* Record ranges of motion with live updates
|
35 |
*/
|
src/lerobot/web/find_port.ts
CHANGED
@@ -37,8 +37,6 @@ import type {
|
|
37 |
} from "./types/robot-connection.js";
|
38 |
import type {
|
39 |
Serial,
|
40 |
-
SerialPortRequestOptions,
|
41 |
-
SerialPortFilter,
|
42 |
FindPortOptions,
|
43 |
FindPortProcess,
|
44 |
} from "./types/port-discovery.js";
|
|
|
37 |
} from "./types/robot-connection.js";
|
38 |
import type {
|
39 |
Serial,
|
|
|
|
|
40 |
FindPortOptions,
|
41 |
FindPortProcess,
|
42 |
} from "./types/port-discovery.js";
|
src/lerobot/web/teleoperate.ts
CHANGED
@@ -20,6 +20,13 @@ import type {
|
|
20 |
TeleoperationProcess,
|
21 |
} from "./types/teleoperation.js";
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
/**
|
24 |
* Create motor configurations from robot hardware config
|
25 |
* Pure function - converts robot specs to motor configs with defaults
|
|
|
20 |
TeleoperationProcess,
|
21 |
} from "./types/teleoperation.js";
|
22 |
|
23 |
+
// Re-export types for external use
|
24 |
+
export type {
|
25 |
+
MotorConfig,
|
26 |
+
TeleoperationState,
|
27 |
+
TeleoperationProcess,
|
28 |
+
} from "./types/teleoperation.js";
|
29 |
+
|
30 |
/**
|
31 |
* Create motor configurations from robot hardware config
|
32 |
* Pure function - converts robot specs to motor configs with defaults
|