mujoco-react 8.6.0 → 8.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,83 @@ Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper a
18
18
  npm install mujoco-react three @react-three/fiber @react-three/drei
19
19
  ```
20
20
 
21
+ ## Vite Plugin and Type-Safe Names
22
+
23
+ Use the Vite plugin to generate TanStack-style declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
24
+
25
+ ```ts
26
+ // vite.config.ts
27
+ import { defineConfig } from "vite";
28
+ import { mujocoReact } from "mujoco-react/vite";
29
+
30
+ export default defineConfig({
31
+ plugins: [
32
+ mujocoReact({
33
+ models: {
34
+ franka: "models/panda/scene.xml",
35
+ spot: "models/spot/scene.xml",
36
+ },
37
+ }),
38
+ ],
39
+ });
40
+ ```
41
+
42
+ The plugin writes `src/mujoco-register.gen.d.ts` during dev and build. Commit that generated file:
43
+
44
+ ```ts
45
+ // src/mujoco-register.gen.d.ts
46
+ // Auto-generated by mujoco-react. Do not edit.
47
+
48
+ import "mujoco-react";
49
+ import type { RobotResource } from "mujoco-react";
50
+
51
+ declare module "mujoco-react" {
52
+ interface Register {
53
+ robots: {
54
+ franka: {
55
+ actuators: "joint1" | "joint2" | "joint3" | "gripper";
56
+ sensors: "force_sensor" | "torque_sensor";
57
+ bodies: "link0" | "link1" | "hand";
58
+ joints: "joint1" | "joint2" | "joint3";
59
+ sites: "tcp";
60
+ geoms: "floor";
61
+ keyframes: "home";
62
+ };
63
+ spot: {
64
+ actuators: "fl_hx" | "fl_hy" | "fl_kn";
65
+ sensors: never;
66
+ bodies: "body" | "fl_hip" | "fl_uleg";
67
+ joints: "fl_hx" | "fl_hy" | "fl_kn";
68
+ sites: never;
69
+ geoms: "floor";
70
+ keyframes: "home";
71
+ };
72
+ };
73
+ actuators: "joint1" | "joint2" | "joint3" | "gripper" | "fl_hx" | "fl_hy" | "fl_kn";
74
+ sensors: "force_sensor" | "torque_sensor";
75
+ bodies: "link0" | "link1" | "hand" | "body" | "fl_hip" | "fl_uleg";
76
+ }
77
+
78
+ export namespace RobotActuators {
79
+ export type franka = RobotResource<"franka", "actuators">;
80
+ export type spot = RobotResource<"spot", "actuators">;
81
+ }
82
+
83
+ export namespace RobotSites {
84
+ export type franka = RobotResource<"franka", "sites">;
85
+ export type spot = RobotResource<"spot", "sites">;
86
+ }
87
+ }
88
+ ```
89
+
90
+ Once generated, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` accept the global union. For robot-scoped reusable code, use generated namespace types such as `RobotActuators.franka`, `RobotSites.franka`, and `RobotBodies.franka`. Generic helpers like `RobotActuators<"franka">` are still available for reusable library code. When no `Register` augmentation is present, names fall back to `string`.
91
+
92
+ Non-Vite projects can generate the same file with:
93
+
94
+ ```bash
95
+ npx mujoco-react codegen franka=models/panda/scene.xml spot=models/spot/scene.xml
96
+ ```
97
+
21
98
  ## Load a Model
22
99
 
23
100
  ```tsx
@@ -87,6 +164,23 @@ function MyController() {
87
164
 
88
165
  Controllers are just React children that read sensors, write `data.ctrl`, apply forces, or call the `MujocoSimAPI` at physics-step time.
89
166
 
167
+ With generated resource types, reusable controllers can be scoped to one robot. Configure stable robot keys in the Vite plugin, then use those keys in helper types:
168
+
169
+ ```tsx
170
+ import { useCtrl, useIkController } from "mujoco-react";
171
+ import type { RobotActuators, RobotSites } from "mujoco-react";
172
+
173
+ const frankaGripper: RobotActuators.franka = "gripper";
174
+ const frankaTcp: RobotSites.franka = "tcp";
175
+
176
+ function FrankaTypedControls() {
177
+ const gripper = useCtrl(frankaGripper);
178
+ const ik = useIkController({ siteName: frankaTcp });
179
+
180
+ return null;
181
+ }
182
+ ```
183
+
90
184
  ## Use the Sim API
91
185
 
92
186
  ```tsx
@@ -304,49 +398,6 @@ const gripperIk = useIkController({
304
398
  });
