@vizij/render 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,6 +101,18 @@ The store tracks world graph entries, controllers, debug overlays, and renderabl
101
101
 
102
102
  All exports are re-exported through `src/index.tsx`, so a simple `import { loadGLTF } from "@vizij/render"` works.
103
103
 
104
+ ### Dual-format Vizij bundles
105
+
106
+ Vizij scenes persist authoring metadata inside GLBs so third-party tools see baked animation, while Vizij runtimes retain orchestrator graphs and clips.
107
+
108
+ - Every renderable still carries a `RobotData` extension in `userData` describing features and animatable bindings.
109
+ - The exporter now writes a root-level `extensions.VIZIJ_bundle` block following the schema in [`src/types/vizij-bundle.ts`](./src/types/vizij-bundle.ts). It contains rig graphs, pose configs, stored Vizij clips, and provenance hashes.
110
+ - Use `exportScene(group, { bundle, animations })` to embed both the Vizij bundle and optional baked `THREE.AnimationClip` instances. The helper attaches the bundle only for the export call and restores the original object.
111
+ - When loading assets, prefer `loadGLTFWithBundle` / `loadGLTFFromBlobWithBundle` to retrieve `{ world, animatables, bundle }`. The legacy tuple helpers remain available if you do not need bundle metadata.
112
+ - `extractVizijBundle(scene)` and `applyVizijBundle(scene, bundle)` (under `src/functions/vizij-bundle.ts`) let advanced tooling inspect or mutate bundles without triggering a fresh export.
113
+
114
+ With this structure, authoring tools can round-trip orchestrator assets while shipping native glTF animations for viewers that do not understand Vizij.
115
+
104
116
  ---
105
117
 
106
118
  ## Development & Testing
package/dist/index.d.mts CHANGED
@@ -4,7 +4,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { Canvas, ThreeEvent } from '@react-three/fiber';
5
5
  import * as zustand from 'zustand';
6
6
  import * as THREE from 'three';
7
- import { Mesh, Group as Group$1, BufferGeometry, ShapeGeometry } from 'three';
7
+ import { Mesh, Group as Group$1, BufferGeometry, ShapeGeometry, AnimationClip, Object3D } from 'three';
8
8
  import { RawValue, AnimatableValue, RawVector2 } from '@vizij/utils';
9
9
 
