@vizij/runtime-react 0.0.13 → 0.1.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.
@@ -1,27 +1,43 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ VizijRuntimeFace: () => VizijRuntimeFace,
24
+ VizijRuntimeProvider: () => VizijRuntimeProvider,
25
+ useRigInput: () => useRigInput,
26
+ useVizijOutputs: () => useVizijOutputs,
27
+ useVizijRuntime: () => useVizijRuntime
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
1
31
  // src/VizijRuntimeProvider.tsx
2
- import {
3
- useCallback,
4
- useEffect,
5
- useMemo,
6
- useRef,
7
- useState
8
- } from "react";
9
- import {
10
- VizijContext,
11
- createVizijStore,
12
- loadGLTFWithBundle,
13
- loadGLTFFromBlobWithBundle
14
- } from "@vizij/render";
15
- import {
16
- OrchestratorProvider,
17
- useOrchestrator,
18
- useOrchFrame
19
- } from "@vizij/orchestrator-react";
20
- import { valueAsNumber as valueAsNumber2 } from "@vizij/value-json";
32
+ var import_react2 = require("react");
33
+ var import_render = require("@vizij/render");
34
+ var import_orchestrator_react = require("@vizij/orchestrator-react");
35
+ var import_node_graph_authoring = require("@vizij/node-graph-authoring");
36
+ var import_value_json2 = require("@vizij/value-json");
21
37
 
22
38
  // src/context.ts
23
- import { createContext } from "react";
24
- var VizijRuntimeContext = createContext(null);
39
+ var import_react = require("react");
40
+ var VizijRuntimeContext = (0, import_react.createContext)(null);
25
41
 
26
42
  // src/utils/graph.ts