305
399
  ```
306
400
 
307
- ## Type-Safe Resource Names
308
-
309
- Use the Vite plugin to generate TanStack-style declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
310
-
311
- ```ts
312
- // vite.config.ts
313
- import { defineConfig } from "vite";
314
- import { mujocoReact } from "mujoco-react/vite";
315
-
316
- export default defineConfig({
317
- plugins: [
318
- mujocoReact({
319
- models: "models/panda/scene.xml",
320
- }),
321
- ],
322
- });
323
- ```
324
-
325
- The plugin writes `src/mujoco-register.gen.d.ts` during dev and build. Commit that generated file:
326
-
327
- ```ts
328
- // src/mujoco-register.gen.d.ts
329
- // Auto-generated by mujoco-react. Do not edit.
330
-
331
- import "mujoco-react";
332
-
333
- declare module "mujoco-react" {
334
- interface Register {
335
- actuators: "joint1" | "joint2" | "joint3" | "gripper";
336
- sensors: "force_sensor" | "torque_sensor";
337
- bodies: "link0" | "link1" | "hand";
338
- }
339
- }
340
- ```
341
-
342
- Once generated, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` will only accept the declared names. When no `Register` augmentation is present, names fall back to `string`.
343
-
344
- Non-Vite projects can generate the same file with:
345
-
346
- ```bash
347
- npx mujoco-react codegen models/panda/scene.xml
348
- ```
349
-
350
401
  ## Loading Models
351
402
 
352
403
  The loader fetches `src + sceneFile`, parses the XML for dependencies (meshes, textures, includes), recursively fetches those too, and writes everything to MuJoCo's in-memory WASM filesystem.