10
10
  declare function InnerController({ animatableId, namespace, subfield, className, }: {
@@ -282,6 +282,94 @@ interface Selection {
282
282
  };
283
283
  }
284
284
 
285
+ type VizijBundleVersion = 1;
286
+ type VizijBundleGraphKind = "rig" | "pose" | "pose-driver" | "animation-bridge" | "low-level" | string;
287
+ type VizijPoseId = string;
288
+ type VizijAnimationId = string;
289
+ type VizijGraphId = string;
290
+ interface VizijBundleGraphMetadata {
291
+ hash?: string;
292
+ source?: string;
293
+ kind?: VizijBundleGraphKind;
294
+ exportedAt?: string;
295
+ [key: string]: unknown;
296
+ }
297
+ interface VizijBundleGraphEntry {
298
+ id: VizijGraphId;
299
+ kind: VizijBundleGraphKind;
300
+ spec: Record<string, unknown>;
301
+ label?: string;
302
+ metadata?: VizijBundleGraphMetadata;
303
+ }
304
+ interface VizijPoseDefinition {
305
+ id: VizijPoseId;
306
+ name?: string;
307
+ description?: string;
308
+ values: Record<string, number | undefined>;
309
+ }
310
+ interface VizijPoseRigConfig {
311
+ version: number;
312
+ faceId?: string | null;
313
+ title?: string;
314
+ description?: string;
315
+ neutralInputs: Record<string, number>;
316
+ poses: VizijPoseDefinition[];
317
+ metadata?: Record<string, unknown>;
318
+ [key: string]: unknown;
319
+ }
320
+ interface VizijBundlePoseSection {
321
+ config: VizijPoseRigConfig;
322
+ metadata?: {
323
+ hash?: string;
324
+ exportedAt?: string;
325
+ [key: string]: unknown;
326
+ };
327
+ }
328
+ interface VizijBundleAnimationKeyframe {
329
+ time: number;
330
+ value: number;
331
+ easing?: "linear" | "easeIn" | "easeOut" | "easeInOut" | string;
332
+ inTangent?: number | null;
333
+ outTangent?: number | null;
334
+ [key: string]: unknown;
335
+ }
336
+ interface VizijBundleAnimationTrack {
337
+ channel: string;
338
+ keyframes: VizijBundleAnimationKeyframe[];
339
+ interpolation?: "step" | "linear" | "cubic" | string;
340
+ [key: string]: unknown;
341
+ }
342
+ interface VizijBundleAnimationClip {
343
+ id: VizijAnimationId;
344
+ name?: string;
345
+ duration?: number;
346
+ tracks: VizijBundleAnimationTrack[];
347
+ metadata?: Record<string, unknown>;
348
+ [key: string]: unknown;
349
+ }
350
+ interface VizijBundleAnimationEntry {
351
+ id: VizijAnimationId;
352
+ clip: VizijBundleAnimationClip;
353
+ metadata?: {
354
+ hash?: string;
355
+ sampleRateHz?: number;
356
+ rigGraphHash?: string;
357
+ poseGraphHash?: string | null;
358
+ bakedClipIndex?: number | null;
359
+ tolerance?: number;
360
+ exportedAt?: string;
361
+ [key: string]: unknown;
362
+ };
363
+ }
364
+ interface VizijBundleExtension {
365
+ version: VizijBundleVersion;
366
+ exportedAt?: string;
367
+ graphs?: VizijBundleGraphEntry[];
368
+ poses?: VizijBundlePoseSection | null;
369
+ animations?: VizijBundleAnimationEntry[];
370
+ metadata?: Record<string, unknown>;
371
+ }
372
+
285
373
  interface VizijData {
286
374
  world: World;
287
375
  animatables: Record<string, AnimatableValue>;
@@ -442,6 +530,19 @@ declare function loadGLTFFromBlob(blob: Blob, namespaces: string[], aggressiveIm
442
530
  center: RawVector2;
443
531
  size: RawVector2;
444
532
  }): Promise<[World, Record<string, AnimatableValue>]>;
533
+ type LoadedVizijAsset = {
534
+ world: World;
535
+ animatables: Record<string, AnimatableValue>;
536
+ bundle: VizijBundleExtension | null;
537
+ };
538
+ declare function loadGLTFWithBundle(url: string, namespaces: string[], aggressiveImport?: boolean, rootBounds?: {
539
+ center: RawVector2;
540
+ size: RawVector2;
541
+ }): Promise<LoadedVizijAsset>;
542
+ declare function loadGLTFFromBlobWithBundle(blob: Blob, namespaces: string[], aggressiveImport?: boolean, rootBounds?: {
543
+ center: RawVector2;
544
+ size: RawVector2;
545
+ }): Promise<LoadedVizijAsset>;
445
546
 
446
547
  /**
447
548
  * Loads a GLTF model from a Blob and returns the Three.js scene containing the model.
@@ -451,6 +552,15 @@ declare function loadGLTFFromBlob(blob: Blob, namespaces: string[], aggressiveIm
451
552
  */
452
553
  declare const loadGltfFromBlob: (blob: Blob, namespaces: string[]) => Promise<[World, Record<string, AnimatableValue>]>;
453
554
 
454
- declare function exportScene(data: Group$1, fileName?: string): void;
555
+ type ExportSceneOptions = {
556
+ fileName?: string;
557
+ bundle?: VizijBundleExtension | null;
558
+ animations?: AnimationClip[];
559
+ binary?: boolean;
560
+ };
561
+ declare function exportScene(data: Group$1, fileNameOrOptions?: string | ExportSceneOptions): void;
562
+
563
+ declare function extractVizijBundle(object: Object3D, parserJson?: unknown): VizijBundleExtension | null;
564
+ declare function applyVizijBundle(object: Object3D, bundle: VizijBundleExtension | null): () => void;
455
565
 
456
- export { type AnimatedFeature, Controller, type Ellipse, type EllipseFeature, EmptyModelError, type Feature, type Group, type GroupFeature, InnerVizij, type InnerVizijProps, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, Vizij, type VizijActions, VizijContext, type VizijData, type VizijProps, VizijSlice, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, createVizijStore, exportScene, loadGLTF, loadGLTFFromBlob, loadGltfFromBlob, useDefaultVizijStore, useFeatures, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
566
+ export { type AnimatedFeature, Controller, type Ellipse, type EllipseFeature, EmptyModelError, type ExportSceneOptions, type Feature, type Group, type GroupFeature, InnerVizij, type InnerVizijProps, type LoadedVizijAsset, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, Vizij, type VizijActions, type VizijAnimationId, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleVersion, VizijContext, type VizijData, type VizijGraphId, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProps, VizijSlice, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, createVizijStore, exportScene, extractVizijBundle, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, loadGltfFromBlob, useDefaultVizijStore, useFeatures, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { Canvas, ThreeEvent } from '@react-three/fiber';
5
5
  import * as zustand from 'zustand';
6
6
  import * as THREE from 'three';
7
- import { Mesh, Group as Group$1, BufferGeometry, ShapeGeometry } from 'three';
7
+ import { Mesh, Group as Group$1, BufferGeometry, ShapeGeometry, AnimationClip, Object3D } from 'three';
8
8
  import { RawValue, AnimatableValue, RawVector2 } from '@vizij/utils';
9
9
 
10
10
  declare function InnerController({ animatableId, namespace, subfield, className, }: {
@@ -282,6 +282,94 @@ interface Selection {
282
282
  };
283
283
  }
284
284
 
285
+ type VizijBundleVersion = 1;
286
+ type VizijBundleGraphKind = "rig" | "pose" | "pose-driver" | "animation-bridge" | "low-level" | string;
287
+ type VizijPoseId = string;
288
+ type VizijAnimationId = string;
289
+ type VizijGraphId = string;
290
+ interface VizijBundleGraphMetadata {
291
+ hash?: string;
292
+ source?: string;
293
+ kind?: VizijBundleGraphKind;
294
+ exportedAt?: string;
295
+ [key: string]: unknown;
296
+ }
297
+ interface VizijBundleGraphEntry {
298
+ id: VizijGraphId;
299
+ kind: VizijBundleGraphKind;
300
+ spec: Record<string, unknown>;
301
+ label?: string;
302
+ metadata?: VizijBundleGraphMetadata;
303
+ }
304
+ interface VizijPoseDefinition {
305
+ id: VizijPoseId;
306
+ name?: string;
307
+ description?: string;
308
+ values: Record<string, number | undefined>;
309
+ }
310
+ interface VizijPoseRigConfig {
311
+ version: number;
312
+ faceId?: string | null;
313
+ title?: string;
314
+ description?: string;
315
+ neutralInputs: Record<string, number>;
316
+ poses: VizijPoseDefinition[];
317
+ metadata?: Record<string, unknown>;
318
+ [key: string]: unknown;
319
+ }
320
+ interface VizijBundlePoseSection {
321
+ config: VizijPoseRigConfig;
322
+ metadata?: {
323
+ hash?: string;
324
+ exportedAt?: string;
325
+ [key: string]: unknown;
326
+ };
327
+ }
328
+ interface VizijBundleAnimationKeyframe {
329
+ time: number;
330
+ value: number;
331
+ easing?: "linear" | "easeIn" | "easeOut" | "easeInOut" | string;
332
+ inTangent?: number | null;
333
+ outTangent?: number | null;
334
+ [key: string]: unknown;
335
+ }
336
+ interface VizijBundleAnimationTrack {
337
+ channel: string;
338
+ keyframes: VizijBundleAnimationKeyframe[];
339
+ interpolation?: "step" | "linear" | "cubic" | string;
340
+ [key: string]: unknown;
341
+ }
342
+ interface VizijBundleAnimationClip {
343
+ id: VizijAnimationId;
344
+ name?: string;
345
+ duration?: number;
346
+ tracks: VizijBundleAnimationTrack[];
347
+ metadata?: Record<string, unknown>;
348
+ [key: string]: unknown;
349
+ }
350
+ interface VizijBundleAnimationEntry {
351
+ id: VizijAnimationId;
352
+ clip: VizijBundleAnimationClip;
353
+ metadata?: {
354
+ hash?: string;
355
+ sampleRateHz?: number;
356
+ rigGraphHash?: string;
357
+ poseGraphHash?: string | null;
358
+ bakedClipIndex?: number | null;
359
+ tolerance?: number;
360
+ exportedAt?: string;
361
+ [key: string]: unknown;
362
+ };
363
+ }
364
+ interface VizijBundleExtension {
365
+ version: VizijBundleVersion;
366
+ exportedAt?: string;
367
+ graphs?: VizijBundleGraphEntry[];
368
+ poses?: VizijBundlePoseSection | null;
369
+ animations?: VizijBundleAnimationEntry[];
370
+ metadata?: Record<string, unknown>;
371
+ }
372
+
285
373
  interface VizijData {
286
374
  world: World;
287
375
  animatables: Record<string, AnimatableValue>;
@@ -442,6 +530,19 @@ declare function loadGLTFFromBlob(blob: Blob, namespaces: string[], aggressiveIm
442
530
  center: RawVector2;
443
531
  size: RawVector2;
444
532
  }): Promise<[World, Record<string, AnimatableValue>]>;
533
+ type LoadedVizijAsset = {
534
+ world: World;
535
+ animatables: Record<string, AnimatableValue>;
536
+ bundle: VizijBundleExtension | null;
537
+ };
538
+ declare function loadGLTFWithBundle(url: string, namespaces: string[], aggressiveImport?: boolean, rootBounds?: {
539
+ center: RawVector2;
540
+ size: RawVector2;
541
+ }): Promise<LoadedVizijAsset>;
542
+ declare function loadGLTFFromBlobWithBundle(blob: Blob, namespaces: string[], aggressiveImport?: boolean, rootBounds?: {
543
+ center: RawVector2;
544
+ size: RawVector2;
545
+ }): Promise<LoadedVizijAsset>;
445
546
 
446
547
  /**
447
548
  * Loads a GLTF model from a Blob and returns the Three.js scene containing the model.
@@ -451,6 +552,15 @@ declare function loadGLTFFromBlob(blob: Blob, namespaces: string[], aggressiveIm
451
552
  */
452
553
  declare const loadGltfFromBlob: (blob: Blob, namespaces: string[]) => Promise<[World, Record<string, AnimatableValue>]>;
453
554
 
454
- declare function exportScene(data: Group$1, fileName?: string): void;
555
+ type ExportSceneOptions = {
556
+ fileName?: string;
557
+ bundle?: VizijBundleExtension | null;
558
+ animations?: AnimationClip[];
559
+ binary?: boolean;
560
+ };
561
+ declare function exportScene(data: Group$1, fileNameOrOptions?: string | ExportSceneOptions): void;
562
+
563
+ declare function extractVizijBundle(object: Object3D, parserJson?: unknown): VizijBundleExtension | null;
564
+ declare function applyVizijBundle(object: Object3D, bundle: VizijBundleExtension | null): () => void;
455
565
 
456
- export { type AnimatedFeature, Controller, type Ellipse, type EllipseFeature, EmptyModelError, type Feature, type Group, type GroupFeature, InnerVizij, type InnerVizijProps, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, Vizij, type VizijActions, VizijContext, type VizijData, type VizijProps, VizijSlice, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, createVizijStore, exportScene, loadGLTF, loadGLTFFromBlob, loadGltfFromBlob, useDefaultVizijStore, useFeatures, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
566
+ export { type AnimatedFeature, Controller, type Ellipse, type EllipseFeature, EmptyModelError, type ExportSceneOptions, type Feature, type Group, type GroupFeature, InnerVizij, type InnerVizijProps, type LoadedVizijAsset, type Rectangle, type RectangleFeature, type RenderableBase, type RenderableFeature, type Selection, type Shape, type ShapeFeature, ShapeMaterial, type StaticFeature, type Stored, type StoredAnimatedFeature, type StoredEllipse, type StoredFeatures, type StoredGroup, type StoredRectangle, type StoredRenderable, type StoredShape, Vizij, type VizijActions, type VizijAnimationId, type VizijBundleAnimationClip, type VizijBundleAnimationEntry, type VizijBundleAnimationKeyframe, type VizijBundleAnimationTrack, type VizijBundleExtension, type VizijBundleGraphEntry, type VizijBundleGraphKind, type VizijBundleGraphMetadata, type VizijBundlePoseSection, type VizijBundleVersion, VizijContext, type VizijData, type VizijGraphId, type VizijPoseDefinition, type VizijPoseId, type VizijPoseRigConfig, type VizijProps, VizijSlice, type VizijStore, type VizijStoreGetter, type VizijStoreSetter, type World, applyVizijBundle, createVizijStore, exportScene, extractVizijBundle, loadGLTF, loadGLTFFromBlob, loadGLTFFromBlobWithBundle, loadGLTFWithBundle, loadGltfFromBlob, useDefaultVizijStore, useFeatures, useVizijStore, useVizijStoreGetter, useVizijStoreSetter, useVizijStoreSubscription };
package/dist/index.js CHANGED
@@ -37,10 +37,14 @@ __export(index_exports, {
37
37
  Vizij: () => Vizij,
38
38
  VizijContext: () => VizijContext,
39
39
  VizijSlice: () => VizijSlice,
40
+ applyVizijBundle: () => applyVizijBundle,
40
41
  createVizijStore: () => createVizijStore,
41
42
  exportScene: () => exportScene,
43
+ extractVizijBundle: () => extractVizijBundle,
42
44
  loadGLTF: () => loadGLTF,
43
45
  loadGLTFFromBlob: () => loadGLTFFromBlob,
46
+ loadGLTFFromBlobWithBundle: () => loadGLTFFromBlobWithBundle,
47
+ loadGLTFWithBundle: () => loadGLTFWithBundle,
44
48
  loadGltfFromBlob: () => loadGltfFromBlob,
45
49
  useDefaultVizijStore: () => useDefaultVizijStore,
46
50
  useFeatures: () => useFeatures,
@@ -2521,7 +2525,9 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2521
2525
  });
2522
2526
  const useRobotData = !aggressiveImport || hasRobotData;
2523
2527
  if (useRobotData) {
2524
- group.traverse((child) => {
2528
+ const stack = [group];
2529
+ while (stack.length > 0) {
2530
+ const child = stack.pop();
2525
2531
  if (child.userData?.gltfExtensions?.RobotData) {
2526
2532
  const data = child.userData.gltfExtensions.RobotData;
2527
2533
  applyStoredRenderableNames(child, data);
@@ -2590,7 +2596,10 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2590
2596
  throw new Error(`Unhandled type`);
2591
2597
  }
2592
2598
  }
2593
- });
2599
+ if (child.children) {
2600
+ stack.push(...child.children);
2601
+ }
2602
+ }
2594
2603
  } else {
2595
2604
  const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
2596
2605
  if (!derivedRootBounds) {
@@ -2758,6 +2767,106 @@ function deriveRootBounds(group) {
2758
2767
  };
2759
2768
  }
2760
2769
 
2770
+ // src/functions/vizij-bundle.ts
2771
+ var BUNDLE_KEYS = ["VIZIJ_bundle"];
2772
+ function cloneBundle(value) {
2773
+ return JSON.parse(JSON.stringify(value));
2774
+ }
2775
+ function readExtensionValue(extensionContainer) {
2776
+ for (const key of BUNDLE_KEYS) {
2777
+ if (extensionContainer && Object.prototype.hasOwnProperty.call(extensionContainer, key)) {
2778
+ const value = extensionContainer[key];
2779
+ if (value && typeof value === "object") {
2780
+ return { key, value };
2781
+ }
2782
+ }
2783
+ }
2784
+ return null;
2785
+ }
2786
+ function searchObjectForBundle(object) {
2787
+ const stack = [object];
2788
+ while (stack.length > 0) {
2789
+ const current = stack.pop();
2790
+ const extensions = current?.userData?.gltfExtensions ?? current?.userData?.extensions ?? null;
2791
+ if (extensions && typeof extensions === "object") {
2792
+ const match = readExtensionValue(extensions);
2793
+ if (match) {
2794
+ return cloneBundle(match.value);
2795
+ }
2796
+ }
2797
+ if (current.children && current.children.length > 0) {
2798
+ stack.push(...current.children);
2799
+ }
2800
+ }
2801
+ return null;
2802
+ }
2803
+ function searchParserJsonForBundle(parserJson) {
2804
+ if (!parserJson || typeof parserJson !== "object") {
2805
+ return null;
2806
+ }
2807
+ const nodes = Array.isArray(parserJson.nodes) ? parserJson.nodes : [];
2808
+ for (const node of nodes) {
2809
+ const extensions = node && typeof node === "object" ? node.extensions : null;
2810
+ if (extensions && typeof extensions === "object") {
2811
+ const match = readExtensionValue(extensions);
2812
+ if (match) {
2813
+ return cloneBundle(match.value);
2814
+ }
2815
+ }
2816
+ }
2817
+ const scenes = Array.isArray(parserJson.scenes) ? parserJson.scenes : [];
2818
+ for (const scene of scenes) {
2819
+ const extensions = scene && typeof scene === "object" ? scene.extensions : null;
2820
+ if (extensions && typeof extensions === "object") {
2821
+ const match = readExtensionValue(extensions);
2822
+ if (match) {
2823
+ return cloneBundle(match.value);
2824
+ }
2825
+ }
2826
+ }
2827
+ return null;
2828
+ }
2829
+ function extractVizijBundle(object, parserJson) {
2830
+ const fromObject = searchObjectForBundle(object);
2831
+ if (fromObject) {
2832
+ return fromObject;
2833
+ }
2834
+ const fromParser = searchParserJsonForBundle(parserJson);
2835
+ if (fromParser) {
2836
+ return fromParser;
2837
+ }
2838
+ return null;
2839
+ }
2840
+ function applyVizijBundle(object, bundle) {
2841
+ const userData = object.userData && typeof object.userData === "object" ? object.userData : {};
2842
+ const originalExtensions = userData.gltfExtensions;
2843
+ let applied = false;
2844
+ if (bundle) {
2845
+ userData.gltfExtensions = {
2846
+ ...originalExtensions ?? {},
2847
+ VIZIJ_bundle: bundle
2848
+ };
2849
+ object.userData = userData;
2850
+ applied = true;
2851
+ }
2852
+ return () => {
2853
+ if (!applied) {
2854
+ return;
2855
+ }
2856
+ if (originalExtensions) {
2857
+ userData.gltfExtensions = originalExtensions;
2858
+ } else {
2859
+ if (userData.gltfExtensions) {
2860
+ delete userData.gltfExtensions;
2861
+ }
2862
+ if (Object.keys(userData).length === 0) {
2863
+ delete object.userData;
2864
+ }
2865
+ }
2866
+ applied = false;
2867
+ };
2868
+ }
2869
+
2761
2870
  // src/functions/load-gltf.ts
2762
2871
  THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
2763
2872
  var EmptyModelError = class extends Error {
@@ -2771,24 +2880,27 @@ async function loadGLTF(url, namespaces, aggressiveImport = false, rootBounds) {
2771
2880
  modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
2772
2881
  const modelData = await modelLoader.loadAsync(url);
2773
2882
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2774
- return traverseThree(
2883
+ const asset = parseScene(
2775
2884
  modelData.scene,
2776
2885
  actualizedNamespaces,
2777
2886
  aggressiveImport,
2778
- rootBounds
2887
+ rootBounds,
2888
+ modelData?.parser?.json
2779
2889
  );
2890
+ return [asset.world, asset.animatables];
2780
2891
  }
2781
2892
  async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, rootBounds) {
2782
2893
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2783
2894
  if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
2784
2895
  const objectUrl = URL.createObjectURL(blob);
2785
2896
  try {
2786
- return await loadGLTF(
2897
+ const asset = await loadGLTFWithBundle(
2787
2898
  objectUrl,
2788
2899
  actualizedNamespaces,
2789
2900
  aggressiveImport,
2790
2901
  rootBounds
2791
2902
  );
2903
+ return [asset.world, asset.animatables];
2792
2904
  } finally {
2793
2905
  URL.revokeObjectURL(objectUrl);
2794
2906
  }
@@ -2802,14 +2914,83 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
2802
2914
  "",
2803
2915
  (gltf) => {
2804
2916
  try {
2805
- resolve(
2806
- traverseThree(
2807
- gltf.scene,
2808
- actualizedNamespaces,
2809
- aggressiveImport,
2810
- rootBounds
2811
- )
2917
+ const asset = parseScene(
2918
+ gltf.scene,
2919
+ actualizedNamespaces,
2920
+ aggressiveImport,
2921
+ rootBounds,
2922
+ gltf?.parser?.json
2812
2923
  );
2924
+ resolve([asset.world, asset.animatables]);
2925
+ } catch (error) {
2926
+ if (error instanceof Error) {
2927
+ reject(error);
2928
+ } else {
2929
+ reject(new Error(String(error)));
2930
+ }
2931
+ }
2932
+ },
2933
+ (error) => {
2934
+ reject(new Error(`Error loading GLTF: ${error.message}`));
2935
+ }
2936
+ );
2937
+ });
2938
+ }
2939
+ function parseScene(scene, namespaces, aggressiveImport, rootBounds, parserJson) {
2940
+ const [world, animatables] = traverseThree(
2941
+ scene,
2942
+ namespaces,
2943
+ aggressiveImport,
2944
+ rootBounds
2945
+ );
2946
+ const bundle = extractVizijBundle(scene, parserJson);
2947
+ return { world, animatables, bundle };
2948
+ }
2949
+ async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds) {
2950
+ const modelLoader = new import_three_stdlib.GLTFLoader();
2951
+ modelLoader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
2952
+ const modelData = await modelLoader.loadAsync(url);
2953
+ const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2954
+ return parseScene(
2955
+ modelData.scene,
2956
+ actualizedNamespaces,
2957
+ aggressiveImport,
2958
+ rootBounds,
2959
+ modelData?.parser?.json
2960
+ );
2961
+ }
2962
+ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = false, rootBounds) {
2963
+ const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2964
+ if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
2965
+ const objectUrl = URL.createObjectURL(blob);
2966
+ try {
2967
+ return await loadGLTFWithBundle(
2968
+ objectUrl,
2969
+ actualizedNamespaces,
2970
+ aggressiveImport,
2971
+ rootBounds
2972
+ );
2973
+ } finally {
2974
+ URL.revokeObjectURL(objectUrl);
2975
+ }
2976
+ }
2977
+ const arrayBuffer = typeof blob.arrayBuffer === "function" ? await blob.arrayBuffer() : await new Response(blob).arrayBuffer();
2978
+ return new Promise((resolve, reject) => {
2979
+ const loader = new import_three_stdlib.GLTFLoader();
2980
+ loader.setDRACOLoader(new import_three_stdlib.DRACOLoader());
2981
+ loader.parse(
2982
+ arrayBuffer,
2983
+ "",
2984
+ (gltf) => {
2985
+ try {
2986
+ const asset = parseScene(
2987
+ gltf.scene,
2988
+ actualizedNamespaces,
2989
+ aggressiveImport,
2990
+ rootBounds,
2991
+ gltf?.parser?.json
2992
+ );
2993
+ resolve(asset);
2813
2994
  } catch (error) {
2814
2995
  if (error instanceof Error) {
2815
2996
  reject(error);
@@ -2858,7 +3039,11 @@ var loadGltfFromBlob = (blob, namespaces) => {
2858
3039
  var import_three_stdlib3 = require("three-stdlib");
2859
3040
  var THREE6 = __toESM(require("three"));
2860
3041
  THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
2861
- function exportScene(data, fileName = "scene.glb") {
3042
+ function exportScene(data, fileNameOrOptions = "scene.glb") {
3043
+ const options = typeof fileNameOrOptions === "string" ? { fileName: fileNameOrOptions } : fileNameOrOptions ?? {};
3044
+ const fileName = options.fileName ?? "scene.glb";
3045
+ const animationClips = Array.isArray(options.animations) ? options.animations.filter(Boolean) : [];
3046
+ const shouldAttachBundle = Boolean(options.bundle);
2862
3047
  const exporter = new import_three_stdlib3.GLTFExporter();
2863
3048
  exporter.register(() => ({
2864
3049
  writeMesh(mesh, meshDef) {
@@ -2868,34 +3053,48 @@ function exportScene(data, fileName = "scene.glb") {
2868
3053
  }
2869
3054
  }
2870
3055
  }));
2871
- exporter.parse(
2872
- data,
2873
- (gltf) => {
2874
- if (!(gltf instanceof ArrayBuffer)) {
2875
- throw new Error("Failed to export scene!");
2876
- }
2877
- const link = document.createElement("a");
2878
- link.href = URL.createObjectURL(
2879
- new Blob([gltf], {
2880
- type: "application/octet-stream"
2881
- })
2882
- );
2883
- const trimmed = fileName.trim();
2884
- const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
2885
- const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
2886
- link.download = downloadName;
2887
- link.click();
2888
- URL.revokeObjectURL(link.href);
2889
- },
2890
- () => {
2891
- },
2892
- {
2893
- trs: true,
2894
- onlyVisible: false,
2895
- binary: true,
2896
- includeCustomExtensions: true
2897
- }
2898
- );
3056
+ const detachBundle = shouldAttachBundle && options.bundle ? applyVizijBundle(data, options.bundle) : () => {
3057
+ };
3058
+ const binary = options.binary ?? true;
3059
+ const exporterOptions = {
3060
+ trs: true,
3061
+ onlyVisible: false,
3062
+ binary,
3063
+ includeCustomExtensions: true
3064
+ };
3065
+ if (animationClips.length > 0) {
3066
+ exporterOptions.animations = animationClips;
3067
+ }
3068
+ try {
3069
+ exporter.parse(
3070
+ data,
3071
+ (gltf) => {
3072
+ detachBundle();
3073
+ if (!(gltf instanceof ArrayBuffer)) {
3074
+ throw new Error("Failed to export scene!");
3075
+ }
3076
+ const link = document.createElement("a");
3077
+ link.href = URL.createObjectURL(
3078
+ new Blob([gltf], {
3079
+ type: "application/octet-stream"
3080
+ })
3081
+ );
3082
+ const trimmed = fileName.trim();
3083
+ const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
3084
+ const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
3085
+ link.download = downloadName;
3086
+ link.click();
3087
+ URL.revokeObjectURL(link.href);
3088
+ },
3089
+ () => {
3090
+ detachBundle();
3091
+ },
3092
+ exporterOptions
3093
+ );
3094
+ } catch (error) {
3095
+ detachBundle();
3096
+ throw error;
3097
+ }
2899
3098
  }
2900
3099
  // Annotate the CommonJS export names for ESM import in node:
2901
3100
  0 && (module.exports = {
@@ -2906,10 +3105,14 @@ function exportScene(data, fileName = "scene.glb") {
2906
3105
  Vizij,
2907
3106
  VizijContext,
2908
3107
  VizijSlice,
3108
+ applyVizijBundle,
2909
3109
  createVizijStore,
2910
3110
  exportScene,
3111
+ extractVizijBundle,
2911
3112
  loadGLTF,
2912
3113
  loadGLTFFromBlob,
3114
+ loadGLTFFromBlobWithBundle,
3115
+ loadGLTFWithBundle,
2913
3116
  loadGltfFromBlob,
2914
3117
  useDefaultVizijStore,
2915
3118
  useFeatures,
package/dist/index.mjs CHANGED
@@ -2526,7 +2526,9 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2526
2526
  });
2527
2527
  const useRobotData = !aggressiveImport || hasRobotData;
2528
2528
  if (useRobotData) {
2529
- group.traverse((child) => {
2529
+ const stack = [group];
2530
+ while (stack.length > 0) {
2531
+ const child = stack.pop();
2530
2532
  if (child.userData?.gltfExtensions?.RobotData) {
2531
2533
  const data = child.userData.gltfExtensions.RobotData;
2532
2534
  applyStoredRenderableNames(child, data);
@@ -2595,7 +2597,10 @@ function traverseThree(group, namespaces, aggressiveImport = false, rootBounds)
2595
2597
  throw new Error(`Unhandled type`);
2596
2598
  }
2597
2599
  }
2598
- });
2600
+ if (child.children) {
2601
+ stack.push(...child.children);
2602
+ }
2603
+ }
2599
2604
  } else {
2600
2605
  const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
2601
2606
  if (!derivedRootBounds) {
@@ -2763,6 +2768,106 @@ function deriveRootBounds(group) {
2763
2768
  };
2764
2769
  }
2765
2770
 
2771
+ // src/functions/vizij-bundle.ts
2772
+ var BUNDLE_KEYS = ["VIZIJ_bundle"];
2773
+ function cloneBundle(value) {
2774
+ return JSON.parse(JSON.stringify(value));
2775
+ }
2776
+ function readExtensionValue(extensionContainer) {
2777
+ for (const key of BUNDLE_KEYS) {
2778
+ if (extensionContainer && Object.prototype.hasOwnProperty.call(extensionContainer, key)) {
2779
+ const value = extensionContainer[key];
2780
+ if (value && typeof value === "object") {
2781
+ return { key, value };
2782
+ }
2783
+ }
2784
+ }
2785
+ return null;
2786
+ }
2787
+ function searchObjectForBundle(object) {
2788
+ const stack = [object];
2789
+ while (stack.length > 0) {
2790
+ const current = stack.pop();
2791
+ const extensions = current?.userData?.gltfExtensions ?? current?.userData?.extensions ?? null;
2792
+ if (extensions && typeof extensions === "object") {
2793
+ const match = readExtensionValue(extensions);
2794
+ if (match) {
2795
+ return cloneBundle(match.value);
2796
+ }
2797
+ }
2798
+ if (current.children && current.children.length > 0) {
2799
+ stack.push(...current.children);
2800
+ }
2801
+ }
2802
+ return null;
2803
+ }
2804
+ function searchParserJsonForBundle(parserJson) {
2805
+ if (!parserJson || typeof parserJson !== "object") {
2806
+ return null;
2807
+ }
2808
+ const nodes = Array.isArray(parserJson.nodes) ? parserJson.nodes : [];
2809
+ for (const node of nodes) {
2810
+ const extensions = node && typeof node === "object" ? node.extensions : null;
2811
+ if (extensions && typeof extensions === "object") {
2812
+ const match = readExtensionValue(extensions);
2813
+ if (match) {
2814
+ return cloneBundle(match.value);
2815
+ }
2816
+ }
2817
+ }
2818
+ const scenes = Array.isArray(parserJson.scenes) ? parserJson.scenes : [];
2819
+ for (const scene of scenes) {
2820
+ const extensions = scene && typeof scene === "object" ? scene.extensions : null;
2821
+ if (extensions && typeof extensions === "object") {
2822
+ const match = readExtensionValue(extensions);
2823
+ if (match) {
2824
+ return cloneBundle(match.value);
2825
+ }
2826
+ }
2827
+ }
2828
+ return null;
2829
+ }
2830
+ function extractVizijBundle(object, parserJson) {
2831
+ const fromObject = searchObjectForBundle(object);
2832
+ if (fromObject) {
2833
+ return fromObject;
2834
+ }
2835
+ const fromParser = searchParserJsonForBundle(parserJson);
2836
+ if (fromParser) {
2837
+ return fromParser;
2838
+ }
2839
+ return null;
2840
+ }
2841
+ function applyVizijBundle(object, bundle) {
2842
+ const userData = object.userData && typeof object.userData === "object" ? object.userData : {};
2843
+ const originalExtensions = userData.gltfExtensions;
2844
+ let applied = false;
2845
+ if (bundle) {
2846
+ userData.gltfExtensions = {
2847
+ ...originalExtensions ?? {},
2848
+ VIZIJ_bundle: bundle
2849
+ };
2850
+ object.userData = userData;
2851
+ applied = true;
2852
+ }
2853
+ return () => {
2854
+ if (!applied) {
2855
+ return;
2856
+ }
2857
+ if (originalExtensions) {
2858
+ userData.gltfExtensions = originalExtensions;
2859
+ } else {
2860
+ if (userData.gltfExtensions) {
2861
+ delete userData.gltfExtensions;
2862
+ }
2863
+ if (Object.keys(userData).length === 0) {
2864
+ delete object.userData;
2865
+ }
2866
+ }
2867
+ applied = false;
2868
+ };
2869
+ }
2870
+
2766
2871
  // src/functions/load-gltf.ts
2767
2872
  THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
2768
2873
  var EmptyModelError = class extends Error {
@@ -2776,24 +2881,27 @@ async function loadGLTF(url, namespaces, aggressiveImport = false, rootBounds) {
2776
2881
  modelLoader.setDRACOLoader(new DRACOLoader());
2777
2882
  const modelData = await modelLoader.loadAsync(url);
2778
2883
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2779
- return traverseThree(
2884
+ const asset = parseScene(
2780
2885
  modelData.scene,
2781
2886
  actualizedNamespaces,
2782
2887
  aggressiveImport,
2783
- rootBounds
2888
+ rootBounds,
2889
+ modelData?.parser?.json
2784
2890
  );
2891
+ return [asset.world, asset.animatables];
2785
2892
  }
2786
2893
  async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, rootBounds) {
2787
2894
  const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2788
2895
  if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
2789
2896
  const objectUrl = URL.createObjectURL(blob);
2790
2897
  try {
2791
- return await loadGLTF(
2898
+ const asset = await loadGLTFWithBundle(
2792
2899
  objectUrl,
2793
2900
  actualizedNamespaces,
2794
2901
  aggressiveImport,
2795
2902
  rootBounds
2796
2903
  );
2904
+ return [asset.world, asset.animatables];
2797
2905
  } finally {
2798
2906
  URL.revokeObjectURL(objectUrl);
2799
2907
  }
@@ -2807,14 +2915,83 @@ async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, root
2807
2915
  "",
2808
2916
  (gltf) => {
2809
2917
  try {
2810
- resolve(
2811
- traverseThree(
2812
- gltf.scene,
2813
- actualizedNamespaces,
2814
- aggressiveImport,
2815
- rootBounds
2816
- )
2918
+ const asset = parseScene(
2919
+ gltf.scene,
2920
+ actualizedNamespaces,
2921
+ aggressiveImport,
2922
+ rootBounds,
2923
+ gltf?.parser?.json
2817
2924
  );
2925
+ resolve([asset.world, asset.animatables]);
2926
+ } catch (error) {
2927
+ if (error instanceof Error) {
2928
+ reject(error);
2929
+ } else {
2930
+ reject(new Error(String(error)));
2931
+ }
2932
+ }
2933
+ },
2934
+ (error) => {
2935
+ reject(new Error(`Error loading GLTF: ${error.message}`));
2936
+ }
2937
+ );
2938
+ });
2939
+ }
2940
+ function parseScene(scene, namespaces, aggressiveImport, rootBounds, parserJson) {
2941
+ const [world, animatables] = traverseThree(
2942
+ scene,
2943
+ namespaces,
2944
+ aggressiveImport,
2945
+ rootBounds
2946
+ );
2947
+ const bundle = extractVizijBundle(scene, parserJson);
2948
+ return { world, animatables, bundle };
2949
+ }
2950
+ async function loadGLTFWithBundle(url, namespaces, aggressiveImport = false, rootBounds) {
2951
+ const modelLoader = new GLTFLoader();
2952
+ modelLoader.setDRACOLoader(new DRACOLoader());
2953
+ const modelData = await modelLoader.loadAsync(url);
2954
+ const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2955
+ return parseScene(
2956
+ modelData.scene,
2957
+ actualizedNamespaces,
2958
+ aggressiveImport,
2959
+ rootBounds,
2960
+ modelData?.parser?.json
2961
+ );
2962
+ }
2963
+ async function loadGLTFFromBlobWithBundle(blob, namespaces, aggressiveImport = false, rootBounds) {
2964
+ const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
2965
+ if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
2966
+ const objectUrl = URL.createObjectURL(blob);
2967
+ try {
2968
+ return await loadGLTFWithBundle(
2969
+ objectUrl,
2970
+ actualizedNamespaces,
2971
+ aggressiveImport,
2972
+ rootBounds
2973
+ );
2974
+ } finally {
2975
+ URL.revokeObjectURL(objectUrl);
2976
+ }
2977
+ }
2978
+ const arrayBuffer = typeof blob.arrayBuffer === "function" ? await blob.arrayBuffer() : await new Response(blob).arrayBuffer();
2979
+ return new Promise((resolve, reject) => {
2980
+ const loader = new GLTFLoader();
2981
+ loader.setDRACOLoader(new DRACOLoader());
2982
+ loader.parse(
2983
+ arrayBuffer,
2984
+ "",
2985
+ (gltf) => {
2986
+ try {
2987
+ const asset = parseScene(
2988
+ gltf.scene,
2989
+ actualizedNamespaces,
2990
+ aggressiveImport,
2991
+ rootBounds,
2992
+ gltf?.parser?.json
2993
+ );
2994
+ resolve(asset);
2818
2995
  } catch (error) {
2819
2996
  if (error instanceof Error) {
2820
2997
  reject(error);
@@ -2863,7 +3040,11 @@ var loadGltfFromBlob = (blob, namespaces) => {
2863
3040
  import { GLTFExporter } from "three-stdlib";
2864
3041
  import * as THREE6 from "three";
2865
3042
  THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
2866
- function exportScene(data, fileName = "scene.glb") {
3043
+ function exportScene(data, fileNameOrOptions = "scene.glb") {
3044
+ const options = typeof fileNameOrOptions === "string" ? { fileName: fileNameOrOptions } : fileNameOrOptions ?? {};
3045
+ const fileName = options.fileName ?? "scene.glb";
3046
+ const animationClips = Array.isArray(options.animations) ? options.animations.filter(Boolean) : [];
3047
+ const shouldAttachBundle = Boolean(options.bundle);
2867
3048
  const exporter = new GLTFExporter();
2868
3049
  exporter.register(() => ({
2869
3050
  writeMesh(mesh, meshDef) {
@@ -2873,34 +3054,48 @@ function exportScene(data, fileName = "scene.glb") {
2873
3054
  }
2874
3055
  }
2875
3056
  }));
2876
- exporter.parse(
2877
- data,
2878
- (gltf) => {
2879
- if (!(gltf instanceof ArrayBuffer)) {
2880
- throw new Error("Failed to export scene!");
2881
- }
2882
- const link = document.createElement("a");
2883
- link.href = URL.createObjectURL(
2884
- new Blob([gltf], {
2885
- type: "application/octet-stream"
2886
- })
2887
- );
2888
- const trimmed = fileName.trim();
2889
- const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
2890
- const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
2891
- link.download = downloadName;
2892
- link.click();
2893
- URL.revokeObjectURL(link.href);
2894
- },
2895
- () => {
2896
- },
2897
- {
2898
- trs: true,
2899
- onlyVisible: false,
2900
- binary: true,
2901
- includeCustomExtensions: true
2902
- }
2903
- );
3057
+ const detachBundle = shouldAttachBundle && options.bundle ? applyVizijBundle(data, options.bundle) : () => {
3058
+ };
3059
+ const binary = options.binary ?? true;
3060
+ const exporterOptions = {
3061
+ trs: true,
3062
+ onlyVisible: false,
3063
+ binary,
3064
+ includeCustomExtensions: true
3065
+ };
3066
+ if (animationClips.length > 0) {
3067
+ exporterOptions.animations = animationClips;
3068
+ }
3069
+ try {
3070
+ exporter.parse(
3071
+ data,
3072
+ (gltf) => {
3073
+ detachBundle();
3074
+ if (!(gltf instanceof ArrayBuffer)) {
3075
+ throw new Error("Failed to export scene!");
3076
+ }
3077
+ const link = document.createElement("a");
3078
+ link.href = URL.createObjectURL(
3079
+ new Blob([gltf], {
3080
+ type: "application/octet-stream"
3081
+ })
3082
+ );
3083
+ const trimmed = fileName.trim();
3084
+ const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
3085
+ const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
3086
+ link.download = downloadName;
3087
+ link.click();
3088
+ URL.revokeObjectURL(link.href);
3089
+ },
3090
+ () => {
3091
+ detachBundle();
3092
+ },
3093
+ exporterOptions
3094
+ );
3095
+ } catch (error) {
3096
+ detachBundle();
3097
+ throw error;
3098
+ }
2904
3099
  }
2905
3100
  export {
2906
3101
  Controller,
@@ -2910,10 +3105,14 @@ export {
2910
3105
  Vizij,
2911
3106
  VizijContext,
2912
3107
  VizijSlice,
3108
+ applyVizijBundle,
2913
3109
  createVizijStore,
2914
3110
  exportScene,
3111
+ extractVizijBundle,
2915
3112
  loadGLTF,
2916
3113
  loadGLTFFromBlob,
3114
+ loadGLTFFromBlobWithBundle,
3115
+ loadGLTFWithBundle,
2917
3116
  loadGltfFromBlob,
2918
3117
  useDefaultVizijStore,
2919
3118
  useFeatures,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vizij/render",
3
3
  "description": "Higher-level visualization and interaction components for robot and ai faces.",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "lint:fix": "pnpm --filter \"$npm_package_name\" exec eslint --ext .js,.jsx,.ts,.tsx --fix -- .",
32
32
  "prettier:check": "prettier --check .",
33
33
  "prettier:write": "prettier --write .",
34
- "test": "vitest --run --passWithNoTests",
34
+ "test": "node --loader ./tests/node-ts-loader.mjs --test tests/*.node-test.mjs",
35
35
  "clean": "rm -rf dist .turbo coverage tsconfig.tsbuildinfo",
36
36
  "reset": "rm -rf node_modules",
37
37
  "reset:hard": "pnpm run reset && rm -f pnpm-lock.yaml package-lock.json yarn.lock",
@@ -72,8 +72,7 @@
72
72
  "zustand": "^5.0.2",
73
73
  "tsup": "^8.0.1",
74
74
  "typescript": "^5.5.0",
75
- "prettier": "^3.4.2",
76
- "vitest": "^3.2.4"
75
+ "prettier": "^3.4.2"
77
76
  },
78
77
  "size-limit": [
79
78
  {