27
43
  function getNodes(spec) {
@@ -84,15 +100,7 @@ function collectInputPathMap(spec) {
84
100
  }
85
101
 
86
102
  // src/utils/valueConversion.ts
87
- import {
88
- isNormalizedValue,
89
- valueAsBool,
90
- valueAsColorRgba,
91
- valueAsNumber,
92
- valueAsText,
93
- valueAsTransform,
94
- valueAsVector
95
- } from "@vizij/value-json";
103
+ var import_value_json = require("@vizij/value-json");
96
104
  function numericArrayToRaw(arr) {
97
105
  const normalised = arr.map((entry) => Number(entry ?? 0));
98
106
  switch (normalised.length) {
@@ -144,20 +152,20 @@ function valueJSONToRaw(value) {
144
152
  ]);
145
153
  return Object.fromEntries(entries);
146
154
  }
147
- if (!isNormalizedValue(value)) {
155
+ if (!(0, import_value_json.isNormalizedValue)(value)) {
148
156
  return void 0;
149
157
  }
150
158
  switch (value.type) {
151
159
  case "float": {
152
- const num = valueAsNumber(value);
160
+ const num = (0, import_value_json.valueAsNumber)(value);
153
161
  return typeof num === "number" ? num : void 0;
154
162
  }
155
163
  case "bool": {
156
- const boolVal = valueAsBool(value);
164
+ const boolVal = (0, import_value_json.valueAsBool)(value);
157
165
  return typeof boolVal === "boolean" ? boolVal : void 0;
158
166
  }
159
167
  case "text": {
160
- const text = valueAsText(value);
168
+ const text = (0, import_value_json.valueAsText)(value);
161
169
  return typeof text === "string" ? text : void 0;
162
170
  }
163
171
  case "vec2":
@@ -165,11 +173,11 @@ function valueJSONToRaw(value) {
165
173
  case "vec4":
166
174
  case "quat":
167
175
  case "vector": {
168
- const vec = valueAsVector(value);
176
+ const vec = (0, import_value_json.valueAsVector)(value);
169
177
  return vec ? numericArrayToRaw(vec) : void 0;
170
178
  }
171
179
  case "colorrgba": {
172
- const color = valueAsColorRgba(value);
180
+ const color = (0, import_value_json.valueAsColorRgba)(value);
173
181
  if (!color) {
174
182
  return void 0;
175
183
  }
@@ -177,7 +185,7 @@ function valueJSONToRaw(value) {
177
185
  return { r, g, b, a };
178
186
  }
179
187
  case "transform": {
180
- const transform = valueAsTransform(value);
188
+ const transform = (0, import_value_json.valueAsTransform)(value);
181
189
  if (!transform) {
182
190
  return void 0;
183
191
  }
@@ -207,7 +215,10 @@ function valueJSONToRaw(value) {
207
215
  }
208
216
 
209
217
  // src/VizijRuntimeProvider.tsx
210
- import { jsx } from "react/jsx-runtime";
218
+ var import_jsx_runtime = require("react/jsx-runtime");
219
+ var ACTIVE_GRACE_MS = 250;
220
+ var VISIBLE_IDLE_FPS = 30;
221
+ var HIDDEN_IDLE_FPS = 1;
211
222
  var DEFAULT_MERGE = {
212
223
  outputs: "add",
213
224
  intermediate: "add"
@@ -245,6 +256,166 @@ function normalisePath(path) {
245
256
  function normaliseBundleKind(kind) {
246
257
  return typeof kind === "string" ? kind.toLowerCase() : "";
247
258
  }
259
+ function addConstraintVariant(map, key, constraint) {
260
+ if (!key) return;
261
+ if (!map[key]) {
262
+ map[key] = constraint;
263
+ }
264
+ }
265
+ function stripRigFacePrefix(path) {
266
+ const trimmed = path.startsWith("/") ? path.slice(1) : path;
267
+ const match = /^rig\/[^/]+\/(.+)$/.exec(trimmed);
268
+ if (match && match[1]) {
269
+ return match[1];
270
+ }
271
+ if (trimmed.startsWith("rig/")) {
272
+ return trimmed.slice("rig/".length);
273
+ }
274
+ return trimmed;
275
+ }
276
+ function extractInputConstraints(spec, extraInputs, namespace) {
277
+ if (!spec || typeof spec !== "object") {
278
+ return {};
279
+ }
280
+ const inputs = [];
281
+ if (Array.isArray(extraInputs)) {
282
+ inputs.push(...extraInputs);
283
+ }
284
+ const entries = spec.metadata?.vizij?.inputs;
285
+ if (Array.isArray(entries)) {
286
+ entries.forEach((entry) => {
287
+ if (entry && typeof entry === "object") {
288
+ inputs.push(entry);
289
+ }
290
+ });
291
+ }
292
+ if (inputs.length === 0) {
293
+ return {};
294
+ }
295
+ const map = {};
296
+ inputs.forEach((entry) => {
297
+ const path = entry.path;
298
+ if (typeof path !== "string") return;
299
+ const namespaced = namespaceTypedPath(path, namespace);
300
+ const stripped = stripRigFacePrefix(path);
301
+ const strippedNamespaced = stripped ? namespaceTypedPath(stripped, namespace) : stripped;
302
+ const min = entry.range?.min;
303
+ const max = entry.range?.max;
304
+ const defaultValue = entry.defaultValue;
305
+ const constraint = {
306
+ ...Number.isFinite(Number(min)) ? { min: Number(min) } : {},
307
+ ...Number.isFinite(Number(max)) ? { max: Number(max) } : {},
308
+ ...Number.isFinite(Number(defaultValue)) ? { defaultValue: Number(defaultValue) } : {}
309
+ };
310
+ addConstraintVariant(map, namespaced, constraint);
311
+ addConstraintVariant(map, path, constraint);
312
+ if (stripped) {
313
+ addConstraintVariant(map, stripped, constraint);
314
+ }
315
+ if (strippedNamespaced) {
316
+ addConstraintVariant(map, strippedNamespaced, constraint);
317
+ }
318
+ });
319
+ return map;
320
+ }
321
+ function namespaceTypedPath(path, namespace) {
322
+ const trimmed = typeof path === "string" ? path.trim() : "";
323
+ if (!trimmed) {
324
+ return trimmed;
325
+ }
326
+ const prefix = `${namespace}/`;
327
+ if (trimmed.startsWith(prefix)) {
328
+ return trimmed;
329
+ }
330
+ if (trimmed.startsWith("debug/")) {
331
+ const rest = trimmed.slice("debug/".length);
332
+ const namespacedRest = namespaceTypedPath(rest, namespace);
333
+ return namespacedRest.startsWith("debug/") ? namespacedRest : `debug/${namespacedRest}`;
334
+ }
335
+ return `${prefix}${trimmed}`;
336
+ }
337
+ function stripNamespace(path, namespace) {
338
+ const prefix = `${namespace}/`;
339
+ if (path.startsWith(prefix)) {
340
+ return path.slice(prefix.length);
341
+ }
342
+ const debugPrefix = `debug/${prefix}`;
343
+ if (path.startsWith(debugPrefix)) {
344
+ return path.slice(debugPrefix.length);
345
+ }
346
+ if (path.startsWith("debug/")) {
347
+ return path.slice("debug/".length);
348
+ }
349
+ return path;
350
+ }
351
+ function namespaceControllerId(id, namespace, kind = "graph") {
352
+ if (!id) {
353
+ return void 0;
354
+ }
355
+ const trimmed = id.trim();
356
+ if (!trimmed) {
357
+ return void 0;
358
+ }
359
+ const prefix = `${namespace}/${kind}/`;
360
+ if (trimmed.startsWith(prefix)) {
361
+ return trimmed;
362
+ }
363
+ return `${prefix}${trimmed}`;
364
+ }
365
+ function namespaceSubscriptions(subs, namespace) {
366
+ if (!subs) {
367
+ return void 0;
368
+ }
369
+ const inputs = Array.isArray(subs.inputs) ? subs.inputs.map((path) => namespaceTypedPath(path, namespace)) : void 0;
370
+ const outputs = Array.isArray(subs.outputs) ? subs.outputs.map((path) => namespaceTypedPath(path, namespace)) : void 0;
371
+ if (!inputs && !outputs) {
372
+ return subs;
373
+ }
374
+ return {
375
+ ...subs,
376
+ ...inputs ? { inputs } : {},
377
+ ...outputs ? { outputs } : {}
378
+ };
379
+ }
380
+ function namespaceGraphSpec(spec, namespace) {
381
+ if (!spec || typeof spec !== "object") {
382
+ return spec;
383
+ }
384
+ const nodes = spec.nodes;
385
+ if (!Array.isArray(nodes)) {
386
+ return spec;
387
+ }
388
+ let changed = false;
389
+ const nextNodes = nodes.map((node) => {
390
+ if (!node || typeof node !== "object") {
391
+ return node;
392
+ }
393
+ const path = node.params?.path;
394
+ if (typeof path !== "string") {
395
+ return node;
396
+ }
397
+ const namespacedPath = namespaceTypedPath(path, namespace);
398
+ if (namespacedPath === path) {
399
+ return node;
400
+ }
401
+ changed = true;
402
+ return {
403
+ ...node,
404
+ params: {
405
+ ...node.params ?? {},
406
+ path: namespacedPath
407
+ }
408
+ };
409
+ });
410
+ if (!changed) {
411
+ return spec;
412
+ }
413
+ return {
414
+ ...spec,
415
+ nodes: nextNodes
416
+ };
417
+ }
418
+ var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
248
419
  function pickBundleGraph(bundle, preferredKinds) {
249
420
  if (!bundle?.graphs || bundle.graphs.length === 0) {
250
421
  return null;
@@ -264,14 +435,91 @@ function pickBundleGraph(bundle, preferredKinds) {
264
435
  }
265
436
  return null;
266
437
  }
438
+ function extractIrGraph(payload) {
439
+ if (!payload || typeof payload !== "object") {
440
+ return void 0;
441
+ }
442
+ return payload;
443
+ }
267
444
  function convertBundleGraph(entry) {
268
- if (!entry || !entry.id || !entry.spec) {
445
+ if (!entry || !entry.id) {
446
+ return null;
447
+ }
448
+ const rawSpec = entry.spec;
449
+ const inputMetadata = extractVizijInputMetadata(
450
+ rawSpec
451
+ );
452
+ const spec = rawSpec ? stripVizijMetadata(rawSpec) : void 0;
453
+ const ir = extractIrGraph(entry.ir);
454
+ if (!spec && !ir) {
269
455
  return null;
270
456
  }
271
457
  return {
272
458
  id: entry.id,
273
- spec: entry.spec
459
+ spec,
460
+ ir: ir ?? null,
461
+ inputMetadata
462
+ };
463
+ }
464
+ function resolveGraphSpec(asset, context) {
465
+ if (asset.spec) {
466
+ return stripVizijMetadata(asset.spec);
467
+ }
468
+ if (asset.ir) {
469
+ try {
470
+ const compiled = (0, import_node_graph_authoring.compileIrGraph)(asset.ir, { preferLegacySpec: false });
471
+ if (compiled.issues && compiled.issues.length > 0) {
472
+ console.warn(
473
+ `[vizij-runtime] IR compile for graph "${context}" reported issues`,
474
+ compiled.issues
475
+ );
476
+ }
477
+ return stripVizijMetadata(compiled.spec);
478
+ } catch (error) {
479
+ console.warn(
480
+ `[vizij-runtime] Failed to compile IR graph "${context}"`,
481
+ error
482
+ );
483
+ }
484
+ }
485
+ return null;
486
+ }
487
+ function stripVizijMetadata(spec) {
488
+ if (!spec || typeof spec !== "object") {
489
+ return spec;
490
+ }
491
+ const cloned = {
492
+ ...spec,
493
+ nodes: spec.nodes ? spec.nodes.map((node) => ({ ...node })) : spec.nodes,
494
+ edges: spec.edges ? spec.edges.map((edge) => ({ ...edge })) : spec.edges
274
495
  };
496
+ if (cloned.metadata && typeof cloned.metadata === "object") {
497
+ const metadata = { ...cloned.metadata };
498
+ if ("vizij" in metadata) {
499
+ delete metadata.vizij;
500
+ }
501
+ if (Object.keys(metadata).length === 0) {
502
+ delete cloned.metadata;
503
+ } else {
504
+ cloned.metadata = metadata;
505
+ }
506
+ }
507
+ return cloned;
508
+ }
509
+ function extractVizijInputMetadata(spec) {
510
+ if (!spec || typeof spec !== "object") {
511
+ return [];
512
+ }
513
+ const inputs = spec.metadata?.vizij?.inputs;
514
+ if (!Array.isArray(inputs)) {
515
+ return [];
516
+ }
517
+ return inputs.map((entry) => {
518
+ if (!entry || typeof entry !== "object") {
519
+ return null;
520
+ }
521
+ return entry;
522
+ }).filter(Boolean);
275
523
  }
276
524
  function convertBundleAnimations(entries) {
277
525
  if (!Array.isArray(entries) || entries.length === 0) {
@@ -495,36 +743,53 @@ function VizijRuntimeProvider({
495
743
  autoCreate = true,
496
744
  createOptions,
497
745
  autostart = false,
746
+ driveOrchestrator = true,
498
747
  mergeStrategy,
499
748
  onRegisterControllers,
500
- onStatusChange
749
+ onStatusChange,
750
+ orchestratorScope = "auto"
501
751
  }) {
502
- const storeRef = useRef();
752
+ const storeRef = (0, import_react2.useRef)(null);
503
753
  if (!storeRef.current) {
504
- storeRef.current = createVizijStore();
754
+ storeRef.current = (0, import_render.createVizijStore)();
755
+ }
756
+ const parentOrchestrator = (0, import_react2.useContext)(import_orchestrator_react.OrchestratorContext);
757
+ const hasParentOrchestrator = Boolean(parentOrchestrator);
758
+ const shouldProvideOrchestrator = orchestratorScope === "isolated" || !hasParentOrchestrator && orchestratorScope !== "shared";
759
+ if (orchestratorScope === "shared" && !hasParentOrchestrator) {
760
+ console.warn(
761
+ '[vizij-runtime] orchestratorScope="shared" requires an OrchestratorProvider higher in the tree; falling back to an isolated provider.'
762
+ );
505
763
  }
506
- return /* @__PURE__ */ jsx(VizijContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ jsx(
507
- OrchestratorProvider,
764
+ const runtimeTree = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_render.VizijContext.Provider, { value: storeRef.current, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
765
+ VizijRuntimeProviderInner,
508
766
  {
767
+ assetBundle,
768
+ namespace: namespaceProp,
769
+ faceId: faceIdProp,
509
770
  autoCreate,
510
- createOptions,
511
771
  autostart,
512
- children: /* @__PURE__ */ jsx(
513
- VizijRuntimeProviderInner,
514
- {
515
- assetBundle,
516
- namespace: namespaceProp,
517
- faceId: faceIdProp,
518
- autoCreate,
519
- mergeStrategy,
520
- onRegisterControllers,
521
- onStatusChange,
522
- store: storeRef.current,
523
- children
524
- }
525
- )
772
+ driveOrchestrator,
773
+ createOptions,
774
+ mergeStrategy,
775
+ onRegisterControllers,
776
+ onStatusChange,
777
+ store: storeRef.current,
778
+ children
526
779
  }
527
780
  ) });
781
+ if (!shouldProvideOrchestrator) {
782
+ return runtimeTree;
783
+ }
784
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
785
+ import_orchestrator_react.OrchestratorProvider,
786
+ {
787
+ autoCreate,
788
+ createOptions,
789
+ autostart: false,
790
+ children: runtimeTree
791
+ }
792
+ );
528
793
  }
529
794
  function VizijRuntimeProviderInner({
530
795
  assetBundle: initialAssetBundle,
@@ -535,9 +800,12 @@ function VizijRuntimeProviderInner({
535
800
  onStatusChange,
536
801
  store,
537
802
  children,
538
- autoCreate
803
+ autoCreate,
804
+ autostart,
805
+ createOptions,
806
+ driveOrchestrator
539
807
  }) {
540
- const [extractedBundle, setExtractedBundle] = useState(() => {
808
+ const [extractedBundle, setExtractedBundle] = (0, import_react2.useState)(() => {
541
809
  if (initialAssetBundle.bundle) {
542
810
  return initialAssetBundle.bundle;
543
811
  }
@@ -546,8 +814,8 @@ function VizijRuntimeProviderInner({
546
814
  }
547
815
  return null;
548
816
  });
549
- const [extractedAnimations, setExtractedAnimations] = useState([]);
550
- useEffect(() => {
817
+ const [extractedAnimations, setExtractedAnimations] = (0, import_react2.useState)([]);
818
+ (0, import_react2.useEffect)(() => {
551
819
  if (initialAssetBundle.bundle) {
552
820
  setExtractedBundle(initialAssetBundle.bundle);
553
821
  return;
@@ -558,7 +826,7 @@ function VizijRuntimeProviderInner({
558
826
  setExtractedBundle(null);
559
827
  }
560
828
  }, [initialAssetBundle]);
561
- const assetBundle = useMemo(
829
+ const assetBundle = (0, import_react2.useMemo)(
562
830
  () => mergeAssetBundle(
563
831
  initialAssetBundle,
564
832
  extractedBundle,
@@ -575,14 +843,14 @@ function VizijRuntimeProviderInner({
575
843
  removeGraph,
576
844
  removeAnimation,
577
845
  listControllers,
578
- setInput,
846
+ setInput: orchestratorSetInput,
579
847
  getPathSnapshot,
580
848
  step: stepRuntime
581
- } = useOrchestrator();
582
- const frame = useOrchFrame();
849
+ } = (0, import_orchestrator_react.useOrchestrator)();
850
+ const frame = (0, import_orchestrator_react.useOrchFrame)();
583
851
  const namespace = namespaceProp ?? assetBundle.namespace ?? "default";
584
852
  const faceId = faceIdProp ?? assetBundle.faceId ?? assetBundle.pose?.config?.faceId ?? assetBundle.pose?.config?.faceId ?? void 0;
585
- const [status, setStatus] = useState({
853
+ const [status, setStatus] = (0, import_react2.useState)({
586
854
  loading: true,
587
855
  ready: false,
588
856
  error: null,
@@ -591,19 +859,98 @@ function VizijRuntimeProviderInner({
591
859
  faceId,
592
860
  rootId: null,
593
861
  outputPaths: [],
862
+ stepHz: void 0,
594
863
  controllers: { graphs: [], anims: [] }
595
864
  });
596
- const errorsRef = useRef([]);
597
- const outputPathsRef = useRef(/* @__PURE__ */ new Set());
598
- const rigInputMapRef = useRef({});
599
- const registeredGraphsRef = useRef([]);
600
- const registeredAnimationsRef = useRef([]);
601
- const mergedGraphRef = useRef(null);
602
- const animationTweensRef = useRef(/* @__PURE__ */ new Map());
603
- const clipPlaybackRef = useRef(/* @__PURE__ */ new Map());
604
- const rafHandleRef = useRef(null);
605
- const lastFrameTimeRef = useRef(null);
606
- const reportStatus = useCallback(
865
+ const errorsRef = (0, import_react2.useRef)([]);
866
+ const outputPathsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
867
+ const baseOutputPathsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
868
+ const namespacedOutputPathsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
869
+ const namespaceRef = (0, import_react2.useRef)(namespace);
870
+ const driveOrchestratorRef = (0, import_react2.useRef)(driveOrchestrator);
871
+ const rigInputMapRef = (0, import_react2.useRef)({});
872
+ const registeredGraphsRef = (0, import_react2.useRef)([]);
873
+ const registeredAnimationsRef = (0, import_react2.useRef)([]);
874
+ const mergedGraphRef = (0, import_react2.useRef)(null);
875
+ const [inputConstraints, setInputConstraints] = (0, import_react2.useState)({});
876
+ const avgStepDtRef = (0, import_react2.useRef)(null);
877
+ const animationTweensRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
878
+ const clipPlaybackRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
879
+ const stagedInputsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
880
+ const autostartRef = (0, import_react2.useRef)(autostart);
881
+ const lastActivityTimeRef = (0, import_react2.useRef)(now());
882
+ const [loopMode, setLoopMode] = (0, import_react2.useState)("stopped");
883
+ const loopModeRef = (0, import_react2.useRef)("stopped");
884
+ (0, import_react2.useEffect)(() => {
885
+ loopModeRef.current = loopMode;
886
+ }, [loopMode]);
887
+ const runtimeMountedRef = (0, import_react2.useRef)(true);
888
+ (0, import_react2.useEffect)(() => {
889
+ return () => {
890
+ runtimeMountedRef.current = false;
891
+ };
892
+ }, []);
893
+ (0, import_react2.useEffect)(() => {
894
+ const rigAsset = assetBundle.rig;
895
+ if (!rigAsset) {
896
+ setInputConstraints({});
897
+ return;
898
+ }
899
+ const rigSpec = resolveGraphSpec(
900
+ rigAsset,
901
+ `${rigAsset.id ?? "rig"} graph (constraints)`
902
+ );
903
+ const constraints = extractInputConstraints(
904
+ rigSpec,
905
+ rigAsset.inputMetadata,
906
+ namespace
907
+ );
908
+ setInputConstraints(constraints);
909
+ const isDevEnv = typeof globalThis !== "undefined" && Boolean(globalThis?.process?.env?.NODE_ENV !== "production");
910
+ if (isDevEnv) {
911
+ const size = Object.keys(constraints).length;
912
+ console.log("[vizij-runtime] input constraints computed", size, {
913
+ namespace,
914
+ rigId: rigAsset.id
915
+ });
916
+ }
917
+ }, [assetBundle.rig, namespace]);
918
+ const requestLoopMode = (0, import_react2.useCallback)((mode) => {
919
+ if (!runtimeMountedRef.current) {
920
+ return;
921
+ }
922
+ setLoopMode((prev) => prev === mode ? prev : mode);
923
+ }, []);
924
+ const hasActiveAnimations = (0, import_react2.useCallback)(() => {
925
+ return animationTweensRef.current.size > 0 || clipPlaybackRef.current.size > 0;
926
+ }, []);
927
+ const computeDesiredLoopMode = (0, import_react2.useCallback)(() => {
928
+ const hasAnimations = hasActiveAnimations();
929
+ const recentlyActive = now() - lastActivityTimeRef.current <= ACTIVE_GRACE_MS;
930
+ if (autostartRef.current && (hasAnimations || recentlyActive)) {
931
+ return "active";
932
+ }
933
+ if (autostartRef.current) {
934
+ return "idle-visible";
935
+ }
936
+ return "idle-hidden";
937
+ }, [hasActiveAnimations]);
938
+ const updateLoopMode = (0, import_react2.useCallback)(() => {
939
+ requestLoopMode(computeDesiredLoopMode());
940
+ }, [computeDesiredLoopMode, requestLoopMode]);
941
+ const markActivity = (0, import_react2.useCallback)(() => {
942
+ lastActivityTimeRef.current = now();
943
+ updateLoopMode();
944
+ }, [updateLoopMode]);
945
+ const setInput = (0, import_react2.useCallback)(
946
+ (path, value, shape) => {
947
+ markActivity();
948
+ const namespacedPath = namespaceTypedPath(path, namespaceRef.current);
949
+ stagedInputsRef.current.set(namespacedPath, { value, shape });
950
+ },
951
+ [markActivity]
952
+ );
953
+ const reportStatus = (0, import_react2.useCallback)(
607
954
  (updater) => {
608
955
  setStatus((prev) => {
609
956
  const next = updater(prev);
@@ -613,7 +960,7 @@ function VizijRuntimeProviderInner({
613
960
  },
614
961
  [onStatusChange]
615
962
  );
616
- const pushError = useCallback(
963
+ const pushError = (0, import_react2.useCallback)(
617
964
  (error) => {
618
965
  errorsRef.current = [...errorsRef.current, error];
619
966
  reportStatus((prev) => ({
@@ -625,7 +972,7 @@ function VizijRuntimeProviderInner({
625
972
  },
626
973
  [reportStatus]
627
974
  );
628
- const resetErrors = useCallback(() => {
975
+ const resetErrors = (0, import_react2.useCallback)(() => {
629
976
  errorsRef.current = [];
630
977
  reportStatus((prev) => ({
631
978
  ...prev,
@@ -633,7 +980,11 @@ function VizijRuntimeProviderInner({
633
980
  errors: []
634
981
  }));
635
982
  }, [reportStatus]);
636
- const clearControllers = useCallback(() => {
983
+ (0, import_react2.useEffect)(() => {
984
+ autostartRef.current = autostart;
985
+ updateLoopMode();
986
+ }, [autostart, updateLoopMode]);
987
+ const clearControllers = (0, import_react2.useCallback)(() => {
637
988
  const existing = listControllers();
638
989
  existing.graphs.forEach((id) => {
639
990
  try {
@@ -662,17 +1013,24 @@ function VizijRuntimeProviderInner({
662
1013
  registeredGraphsRef.current = [];
663
1014
  registeredAnimationsRef.current = [];
664
1015
  mergedGraphRef.current = null;
1016
+ outputPathsRef.current = /* @__PURE__ */ new Set();
1017
+ baseOutputPathsRef.current = /* @__PURE__ */ new Set();
1018
+ namespacedOutputPathsRef.current = /* @__PURE__ */ new Set();
665
1019
  }, [listControllers, removeAnimation, removeGraph, pushError]);
666
- useEffect(() => {
1020
+ (0, import_react2.useEffect)(() => {
1021
+ namespaceRef.current = namespace;
667
1022
  reportStatus((prev) => ({
668
1023
  ...prev,
669
1024
  namespace,
670
1025
  faceId
671
1026
  }));
672
1027
  }, [namespace, faceId, reportStatus]);
1028
+ (0, import_react2.useEffect)(() => {
1029
+ driveOrchestratorRef.current = driveOrchestrator;
1030
+ }, [driveOrchestrator]);
673
1031
  const glbAsset = initialAssetBundle.glb;
674
1032
  const baseBundle = initialAssetBundle.bundle ?? null;
675
- useEffect(() => {
1033
+ (0, import_react2.useEffect)(() => {
676
1034
  let cancelled = false;
677
1035
  resetErrors();
678
1036
  reportStatus((prev) => ({
@@ -691,7 +1049,7 @@ function VizijRuntimeProviderInner({
691
1049
  let bundle = baseBundle;
692
1050
  let gltfAnimations;
693
1051
  if (glbAsset.kind === "url") {
694
- const loaded = await loadGLTFWithBundle(
1052
+ const loaded = await (0, import_render.loadGLTFWithBundle)(
695
1053
  glbAsset.src,
696
1054
  [namespace],
697
1055
  glbAsset.aggressiveImport ?? false,
@@ -702,7 +1060,7 @@ function VizijRuntimeProviderInner({
702
1060
  bundle = loaded.bundle ?? bundle;
703
1061
  gltfAnimations = pickExtractedAnimations(loaded);
704
1062
  } else if (glbAsset.kind === "blob") {
705
- const loaded = await loadGLTFFromBlobWithBundle(
1063
+ const loaded = await (0, import_render.loadGLTFFromBlobWithBundle)(
706
1064
  glbAsset.blob,
707
1065
  [namespace],
708
1066
  glbAsset.aggressiveImport ?? false,
@@ -764,9 +1122,9 @@ function VizijRuntimeProviderInner({
764
1122
  setExtractedBundle,
765
1123
  setExtractedAnimations
766
1124
  ]);
767
- useEffect(() => {
1125
+ (0, import_react2.useEffect)(() => {
768
1126
  if (!ready && autoCreate) {
769
- createOrchestrator().catch((err) => {
1127
+ createOrchestrator(createOptions).catch((err) => {
770
1128
  pushError({
771
1129
  message: "Failed to create orchestrator runtime",
772
1130
  cause: err,
@@ -775,10 +1133,20 @@ function VizijRuntimeProviderInner({
775
1133
  });
776
1134
  });
777
1135
  }
778
- }, [ready, autoCreate, createOrchestrator, pushError]);
779
- const registerControllers = useCallback(async () => {
780
- const normalize = (spec) => spec;
1136
+ }, [ready, autoCreate, createOptions, createOrchestrator, pushError]);
1137
+ const registerControllers = (0, import_react2.useCallback)(async () => {
781
1138
  clearControllers();
1139
+ const baseOutputPaths = /* @__PURE__ */ new Set();
1140
+ const namespacedOutputPaths = /* @__PURE__ */ new Set();
1141
+ const recordOutputs = (paths) => {
1142
+ paths.forEach((path) => {
1143
+ const trimmed = path.trim();
1144
+ if (!trimmed) return;
1145
+ const basePath = stripNamespace(trimmed, namespace);
1146
+ baseOutputPaths.add(basePath);
1147
+ namespacedOutputPaths.add(namespaceTypedPath(trimmed, namespace));
1148
+ });
1149
+ };
782
1150
  const rigAsset = assetBundle.rig;
783
1151
  if (!rigAsset) {
784
1152
  pushError({
@@ -788,37 +1156,67 @@ function VizijRuntimeProviderInner({
788
1156
  });
789
1157
  return;
790
1158
  }
791
- const rigSpec = normalize(rigAsset.spec);
1159
+ const rigSpec = resolveGraphSpec(rigAsset, `${rigAsset.id ?? "rig"} graph`);
1160
+ if (!rigSpec) {
1161
+ pushError({
1162
+ message: "Rig graph is missing a usable spec or IR payload.",
1163
+ phase: "registration",
1164
+ timestamp: performance.now()
1165
+ });
1166
+ return;
1167
+ }
792
1168
  const rigOutputs = collectOutputPaths(rigSpec);
793
1169
  const rigInputs = collectInputPaths(rigSpec);
794
1170
  rigInputMapRef.current = collectInputPathMap(rigSpec);
795
- outputPathsRef.current = new Set(rigOutputs);
796
- const rigConfig = {
797
- id: rigAsset.id,
798
- spec: rigSpec,
799
- subs: rigAsset.subscriptions ?? {
800
- inputs: rigInputs,
801
- outputs: rigOutputs
802
- }
1171
+ recordOutputs(rigOutputs);
1172
+ const rigSubs = rigAsset.subscriptions ?? {
1173
+ inputs: rigInputs,
1174
+ outputs: rigOutputs
803
1175
  };
804
- const graphConfigs = [rigConfig];
805
- if (assetBundle.pose?.graph) {
806
- const poseSpec = normalize(assetBundle.pose.graph.spec);
807
- const poseOutputs = collectOutputPaths(poseSpec);
808
- const poseInputs = collectInputPaths(poseSpec);
809
- graphConfigs.push({
810
- id: assetBundle.pose.graph.id,
811
- spec: poseSpec,
812
- subs: assetBundle.pose.graph.subscriptions ?? {
1176
+ const graphConfigs = [
1177
+ {
1178
+ id: namespaceControllerId(rigAsset.id, namespace, "graph"),
1179
+ spec: namespaceGraphSpec(rigSpec, namespace),
1180
+ subs: namespaceSubscriptions(rigSubs, namespace)
1181
+ }
1182
+ ];
1183
+ const poseGraphAsset = assetBundle.pose?.graph;
1184
+ if (poseGraphAsset) {
1185
+ const poseSpec = resolveGraphSpec(
1186
+ poseGraphAsset,
1187
+ `${poseGraphAsset.id ?? "pose"} graph`
1188
+ );
1189
+ if (poseSpec) {
1190
+ const poseOutputs = collectOutputPaths(poseSpec);
1191
+ const poseInputs = collectInputPaths(poseSpec);
1192
+ recordOutputs(poseOutputs);
1193
+ const poseSubs = poseGraphAsset.subscriptions ?? {
813
1194
  inputs: poseInputs,
814
1195
  outputs: poseOutputs
815
- }
816
- });
1196
+ };
1197
+ graphConfigs.push({
1198
+ id: namespaceControllerId(poseGraphAsset.id, namespace, "graph"),
1199
+ spec: namespaceGraphSpec(poseSpec, namespace),
1200
+ subs: namespaceSubscriptions(poseSubs, namespace)
1201
+ });
1202
+ } else {
1203
+ console.warn(
1204
+ "[vizij-runtime] Pose graph is missing a usable spec or IR payload; skipping registration."
1205
+ );
1206
+ }
817
1207
  }
1208
+ outputPathsRef.current = namespacedOutputPaths;
1209
+ baseOutputPathsRef.current = baseOutputPaths;
1210
+ namespacedOutputPathsRef.current = namespacedOutputPaths;
818
1211
  const graphIds = [];
819
1212
  try {
820
1213
  if (graphConfigs.length > 1) {
821
1214
  const mergedId = registerMergedGraph({
1215
+ id: namespaceControllerId(
1216
+ mergedGraphRef.current ?? `merged-${namespace}`,
1217
+ namespace,
1218
+ "merged"
1219
+ ) ?? void 0,
822
1220
  graphs: graphConfigs,
823
1221
  strategy: mergeStrategy ?? DEFAULT_MERGE
824
1222
  });
@@ -842,8 +1240,9 @@ function VizijRuntimeProviderInner({
842
1240
  const animationIds = [];
843
1241
  for (const anim of assetBundle.animations ?? []) {
844
1242
  try {
1243
+ const controllerId = namespaceControllerId(anim.id, namespace, "animation") ?? anim.id;
845
1244
  const config = {
846
- id: anim.id,
1245
+ id: controllerId,
847
1246
  setup: {
848
1247
  animation: anim.clip,
849
1248
  ...anim.setup ?? {}
@@ -888,6 +1287,7 @@ function VizijRuntimeProviderInner({
888
1287
  clearControllers,
889
1288
  listControllers,
890
1289
  mergeStrategy,
1290
+ namespace,
891
1291
  onRegisterControllers,
892
1292
  pushError,
893
1293
  registerAnimation,
@@ -896,7 +1296,7 @@ function VizijRuntimeProviderInner({
896
1296
  reportStatus,
897
1297
  setInput
898
1298
  ]);
899
- useEffect(() => {
1299
+ (0, import_react2.useEffect)(() => {
900
1300
  if (!ready || status.loading) {
901
1301
  return;
902
1302
  }
@@ -909,7 +1309,7 @@ function VizijRuntimeProviderInner({
909
1309
  });
910
1310
  });
911
1311
  }, [ready, status.loading, registerControllers, pushError]);
912
- useEffect(() => {
1312
+ (0, import_react2.useEffect)(() => {
913
1313
  if (!frame) {
914
1314
  return;
915
1315
  }
@@ -917,21 +1317,29 @@ function VizijRuntimeProviderInner({
917
1317
  if (!writes.length) {
918
1318
  return;
919
1319
  }
920
- const setWorldValue = store.getState().setValue;
1320
+ const setWorldValues = store.getState().setValues;
921
1321
  const namespaceValue = status.namespace;
1322
+ const batched = [];
1323
+ const namespacedOutputs = namespacedOutputPathsRef.current;
1324
+ const baseOutputs = baseOutputPathsRef.current;
922
1325
  writes.forEach((write) => {
923
1326
  const path = normalisePath(write.path);
924
- if (!outputPathsRef.current.has(path)) {
1327
+ if (!namespacedOutputs.has(path)) {
925
1328
  return;
926
1329
  }
927
1330
  const raw = valueJSONToRaw(write.value);
928
1331
  if (raw === void 0) {
929
1332
  return;
930
1333
  }
931
- setWorldValue(path, namespaceValue, raw);
1334
+ const basePath = stripNamespace(path, namespaceValue);
1335
+ const targetPath = baseOutputs.has(basePath) ? basePath : path;
1336
+ batched.push({ id: targetPath, namespace: namespaceValue, value: raw });
932
1337
  });
1338
+ if (batched.length > 0) {
1339
+ setWorldValues(batched);
1340
+ }
933
1341
  }, [frame, status.namespace, store]);
934
- const stagePoseNeutral = useCallback(
1342
+ const stagePoseNeutral = (0, import_react2.useCallback)(
935
1343
  (force = false) => {
936
1344
  const neutral = assetBundle.pose?.config?.neutralInputs ?? {};
937
1345
  const rigMap = rigInputMapRef.current;
@@ -963,41 +1371,20 @@ function VizijRuntimeProviderInner({
963
1371
  },
964
1372
  [assetBundle.pose?.config?.neutralInputs, setInput]
965
1373
  );
966
- const setRendererValue = useCallback(
1374
+ const setRendererValue = (0, import_react2.useCallback)(
967
1375
  (id, ns, value) => {
968
1376
  store.getState().setValue(id, ns, value);
969
1377
  },
970
1378
  [store]
971
1379
  );
972
- const cancelAnimation = useCallback((path) => {
1380
+ const cancelAnimation = (0, import_react2.useCallback)((path) => {
973
1381
  if (animationTweensRef.current.has(path)) {
974
1382
  const entry = animationTweensRef.current.get(path);
975
1383
  animationTweensRef.current.delete(path);
976
1384
  entry?.resolve();
977
1385
  }
978
1386
  }, []);
979
- const scheduleLoop = useCallback(() => {
980
- if (rafHandleRef.current !== null) {
981
- return;
982
- }
983
- const tick = (timestamp) => {
984
- if (lastFrameTimeRef.current == null) {
985
- lastFrameTimeRef.current = timestamp;
986
- }
987
- const dt = Math.max(0, (timestamp - lastFrameTimeRef.current) / 1e3);
988
- lastFrameTimeRef.current = timestamp;
989
- advanceAnimationTweens(dt);
990
- advanceClipPlayback(dt);
991
- if (animationTweensRef.current.size > 0 || clipPlaybackRef.current.size > 0) {
992
- rafHandleRef.current = requestAnimationFrame(tick);
993
- } else {
994
- rafHandleRef.current = null;
995
- lastFrameTimeRef.current = null;
996
- }
997
- };
998
- rafHandleRef.current = requestAnimationFrame(tick);
999
- }, []);
1000
- const advanceAnimationTweens = useCallback(
1387
+ const advanceAnimationTweens = (0, import_react2.useCallback)(
1001
1388
  (dt) => {
1002
1389
  if (animationTweensRef.current.size === 0) {
1003
1390
  return;
@@ -1019,7 +1406,7 @@ function VizijRuntimeProviderInner({
1019
1406
  },
1020
1407
  [setInput]
1021
1408
  );
1022
- const sampleTrack = useCallback(
1409
+ const sampleTrack = (0, import_react2.useCallback)(
1023
1410
  (track, time) => {
1024
1411
  const keyframes = Array.isArray(track.keyframes) ? track.keyframes : [];
1025
1412
  if (!keyframes.length) {
@@ -1050,7 +1437,7 @@ function VizijRuntimeProviderInner({
1050
1437
  },
1051
1438
  []
1052
1439
  );
1053
- const advanceClipPlayback = useCallback(
1440
+ const advanceClipPlayback = (0, import_react2.useCallback)(
1054
1441
  (dt) => {
1055
1442
  if (clipPlaybackRef.current.size === 0) {
1056
1443
  return;
@@ -1099,21 +1486,22 @@ function VizijRuntimeProviderInner({
1099
1486
  },
1100
1487
  [assetBundle.animations, sampleTrack, setInput]
1101
1488
  );
1102
- const animateValue = useCallback(
1489
+ const animateValue = (0, import_react2.useCallback)(
1103
1490
  (path, target, options) => {
1104
1491
  const easing = resolveEasing(options?.easing);
1105
1492
  const duration = Math.max(0, options?.duration ?? DEFAULT_DURATION);
1106
1493
  cancelAnimation(path);
1107
- const current = getPathSnapshot(path);
1108
- const fromValue = valueAsNumber2(current);
1109
- const toValue = valueAsNumber2(target);
1494
+ const namespacedPath = namespaceTypedPath(path, namespaceRef.current);
1495
+ const current = getPathSnapshot(namespacedPath);
1496
+ const fromValue = (0, import_value_json2.valueAsNumber)(current);
1497
+ const toValue = (0, import_value_json2.valueAsNumber)(target);
1110
1498
  if (fromValue == null || toValue == null || duration === 0) {
1111
1499
  setInput(path, target);
1112
1500
  return Promise.resolve();
1113
1501
  }
1114
1502
  return new Promise((resolve) => {
1115
1503
  animationTweensRef.current.set(path, {
1116
- path,
1504
+ path: namespacedPath,
1117
1505
  from: fromValue,
1118
1506
  to: toValue,
1119
1507
  duration,
@@ -1121,12 +1509,12 @@ function VizijRuntimeProviderInner({
1121
1509
  easing,
1122
1510
  resolve
1123
1511
  });
1124
- scheduleLoop();
1512
+ markActivity();
1125
1513
  });
1126
1514
  },
1127
- [cancelAnimation, getPathSnapshot, scheduleLoop, setInput]
1515
+ [cancelAnimation, getPathSnapshot, markActivity, setInput]
1128
1516
  );
1129
- const playAnimation = useCallback(
1517
+ const playAnimation = (0, import_react2.useCallback)(
1130
1518
  (id, options) => {
1131
1519
  const clip = assetBundle.animations?.find((anim) => anim.id === id);
1132
1520
  if (!clip) {
@@ -1149,12 +1537,12 @@ function VizijRuntimeProviderInner({
1149
1537
  weight,
1150
1538
  resolve
1151
1539
  });
1152
- scheduleLoop();
1540
+ markActivity();
1153
1541
  });
1154
1542
  },
1155
- [assetBundle.animations, scheduleLoop]
1543
+ [assetBundle.animations, markActivity]
1156
1544
  );
1157
- const stopAnimation = useCallback(
1545
+ const stopAnimation = (0, import_react2.useCallback)(
1158
1546
  (id) => {
1159
1547
  const clip = assetBundle.animations?.find((anim) => anim.id === id);
1160
1548
  const state = clipPlaybackRef.current.get(id);
@@ -1173,7 +1561,7 @@ function VizijRuntimeProviderInner({
1173
1561
  },
1174
1562
  [assetBundle.animations, setInput]
1175
1563
  );
1176
- const registerInputDriver = useCallback(
1564
+ const registerInputDriver = (0, import_react2.useCallback)(
1177
1565
  (id, factory) => {
1178
1566
  const driver = factory({
1179
1567
  setInput,
@@ -1223,32 +1611,111 @@ function VizijRuntimeProviderInner({
1223
1611
  },
1224
1612
  [faceId, namespace, pushError, setInput, setRendererValue]
1225
1613
  );
1226
- const advanceAnimations = useCallback(
1614
+ const advanceAnimations = (0, import_react2.useCallback)(
1227
1615
  (dt) => {
1228
1616
  advanceAnimationTweens(dt);
1229
1617
  advanceClipPlayback(dt);
1230
1618
  },
1231
1619
  [advanceAnimationTweens, advanceClipPlayback]
1232
1620
  );
1233
- const step = useCallback(
1234
- (dt) => {
1621
+ const flushStagedInputs = (0, import_react2.useCallback)(() => {
1622
+ if (stagedInputsRef.current.size === 0) {
1623
+ return;
1624
+ }
1625
+ stagedInputsRef.current.forEach(({ value, shape }, path) => {
1626
+ orchestratorSetInput(path, value, shape);
1627
+ });
1628
+ stagedInputsRef.current.clear();
1629
+ }, [orchestratorSetInput]);
1630
+ const step = (0, import_react2.useCallback)(
1631
+ (dt, opts) => {
1632
+ if (dt > 0 && Number.isFinite(dt)) {
1633
+ const prev = avgStepDtRef.current ?? dt;
1634
+ const alpha = 0.1;
1635
+ avgStepDtRef.current = prev * (1 - alpha) + dt * alpha;
1636
+ }
1235
1637
  advanceAnimations(dt);
1236
- stepRuntime(dt);
1638
+ flushStagedInputs();
1639
+ if (driveOrchestratorRef.current || opts?.forceRuntime) {
1640
+ stepRuntime(dt);
1641
+ }
1237
1642
  },
1238
- [advanceAnimations, stepRuntime]
1643
+ [advanceAnimations, flushStagedInputs, stepRuntime]
1239
1644
  );
1240
- useEffect(() => {
1645
+ (0, import_react2.useEffect)(() => {
1646
+ if (loopMode !== "active") {
1647
+ return;
1648
+ }
1649
+ let rafId = null;
1650
+ let lastTime = null;
1651
+ const tick = (timestamp) => {
1652
+ if (loopModeRef.current !== "active") {
1653
+ return;
1654
+ }
1655
+ if (lastTime == null) {
1656
+ lastTime = timestamp;
1657
+ }
1658
+ const dt = Math.max(0, (timestamp - lastTime) / 1e3);
1659
+ lastTime = timestamp;
1660
+ step(dt || 0);
1661
+ requestLoopMode(computeDesiredLoopMode());
1662
+ rafId = requestAnimationFrame(tick);
1663
+ };
1664
+ rafId = requestAnimationFrame(tick);
1241
1665
  return () => {
1242
- if (rafHandleRef.current !== null) {
1243
- cancelAnimationFrame(rafHandleRef.current);
1244
- rafHandleRef.current = null;
1666
+ if (rafId !== null) {
1667
+ cancelAnimationFrame(rafId);
1245
1668
  }
1246
- lastFrameTimeRef.current = null;
1669
+ };
1670
+ }, [loopMode, computeDesiredLoopMode, requestLoopMode, step]);
1671
+ (0, import_react2.useEffect)(() => {
1672
+ if (loopMode !== "idle-visible" && loopMode !== "idle-hidden") {
1673
+ return;
1674
+ }
1675
+ if (typeof window === "undefined") {
1676
+ return;
1677
+ }
1678
+ const fps = loopMode === "idle-visible" ? VISIBLE_IDLE_FPS : HIDDEN_IDLE_FPS;
1679
+ if (fps <= 0) {
1680
+ return;
1681
+ }
1682
+ let lastTime = now();
1683
+ const interval = 1e3 / fps;
1684
+ const tick = () => {
1685
+ if (loopModeRef.current !== "idle-visible" && loopModeRef.current !== "idle-hidden") {
1686
+ return;
1687
+ }
1688
+ const current = now();
1689
+ const dt = Math.max(0, (current - lastTime) / 1e3);
1690
+ lastTime = current;
1691
+ step(dt || 0);
1692
+ requestLoopMode(computeDesiredLoopMode());
1693
+ };
1694
+ const intervalId = window.setInterval(tick, interval);
1695
+ return () => {
1696
+ window.clearInterval(intervalId);
1697
+ };
1698
+ }, [loopMode, computeDesiredLoopMode, requestLoopMode, step]);
1699
+ (0, import_react2.useEffect)(() => {
1700
+ return () => {
1247
1701
  animationTweensRef.current.clear();
1248
1702
  clipPlaybackRef.current.clear();
1249
1703
  };
1250
1704
  }, []);
1251
- const contextValue = useMemo(
1705
+ (0, import_react2.useEffect)(() => {
1706
+ if (typeof window === "undefined") {
1707
+ return;
1708
+ }
1709
+ const id = window.setInterval(() => {
1710
+ const avg = avgStepDtRef.current;
1711
+ const stepHz = avg && Number.isFinite(avg) && avg > 0 ? 1 / avg : void 0;
1712
+ reportStatus(
1713
+ (prev) => prev.stepHz === stepHz ? prev : { ...prev, stepHz }
1714
+ );
1715
+ }, 500);
1716
+ return () => window.clearInterval(id);
1717
+ }, [reportStatus]);
1718
+ const contextValue = (0, import_react2.useMemo)(
1252
1719
  () => ({
1253
1720
  ...status,
1254
1721
  assetBundle,
@@ -1261,7 +1728,8 @@ function VizijRuntimeProviderInner({
1261
1728
  playAnimation,
1262
1729
  stopAnimation,
1263
1730
  step,
1264
- advanceAnimations
1731
+ advanceAnimations,
1732
+ inputConstraints
1265
1733
  }),
1266
1734
  [
1267
1735
  status,
@@ -1275,20 +1743,21 @@ function VizijRuntimeProviderInner({
1275
1743
  playAnimation,
1276
1744
  stopAnimation,
1277
1745
  step,
1278
- advanceAnimations
1746
+ advanceAnimations,
1747
+ inputConstraints
1279
1748
  ]
1280
1749
  );
1281
- return /* @__PURE__ */ jsx(VizijRuntimeContext.Provider, { value: contextValue, children });
1750
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VizijRuntimeContext.Provider, { value: contextValue, children });
1282
1751
  }
1283
1752
 
1284
1753
  // src/VizijRuntimeFace.tsx
1285
- import { memo } from "react";
1286
- import { Vizij } from "@vizij/render";
1754
+ var import_react4 = require("react");
1755
+ var import_render2 = require("@vizij/render");
1287
1756
 
1288
1757
  // src/hooks/useVizijRuntime.ts
1289
- import { useContext } from "react";
1758
+ var import_react3 = require("react");
1290
1759
  function useVizijRuntime() {
1291
- const ctx = useContext(VizijRuntimeContext);
1760
+ const ctx = (0, import_react3.useContext)(VizijRuntimeContext);
1292
1761
  if (!ctx) {
1293
1762
  throw new Error(
1294
1763
  "useVizijRuntime must be used within a VizijRuntimeProvider."
@@ -1298,7 +1767,7 @@ function useVizijRuntime() {
1298
1767
  }
1299
1768
 
1300
1769
  // src/VizijRuntimeFace.tsx
1301
- import { jsx as jsx2 } from "react/jsx-runtime";
1770
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1302
1771
  function VizijRuntimeFaceInner({
1303
1772
  namespaceOverride,
1304
1773
  ...props
@@ -1307,8 +1776,8 @@ function VizijRuntimeFaceInner({
1307
1776
  if (!rootId) {
1308
1777
  return null;
1309
1778
  }
1310
- return /* @__PURE__ */ jsx2(
1311
- Vizij,
1779
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1780
+ import_render2.Vizij,
1312
1781
  {
1313
1782
  ...props,
1314
1783
  rootId,
@@ -1316,19 +1785,17 @@ function VizijRuntimeFaceInner({
1316
1785
  }
1317
1786
  );
1318
1787
  }
1319
- var VizijRuntimeFace = memo(VizijRuntimeFaceInner);
1788
+ var VizijRuntimeFace = (0, import_react4.memo)(VizijRuntimeFaceInner);
1320
1789
 
1321
1790
  // src/hooks/useVizijOutputs.ts
1322
- import {
1323
- useVizijStore
1324
- } from "@vizij/render";
1325
- import { getLookup } from "@vizij/utils";
1791
+ var import_render3 = require("@vizij/render");
1792
+ var import_utils = require("@vizij/utils");
1326
1793
  function useVizijOutputs(paths) {
1327
1794
  const { namespace } = useVizijRuntime();
1328
- return useVizijStore((state) => {
1795
+ return (0, import_render3.useVizijStore)((state) => {
1329
1796
  const result = {};
1330
1797
  paths.forEach((path) => {
1331
- const lookup = getLookup(namespace, path);
1798
+ const lookup = (0, import_utils.getLookup)(namespace, path);
1332
1799
  result[path] = state.values.get(lookup);
1333
1800
  });
1334
1801
  return result;
@@ -1336,15 +1803,15 @@ function useVizijOutputs(paths) {
1336
1803
  }
1337
1804
 
1338
1805
  // src/hooks/useRigInput.ts
1339
- import { useCallback as useCallback2 } from "react";
1340
- import { useVizijStore as useVizijStore2 } from "@vizij/render";
1341
- import { getLookup as getLookup2 } from "@vizij/utils";
1806
+ var import_react5 = require("react");
1807
+ var import_render4 = require("@vizij/render");
1808
+ var import_utils2 = require("@vizij/utils");
1342
1809
  function useRigInput(path) {
1343
1810
  const { namespace, setInput } = useVizijRuntime();
1344
- const value = useVizijStore2(
1345
- (state) => state.values.get(getLookup2(namespace, path))
1346
- );
1347
- const setter = useCallback2(
1811
+ const value = (0, import_render4.useVizijStore)((state) => {
1812
+ return state.values.get((0, import_utils2.getLookup)(namespace, path));
1813
+ });
1814
+ const setter = (0, import_react5.useCallback)(
1348
1815
  (next, shape) => {
1349
1816
  setInput(path, next, shape);
1350
1817
  },
@@ -1352,10 +1819,11 @@ function useRigInput(path) {
1352
1819
  );
1353
1820
  return [value, setter];
1354
1821
  }
1355
- export {
1822
+ // Annotate the CommonJS export names for ESM import in node:
1823
+ 0 && (module.exports = {
1356
1824
  VizijRuntimeFace,
1357
1825
  VizijRuntimeProvider,
1358
1826
  useRigInput,
1359
1827
  useVizijOutputs,
1360
1828
  useVizijRuntime
1361
- };
1829
+ });