@@ -384,15 +435,22 @@ interface SceneConfig {
384
435
  Load browser-selected MJCF or URDF files directly. Folder uploads preserve `webkitRelativePath`, and flat uploads fall back to matching referenced mesh/texture assets by basename:
385
436
 
386
437
  ```tsx
438
+ import { useEffect, useRef } from "react";
439
+ import { useMujoco } from "mujoco-react";
440
+
387
441
  function ModelUpload() {
388
442
  const sim = useMujoco();
443
+ const inputRef = useRef<HTMLInputElement>(null);
444
+
445
+ useEffect(() => {
446
+ inputRef.current?.setAttribute("webkitdirectory", "");
447
+ }, []);
389
448
 
390
449
  return (
391
450
  <input
451
+ ref={inputRef}
392
452
  type="file"
393
453
  multiple
394
- // @ts-expect-error Chromium/WebKit folder uploads
395
- webkitdirectory=""
396
454
  onChange={(event) => {
397
455
  if (sim.isReady && event.currentTarget.files) {
398
456
  sim.api.loadFromFiles(event.currentTarget.files);
@@ -6,13 +6,14 @@ import { generateMujocoRegister } from '../dist/vite.js';
6
6
  const usage = `
7
7
  Usage:
8
8
  mujoco-react codegen <scene.xml> [...more.xml] [--out src/mujoco-register.gen.d.ts] [--watch]
9
+ mujoco-react codegen franka=models/panda/scene.xml spot=models/spot/scene.xml
9
10
 
10
11
  Vite users usually do not need this command. Prefer:
11
12
 
12
13
  import { mujocoReact } from "mujoco-react/vite";
13
14
 
14
15
  export default defineConfig({
15
- plugins: [mujocoReact({ models: "models/panda/scene.xml" })],
16
+ plugins: [mujocoReact({ models: { franka: "models/panda/scene.xml" } })],
16
17
  });
17
18
  `;
18
19
 
@@ -39,6 +40,7 @@ const models = commandArgs.filter((arg, index) => {
39
40
  const previous = commandArgs[index - 1];
40
41
  return previous !== '--out' && previous !== '--module';
41
42
  });
43
+ const modelInput = parseModels(models);
42
44
 
43
45
  if (!models.length) {
44
46
  console.error(usage.trim());
@@ -69,7 +71,7 @@ if (shouldWatch) {
69
71
 
70
72
  async function generate() {
71
73
  const result = await generateMujocoRegister({
72
- models,
74
+ models: modelInput,
73
75
  out,
74
76
  moduleName,
75
77
  root: process.cwd(),
@@ -79,6 +81,16 @@ async function generate() {
79
81
  console.log(`[mujoco-react] generated ${path.relative(process.cwd(), result.out)} (${total} names)`);
80
82
  }
81
83
 
84
+ function parseModels(values) {
85
+ if (values.every((value) => value.includes('='))) {
86
+ return Object.fromEntries(values.map((value) => {
87
+ const index = value.indexOf('=');
88
+ return [value.slice(0, index), value.slice(index + 1)];
89
+ }));
90
+ }
91
+ return values;
92
+ }
93
+
82
94
  function valueAfter(values, flag) {
83
95
  const index = values.indexOf(flag);
84
96
  if (index === -1) return undefined;
package/dist/index.d.ts CHANGED
@@ -16,6 +16,13 @@ import * as THREE from 'three';
16
16
  * ```ts
17
17
  * declare module 'mujoco-react' {
18
18
  * interface Register {
19
+ * robots: {
20
+ * panda: {
21
+ * actuators: 'joint1' | 'joint2' | 'gripper';
22
+ * sensors: 'force_sensor' | 'torque_sensor';
23
+ * bodies: 'link0' | 'link1' | 'hand';
24
+ * };
25
+ * };
19
26
  * actuators: 'joint1' | 'joint2' | 'gripper';
20
27
  * sensors: 'force_sensor' | 'torque_sensor';
21
28
  * bodies: 'link0' | 'link1' | 'hand';
@@ -27,6 +34,20 @@ import * as THREE from 'three';
27
34
  */
28
35
  interface Register {
29
36
  }
37
+ type RegisteredRobotMap = Register extends {
38
+ robots: infer T extends Record<string, Record<string, string>>;
39
+ } ? T : never;
40
+ type Robots = [RegisteredRobotMap] extends [never] ? string : Extract<keyof RegisteredRobotMap, string>;
41
+ type RobotResource<TRobot extends string, TKey extends string> = [
42
+ RegisteredRobotMap
43
+ ] extends [never] ? string : TRobot extends keyof RegisteredRobotMap ? TKey extends keyof RegisteredRobotMap[TRobot] ? RegisteredRobotMap[TRobot][TKey] : string : never;
44
+ type RobotActuators<TRobot extends string> = RobotResource<TRobot, 'actuators'>;
45
+ type RobotSensors<TRobot extends string> = RobotResource<TRobot, 'sensors'>;
46
+ type RobotBodies<TRobot extends string> = RobotResource<TRobot, 'bodies'>;
47
+ type RobotJoints<TRobot extends string> = RobotResource<TRobot, 'joints'>;
48
+ type RobotSites<TRobot extends string> = RobotResource<TRobot, 'sites'>;
49
+ type RobotGeoms<TRobot extends string> = RobotResource<TRobot, 'geoms'>;
50
+ type RobotKeyframes<TRobot extends string> = RobotResource<TRobot, 'keyframes'>;
30
51
  type Actuators = Register extends {
31
52
  actuators: infer T extends string;
32
53
  } ? T : string;
@@ -1483,4 +1504,4 @@ interface CameraAnimationAPI {
1483
1504
  */
1484
1505
  declare function useCameraAnimation(): CameraAnimationAPI;
1485
1506
 
1486
- export { type ActuatedJointInfo, type ActuatorInfo, type Actuators, type Bodies, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControlGroupInfo, type ControlGroupSelector, type ControlJointInfo, type ControllerComponent, type ControllerOptions, type CtrlHandle, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type Geoms, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, InstancedGeomRenderer, type JointInfo, type JointStateResult, type Joints, type KeyBinding, type KeyboardTeleopConfig, type Keyframes, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoLoader, type MujocoLoaderOptions, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, type MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, type ObservationConfig, type ObservationHandle, type ObservationLayoutItem, type ObservationOutput, type ObservationResult, type PhysicsConfig, type PhysicsStepCallback, type PlaybackState, type PolicyConfig, type RayHit, type Register, type ResourceSelector, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorHandle, type SensorInfo, type SensorResult, type Sensors, type SiteInfo, type SitePositionResult, type Sites, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, type TrajectoryInput, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, buildObservation, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
1507
+ export { type ActuatedJointInfo, type ActuatorInfo, type Actuators, type Bodies, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControlGroupInfo, type ControlGroupSelector, type ControlJointInfo, type ControllerComponent, type ControllerOptions, type CtrlHandle, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type Geoms, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, InstancedGeomRenderer, type JointInfo, type JointStateResult, type Joints, type KeyBinding, type KeyboardTeleopConfig, type Keyframes, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoLoader, type MujocoLoaderOptions, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, type MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, type ObservationConfig, type ObservationHandle, type ObservationLayoutItem, type ObservationOutput, type ObservationResult, type PhysicsConfig, type PhysicsStepCallback, type PlaybackState, type PolicyConfig, type RayHit, type Register, type RegisteredRobotMap, type ResourceSelector, type RobotActuators, type RobotBodies, type RobotGeoms, type RobotJoints, type RobotKeyframes, type RobotResource, type RobotSensors, type RobotSites, type Robots, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorHandle, type SensorInfo, type SensorResult, type Sensors, type SiteInfo, type SitePositionResult, type Sites, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, type TrajectoryInput, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, buildObservation, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };