mujoco-react 9.2.0 → 9.4.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/dist/spark.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as _sparkjsdev_spark from '@sparkjsdev/spark';
3
- import { n as SplatEnvironmentProps } from './types-S8ggQY2n.js';
3
+ import { n as SplatEnvironmentProps, q as PairedSplatEnvironmentConfig, S as SceneConfig, t as SplatEnvironmentReadiness, o as VisualScenarioConfig } from './types-BuJ4boaq.js';
4
4
  import 'react';
5
5
  import '@react-three/fiber';
6
6
  import 'three';
@@ -17,6 +17,14 @@ interface SparkSplatLifecycle {
17
17
  props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;
18
18
  reset: () => void;
19
19
  }
20
+ interface SparkSplatEnvironmentState {
21
+ environment: PairedSplatEnvironmentConfig | undefined;
22
+ sceneConfig: SceneConfig;
23
+ readiness: SplatEnvironmentReadiness;
24
+ lifecycle: SparkSplatLifecycle;
25
+ props: Pick<SparkSplatEnvironmentProps, 'environment' | 'scenario' | 'src' | 'format' | 'onStatusChange' | 'onError'>;
26
+ enabled: boolean;
27
+ }
20
28
  interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
21
29
  /** Enable Spark LoD handling for large splat assets. Default: true. */
22
30
  lod?: boolean | 'quality';
@@ -30,6 +38,20 @@ interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {
30
38
  onLoad?: (mesh: SparkSplatMeshInstance) => void;
31
39
  onError?: (error: Error) => void;
32
40
  }
41
+ /**
42
+ * Resolve a visual scenario's paired splat environment, compose its MJCF
43
+ * collision proxy into the MuJoCo scene config, and expose Spark lifecycle
44
+ * props for `<SparkSplatEnvironment />`.
45
+ */
46
+ declare function useSparkSplatEnvironment({ sceneConfig, scenario, environment, enabled, renderer, onError, onStatusChange, }: {
47
+ sceneConfig: SceneConfig;
48
+ scenario?: VisualScenarioConfig;
49
+ environment?: PairedSplatEnvironmentConfig;
50
+ enabled?: boolean;
51
+ renderer?: 'spark';
52
+ onError?: (error: Error) => void;
53
+ onStatusChange?: (status: SparkSplatStatus) => void;
54
+ }): SparkSplatEnvironmentState;
33
55
  /**
34
56
  * Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.
35
57
  *
@@ -50,4 +72,4 @@ declare function useSparkSplatLifecycle({ enabled, initialStatus, onError, onSta
50
72
  */
51
73
  declare function SparkSplatEnvironment({ environment, scenario, renderer, src, format, collisionProxy, collisionProxyMetadata, showPlaceholder, children, lod, hideGroundMeshes, onStatusChange, onLoad, onError, ...groupProps }: SparkSplatEnvironmentProps): react_jsx_runtime.JSX.Element;
52
74
 
53
- export { SparkSplatEnvironment, type SparkSplatEnvironmentProps, type SparkSplatLifecycle, type SparkSplatStatus, useSparkSplatLifecycle };
75
+ export { SparkSplatEnvironment, type SparkSplatEnvironmentProps, type SparkSplatEnvironmentState, type SparkSplatLifecycle, type SparkSplatStatus, useSparkSplatEnvironment, useSparkSplatLifecycle };
package/dist/spark.js CHANGED
@@ -1,9 +1,56 @@
1
- import { useSplatEnvironment, SplatEnvironment } from './chunk-33CV6HSV.js';
1
+ import { useSplatSceneConfig, useSplatEnvironment, SplatEnvironment } from './chunk-VDSEPZYQ.js';
2
2
  import { useThree } from '@react-three/fiber';
3
- import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
3
+ import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
4
4
  import * as THREE from 'three';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
7
+ var sparkDisposeRejectionHandlerRegistered = false;
8
+ function useSparkSplatEnvironment({
9
+ sceneConfig,
10
+ scenario,
11
+ environment,
12
+ enabled = true,
13
+ renderer = "spark",
14
+ onError,
15
+ onStatusChange
16
+ }) {
17
+ const splatScene = useSplatSceneConfig({
18
+ sceneConfig,
19
+ scenario,
20
+ environment,
21
+ enabled,
22
+ renderer
23
+ });
24
+ const metadata = useSplatEnvironment({
25
+ scenario,
26
+ environment: splatScene.environment,
27
+ renderer
28
+ });
29
+ const renderEnabled = enabled && Boolean(metadata.src);
30
+ const readiness = enabled ? metadata.readiness : splatScene.readiness;
31
+ const lifecycle = useSparkSplatLifecycle({
32
+ enabled: renderEnabled,
33
+ onError,
34
+ onStatusChange
35
+ });
36
+ return useMemo(
37
+ () => ({
38
+ environment: splatScene.environment,
39
+ sceneConfig: splatScene.sceneConfig,
40
+ readiness,
41
+ lifecycle,
42
+ props: {
43
+ environment: splatScene.environment,
44
+ scenario: enabled ? scenario : void 0,
45
+ src: enabled ? metadata.src : void 0,
46
+ format: metadata.format,
47
+ ...lifecycle.props
48
+ },
49
+ enabled: renderEnabled
50
+ }),
51
+ [enabled, lifecycle, metadata, readiness, renderEnabled, scenario, splatScene]
52
+ );
53
+ }
7
54
  function useSparkSplatLifecycle({
8
55
  enabled = true,
9
56
  initialStatus,
@@ -101,6 +148,7 @@ function SparkSplatEnvironment({
101
148
  }, [onError]);
102
149
  useEffect(() => {
103
150
  let disposed = false;
151
+ ensureSparkDisposeRejectionHandler();
104
152
  function setLifecycleStatus(nextStatus) {
105
153
  setStatus(nextStatus);
106
154
  onStatusChangeRef.current?.(nextStatus);
@@ -208,6 +256,7 @@ function SparkSplatEnvironment({
208
256
  }
209
257
  function safelyDisposeSparkResource(resource) {
210
258
  try {
259
+ silenceSparkWorkerTerminateRejections(resource);
211
260
  const result = resource.dispose?.();
212
261
  if (isPromiseLike(result)) {
213
262
  void Promise.resolve(result).catch(handleSparkDisposeError);
@@ -219,17 +268,54 @@ function safelyDisposeSparkResource(resource) {
219
268
  function isPromiseLike(value) {
220
269
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
221
270
  }
271
+ function silenceSparkWorkerTerminateRejections(resource) {
272
+ const workers = getSparkWorkers(resource);
273
+ for (const worker of workers) {
274
+ if (!worker.messages) continue;
275
+ for (const message of Object.values(worker.messages)) {
276
+ const reject = message.reject;
277
+ if (!reject) continue;
278
+ message.reject = (error) => {
279
+ if (!isSparkWorkerTerminateError(error)) {
280
+ reject(error);
281
+ }
282
+ };
283
+ }
284
+ }
285
+ }
286
+ function getSparkWorkers(resource) {
287
+ const sparkResource = resource;
288
+ return [
289
+ sparkResource.worker,
290
+ sparkResource.sortWorker,
291
+ sparkResource.lodWorker
292
+ ].filter((worker) => Boolean(worker));
293
+ }
222
294
  function handleSparkDisposeError(error) {
223
295
  if (error instanceof Error && error.message.toLowerCase().includes("worker terminate")) {
224
296
  return;
225
297
  }
226
298
  console.warn("[mujoco-react] Spark resource disposal failed.", error);
227
299
  }
300
+ function ensureSparkDisposeRejectionHandler() {
301
+ if (sparkDisposeRejectionHandlerRegistered || typeof window === "undefined" || typeof window.addEventListener !== "function") {
302
+ return;
303
+ }
304
+ sparkDisposeRejectionHandlerRegistered = true;
305
+ window.addEventListener("unhandledrejection", (event) => {
306
+ if (isSparkWorkerTerminateError(event.reason)) {
307
+ event.preventDefault();
308
+ }
309
+ });
310
+ }
311
+ function isSparkWorkerTerminateError(reason) {
312
+ return reason instanceof Error && reason.message.toLowerCase().includes("worker terminate");
313
+ }
228
314
  /**
229
315
  * @license
230
316
  * SPDX-License-Identifier: Apache-2.0
231
317
  */
232
318
 
233
- export { SparkSplatEnvironment, useSparkSplatLifecycle };
319
+ export { SparkSplatEnvironment, useSparkSplatEnvironment, useSparkSplatLifecycle };
234
320
  //# sourceMappingURL=spark.js.map
235
321
  //# sourceMappingURL=spark.js.map
package/dist/spark.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":"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","file":"spark.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport * as THREE from 'three';\nimport {\n SplatEnvironment,\n useSplatEnvironment,\n} from './components/VisualScenario';\nimport type {\n SplatEnvironmentProps,\n} from './types';\n\ntype SparkModule = typeof import('@sparkjsdev/spark');\ntype SparkRendererInstance = InstanceType<SparkModule['SparkRenderer']>;\ntype SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;\ntype SparkDisposable = {\n dispose?: () => unknown;\n};\n\nexport type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';\n\nexport interface SparkSplatLifecycle {\n status: SparkSplatStatus;\n error: Error | null;\n isLoading: boolean;\n isReady: boolean;\n isError: boolean;\n props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;\n reset: () => void;\n}\n\nexport interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {\n /** Enable Spark LoD handling for large splat assets. Default: true. */\n lod?: boolean | 'quality';\n /**\n * Hide meshes whose names include floor, ground, or plane while the splat is\n * active. This mirrors the common hybrid-rendering setup where MJCF keeps\n * collision geometry but the splat owns the visual environment.\n */\n hideGroundMeshes?: boolean;\n onStatusChange?: (status: SparkSplatStatus) => void;\n onLoad?: (mesh: SparkSplatMeshInstance) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.\n *\n * Use the returned `props` with `<SparkSplatEnvironment {...lifecycle.props} />`\n * to avoid repeating status/error state in app code.\n */\nexport function useSparkSplatLifecycle({\n enabled = true,\n initialStatus,\n onError,\n onStatusChange,\n}: {\n enabled?: boolean;\n initialStatus?: SparkSplatStatus;\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n} = {}): SparkSplatLifecycle {\n const [status, setStatus] = useState<SparkSplatStatus>(\n initialStatus ?? (enabled ? 'loading' : 'idle')\n );\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n const handleStatusChange = useCallback(\n (nextStatus: SparkSplatStatus) => {\n setStatus(nextStatus);\n if (nextStatus !== 'error') {\n setError(null);\n }\n onStatusChange?.(nextStatus);\n },\n [onStatusChange]\n );\n\n const handleError = useCallback(\n (nextError: Error) => {\n setError(nextError);\n setStatus('error');\n onError?.(nextError);\n },\n [onError]\n );\n\n const reset = useCallback(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n return useMemo(\n () => ({\n status,\n error,\n isLoading: status === 'loading',\n isReady: status === 'ready',\n isError: status === 'error',\n props: {\n onStatusChange: handleStatusChange,\n onError: handleError,\n },\n reset,\n }),\n [error, handleError, handleStatusChange, reset, status]\n );\n}\n\n/**\n * Optional SparkJS-backed Gaussian splat renderer for React Three Fiber scenes.\n *\n * Import from `mujoco-react/spark` and install `@sparkjsdev/spark` in the app\n * that uses it. The core `mujoco-react` entrypoint does not depend on Spark.\n */\nexport function SparkSplatEnvironment({\n environment,\n scenario,\n renderer = 'spark',\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n showPlaceholder,\n children,\n lod = true,\n hideGroundMeshes = false,\n onStatusChange,\n onLoad,\n onError,\n ...groupProps\n}: SparkSplatEnvironmentProps) {\n const groupRef = useRef<THREE.Group>(null);\n const sparkRef = useRef<SparkRendererInstance | null>(null);\n const meshRef = useRef<SparkSplatMeshInstance | null>(null);\n const hiddenMeshesRef = useRef<THREE.Mesh[]>([]);\n const onStatusChangeRef = useRef(onStatusChange);\n const onLoadRef = useRef(onLoad);\n const onErrorRef = useRef(onError);\n const [status, setStatus] = useState<SparkSplatStatus>('idle');\n const { gl, invalidate } = useThree();\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n\n useEffect(() => {\n onStatusChangeRef.current = onStatusChange;\n }, [onStatusChange]);\n\n useEffect(() => {\n onLoadRef.current = onLoad;\n }, [onLoad]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n let disposed = false;\n\n function setLifecycleStatus(nextStatus: SparkSplatStatus) {\n setStatus(nextStatus);\n onStatusChangeRef.current?.(nextStatus);\n }\n\n function restoreHiddenMeshes() {\n for (const mesh of hiddenMeshesRef.current) {\n mesh.visible = true;\n }\n hiddenMeshesRef.current = [];\n }\n\n async function loadSplat() {\n if (!metadata.src) {\n setLifecycleStatus('idle');\n return;\n }\n\n if (metadata.format !== 'spz') {\n const unsupportedFormatError = new Error(\n `SparkSplatEnvironment only supports .spz assets; received \"${metadata.format}\".`\n );\n setLifecycleStatus('error');\n onErrorRef.current?.(unsupportedFormatError);\n return;\n }\n\n setLifecycleStatus('loading');\n\n try {\n const sparkModule = await import('@sparkjsdev/spark');\n if (disposed || !groupRef.current) return;\n\n const spark = new sparkModule.SparkRenderer({\n renderer: gl,\n onDirty: invalidate,\n });\n const mesh = new sparkModule.SplatMesh({\n url: metadata.src,\n lod,\n });\n mesh.name = 'GaussianSplatMesh';\n\n groupRef.current.add(spark);\n groupRef.current.add(mesh);\n sparkRef.current = spark;\n meshRef.current = mesh;\n\n if (hideGroundMeshes && groupRef.current.parent) {\n groupRef.current.parent.traverse((object) => {\n if (\n !(object instanceof THREE.Mesh) ||\n object === (mesh as unknown as THREE.Object3D)\n ) {\n return;\n }\n const name = object.name.toLowerCase();\n if (\n name.includes('floor') ||\n name.includes('ground') ||\n name.includes('plane')\n ) {\n object.visible = false;\n hiddenMeshesRef.current.push(object);\n }\n });\n }\n\n await mesh.initialized;\n if (disposed) return;\n setLifecycleStatus('ready');\n onLoadRef.current?.(mesh);\n invalidate();\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n setLifecycleStatus('error');\n onErrorRef.current?.(normalizedError);\n }\n }\n\n void loadSplat();\n\n return () => {\n disposed = true;\n restoreHiddenMeshes();\n\n if (meshRef.current) {\n groupRef.current?.remove(meshRef.current);\n safelyDisposeSparkResource(meshRef.current);\n meshRef.current = null;\n }\n\n if (sparkRef.current) {\n groupRef.current?.remove(sparkRef.current);\n safelyDisposeSparkResource(sparkRef.current);\n sparkRef.current = null;\n }\n };\n }, [\n gl,\n hideGroundMeshes,\n invalidate,\n lod,\n metadata.format,\n metadata.src,\n ]);\n\n return (\n <SplatEnvironment\n {...groupProps}\n environment={environment}\n scenario={scenario}\n renderer={renderer}\n src={metadata.src}\n format={metadata.format}\n collisionProxyMetadata={metadata.collisionProxy}\n collisionProxy={collisionProxy}\n showPlaceholder={showPlaceholder ?? status !== 'ready'}\n >\n <group ref={groupRef} />\n {children}\n </SplatEnvironment>\n );\n}\n\nfunction safelyDisposeSparkResource(resource: SparkDisposable) {\n try {\n const result = resource.dispose?.();\n if (isPromiseLike(result)) {\n void Promise.resolve(result).catch(handleSparkDisposeError);\n }\n } catch (error) {\n handleSparkDisposeError(error);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\nfunction handleSparkDisposeError(error: unknown) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('worker terminate')\n ) {\n return;\n }\n\n console.warn('[mujoco-react] Spark resource disposal failed.', error);\n}\n"]}
1
+ {"version":3,"sources":["../src/spark.tsx"],"names":[],"mappings":"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","file":"spark.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport * as THREE from 'three';\nimport {\n SplatEnvironment,\n useSplatEnvironment,\n useSplatSceneConfig,\n} from './components/VisualScenario';\nimport type {\n PairedSplatEnvironmentConfig,\n SceneConfig,\n SplatEnvironmentProps,\n SplatEnvironmentReadiness,\n VisualScenarioConfig,\n} from './types';\n\ntype SparkModule = typeof import('@sparkjsdev/spark');\ntype SparkRendererInstance = InstanceType<SparkModule['SparkRenderer']>;\ntype SparkSplatMeshInstance = InstanceType<SparkModule['SplatMesh']>;\ntype SparkDisposable = {\n dispose?: () => unknown;\n};\ntype SparkWorkerMessage = {\n reject?: (error: unknown) => void;\n};\ntype SparkWorkerLike = {\n messages?: Record<string, SparkWorkerMessage>;\n};\ntype SparkResourceWithWorkers = SparkDisposable & {\n worker?: SparkWorkerLike;\n sortWorker?: SparkWorkerLike;\n lodWorker?: SparkWorkerLike;\n};\n\nexport type SparkSplatStatus = 'idle' | 'loading' | 'ready' | 'error';\n\nlet sparkDisposeRejectionHandlerRegistered = false;\n\nexport interface SparkSplatLifecycle {\n status: SparkSplatStatus;\n error: Error | null;\n isLoading: boolean;\n isReady: boolean;\n isError: boolean;\n props: Pick<SparkSplatEnvironmentProps, 'onStatusChange' | 'onError'>;\n reset: () => void;\n}\n\nexport interface SparkSplatEnvironmentState {\n environment: PairedSplatEnvironmentConfig | undefined;\n sceneConfig: SceneConfig;\n readiness: SplatEnvironmentReadiness;\n lifecycle: SparkSplatLifecycle;\n props: Pick<\n SparkSplatEnvironmentProps,\n 'environment' | 'scenario' | 'src' | 'format' | 'onStatusChange' | 'onError'\n >;\n enabled: boolean;\n}\n\nexport interface SparkSplatEnvironmentProps extends SplatEnvironmentProps {\n /** Enable Spark LoD handling for large splat assets. Default: true. */\n lod?: boolean | 'quality';\n /**\n * Hide meshes whose names include floor, ground, or plane while the splat is\n * active. This mirrors the common hybrid-rendering setup where MJCF keeps\n * collision geometry but the splat owns the visual environment.\n */\n hideGroundMeshes?: boolean;\n onStatusChange?: (status: SparkSplatStatus) => void;\n onLoad?: (mesh: SparkSplatMeshInstance) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Resolve a visual scenario's paired splat environment, compose its MJCF\n * collision proxy into the MuJoCo scene config, and expose Spark lifecycle\n * props for `<SparkSplatEnvironment />`.\n */\nexport function useSparkSplatEnvironment({\n sceneConfig,\n scenario,\n environment,\n enabled = true,\n renderer = 'spark',\n onError,\n onStatusChange,\n}: {\n sceneConfig: SceneConfig;\n scenario?: VisualScenarioConfig;\n environment?: PairedSplatEnvironmentConfig;\n enabled?: boolean;\n renderer?: 'spark';\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n}): SparkSplatEnvironmentState {\n const splatScene = useSplatSceneConfig({\n sceneConfig,\n scenario,\n environment,\n enabled,\n renderer,\n });\n const metadata = useSplatEnvironment({\n scenario,\n environment: splatScene.environment,\n renderer,\n });\n const renderEnabled = enabled && Boolean(metadata.src);\n const readiness = enabled ? metadata.readiness : splatScene.readiness;\n const lifecycle = useSparkSplatLifecycle({\n enabled: renderEnabled,\n onError,\n onStatusChange,\n });\n\n return useMemo(\n () => ({\n environment: splatScene.environment,\n sceneConfig: splatScene.sceneConfig,\n readiness,\n lifecycle,\n props: {\n environment: splatScene.environment,\n scenario: enabled ? scenario : undefined,\n src: enabled ? metadata.src : undefined,\n format: metadata.format,\n ...lifecycle.props,\n },\n enabled: renderEnabled,\n }),\n [enabled, lifecycle, metadata, readiness, renderEnabled, scenario, splatScene]\n );\n}\n\n/**\n * Tracks Spark 3DGS loading state for UI that wraps `SparkSplatEnvironment`.\n *\n * Use the returned `props` with `<SparkSplatEnvironment {...lifecycle.props} />`\n * to avoid repeating status/error state in app code.\n */\nexport function useSparkSplatLifecycle({\n enabled = true,\n initialStatus,\n onError,\n onStatusChange,\n}: {\n enabled?: boolean;\n initialStatus?: SparkSplatStatus;\n onError?: (error: Error) => void;\n onStatusChange?: (status: SparkSplatStatus) => void;\n} = {}): SparkSplatLifecycle {\n const [status, setStatus] = useState<SparkSplatStatus>(\n initialStatus ?? (enabled ? 'loading' : 'idle')\n );\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n const handleStatusChange = useCallback(\n (nextStatus: SparkSplatStatus) => {\n setStatus(nextStatus);\n if (nextStatus !== 'error') {\n setError(null);\n }\n onStatusChange?.(nextStatus);\n },\n [onStatusChange]\n );\n\n const handleError = useCallback(\n (nextError: Error) => {\n setError(nextError);\n setStatus('error');\n onError?.(nextError);\n },\n [onError]\n );\n\n const reset = useCallback(() => {\n setStatus(enabled ? initialStatus ?? 'loading' : 'idle');\n setError(null);\n }, [enabled, initialStatus]);\n\n return useMemo(\n () => ({\n status,\n error,\n isLoading: status === 'loading',\n isReady: status === 'ready',\n isError: status === 'error',\n props: {\n onStatusChange: handleStatusChange,\n onError: handleError,\n },\n reset,\n }),\n [error, handleError, handleStatusChange, reset, status]\n );\n}\n\n/**\n * Optional SparkJS-backed Gaussian splat renderer for React Three Fiber scenes.\n *\n * Import from `mujoco-react/spark` and install `@sparkjsdev/spark` in the app\n * that uses it. The core `mujoco-react` entrypoint does not depend on Spark.\n */\nexport function SparkSplatEnvironment({\n environment,\n scenario,\n renderer = 'spark',\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n showPlaceholder,\n children,\n lod = true,\n hideGroundMeshes = false,\n onStatusChange,\n onLoad,\n onError,\n ...groupProps\n}: SparkSplatEnvironmentProps) {\n const groupRef = useRef<THREE.Group>(null);\n const sparkRef = useRef<SparkRendererInstance | null>(null);\n const meshRef = useRef<SparkSplatMeshInstance | null>(null);\n const hiddenMeshesRef = useRef<THREE.Mesh[]>([]);\n const onStatusChangeRef = useRef(onStatusChange);\n const onLoadRef = useRef(onLoad);\n const onErrorRef = useRef(onError);\n const [status, setStatus] = useState<SparkSplatStatus>('idle');\n const { gl, invalidate } = useThree();\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n\n useEffect(() => {\n onStatusChangeRef.current = onStatusChange;\n }, [onStatusChange]);\n\n useEffect(() => {\n onLoadRef.current = onLoad;\n }, [onLoad]);\n\n useEffect(() => {\n onErrorRef.current = onError;\n }, [onError]);\n\n useEffect(() => {\n let disposed = false;\n ensureSparkDisposeRejectionHandler();\n\n function setLifecycleStatus(nextStatus: SparkSplatStatus) {\n setStatus(nextStatus);\n onStatusChangeRef.current?.(nextStatus);\n }\n\n function restoreHiddenMeshes() {\n for (const mesh of hiddenMeshesRef.current) {\n mesh.visible = true;\n }\n hiddenMeshesRef.current = [];\n }\n\n async function loadSplat() {\n if (!metadata.src) {\n setLifecycleStatus('idle');\n return;\n }\n\n if (metadata.format !== 'spz') {\n const unsupportedFormatError = new Error(\n `SparkSplatEnvironment only supports .spz assets; received \"${metadata.format}\".`\n );\n setLifecycleStatus('error');\n onErrorRef.current?.(unsupportedFormatError);\n return;\n }\n\n setLifecycleStatus('loading');\n\n try {\n const sparkModule = await import('@sparkjsdev/spark');\n if (disposed || !groupRef.current) return;\n\n const spark = new sparkModule.SparkRenderer({\n renderer: gl,\n onDirty: invalidate,\n });\n const mesh = new sparkModule.SplatMesh({\n url: metadata.src,\n lod,\n });\n mesh.name = 'GaussianSplatMesh';\n\n groupRef.current.add(spark);\n groupRef.current.add(mesh);\n sparkRef.current = spark;\n meshRef.current = mesh;\n\n if (hideGroundMeshes && groupRef.current.parent) {\n groupRef.current.parent.traverse((object) => {\n if (\n !(object instanceof THREE.Mesh) ||\n object === (mesh as unknown as THREE.Object3D)\n ) {\n return;\n }\n const name = object.name.toLowerCase();\n if (\n name.includes('floor') ||\n name.includes('ground') ||\n name.includes('plane')\n ) {\n object.visible = false;\n hiddenMeshesRef.current.push(object);\n }\n });\n }\n\n await mesh.initialized;\n if (disposed) return;\n setLifecycleStatus('ready');\n onLoadRef.current?.(mesh);\n invalidate();\n } catch (error) {\n const normalizedError =\n error instanceof Error ? error : new Error(String(error));\n setLifecycleStatus('error');\n onErrorRef.current?.(normalizedError);\n }\n }\n\n void loadSplat();\n\n return () => {\n disposed = true;\n restoreHiddenMeshes();\n\n if (meshRef.current) {\n groupRef.current?.remove(meshRef.current);\n safelyDisposeSparkResource(meshRef.current);\n meshRef.current = null;\n }\n\n if (sparkRef.current) {\n groupRef.current?.remove(sparkRef.current);\n safelyDisposeSparkResource(sparkRef.current);\n sparkRef.current = null;\n }\n };\n }, [\n gl,\n hideGroundMeshes,\n invalidate,\n lod,\n metadata.format,\n metadata.src,\n ]);\n\n return (\n <SplatEnvironment\n {...groupProps}\n environment={environment}\n scenario={scenario}\n renderer={renderer}\n src={metadata.src}\n format={metadata.format}\n collisionProxyMetadata={metadata.collisionProxy}\n collisionProxy={collisionProxy}\n showPlaceholder={showPlaceholder ?? status !== 'ready'}\n >\n <group ref={groupRef} />\n {children}\n </SplatEnvironment>\n );\n}\n\nfunction safelyDisposeSparkResource(resource: SparkDisposable) {\n try {\n silenceSparkWorkerTerminateRejections(resource);\n const result = resource.dispose?.();\n if (isPromiseLike(result)) {\n void Promise.resolve(result).catch(handleSparkDisposeError);\n }\n } catch (error) {\n handleSparkDisposeError(error);\n }\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'then' in value &&\n typeof (value as { then?: unknown }).then === 'function'\n );\n}\n\nfunction silenceSparkWorkerTerminateRejections(resource: SparkDisposable) {\n const workers = getSparkWorkers(resource);\n for (const worker of workers) {\n if (!worker.messages) continue;\n\n for (const message of Object.values(worker.messages)) {\n const reject = message.reject;\n if (!reject) continue;\n\n message.reject = (error: unknown) => {\n if (!isSparkWorkerTerminateError(error)) {\n reject(error);\n }\n };\n }\n }\n}\n\nfunction getSparkWorkers(resource: SparkDisposable): SparkWorkerLike[] {\n const sparkResource = resource as SparkResourceWithWorkers;\n return [\n sparkResource.worker,\n sparkResource.sortWorker,\n sparkResource.lodWorker,\n ].filter((worker): worker is SparkWorkerLike => Boolean(worker));\n}\n\nfunction handleSparkDisposeError(error: unknown) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('worker terminate')\n ) {\n return;\n }\n\n console.warn('[mujoco-react] Spark resource disposal failed.', error);\n}\n\nfunction ensureSparkDisposeRejectionHandler() {\n if (\n sparkDisposeRejectionHandlerRegistered ||\n typeof window === 'undefined' ||\n typeof window.addEventListener !== 'function'\n ) {\n return;\n }\n\n sparkDisposeRejectionHandlerRegistered = true;\n window.addEventListener('unhandledrejection', (event) => {\n if (isSparkWorkerTerminateError(event.reason)) {\n event.preventDefault();\n }\n });\n}\n\nfunction isSparkWorkerTerminateError(reason: unknown) {\n return (\n reason instanceof Error &&\n reason.message.toLowerCase().includes('worker terminate')\n );\n}\n"]}
@@ -39,7 +39,7 @@ type Robots = [RegisteredRobotMap] extends [never] ? string : Extract<keyof Regi
39
39
  type RobotResource<TRobot extends string, TKey extends string> = [
40
40
  RegisteredRobotMap
41
41
  ] extends [never] ? string : TRobot extends keyof RegisteredRobotMap ? TKey extends keyof RegisteredRobotMap[TRobot] ? RegisteredRobotMap[TRobot][TKey] : string : never;
42
- type RegisterResourceKey = 'actuators' | 'sensors' | 'bodies' | 'joints' | 'sites' | 'geoms' | 'keyframes';
42
+ type RegisterResourceKey = 'actuators' | 'sensors' | 'bodies' | 'joints' | 'sites' | 'geoms' | 'keyframes' | 'cameras';
43
43
  type RobotResourceObject<TRobot extends string, TKey extends RegisterResourceKey> = string extends RobotResource<TRobot, TKey> ? Record<string, string> : {
44
44
  readonly [K in RobotResource<TRobot, TKey>]: K;
45
45
  };
@@ -68,6 +68,8 @@ type RobotGeoms<TRobot extends string> = RobotResource<TRobot, 'geoms'>;
68
68
  declare const RobotGeoms: RobotResourceCategory<'geoms'>;
69
69
  type RobotKeyframes<TRobot extends string> = RobotResource<TRobot, 'keyframes'>;
70
70
  declare const RobotKeyframes: RobotResourceCategory<'keyframes'>;
71
+ type RobotCameras<TRobot extends string> = RobotResource<TRobot, 'cameras'>;
72
+ declare const RobotCameras: RobotResourceCategory<'cameras'>;
71
73
  type Actuators = Register extends {
72
74
  actuators: infer T extends string;
73
75
  } ? T : string;
@@ -89,6 +91,9 @@ type Geoms = Register extends {
89
91
  type Keyframes = Register extends {
90
92
  keyframes: infer T extends string;
91
93
  } ? T : string;
94
+ type Cameras = Register extends {
95
+ cameras: infer T extends string;
96
+ } ? T : string;
92
97
  /**
93
98
  * A single MuJoCo contact from the WASM module.
94
99
  * Accessed via `data.contact.get(i)`.
@@ -131,6 +136,7 @@ interface MujocoModel {
131
136
  nflex: number;
132
137
  nmesh: number;
133
138
  nmat: number;
139
+ ncam?: number;
134
140
  names: Int8Array;
135
141
  name_bodyadr: Int32Array;
136
142
  name_jntadr: Int32Array;
@@ -140,6 +146,7 @@ interface MujocoModel {
140
146
  name_keyadr: Int32Array;
141
147
  name_sensoradr: Int32Array;
142
148
  name_tendonadr: Int32Array;
149
+ name_camadr?: Int32Array;
143
150
  body_mass: Float64Array;
144
151
  body_parentid: Int32Array;
145
152
  body_jntnum: Int32Array;
@@ -204,6 +211,10 @@ interface MujocoModel {
204
211
  light_cutoff: Float32Array;
205
212
  light_exponent: Float32Array;
206
213
  light_intensity: Float32Array;
214
+ cam_bodyid?: Int32Array;
215
+ cam_pos?: Float64Array;
216
+ cam_quat?: Float64Array;
217
+ cam_fovy?: Float64Array;
207
218
  ten_wrapadr: Int32Array;
208
219
  ten_wrapnum: Int32Array;
209
220
  ten_range: Float64Array;
@@ -240,6 +251,9 @@ interface MujocoData {
240
251
  qfrc_bias: Float64Array;
241
252
  site_xpos: Float64Array;
242
253
  site_xmat: Float64Array;
254
+ cam_xpos?: Float64Array;
255
+ cam_xmat?: Float64Array;
256
+ xmat?: Float64Array;
243
257
  sensordata: Float64Array;
244
258
  ncon: number;
245
259
  contact: MujocoContactArray;
@@ -519,6 +533,14 @@ interface SensorInfo {
519
533
  dim: number;
520
534
  adr: number;
521
535
  }
536
+ interface CameraInfo {
537
+ id: number;
538
+ name: string;
539
+ bodyId: number;
540
+ fov: number | null;
541
+ position: [number, number, number] | null;
542
+ quaternion: [number, number, number, number] | null;
543
+ }
522
544
  interface ContactInfo {
523
545
  geom1: number;
524
546
  geom1Name: string;
@@ -691,11 +713,28 @@ interface PairedSplatEnvironmentConfig {
691
713
  description?: string;
692
714
  /** Visual-only Gaussian splat asset. */
693
715
  splat: SplatAssetConfig;
694
- /** MJCF/XML contact geometry paired with the visual splat. */
695
- collisionProxy: SplatCollisionProxyConfig & {
716
+ /** Optional MJCF/XML contact geometry paired with the visual splat. */
717
+ collisionProxy?: SplatCollisionProxyConfig & {
696
718
  xmlPath: string;
697
719
  };
698
720
  }
721
+ declare const SplatEnvironmentReadinessStatus: {
722
+ readonly Disabled: "disabled";
723
+ readonly MissingSplat: "missing-splat";
724
+ readonly MissingCollisionProxy: "missing-collision-proxy";
725
+ readonly UnsupportedFormat: "unsupported-format";
726
+ readonly Ready: "ready";
727
+ };
728
+ type SplatEnvironmentReadinessStatus = (typeof SplatEnvironmentReadinessStatus)[keyof typeof SplatEnvironmentReadinessStatus];
729
+ interface SplatEnvironmentReadiness {
730
+ status: SplatEnvironmentReadinessStatus;
731
+ ready: boolean;
732
+ requiresCollisionProxy: boolean;
733
+ missing: Array<'splat' | 'collisionProxy'>;
734
+ format?: SplatFormat;
735
+ renderer?: SplatRendererKind;
736
+ message: string;
737
+ }
699
738
  interface SplatEnvironmentMetadataInput {
700
739
  environment?: PairedSplatEnvironmentConfig;
701
740
  scenario?: VisualScenarioConfig;
@@ -708,9 +747,61 @@ interface SplatEnvironmentMetadata {
708
747
  src?: string;
709
748
  format: SplatFormat;
710
749
  collisionProxy?: SplatCollisionProxyConfig;
750
+ readiness: SplatEnvironmentReadiness;
711
751
  userData: Record<string, unknown>;
712
752
  }
753
+ interface ResolvedScenarioCameraConfig {
754
+ jitter: number;
755
+ exposure: number;
756
+ noise: number;
757
+ blur: number;
758
+ }
759
+ interface ResolvedScenarioMaterialConfig {
760
+ randomizeObjectColors: boolean;
761
+ randomizeTableMaterial: boolean;
762
+ roughness?: number;
763
+ metalness?: number;
764
+ }
765
+ interface VisualScenarioExecutionContext {
766
+ scenarioId: string;
767
+ scenarioLabel: string;
768
+ variantId?: string;
769
+ seed: number;
770
+ lighting: ScenarioLightingPreset;
771
+ environment?: string;
772
+ camera: ResolvedScenarioCameraConfig;
773
+ materials: ResolvedScenarioMaterialConfig;
774
+ splatEnabled: boolean;
775
+ splatSrc?: string;
776
+ splatFormat: SplatFormat;
777
+ splatRenderer?: SplatRendererKind;
778
+ collisionProxyXmlPath?: string;
779
+ collisionProxyStatus?: SplatCollisionProxyConfig['status'];
780
+ collisionProxyPrimitives: SplatCollisionPrimitive[];
781
+ readiness: SplatEnvironmentReadiness;
782
+ transformSource: 'visualScenario.camera';
783
+ }
784
+ interface VisualScenarioExecutionContextInput {
785
+ scenario?: VisualScenarioConfig;
786
+ environment?: PairedSplatEnvironmentConfig;
787
+ renderer?: SplatRendererKind;
788
+ variantId?: string;
789
+ enabled?: boolean;
790
+ }
713
791
  type SplatSceneInput = PairedSplatEnvironmentConfig | VisualScenarioConfig | undefined | null;
792
+ interface SplatSceneConfigInput {
793
+ sceneConfig: SceneConfig;
794
+ scenario?: VisualScenarioConfig;
795
+ environment?: PairedSplatEnvironmentConfig;
796
+ enabled?: boolean;
797
+ renderer?: SplatRendererKind;
798
+ }
799
+ interface SplatSceneConfigState {
800
+ environment: PairedSplatEnvironmentConfig | undefined;
801
+ sceneConfig: SceneConfig;
802
+ enabled: boolean;
803
+ readiness: SplatEnvironmentReadiness;
804
+ }
714
805
  interface VisualScenarioConfig {
715
806
  id?: string;
716
807
  label?: string;
@@ -826,6 +917,7 @@ interface MujocoSimAPI {
826
917
  getSites(): SiteInfo[];
827
918
  getActuators(): ActuatorInfo[];
828
919
  getSensors(): SensorInfo[];
920
+ getCameras(): CameraInfo[];
829
921
  getModelOption(): ModelOptions;
830
922
  setGravity(g: [number, number, number]): void;
831
923
  setTimestep(dt: number): void;
@@ -886,7 +978,14 @@ interface FrameCaptureAPI {
886
978
  type CameraFrameCaptureVector3 = THREE.Vector3 | readonly [number, number, number];
887
979
  type CameraFrameCaptureQuaternion = THREE.Quaternion | readonly [number, number, number, number];
888
980
  interface CameraFrameCaptureOptions {
981
+ /** Existing Three camera to clone before applying pose overrides. */
889
982
  camera?: THREE.Camera;
983
+ /** Named MuJoCo `<camera>` to render from when available in the loaded model. */
984
+ cameraName?: Cameras;
985
+ /** Named MuJoCo site to use as the rendered camera pose. Useful for robot-mounted optical frames. */
986
+ siteName?: Sites;
987
+ /** Named MuJoCo body to use as the rendered camera pose. */
988
+ bodyName?: Bodies;
890
989
  position?: CameraFrameCaptureVector3;
891
990
  lookAt?: CameraFrameCaptureVector3;
892
991
  quaternion?: CameraFrameCaptureQuaternion;
@@ -898,7 +997,25 @@ interface CameraFrameCaptureOptions {
898
997
  fov?: number;
899
998
  near?: number;
900
999
  far?: number;
901
- }
1000
+ /** Provenance for the camera pose used by the capture. Usually set by the MuJoCo provider. */
1001
+ source?: CameraFrameCaptureSource;
1002
+ }
1003
+ type CameraFrameCaptureSource = {
1004
+ kind: 'mujoco-camera';
1005
+ cameraName: Cameras;
1006
+ } | {
1007
+ kind: 'mujoco-site';
1008
+ siteName: Sites;
1009
+ } | {
1010
+ kind: 'mujoco-body';
1011
+ bodyName: Bodies;
1012
+ } | {
1013
+ kind: 'custom-camera';
1014
+ } | {
1015
+ kind: 'explicit-pose';
1016
+ } | {
1017
+ kind: 'fallback-camera';
1018
+ };
902
1019
  interface CameraFrameCaptureResult {
903
1020
  canvas: HTMLCanvasElement;
904
1021
  camera: THREE.Camera;
@@ -906,6 +1023,7 @@ interface CameraFrameCaptureResult {
906
1023
  type: string;
907
1024
  width: number;
908
1025
  height: number;
1026
+ source: CameraFrameCaptureSource;
909
1027
  }
910
1028
  interface CameraFrameCaptureBlobResult {
911
1029
  canvas: HTMLCanvasElement;
@@ -914,6 +1032,7 @@ interface CameraFrameCaptureBlobResult {
914
1032
  type: string;
915
1033
  width: number;
916
1034
  height: number;
1035
+ source: CameraFrameCaptureSource;
917
1036
  }
918
1037
  interface CameraFrameCaptureAPI {
919
1038
  status: FrameCaptureStatus;
@@ -931,17 +1050,53 @@ interface CameraFrameSequenceFrame {
931
1050
  time: number;
932
1051
  cameras: Record<string, CameraFrameCaptureResult>;
933
1052
  }
1053
+ interface CameraFrameSequenceCameraSummary {
1054
+ key: string;
1055
+ width: number;
1056
+ height: number;
1057
+ source: CameraFrameCaptureSource;
1058
+ frameCount: number;
1059
+ firstFrameIndex: number | null;
1060
+ lastFrameIndex: number | null;
1061
+ firstTimestamp: number | null;
1062
+ lastTimestamp: number | null;
1063
+ }
1064
+ interface CameraFrameSequenceSampleInput extends PhysicsStepInput {
1065
+ frameIndex: number;
1066
+ time: number;
1067
+ }
1068
+ interface CameraFrameSequenceStepInput extends PhysicsStepInput {
1069
+ frameIndex: number;
1070
+ stepIndex: number;
1071
+ time: number;
1072
+ }
934
1073
  interface CameraFrameSequenceOptions {
935
1074
  cameras: readonly CameraFrameSequenceCamera[];
936
1075
  frames: number;
1076
+ /** Number of MuJoCo steps between captured frames. Use 0 for static camera provenance captures. */
937
1077
  stepsPerFrame?: number;
938
1078
  reset?: boolean;
939
1079
  captureInitialFrame?: boolean;
1080
+ retainFrames?: boolean;
1081
+ /**
1082
+ * Require each recorded stream to resolve from exactly one mounted MuJoCo
1083
+ * camera/site/body selector. Defaults to true because sequence recording is
1084
+ * intended for dataset/policy camera streams.
1085
+ */
1086
+ requireMountedSources?: boolean;
1087
+ signal?: AbortSignal;
1088
+ /** Called after stepping and before image capture for this frame. Use this to record synchronized state/action rows. */
1089
+ onSample?: (input: CameraFrameSequenceSampleInput) => void | Promise<void>;
1090
+ /** Called before each MuJoCo step inside sequence recording. Use this to apply policy/control actions. */
1091
+ onBeforeStep?: (input: CameraFrameSequenceStepInput) => void | Promise<void>;
1092
+ /** Called after each MuJoCo step inside sequence recording. Use this for step-level telemetry. */
1093
+ onAfterStep?: (input: CameraFrameSequenceStepInput) => void | Promise<void>;
940
1094
  onFrame?: (frame: CameraFrameSequenceFrame) => void | Promise<void>;
941
1095
  }
942
1096
  interface CameraFrameSequenceResult {
943
1097
  frames: CameraFrameSequenceFrame[];
944
1098
  cameraKeys: string[];
1099
+ cameraSummaries: Record<string, CameraFrameSequenceCameraSummary>;
945
1100
  frameCount: number;
946
1101
  }
947
1102
  interface CameraFrameSequenceRecorderAPI {
@@ -1009,4 +1164,4 @@ interface JointStateResult {
1009
1164
  velocity: React__default.RefObject<number | Float64Array>;
1010
1165
  }
1011
1166
 
1012
- export { type PolicyConfig as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type ActuatorInfo as E, type Sites as F, type GeomInfo as G, type SitePositionResult as H, type IkConfig as I, type Sensors as J, type SensorHandle as K, type SensorInfo as L, type MujocoContextValue as M, type Joints as N, type ObservationConfig as O, type PhysicsStepCallback as P, type JointStateResult as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type Bodies as U, type VisualScenarioEffectsProps as V, type BodyStateResult as W, type Actuators as X, type CtrlHandle as Y, type ContactInfo as Z, type KeyboardTeleopConfig as _, type MujocoCanvasProps as a, type SensorResult as a$, type PolicyVector as a0, type ObservationHandle as a1, type TrajectoryInput as a2, type TrajectoryStateChangeInput as a3, type PlaybackState as a4, type TrajectoryFrame as a5, type FrameCaptureOptions as a6, type FrameCaptureResult as a7, type FrameCaptureBlobResult as a8, type FrameCaptureAPI as a9, type MujocoFrameCaptureOptions as aA, type ObservationLayoutItem as aB, type ObservationOutput as aC, type PhysicsConfig as aD, type PhysicsStepInput as aE, type PolicyActionInput as aF, type PolicyInferenceInput as aG, type PolicyObservationInput as aH, type RayHit as aI, type Register as aJ, type RegisteredRobotMap as aK, type ResetCallbackInput as aL, type ResourceSelector as aM, RobotActuators as aN, RobotBodies as aO, RobotGeoms as aP, RobotJoints as aQ, RobotKeyframes as aR, type RobotResource as aS, RobotResources as aT, RobotSensors as aU, RobotSites as aV, type Robots as aW, type ScenarioCameraConfig as aX, type ScenarioMaterialConfig as aY, type SceneMarker as aZ, type SceneObject as a_, type CameraFrameCaptureOptions as aa, type CameraFrameCaptureAPI as ab, type CameraFrameSequenceRecorderAPI as ac, type CameraFrameCaptureResult as ad, type CameraFrameCaptureBlobResult as ae, type BodyInfo as af, type CameraFrameCaptureQuaternion as ag, type CameraFrameCaptureVector3 as ah, type CameraFrameSequenceCamera as ai, type CameraFrameSequenceFrame as aj, type CameraFrameSequenceOptions as ak, type CameraFrameSequenceResult as al, type ControlJointInfo as am, type FrameCaptureStatus as an, type FrameCaptureTarget as ao, type FrameCaptureTargetRef as ap, type Geoms as aq, type IKSolveFn as ar, type IkGizmoDragInput as as, type IkSolveInput as at, type JointInfo as au, type KeyBinding as av, type Keyframes as aw, type ModelOptions as ax, type MujocoContact as ay, type MujocoContactArray as az, type MujocoSimAPI as b, type SiteInfo as b0, type SplatAssetConfig as b1, type SplatScenarioConfig as b2, type StateSnapshot as b3, type TrajectoryData as b4, type TrajectoryFrameCallbackInput as b5, type VisualScenarioMaterialFilterInput as b6, type XmlPatch as b7, getContact as b8, registerRobotResources as b9, type StepCallbackInput as c, type SelectionCallbackInput as d, type MujocoModule as e, type MujocoModel as f, type MujocoData as g, type ControlGroupSelector as h, type ObservationResult as i, type IkContextValue as j, type IkGizmoProps as k, type SceneLightsProps as l, type ScenarioLightingProps as m, type SplatEnvironmentProps as n, type VisualScenarioConfig as o, type SplatRendererKind as p, type PairedSplatEnvironmentConfig as q, type SplatFormat as r, type SplatCollisionProxyConfig as s, type SplatCollisionPrimitive as t, type ScenarioLightingPreset as u, type SplatEnvironmentMetadataInput as v, type SplatEnvironmentMetadata as w, type SplatSceneInput as x, type DebugProps as y, type ContactListenerProps as z };
1167
+ export { type Bodies as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type ScenarioLightingPreset as E, type SplatEnvironmentMetadataInput as F, type SplatEnvironmentMetadata as G, type SplatSceneInput as H, type IkConfig as I, type DebugProps as J, type GeomInfo as K, type ContactListenerProps as L, type MujocoContextValue as M, type ActuatorInfo as N, type ObservationConfig as O, type PhysicsStepCallback as P, type Sites as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type SitePositionResult as U, type VisualScenarioEffectsProps as V, type Sensors as W, type SensorHandle as X, type SensorInfo as Y, type Joints as Z, type JointStateResult as _, type MujocoCanvasProps as a, RobotActuators as a$, type BodyStateResult as a0, type Actuators as a1, type CtrlHandle as a2, type ContactInfo as a3, type KeyboardTeleopConfig as a4, type PolicyConfig as a5, type PolicyVector as a6, type ObservationHandle as a7, type TrajectoryInput as a8, type TrajectoryStateChangeInput as a9, type FrameCaptureTarget as aA, type FrameCaptureTargetRef as aB, type Geoms as aC, type IKSolveFn as aD, type IkGizmoDragInput as aE, type IkSolveInput as aF, type JointInfo as aG, type KeyBinding as aH, type Keyframes as aI, type ModelOptions as aJ, type MujocoContact as aK, type MujocoContactArray as aL, type MujocoFrameCaptureOptions as aM, type ObservationLayoutItem as aN, type ObservationOutput as aO, type PhysicsConfig as aP, type PhysicsStepInput as aQ, type PolicyActionInput as aR, type PolicyInferenceInput as aS, type PolicyObservationInput as aT, type RayHit as aU, type Register as aV, type RegisteredRobotMap as aW, type ResetCallbackInput as aX, type ResolvedScenarioCameraConfig as aY, type ResolvedScenarioMaterialConfig as aZ, type ResourceSelector as a_, type PlaybackState as aa, type TrajectoryFrame as ab, type FrameCaptureOptions as ac, type FrameCaptureResult as ad, type FrameCaptureBlobResult as ae, type FrameCaptureAPI as af, type CameraFrameCaptureOptions as ag, type CameraFrameCaptureAPI as ah, type CameraFrameSequenceRecorderAPI as ai, type Cameras as aj, type CameraFrameSequenceCamera as ak, type CameraFrameCaptureSource as al, type CameraFrameSequenceOptions as am, type CameraFrameSequenceResult as an, type CameraFrameCaptureResult as ao, type CameraFrameCaptureBlobResult as ap, type BodyInfo as aq, type CameraFrameCaptureQuaternion as ar, type CameraFrameCaptureVector3 as as, type CameraFrameSequenceCameraSummary as at, type CameraFrameSequenceFrame as au, type CameraFrameSequenceSampleInput as av, type CameraFrameSequenceStepInput as aw, type CameraInfo as ax, type ControlJointInfo as ay, type FrameCaptureStatus as az, type MujocoSimAPI as b, RobotBodies as b0, RobotCameras as b1, RobotGeoms as b2, RobotJoints as b3, RobotKeyframes as b4, type RobotResource as b5, RobotResources as b6, RobotSensors as b7, RobotSites as b8, type Robots as b9, type ScenarioCameraConfig as ba, type ScenarioMaterialConfig as bb, type SceneMarker as bc, type SceneObject as bd, type SensorResult as be, type SiteInfo as bf, type SplatAssetConfig as bg, type SplatScenarioConfig as bh, type StateSnapshot as bi, type TrajectoryData as bj, type TrajectoryFrameCallbackInput as bk, type VisualScenarioMaterialFilterInput as bl, type XmlPatch as bm, getContact as bn, registerRobotResources as bo, type StepCallbackInput as c, type SelectionCallbackInput as d, type MujocoModule as e, type MujocoModel as f, type MujocoData as g, type ControlGroupSelector as h, type ObservationResult as i, type IkContextValue as j, type IkGizmoProps as k, type SceneLightsProps as l, type ScenarioLightingProps as m, type SplatEnvironmentProps as n, type VisualScenarioConfig as o, type SplatRendererKind as p, type PairedSplatEnvironmentConfig as q, type SplatFormat as r, type SplatCollisionProxyConfig as s, type SplatEnvironmentReadiness as t, type SplatCollisionPrimitive as u, SplatEnvironmentReadinessStatus as v, type SplatSceneConfigInput as w, type SplatSceneConfigState as x, type VisualScenarioExecutionContextInput as y, type VisualScenarioExecutionContext as z };
package/dist/vite.d.ts CHANGED
@@ -32,7 +32,7 @@ interface MujocoRegisterCodegenResult {
32
32
  files: string[];
33
33
  counts: Record<RegisterKey, number>;
34
34
  }
35
- type RegisterKey = 'actuators' | 'sensors' | 'bodies' | 'joints' | 'sites' | 'geoms' | 'keyframes';
35
+ type RegisterKey = 'actuators' | 'sensors' | 'bodies' | 'joints' | 'sites' | 'geoms' | 'keyframes' | 'cameras';
36
36
  type ModelInput = string | readonly string[] | Record<string, string>;
37
37
  declare function mujocoReact(options: MujocoReactPluginOptions): {
38
38
  name: string;
package/dist/vite.js CHANGED
@@ -2,7 +2,7 @@ import { mkdir, writeFile, readFile } from 'fs/promises';
2
2
  import path from 'path';
3
3
 
4
4
  // src/vite.ts
5
- var REGISTER_KEYS = ["actuators", "sensors", "bodies", "joints", "sites", "geoms", "keyframes"];
5
+ var REGISTER_KEYS = ["actuators", "sensors", "bodies", "joints", "sites", "geoms", "keyframes", "cameras"];
6
6
  var MODEL_EXTENSIONS = /* @__PURE__ */ new Set([".xml", ".mjcf", ".urdf"]);
7
7
  function createEmptyNames() {
8
8
  return {
@@ -12,7 +12,8 @@ function createEmptyNames() {
12
12
  joints: /* @__PURE__ */ new Set(),
13
13
  sites: /* @__PURE__ */ new Set(),
14
14
  geoms: /* @__PURE__ */ new Set(),
15
- keyframes: /* @__PURE__ */ new Set()
15
+ keyframes: /* @__PURE__ */ new Set(),
16
+ cameras: /* @__PURE__ */ new Set()
16
17
  };
17
18
  }
18
19
  function mujocoReact(options) {
@@ -91,7 +92,7 @@ async function generateMujocoRegister(options) {
91
92
  };
92
93
  }
93
94
  async function scanModel(filePath, root, seen, names) {
94
- const normalized = path.normalize(filePath);
95
+ const normalized = path.resolve(filePath);
95
96
  if (seen.has(normalized)) return;
96
97
  seen.add(normalized);
97
98
  const xml = await readFile(normalized, "utf8");
@@ -99,12 +100,13 @@ async function scanModel(filePath, root, seen, names) {
99
100
  collectSimpleTagNames(xml, "joint", names.joints);
100
101
  collectSimpleTagNames(xml, "site", names.sites);
101
102
  collectSimpleTagNames(xml, "geom", names.geoms);
103
+ collectSimpleTagNames(xml, "camera", names.cameras);
102
104
  collectSimpleTagNames(xml, "key", names.keyframes);
103
105
  collectSectionNames(xml, "actuator", names.actuators);
104
106
  collectSectionNames(xml, "sensor", names.sensors);
105
107
  for (const includePath of collectIncludePaths(xml)) {
106
108
  const next = path.resolve(path.dirname(normalized), includePath);
107
- if (next.startsWith(root)) await scanModel(next, root, seen, names);
109
+ if (isPathInsideRoot(next, root)) await scanModel(next, root, seen, names);
108
110
  }
109
111
  }
110
112
  function collectSimpleTagNames(xml, tag, target) {
@@ -217,7 +219,8 @@ function renderNamespaceAliases(models) {
217
219
  joints: "RobotJoints",
218
220
  sites: "RobotSites",
219
221
  geoms: "RobotGeoms",
220
- keyframes: "RobotKeyframes"
222
+ keyframes: "RobotKeyframes",
223
+ cameras: "RobotCameras"
221
224
  };
222
225
  const blocks = REGISTER_KEYS.map((key) => {
223
226
  const aliases = models.filter((model) => isIdentifier(model.id)).map((model) => ` export type ${model.id} = RobotResource<'${escapeTs(model.id)}', '${key}'>;`);
@@ -251,7 +254,7 @@ function shouldInjectRegisterImport(id, root, generatedRegister) {
251
254
  if (file.includes(`${path.sep}node_modules${path.sep}`)) return false;
252
255
  const absolute = path.resolve(file);
253
256
  if (absolute === generatedRegister) return false;
254
- return absolute.startsWith(root);
257
+ return isPathInsideRoot(absolute, root);
255
258
  }
256
259
  function renderGeneratedImport(id, generatedRegister) {
257
260
  const fromDir = path.dirname(stripQuery(id));
@@ -292,7 +295,11 @@ function shouldRegenerate(file, watchedFiles, models, root) {
292
295
  if (watchedFiles.includes(absolute)) return true;
293
296
  if (!MODEL_EXTENSIONS.has(path.extname(absolute).toLowerCase())) return false;
294
297
  const modelDirs = models.map((model) => path.dirname(path.resolve(root, model.file)));
295
- return modelDirs.some((dir) => absolute.startsWith(dir));
298
+ return modelDirs.some((dir) => isPathInsideRoot(absolute, dir));
299
+ }
300
+ function isPathInsideRoot(filePath, root) {
301
+ const relative = path.relative(path.resolve(root), path.resolve(filePath));
302
+ return relative === "" || relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
296
303
  }
297
304
  /**
298
305
  * @license