@liveroom-tech/react-immersive 5.0.2 → 5.2.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.
Files changed (45) hide show
  1. package/README.md +1030 -59
  2. package/dist/binding-builder.css +1 -1
  3. package/dist/binding-builder.js +17 -12
  4. package/dist/binding-builder.mjs +1 -1
  5. package/dist/chunk-4NLJSD3S.mjs +1 -0
  6. package/dist/chunk-B7REIGDG.mjs +1 -0
  7. package/dist/chunk-HYFJ5DYJ.mjs +1 -0
  8. package/dist/chunk-QC3NT76G.mjs +3 -0
  9. package/dist/chunk-T3M3BQ7H.mjs +32 -0
  10. package/dist/chunk-ZPK67UG7.mjs +1 -0
  11. package/dist/hosted.css +1 -1
  12. package/dist/hosted.d.mts +4 -3
  13. package/dist/hosted.d.ts +4 -3
  14. package/dist/hosted.js +16 -11
  15. package/dist/hosted.mjs +1 -1
  16. package/dist/index.css +1 -1
  17. package/dist/index.d.mts +85 -10
  18. package/dist/index.d.ts +85 -10
  19. package/dist/index.js +15 -10
  20. package/dist/index.mjs +1 -1
  21. package/dist/materialVariants-DnZe9eMC.d.mts +309 -0
  22. package/dist/materialVariants-DnZe9eMC.d.ts +309 -0
  23. package/dist/modelInfo-BYebMPdZ.d.mts +43 -0
  24. package/dist/modelInfo-XfUQO7C_.d.ts +43 -0
  25. package/dist/{ModelViewer-CbbJT85n.d.mts → modelViewerProps-CIZ2R_Fv.d.ts} +164 -43
  26. package/dist/{ModelViewer-DOexHkJC.d.ts → modelViewerProps-DoRiTZMn.d.mts} +164 -43
  27. package/dist/{objectCatalog-5W8n5xNQ.d.ts → rendererBackend-QwMzISTn.d.mts} +304 -5
  28. package/dist/{objectCatalog-5W8n5xNQ.d.mts → rendererBackend-QwMzISTn.d.ts} +304 -5
  29. package/dist/simple-model-viewer.css +1 -1
  30. package/dist/simple-model-viewer.d.mts +32 -4
  31. package/dist/simple-model-viewer.d.ts +32 -4
  32. package/dist/simple-model-viewer.js +3 -3
  33. package/dist/simple-model-viewer.mjs +1 -1
  34. package/dist/utils.d.mts +5 -4
  35. package/dist/utils.d.ts +5 -4
  36. package/dist/utils.js +1 -1
  37. package/dist/utils.mjs +1 -1
  38. package/package.json +2 -1
  39. package/dist/chunk-3GX7DOGS.mjs +0 -3
  40. package/dist/chunk-AH5NAZWX.mjs +0 -27
  41. package/dist/chunk-BGYK4Y2Y.mjs +0 -1
  42. package/dist/chunk-WFEYRUZC.mjs +0 -1
  43. package/dist/chunk-XTLSAYAQ.mjs +0 -1
  44. package/dist/modelInfo-CEmmvVCH.d.mts +0 -17
  45. package/dist/modelInfo-CEmmvVCH.d.ts +0 -17
@@ -104,6 +104,12 @@ type SceneModelConfig = {
104
104
  shading: "lit" | "unlit" | "normals";
105
105
  autoRotate?: boolean;
106
106
  autoRotateSpeed?: number;
107
+ /**
108
+ * The glTF material variants (KHR_materials_variants) to show, by name: at
109
+ * most one from each option group (see `MaterialVariantGroup`). Empty, or
110
+ * left out, for the model's default materials.
111
+ */
112
+ activeVariants?: string[];
107
113
  };
108
114
  type SceneCameraConfig = {
109
115
  fieldOfView: number;
@@ -149,7 +155,7 @@ type SceneGroundShadowConfig = {
149
155
  height: number;
150
156
  size: number;
151
157
  };
152
- declare const ANIMATION_LOOP_MODES: readonly ["repeat", "once"];
158
+ declare const ANIMATION_LOOP_MODES: readonly ["repeat", "once", "ping-pong"];
153
159
  type AnimationLoopMode = (typeof ANIMATION_LOOP_MODES)[number];
154
160
  type SceneAnimationClipConfig = {
155
161
  sourceName: string;
@@ -355,6 +361,22 @@ type ScenePostProcessingConfig = {
355
361
  rampInvert: boolean;
356
362
  };
357
363
  };
364
+ type SceneAnnotationCameraView = {
365
+ position: [number, number, number];
366
+ target: [number, number, number];
367
+ azimuthAngle?: number;
368
+ polarAngle?: number;
369
+ fov?: number | null;
370
+ zoom?: number;
371
+ };
372
+ type SceneAnnotationImage = {
373
+ src: string;
374
+ alt?: string;
375
+ };
376
+ type SceneAnnotationLink = {
377
+ href: string;
378
+ label: string;
379
+ };
358
380
  type SceneAnnotationMarker = {
359
381
  id: number;
360
382
  worldPosition: [number, number, number];
@@ -364,6 +386,14 @@ type SceneAnnotationMarker = {
364
386
  anchorLocalPosition?: [number, number, number];
365
387
  title: string;
366
388
  description: string;
389
+ /** World-space camera framing restored on selection and during tours. */
390
+ cameraView?: SceneAnnotationCameraView;
391
+ images?: SceneAnnotationImage[];
392
+ links?: SceneAnnotationLink[];
393
+ category?: string;
394
+ /** Inclusive camera-to-marker distance bounds, in scene units. */
395
+ minDistance?: number;
396
+ maxDistance?: number;
367
397
  };
368
398
  type SceneCinematicWaypoint = {
369
399
  position: [number, number, number];
@@ -384,6 +414,73 @@ type SceneCinematicConfig = {
384
414
  loop: boolean;
385
415
  autoPlay: boolean;
386
416
  };
417
+ declare const SECTION_AXES: readonly ["x", "y", "z"];
418
+ type SceneSectionAxis = (typeof SECTION_AXES)[number];
419
+ type SceneSectionConfig = {
420
+ enabled: boolean;
421
+ /** The world axis the cut runs across; `"y"` gives a floor-plan cut. */
422
+ axis: SceneSectionAxis;
423
+ /**
424
+ * Where the cut is along the axis, as a share of the model's size: 0 at
425
+ * its minimum, 1 at its maximum.
426
+ */
427
+ position: number;
428
+ /**
429
+ * Keep the part above the cut (the higher side along the axis) instead of
430
+ * the part below it.
431
+ */
432
+ flip: boolean;
433
+ };
434
+ declare const DEFAULT_SECTION_CONFIG: SceneSectionConfig;
435
+ /**
436
+ * What an object's data must look like for a rule to apply to it. A list
437
+ * applies when every condition in it matches.
438
+ */
439
+ type SceneStyleCondition = {
440
+ /** A key of `binding.metrics`. */
441
+ metric: string;
442
+ /** Applies when the value is greater than this. */
443
+ above?: number;
444
+ /** Applies when the value is less than this. */
445
+ below?: number;
446
+ } | {
447
+ status: ObjectBindingStatus | ObjectBindingStatus[];
448
+ } | {
449
+ /** A key of `binding.metadata`; dots reach into nested objects. */
450
+ metadata: string;
451
+ equals: string | number | boolean | null;
452
+ };
453
+ type SceneStyleColorScale = {
454
+ /** A key of `binding.metrics`. */
455
+ metric: string;
456
+ /**
457
+ * `[value, "#rrggbb"]` pairs. Values between two stops blend their colours;
458
+ * values outside the stops take the nearest one.
459
+ */
460
+ stops: [number, string][];
461
+ /** The material colour it sets. Default `"baseColor"`. */
462
+ channel?: "baseColor" | "emissive";
463
+ };
464
+ type SceneStylePulse = {
465
+ /** The glow colour at the pulse's peak. Default `"#ff3b30"`. */
466
+ color?: string;
467
+ /** Pulses per second. Default `1`. */
468
+ speed?: number;
469
+ };
470
+ type SceneStyleRule = {
471
+ id: string;
472
+ label?: string;
473
+ /** The objects the rule considers; every bound object when left out. */
474
+ target?: ObjectBindingTarget;
475
+ /** When the rule applies; always, when left out. */
476
+ when?: SceneStyleCondition | SceneStyleCondition[];
477
+ /** Material overrides, over the object's own `style.material`. */
478
+ material?: ObjectBindingMaterial;
479
+ /** A colour taken from a metric's value. */
480
+ colorScale?: SceneStyleColorScale;
481
+ /** A glow that fades in and out, to draw attention. */
482
+ pulse?: boolean | SceneStylePulse;
483
+ };
387
484
  type SceneConfig = {
388
485
  model: SceneModelConfig;
389
486
  camera: SceneCameraConfig;
@@ -397,6 +494,13 @@ type SceneConfig = {
397
494
  animations: SceneAnimationConfig;
398
495
  annotations: SceneAnnotationMarker[];
399
496
  cinematic?: SceneCinematicConfig;
497
+ /**
498
+ * Data-driven looks for bound objects, applied in order, so a later rule
499
+ * wins over an earlier one where both set something.
500
+ */
501
+ styleRules?: SceneStyleRule[];
502
+ /** A cut-away view across the model. Off when left out. */
503
+ section?: SceneSectionConfig;
400
504
  };
401
505
  declare const DEFAULT_SCENE_CONFIG: SceneConfig;
402
506
 
@@ -407,6 +511,146 @@ type SceneConfigPatch = DeepPartial<SceneConfig>;
407
511
  */
408
512
  declare function patchSceneConfig(sceneConfig: SceneConfig, patch: SceneConfigPatch): SceneConfig;
409
513
 
514
+ type AnimationClipInfo = {
515
+ sourceName: string;
516
+ duration: number;
517
+ };
518
+ /** How `play` starts a clip. */
519
+ type AnimationPlayOptions = {
520
+ /**
521
+ * Keep the clips already playing, instead of stopping them, so several
522
+ * clips run at once (a left and a right door, say). Default `false`.
523
+ */
524
+ alongside?: boolean;
525
+ /**
526
+ * Seconds to fade the clip in, and to fade out the clips it replaces, for a
527
+ * smooth blend between them. Default `0`.
528
+ */
529
+ fade?: number;
530
+ /**
531
+ * Play backwards, from wherever the clip is, or from its end. Plays a
532
+ * "door opens" clip as the door closing. Default `false`.
533
+ */
534
+ reverse?: boolean;
535
+ };
536
+ /**
537
+ * `"finished"`: a play-once clip that reached its end and holds its last pose
538
+ * until it's stopped or played again.
539
+ */
540
+ type AnimationClipStatus = "playing" | "paused" | "finished";
541
+ /** One clip that is playing, paused, or holding its finished pose. */
542
+ type AnimationClipState = {
543
+ clip: string;
544
+ status: AnimationClipStatus;
545
+ /** Position in the clip, in seconds. */
546
+ time: number;
547
+ /** Length of the clip, in seconds. */
548
+ duration: number;
549
+ speed: number;
550
+ reverse: boolean;
551
+ };
552
+ type AnimationPlaybackState = {
553
+ /** The clip played most recently, while it's active. */
554
+ currentClip: string | null;
555
+ /** Whether any clip is playing. */
556
+ isPlaying: boolean;
557
+ speed: number;
558
+ time: number;
559
+ duration: number;
560
+ /** Every active clip, most recently played last. */
561
+ active: AnimationClipState[];
562
+ };
563
+ /**
564
+ * Leave out `clip` to act on every active clip; pass one to act on that clip
565
+ * alone.
566
+ */
567
+ type AnimationControls = {
568
+ clips: string[];
569
+ clipDetails?: AnimationClipInfo[];
570
+ play: (clipName: string, options?: AnimationPlayOptions) => void;
571
+ pause: (clipName?: string) => void;
572
+ /** Stops and resets the pose. */
573
+ stop: (clipName?: string) => void;
574
+ /** Without a clip, also the speed of clips played later without their own. */
575
+ setSpeed: (speed: number, clipName?: string) => void;
576
+ seek?: (time: number, clipName?: string) => void;
577
+ getState?: () => AnimationPlaybackState;
578
+ subscribe?: (listener: (state: AnimationPlaybackState) => void) => () => void;
579
+ };
580
+ type ViewerAnimationsProps = {
581
+ clips: string[];
582
+ clipDetails: AnimationClipInfo[];
583
+ currentClip: string | null;
584
+ isPlaying: boolean;
585
+ speed: number;
586
+ time: number;
587
+ duration: number;
588
+ /** Every active clip, most recently played last. */
589
+ active: AnimationClipState[];
590
+ play: (clipName: string, options?: AnimationPlayOptions) => void;
591
+ pause: (clipName?: string) => void;
592
+ stop: (clipName?: string) => void;
593
+ setSpeed: (speed: number, clipName?: string) => void;
594
+ seek: (time: number, clipName?: string) => void;
595
+ handleAnimationsReady: (controls: AnimationControls) => void;
596
+ };
597
+ declare function useViewerAnimations(): ViewerAnimationsProps;
598
+
599
+ /** A glow cycling on one object, from its style rules. */
600
+ type StyledObjectPulse = {
601
+ color: string;
602
+ /** Pulses per second. */
603
+ speed: number;
604
+ };
605
+ type StyledObjectBindings = {
606
+ /**
607
+ * The bindings as the rules style them. A binding no rule changed is the
608
+ * same object as the one passed in.
609
+ */
610
+ bindings: Record<string, ObjectBinding>;
611
+ /** Pulsing objects, by binding key. */
612
+ pulses: Record<string, StyledObjectPulse>;
613
+ /** The binding a styled one came from (itself when no rule changed it). */
614
+ sourceOf: (binding: ObjectBinding) => ObjectBinding;
615
+ };
616
+ /**
617
+ * The colour a colour scale gives a value: a blend of the two stops around it,
618
+ * or the nearest stop outside them. Null when there's no usable stop.
619
+ */
620
+ declare function colorScaleValue(stops: SceneStyleColorScale["stops"], value: number): string | null;
621
+ /**
622
+ * Styles bindings with scene style rules, for rendering. Rules apply in order
623
+ * over each object's own `style.material`, so a later rule wins where two set
624
+ * the same thing. The bindings passed in are never changed.
625
+ */
626
+ declare function applyStyleRules(bindings: Record<string, ObjectBinding>, rules: readonly SceneStyleRule[] | undefined): StyledObjectBindings;
627
+
628
+ /**
629
+ * Objects to focus on. Everything else is ghosted (drawn faintly) or hidden
630
+ * until the isolation is cleared, without changing any binding.
631
+ */
632
+ type ViewerIsolation = {
633
+ /** The objects that stay: an id, a list of ids, `{ group }`, `{ tag }`, or `{ type }`. */
634
+ target: ObjectBindingTarget;
635
+ /** What happens to everything else. Default `"ghost"`. */
636
+ mode?: "ghost" | "hide";
637
+ /** How visible ghosted objects are, 0–1. Default `0.12`. */
638
+ ghostOpacity?: number;
639
+ };
640
+ type IsolatedObjectBindings = StyledObjectBindings & {
641
+ /** Binding keys that are ghosted or hidden. Pointer picking ignores them. */
642
+ setAside: Set<string>;
643
+ };
644
+ /** The binding keys an isolation keeps; empty when it keeps nothing. */
645
+ declare function isolatedBindingKeys(bindings: Record<string, ObjectBinding>, isolation: ViewerIsolation | null | undefined): Set<string>;
646
+ /**
647
+ * Ghosts or hides every object an isolation doesn't keep, for rendering. The
648
+ * bindings passed in are never changed. An isolation that keeps nothing (a
649
+ * target matching no object) changes nothing either, rather than fading out
650
+ * the whole model.
651
+ */
652
+ declare function applyIsolation(styled: StyledObjectBindings, isolation: ViewerIsolation | null | undefined): IsolatedObjectBindings;
653
+
410
654
  type ObjectBindingsActionEffect = {
411
655
  target: ObjectBindingTarget;
412
656
  objectBindings: ObjectBindingPatch;
@@ -437,24 +681,36 @@ type CameraAction = {
437
681
  type CameraActionEffect = {
438
682
  camera: CameraAction;
439
683
  };
440
- type AnimationAction = {
684
+ /**
685
+ * `play` replaces the clips already playing unless `alongside` is set. The
686
+ * others act on `clip`, or on every active clip when it's left out.
687
+ */
688
+ type AnimationAction = ({
441
689
  type: "play";
442
690
  clip: string;
443
- } | {
691
+ } & AnimationPlayOptions) | {
444
692
  type: "pause";
693
+ clip?: string;
445
694
  } | {
446
695
  type: "stop";
696
+ clip?: string;
447
697
  } | {
448
698
  type: "set-speed";
449
699
  speed: number;
700
+ clip?: string;
450
701
  } | {
451
702
  type: "seek";
452
703
  time: number;
704
+ clip?: string;
453
705
  };
454
706
  type AnimationActionEffect = {
455
707
  animation: AnimationAction;
456
708
  };
457
- type ObjectActionEffect = ObjectBindingsActionEffect | SceneConfigActionEffect | VisibilityActionEffect | CameraActionEffect | AnimationActionEffect;
709
+ /** Isolates objects, or with `null` shows everything again. */
710
+ type IsolationActionEffect = {
711
+ isolation: ViewerIsolation | null;
712
+ };
713
+ type ObjectActionEffect = IsolationActionEffect | ObjectBindingsActionEffect | SceneConfigActionEffect | VisibilityActionEffect | CameraActionEffect | AnimationActionEffect;
458
714
 
459
715
  type ObjectBindingAction = {
460
716
  id: "change-color" | "apply-texture" | "toggle-visibility" | string;
@@ -549,6 +805,33 @@ type ObjectBindingCameraState = {
549
805
  fov?: number;
550
806
  zoom?: number;
551
807
  };
808
+ /**
809
+ * An image or a line of text printed onto the object's surface: a logo, a
810
+ * label, a personalisation.
811
+ */
812
+ type ObjectBindingDecal = {
813
+ id: string;
814
+ /** The image to show: a URL or public path. */
815
+ image?: string;
816
+ /** Text to show, when there's no image. */
817
+ text?: string;
818
+ /** Text colour. Default `"#ffffff"`. */
819
+ color?: string;
820
+ /** CSS font family for the text. Default `"sans-serif"`. */
821
+ fontFamily?: string;
822
+ /** Default `"bold"`. */
823
+ fontWeight?: "normal" | "bold";
824
+ /** Where it sits on the object, in the object's own (local) space. */
825
+ position: [number, number, number];
826
+ /** The way the surface faces there, in the object's own space. */
827
+ normal: [number, number, number];
828
+ /** Width, in model units; the height follows the image or text's shape. */
829
+ size: number;
830
+ /** A turn around the surface normal, in radians. Default `0`. */
831
+ rotation?: number;
832
+ /** 0–1. Default `1`. */
833
+ opacity?: number;
834
+ };
552
835
  type ObjectBindingStyle = {
553
836
  material?: ObjectBindingMaterial;
554
837
  };
@@ -566,6 +849,8 @@ type ObjectBinding = {
566
849
  hoverable?: boolean;
567
850
  transform?: ObjectBindingTransform;
568
851
  style?: ObjectBindingStyle;
852
+ /** Images and text printed on the object's surface. */
853
+ decals?: ObjectBindingDecal[];
569
854
  actions?: ObjectBindingAction[];
570
855
  metrics?: Record<string, number>;
571
856
  metadata?: Record<string, unknown>;
@@ -578,6 +863,12 @@ type ObjectActionEvent = {
578
863
  screenX?: number;
579
864
  screenY?: number;
580
865
  };
866
+ /** A point on an object's surface, in the object's own (local) space. */
867
+ type ObjectSurfacePoint = {
868
+ position: [number, number, number];
869
+ /** The way the surface faces at that point. */
870
+ normal: [number, number, number];
871
+ };
581
872
  /** A double-click, right-click, or long-press on a model object. */
582
873
  type ObjectPointerEvent = {
583
874
  binding: ObjectBinding;
@@ -586,6 +877,11 @@ type ObjectPointerEvent = {
586
877
  clientY: number;
587
878
  /** `"mouse"`, `"pen"`, or `"touch"`. */
588
879
  pointerType: string;
880
+ /**
881
+ * Where on the object the pointer was, e.g. to place a decal there. Not
882
+ * reported for streamed (3D Tiles) models.
883
+ */
884
+ surface?: ObjectSurfacePoint;
589
885
  };
590
886
 
591
887
  /** One bindable object in a model, as reported by `onObjectCatalog`. */
@@ -601,4 +897,7 @@ type ViewerObjectInfo = {
601
897
  maximum: [number, number, number];
602
898
  };
603
899
 
604
- export { type SceneWireframeConfig as $, type AnimationAction as A, type BindingEdit as B, type CameraAction as C, DEFAULT_SCENE_CONFIG as D, type EnvironmentPreset as E, type SceneCameraConfig as F, type GlitchModeName as G, type SceneCinematicConfig as H, type SceneCinematicWaypoint as I, type SceneConfig as J, type KernelSizeName as K, type SceneConfigActionEffect as L, MATERIAL_BLENDING_MODES as M, type SceneConfigPatch as N, type ObjectActionEffect as O, type SceneEnvironmentConfig as P, type SceneGroundShadowConfig as Q, type RampTypeName as R, type SceneAnimationClipConfig as S, type SceneLight as T, type SceneLightAttachment as U, type SceneLightType as V, type SceneLightingConfig as W, type SceneModelConfig as X, type SceneModelRenderer as Y, type ScenePostProcessingConfig as Z, type SceneShadowConfig as _, type AnimationActionEffect as a, type ViewerCameraConfig as a0, type ViewerCameraState as a1, type ViewerObjectInfo as a2, type VisibilityActionEffect as a3, material as a4, patchBindings as a5, patchSceneConfig as a6, selectBindingKeys as a7, selectBindings as a8, type AnimationLoopMode as b, type CameraActionEffect as c, type DeepPartial as d, MATERIAL_SIDES as e, type ObjectActionEvent as f, type ObjectBinding as g, type ObjectBindingAction as h, type ObjectBindingCameraState as i, type ObjectBindingMaterial as j, type ObjectBindingMaterialBlendingMode as k, type ObjectBindingMaterialSide as l, type ObjectBindingPatch as m, type ObjectBindingSelector as n, type ObjectBindingStatus as o, type ObjectBindingStyle as p, type ObjectBindingTarget as q, type ObjectBindingTextureChannel as r, type ObjectBindingTextureSlot as s, type ObjectBindingTransform as t, type ObjectBindingType as u, type ObjectBindingsActionEffect as v, type ObjectPointerEvent as w, type SceneAnimationConfig as x, type SceneAnnotationMarker as y, type SceneBackgroundConfig as z };
900
+ /** Which three.js renderer draws a viewer. */
901
+ type RendererBackend = "webgl" | "webgpu";
902
+
903
+ export { type SceneCinematicConfig as $, type AnimationAction as A, type BindingEdit as B, type CameraAction as C, DEFAULT_SCENE_CONFIG as D, type EnvironmentPreset as E, type ObjectBindingTextureChannel as F, type GlitchModeName as G, type ObjectBindingTextureSlot as H, type IsolatedObjectBindings as I, type ObjectBindingTransform as J, type KernelSizeName as K, type ObjectBindingType as L, MATERIAL_BLENDING_MODES as M, type ObjectBindingsActionEffect as N, type ObjectActionEffect as O, type ObjectPointerEvent as P, type ObjectSurfacePoint as Q, type RampTypeName as R, type RendererBackend as S, type SceneAnimationClipConfig as T, type SceneAnimationConfig as U, type SceneAnnotationCameraView as V, type SceneAnnotationImage as W, type SceneAnnotationLink as X, type SceneAnnotationMarker as Y, type SceneBackgroundConfig as Z, type SceneCameraConfig as _, type AnimationActionEffect as a, type SceneCinematicWaypoint as a0, type SceneConfig as a1, type SceneConfigActionEffect as a2, type SceneConfigPatch as a3, type SceneEnvironmentConfig as a4, type SceneGroundShadowConfig as a5, type SceneLight as a6, type SceneLightAttachment as a7, type SceneLightType as a8, type SceneLightingConfig as a9, selectBindingKeys as aA, selectBindings as aB, useViewerAnimations as aC, type SceneModelConfig as aa, type SceneModelRenderer as ab, type ScenePostProcessingConfig as ac, type SceneSectionAxis as ad, type SceneSectionConfig as ae, type SceneShadowConfig as af, type SceneStyleColorScale as ag, type SceneStyleCondition as ah, type SceneStylePulse as ai, type SceneStyleRule as aj, type SceneWireframeConfig as ak, type StyledObjectBindings as al, type StyledObjectPulse as am, type ViewerAnimationsProps as an, type ViewerCameraConfig as ao, type ViewerCameraState as ap, type ViewerIsolation as aq, type ViewerObjectInfo as ar, type VisibilityActionEffect as as, applyIsolation as at, applyStyleRules as au, colorScaleValue as av, isolatedBindingKeys as aw, material as ax, patchBindings as ay, patchSceneConfig as az, type AnimationClipInfo as b, type AnimationClipState as c, type AnimationClipStatus as d, type AnimationControls as e, type AnimationLoopMode as f, type AnimationPlayOptions as g, type AnimationPlaybackState as h, type CameraActionEffect as i, DEFAULT_SECTION_CONFIG as j, type DeepPartial as k, type IsolationActionEffect as l, MATERIAL_SIDES as m, type ObjectActionEvent as n, type ObjectBinding as o, type ObjectBindingAction as p, type ObjectBindingCameraState as q, type ObjectBindingDecal as r, type ObjectBindingMaterial as s, type ObjectBindingMaterialBlendingMode as t, type ObjectBindingMaterialSide as u, type ObjectBindingPatch as v, type ObjectBindingSelector as w, type ObjectBindingStatus as x, type ObjectBindingStyle as y, type ObjectBindingTarget as z };
@@ -1 +1 @@
1
- .bb-loadingOverlay{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;border-radius:14px;background:#f8fafcd1;backdrop-filter:blur(2px)}.bb-loadingSpinner{width:40px;height:40px;border-radius:999px;border:3px solid rgba(148,163,184,.3);border-top-color:#2563eb;animation:bb-spin .8s linear infinite}.bb-loadingProgressRow{display:flex;align-items:center;gap:8px;width:100%;max-width:240px}.bb-loadingProgress{flex:1;height:6px;border-radius:999px;background:#94a3b84d;overflow:hidden}.bb-loadingProgress__bar{height:100%;border-radius:inherit;background:#2563eb;transition:width .15s ease-out}.bb-loadingProgress__label{flex-shrink:0;min-width:34px;text-align:right;color:#475569;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.bb-loadingMessage{max-width:320px;color:#475569;font-size:13px;font-weight:600;line-height:1.5}:root{--ri-styles-simple-model-viewer: 1}.smv-root{--smv-panel-font-family: Inter, "Segoe UI", Helvetica, Arial, sans-serif;--smv-panel-bg: #141824;--smv-panel-bg-strong: #1a1f2e;--smv-panel-bg-strong-hover: #252b3b;--smv-panel-border: rgba(255, 255, 255, .06);--smv-panel-border-strong: rgba(255, 255, 255, .08);--smv-panel-border-active: rgba(255, 255, 255, .18);--smv-panel-shadow-left: 4px 0 24px rgba(0, 0, 0, .35);--smv-panel-shadow-right: -4px 0 24px rgba(0, 0, 0, .35);--smv-panel-text: #f1f5f9;--smv-panel-text-emphasis: #ffffff;--smv-panel-text-muted: #94a3b8;--smv-panel-text-subtle: #64748b;--smv-panel-surface: rgba(255, 255, 255, .05);--smv-panel-surface-hover: rgba(255, 255, 255, .1);--smv-panel-surface-soft: rgba(255, 255, 255, .04);--smv-panel-accent: #3b82f6;--smv-panel-accent-contrast: #ffffff;position:relative;width:100%;height:100%;min-height:0;overflow:hidden;display:flex;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;font-synthesis:none}.smv-canvasArea{flex:1;position:relative;min-width:0}.smv-upload-input{display:none}.smv-upload-toolbar{position:absolute;top:12px;left:50%;z-index:120;display:flex;align-items:center;gap:10px;max-width:calc(100% - 32px);padding:5px 6px;border:1px solid var(--smv-panel-border-strong);border-radius:10px;background:#141824e0;color:var(--smv-panel-text);box-shadow:0 16px 30px #00000047;backdrop-filter:blur(14px);transform:translate(-50%)}.smv-upload-toolbar__name{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--smv-panel-text-muted);font-size:12px;font-weight:500}.smv-upload-toolbar__button{display:inline-flex;align-items:center;gap:7px;min-height:34px;padding:0 14px;border:1px solid rgba(255,255,255,.08);border-radius:10px;background:#3b82f62e;color:var(--smv-panel-text-emphasis);font-size:12px;font-weight:600;cursor:pointer}.smv-upload-toolbar__button--ghost{background:var(--smv-panel-surface);color:var(--smv-panel-text-muted)}.smv-upload-empty-state{display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:24px}.smv-upload-empty-state__card{width:min(440px,100%);padding:28px;border:1px dashed rgba(255,255,255,.2);border-radius:8px;color:var(--smv-panel-text);text-align:center;box-shadow:0 24px 40px #00000047;backdrop-filter:blur(14px)}.smv-upload-empty-state__icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;margin-bottom:18px;border-radius:10px;background:#3b82f62e;color:#bfdbfe}.smv-upload-empty-state__title{margin:0 0 10px;color:var(--smv-panel-text-emphasis);font-size:24px;line-height:1.15}.smv-upload-empty-state__body{margin:0 0 18px;color:var(--smv-panel-text-muted);font-size:14px;font-weight:700;line-height:1.6}.smv-upload-empty-state__body code{color:var(--smv-panel-text-emphasis);font-family:var(--smv-panel-font-family)}.smv-upload-empty-state__button{display:inline-flex;align-items:center;gap:8px;min-height:40px;padding:0 18px;border:none;border-radius:8px;background:#3b82f6;color:#eff6ff;font-size:13px;font-weight:700;cursor:pointer}.smv-upload-drop-overlay{position:absolute;inset:0;z-index:130;display:flex;align-items:center;justify-content:center;padding:24px;background:#080c147a;backdrop-filter:blur(4px)}.smv-upload-drop-overlay__card{display:inline-flex;align-items:center;gap:10px;padding:16px 20px;border:1px dashed rgba(191,219,254,.75);border-radius:10px;background:#1e293beb;color:#dbeafe;font-size:14px;font-weight:700;box-shadow:0 18px 36px #00000047}.smv-canvasArea .bb-loadingOverlay{position:absolute;inset:0;z-index:110;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;border-radius:14px;background:#0f172a75;backdrop-filter:blur(1px)}.smv-canvasArea .bb-loadingSpinner{width:40px;height:40px;border-radius:999px;border:3px solid rgba(226,232,240,.24);border-top-color:#93c5fd;animation:bb-spin .8s linear infinite}.smv-canvasArea .bb-loadingProgressRow{display:flex;align-items:center;gap:8px;width:100%;max-width:240px}.smv-canvasArea .bb-loadingProgress{flex:1;height:6px;border-radius:999px;background:#e2e8f03d;overflow:hidden}.smv-canvasArea .bb-loadingProgress__bar{height:100%;border-radius:inherit;background:#93c5fd;transition:width .15s ease-out}.smv-canvasArea .bb-loadingProgress__label{flex-shrink:0;min-width:34px;text-align:right;color:#f7f8fa;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.smv-canvasArea .bb-loadingMessage{max-width:320px;color:#f7f8fa;font-size:13px;font-weight:600;line-height:1.5}@keyframes bb-spin{to{transform:rotate(360deg)}}.right-panel-wrapper{height:100%;transition:width .45s cubic-bezier(.4,0,.2,1);overflow:visible}.right-panel-wrapper--open{width:300px;flex-shrink:0}.right-panel-wrapper--closed{width:0;flex-shrink:0}.left-panel-wrapper{height:100%;transition:width .45s cubic-bezier(.4,0,.2,1);overflow:visible;order:-1}.left-panel-wrapper--open{width:280px;flex-shrink:0}.left-panel-wrapper--closed{width:0;flex-shrink:0}.side-panel__open-btn.side-panel__open-btn--left{left:12px;right:auto}.side-panel.side-panel--left{left:0;right:auto;width:280px;transform:translate(-100%);border-left:none;border-right:1px solid var(--smv-panel-border);box-shadow:var(--smv-panel-shadow-left)}.side-panel.side-panel--open-left{transform:translate(0)}.side-panel.side-panel--closed-left{transform:translate(-100%)}.env-controls{display:flex;flex-direction:column;overflow-y:auto;flex:1}.env-section{border-bottom:1px solid var(--smv-panel-border-strong)}.env-section__header{display:flex;align-items:center;gap:6px;width:100%;padding:9px 14px;border:none;background:var(--smv-panel-surface-soft);color:var(--smv-panel-text-muted);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;cursor:pointer}.env-section__chevron{transition:transform .15s}.env-section__chevron--collapsed{transform:rotate(-90deg)}.env-section__body{display:flex;flex-direction:column}.env-row{display:flex;gap:6px;align-items:center;min-height:32px;border-bottom:1px solid var(--smv-panel-surface)}.env-row__label{flex:0 0 42%;padding:0 0 0 14px;font-size:11.5px;font-weight:500;color:var(--smv-panel-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.env-row__value{flex:1;display:flex;align-items:center;padding:5px 10px 5px 8px;min-width:0}.env-row__value--slider{gap:6px}.env-checkbox-btn{display:flex;align-items:center;cursor:pointer;background:none;border:none;padding:0}.env-checkbox{width:15px;height:15px;border-radius:3px;border:1.5px solid var(--smv-panel-border-active);background:var(--smv-panel-surface-soft);display:flex;align-items:center;justify-content:center;transition:background .15s,border-color .15s}.env-checkbox--checked{background:var(--smv-panel-accent);border-color:var(--smv-panel-accent)}.env-select{width:100%;padding:5px 8px;border-radius:4px;border:none;background:var(--smv-panel-surface-hover);color:var(--smv-panel-text);font-size:11.5px;outline:none;box-sizing:border-box;text-transform:capitalize}.env-color-swatch{position:relative;width:100%;height:22px;border-radius:4px;border:1px solid rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:pointer}.env-color-swatch__hex{font-size:10.5px;font-weight:700;color:#0f172a;text-shadow:0 1px 1px rgba(255,255,255,.35);pointer-events:none;text-transform:uppercase}.env-color-swatch__input{position:absolute;inset:0;width:100%;height:100%;border:none;padding:0;opacity:0;cursor:pointer}.env-slider{flex:1;accent-color:var(--smv-panel-accent);cursor:pointer;min-width:0}.env-slider__value{flex-shrink:0;min-width:32px;padding:2px 5px;border-radius:4px;background:var(--smv-panel-surface-hover);color:var(--smv-panel-accent);font-size:10.5px;font-weight:600;text-align:center}.side-panel__open-btn{position:absolute;top:12px;right:12px;display:flex;align-items:center;gap:7px;padding:7px 13px;border-radius:7px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-bg-strong);cursor:pointer;font-size:13px;font-weight:500;color:var(--smv-panel-text);z-index:190;transition:background .15s}.side-panel__open-btn:hover{background:var(--smv-panel-bg-strong-hover)}.side-panel{position:absolute;top:0;right:0;width:300px;height:100%;background:var(--smv-panel-bg);display:flex;flex-direction:column;font-family:var(--smv-panel-font-family);transform:translate(100%);transition:transform .45s cubic-bezier(.4,0,.2,1);z-index:200;border-left:1px solid var(--smv-panel-border);box-shadow:var(--smv-panel-shadow-right)}.side-panel--open{transform:translate(0)}.side-panel--closed{transform:translate(100%)}.side-panel__header{padding:24px 20px 16px;border-bottom:1px solid var(--smv-panel-border-strong)}.side-panel__header-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}.side-panel__eyebrow{font-size:15px;font-weight:700;letter-spacing:.12em;color:var(--smv-panel-text-subtle);text-transform:uppercase}.side-panel__title{font-size:22px;font-weight:700;color:var(--smv-panel-text);margin:0;line-height:1.2}.side-panel__close-btn{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-surface);cursor:pointer;color:var(--smv-panel-text-muted);transition:background .15s,color .15s;flex-shrink:0}.side-panel__close-btn:hover{background:var(--smv-panel-surface-hover);color:var(--smv-panel-text)}.side-panel__search-wrap{padding:14px 16px;border-bottom:1px solid var(--smv-panel-border-strong)}.side-panel__search{width:100%;padding:10px 14px;border-radius:10px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-surface);color:var(--smv-panel-text);font-size:13px;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.side-panel__search::placeholder{color:var(--smv-panel-text-subtle)}.side-panel__search:focus{border-color:var(--smv-panel-border-active);background:var(--smv-panel-surface-hover)}.side-panel__list{display:flex;flex-direction:column;padding:10px 0;overflow-y:auto;flex:1}.side-panel__list::-webkit-scrollbar{width:4px}.side-panel__list::-webkit-scrollbar-track{background:transparent}.side-panel__list::-webkit-scrollbar-thumb{background:var(--smv-panel-surface-hover);border-radius:4px}.side-panel__row{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;cursor:pointer;transition:background .12s;border-bottom:1px solid var(--smv-panel-surface-soft)}.side-panel__row:hover,.side-panel__row--hovered{background:var(--smv-panel-surface)}.side-panel__label{font-size:14px;font-weight:600;color:var(--smv-panel-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.side-panel__label--hovered{color:var(--smv-panel-text-emphasis)}.side-panel__vis-btn{display:flex;align-items:center;flex-shrink:0;cursor:pointer;background:none;border:none;padding:0;margin-left:10px}.side-panel__checkbox{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--smv-panel-border-active);background:transparent;display:flex;align-items:center;justify-content:center;transition:background .15s,border-color .15s}.side-panel__checkbox--checked{background:var(--smv-panel-accent);border-color:var(--smv-panel-accent)}@media(max-width:900px){.smv-upload-toolbar{top:56px;gap:8px;max-width:calc(100% - 24px);padding:8px 10px}.smv-upload-toolbar__name{max-width:140px}.right-panel-wrapper--open,.left-panel-wrapper--open{width:0}.side-panel,.side-panel.side-panel--left{width:min(300px,85vw)}}.smv-largeModelBanner{position:absolute;bottom:12px;left:50%;transform:translate(-50%);z-index:20;display:flex;align-items:center;gap:8px;padding:7px 12px 7px 10px;border-radius:8px;background:#1e1400e0;border:1px solid rgba(251,191,36,.35);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);color:#fbbf24;font-size:12px;font-weight:500;line-height:1.4;pointer-events:auto;max-width:calc(100% - 32px);text-align:center}.smv-largeModelBanner__icon{flex-shrink:0}.smv-largeModelBanner__dismiss{flex-shrink:0;display:flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;background:none;border:none;border-radius:4px;color:#fbbf24b3;cursor:pointer;transition:color .15s ease,background .15s ease}.smv-largeModelBanner__dismiss:hover{color:#fbbf24;background:#fbbf241f}
1
+ .bb-loadingOverlay{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;border-radius:14px;background:#f8fafcd1;backdrop-filter:blur(2px)}.bb-loadingSpinner{width:40px;height:40px;border-radius:999px;border:3px solid rgba(148,163,184,.3);border-top-color:#2563eb;animation:bb-spin .8s linear infinite}.bb-loadingProgressRow{display:flex;align-items:center;gap:8px;width:100%;max-width:240px}.bb-loadingProgress{flex:1;height:6px;border-radius:999px;background:#94a3b84d;overflow:hidden}.bb-loadingProgress__bar{height:100%;border-radius:inherit;background:#2563eb;transition:width .15s ease-out}.bb-loadingProgress__label{flex-shrink:0;min-width:34px;text-align:right;color:#475569;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.bb-loadingMessage{max-width:320px;color:#475569;font-size:13px;font-weight:600;line-height:1.5}.ri-viewerSlot{position:relative;width:100%;height:100%;min-width:0;min-height:0}.ri-viewerPlaceholder{position:relative;overflow:hidden}.ri-viewerPlaceholder--inset{position:absolute;inset:0}.ri-poster{position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:var(--ri-poster-fit, contain);pointer-events:none;user-select:none;transition:opacity .3s ease}.ri-poster--hidden{opacity:0}[data-reduced-motion] .ri-poster{transition:none}@media(prefers-reduced-motion:reduce){.ri-poster{transition:none}}.ri-revealButton{position:absolute;top:50%;left:50%;z-index:2;display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border:0;border-radius:999px;background:#111827d1;color:#f9fafb;font:600 14px/1.2 Inter,Segoe UI,Helvetica,Arial,sans-serif;white-space:nowrap;cursor:pointer;transform:translate(-50%,-50%);box-shadow:0 6px 20px #00000040}.ri-revealButton:hover{background:#111827f0}.ri-revealButton:focus-visible{outline:2px solid #93c5fd;outline-offset:3px}:root{--ri-styles-simple-model-viewer: 1}.smv-root{--smv-panel-font-family: Inter, "Segoe UI", Helvetica, Arial, sans-serif;--smv-panel-bg: #141824;--smv-panel-bg-strong: #1a1f2e;--smv-panel-bg-strong-hover: #252b3b;--smv-panel-border: rgba(255, 255, 255, .06);--smv-panel-border-strong: rgba(255, 255, 255, .08);--smv-panel-border-active: rgba(255, 255, 255, .18);--smv-panel-shadow-left: 4px 0 24px rgba(0, 0, 0, .35);--smv-panel-shadow-right: -4px 0 24px rgba(0, 0, 0, .35);--smv-panel-text: #f1f5f9;--smv-panel-text-emphasis: #ffffff;--smv-panel-text-muted: #94a3b8;--smv-panel-text-subtle: #64748b;--smv-panel-surface: rgba(255, 255, 255, .05);--smv-panel-surface-hover: rgba(255, 255, 255, .1);--smv-panel-surface-soft: rgba(255, 255, 255, .04);--smv-panel-accent: #3b82f6;--smv-panel-accent-contrast: #ffffff;position:relative;width:100%;height:100%;min-height:0;overflow:hidden;display:flex;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;font-synthesis:none}.smv-canvasArea{flex:1;position:relative;min-width:0}.smv-upload-input{display:none}.smv-upload-toolbar{position:absolute;top:12px;left:50%;z-index:120;display:flex;align-items:center;gap:10px;max-width:calc(100% - 32px);padding:5px 6px;border:1px solid var(--smv-panel-border-strong);border-radius:10px;background:#141824e0;color:var(--smv-panel-text);box-shadow:0 16px 30px #00000047;backdrop-filter:blur(14px);transform:translate(-50%)}.smv-upload-toolbar__name{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--smv-panel-text-muted);font-size:12px;font-weight:500}.smv-upload-toolbar__button{display:inline-flex;align-items:center;gap:7px;min-height:34px;padding:0 14px;border:1px solid rgba(255,255,255,.08);border-radius:10px;background:#3b82f62e;color:var(--smv-panel-text-emphasis);font-size:12px;font-weight:600;cursor:pointer}.smv-upload-toolbar__button--ghost{background:var(--smv-panel-surface);color:var(--smv-panel-text-muted)}.smv-upload-empty-state{display:flex;align-items:center;justify-content:center;width:100%;height:100%;padding:24px}.smv-upload-empty-state__card{width:min(440px,100%);padding:28px;border:1px dashed rgba(255,255,255,.2);border-radius:8px;color:var(--smv-panel-text);text-align:center;box-shadow:0 24px 40px #00000047;backdrop-filter:blur(14px)}.smv-upload-empty-state__icon{display:inline-flex;align-items:center;justify-content:center;width:56px;height:56px;margin-bottom:18px;border-radius:10px;background:#3b82f62e;color:#bfdbfe}.smv-upload-empty-state__title{margin:0 0 10px;color:var(--smv-panel-text-emphasis);font-size:24px;line-height:1.15}.smv-upload-empty-state__body{margin:0 0 18px;color:var(--smv-panel-text-muted);font-size:14px;font-weight:700;line-height:1.6}.smv-upload-empty-state__body code{color:var(--smv-panel-text-emphasis);font-family:var(--smv-panel-font-family)}.smv-upload-empty-state__button{display:inline-flex;align-items:center;gap:8px;min-height:40px;padding:0 18px;border:none;border-radius:8px;background:#3b82f6;color:#eff6ff;font-size:13px;font-weight:700;cursor:pointer}.smv-upload-drop-overlay{position:absolute;inset:0;z-index:130;display:flex;align-items:center;justify-content:center;padding:24px;background:#080c147a;backdrop-filter:blur(4px)}.smv-upload-drop-overlay__card{display:inline-flex;align-items:center;gap:10px;padding:16px 20px;border:1px dashed rgba(191,219,254,.75);border-radius:10px;background:#1e293beb;color:#dbeafe;font-size:14px;font-weight:700;box-shadow:0 18px 36px #00000047}.smv-canvasArea .bb-loadingOverlay{position:absolute;inset:0;z-index:110;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:24px;text-align:center;border-radius:14px;background:#0f172a75;backdrop-filter:blur(1px)}.smv-canvasArea .bb-loadingSpinner{width:40px;height:40px;border-radius:999px;border:3px solid rgba(226,232,240,.24);border-top-color:#93c5fd;animation:bb-spin .8s linear infinite}.smv-canvasArea .bb-loadingProgressRow{display:flex;align-items:center;gap:8px;width:100%;max-width:240px}.smv-canvasArea .bb-loadingProgress{flex:1;height:6px;border-radius:999px;background:#e2e8f03d;overflow:hidden}.smv-canvasArea .bb-loadingProgress__bar{height:100%;border-radius:inherit;background:#93c5fd;transition:width .15s ease-out}.smv-canvasArea .bb-loadingProgress__label{flex-shrink:0;min-width:34px;text-align:right;color:#f7f8fa;font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.smv-canvasArea .bb-loadingMessage{max-width:320px;color:#f7f8fa;font-size:13px;font-weight:600;line-height:1.5}@keyframes bb-spin{to{transform:rotate(360deg)}}.right-panel-wrapper{height:100%;transition:width .45s cubic-bezier(.4,0,.2,1);overflow:visible}.right-panel-wrapper--open{width:300px;flex-shrink:0}.right-panel-wrapper--closed{width:0;flex-shrink:0}.left-panel-wrapper{height:100%;transition:width .45s cubic-bezier(.4,0,.2,1);overflow:visible;order:-1}.left-panel-wrapper--open{width:280px;flex-shrink:0}.left-panel-wrapper--closed{width:0;flex-shrink:0}.side-panel__open-btn.side-panel__open-btn--left{left:12px;right:auto}.side-panel.side-panel--left{left:0;right:auto;width:280px;transform:translate(-100%);border-left:none;border-right:1px solid var(--smv-panel-border);box-shadow:var(--smv-panel-shadow-left)}.side-panel.side-panel--open-left{transform:translate(0)}.side-panel.side-panel--closed-left{transform:translate(-100%)}.env-controls{display:flex;flex-direction:column;overflow-y:auto;flex:1}.env-section{border-bottom:1px solid var(--smv-panel-border-strong)}.env-section__header{display:flex;align-items:center;gap:6px;width:100%;padding:9px 14px;border:none;background:var(--smv-panel-surface-soft);color:var(--smv-panel-text-muted);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;cursor:pointer}.env-section__chevron{transition:transform .15s}.env-section__chevron--collapsed{transform:rotate(-90deg)}.env-section__body{display:flex;flex-direction:column}.env-row{display:flex;gap:6px;align-items:center;min-height:32px;border-bottom:1px solid var(--smv-panel-surface)}.env-row__label{flex:0 0 42%;padding:0 0 0 14px;font-size:11.5px;font-weight:500;color:var(--smv-panel-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.env-row__value{flex:1;display:flex;align-items:center;padding:5px 10px 5px 8px;min-width:0}.env-row__value--slider{gap:6px}.env-checkbox-btn{display:flex;align-items:center;cursor:pointer;background:none;border:none;padding:0}.env-checkbox{width:15px;height:15px;border-radius:3px;border:1.5px solid var(--smv-panel-border-active);background:var(--smv-panel-surface-soft);display:flex;align-items:center;justify-content:center;transition:background .15s,border-color .15s}.env-checkbox--checked{background:var(--smv-panel-accent);border-color:var(--smv-panel-accent)}.env-select{width:100%;padding:5px 8px;border-radius:4px;border:none;background:var(--smv-panel-surface-hover);color:var(--smv-panel-text);font-size:11.5px;outline:none;box-sizing:border-box;text-transform:capitalize}.env-color-swatch{position:relative;width:100%;height:22px;border-radius:4px;border:1px solid rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:pointer}.env-color-swatch__hex{font-size:10.5px;font-weight:700;color:#0f172a;text-shadow:0 1px 1px rgba(255,255,255,.35);pointer-events:none;text-transform:uppercase}.env-color-swatch__input{position:absolute;inset:0;width:100%;height:100%;border:none;padding:0;opacity:0;cursor:pointer}.env-slider{flex:1;accent-color:var(--smv-panel-accent);cursor:pointer;min-width:0}.env-slider__value{flex-shrink:0;min-width:32px;padding:2px 5px;border-radius:4px;background:var(--smv-panel-surface-hover);color:var(--smv-panel-accent);font-size:10.5px;font-weight:600;text-align:center}.side-panel__open-btn{position:absolute;top:12px;right:12px;display:flex;align-items:center;gap:7px;padding:7px 13px;border-radius:7px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-bg-strong);cursor:pointer;font-size:13px;font-weight:500;color:var(--smv-panel-text);z-index:190;transition:background .15s}.side-panel__open-btn:hover{background:var(--smv-panel-bg-strong-hover)}.side-panel{position:absolute;top:0;right:0;width:300px;height:100%;background:var(--smv-panel-bg);display:flex;flex-direction:column;font-family:var(--smv-panel-font-family);transform:translate(100%);transition:transform .45s cubic-bezier(.4,0,.2,1);z-index:200;border-left:1px solid var(--smv-panel-border);box-shadow:var(--smv-panel-shadow-right)}.side-panel--open{transform:translate(0)}.side-panel--closed{transform:translate(100%)}.side-panel__header{padding:24px 20px 16px;border-bottom:1px solid var(--smv-panel-border-strong)}.side-panel__header-top{display:flex;align-items:center;justify-content:space-between;margin-bottom:4px}.side-panel__eyebrow{font-size:15px;font-weight:700;letter-spacing:.12em;color:var(--smv-panel-text-subtle);text-transform:uppercase}.side-panel__title{font-size:22px;font-weight:700;color:var(--smv-panel-text);margin:0;line-height:1.2}.side-panel__close-btn{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-surface);cursor:pointer;color:var(--smv-panel-text-muted);transition:background .15s,color .15s;flex-shrink:0}.side-panel__close-btn:hover{background:var(--smv-panel-surface-hover);color:var(--smv-panel-text)}.side-panel__search-wrap{padding:14px 16px;border-bottom:1px solid var(--smv-panel-border-strong)}.side-panel__search{width:100%;padding:10px 14px;border-radius:10px;border:1px solid var(--smv-panel-border-strong);background:var(--smv-panel-surface);color:var(--smv-panel-text);font-size:13px;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.side-panel__search::placeholder{color:var(--smv-panel-text-subtle)}.side-panel__search:focus{border-color:var(--smv-panel-border-active);background:var(--smv-panel-surface-hover)}.side-panel__list{display:flex;flex-direction:column;padding:10px 0;overflow-y:auto;flex:1}.side-panel__list::-webkit-scrollbar{width:4px}.side-panel__list::-webkit-scrollbar-track{background:transparent}.side-panel__list::-webkit-scrollbar-thumb{background:var(--smv-panel-surface-hover);border-radius:4px}.side-panel__row{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;cursor:pointer;transition:background .12s;border-bottom:1px solid var(--smv-panel-surface-soft)}.side-panel__row:hover,.side-panel__row--hovered{background:var(--smv-panel-surface)}.side-panel__label{font-size:14px;font-weight:600;color:var(--smv-panel-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.side-panel__labelButton{min-width:0;padding:0;background:none;border:none;font-family:inherit;text-align:left;cursor:pointer}.side-panel__labelButton:focus-visible{outline:2px solid var(--smv-panel-text);outline-offset:2px;border-radius:4px}.side-panel__label--hovered{color:var(--smv-panel-text-emphasis)}.side-panel__vis-btn{display:flex;align-items:center;flex-shrink:0;cursor:pointer;background:none;border:none;padding:0;margin-left:10px}.side-panel__checkbox{width:16px;height:16px;border-radius:4px;border:1.5px solid var(--smv-panel-border-active);background:transparent;display:flex;align-items:center;justify-content:center;transition:background .15s,border-color .15s}.side-panel__checkbox--checked{background:var(--smv-panel-accent);border-color:var(--smv-panel-accent)}@media(max-width:900px){.smv-upload-toolbar{top:56px;gap:8px;max-width:calc(100% - 24px);padding:8px 10px}.smv-upload-toolbar__name{max-width:140px}.right-panel-wrapper--open,.left-panel-wrapper--open{width:0}.side-panel,.side-panel.side-panel--left{width:min(300px,85vw)}}.smv-largeModelBanner{position:absolute;bottom:12px;left:50%;transform:translate(-50%);z-index:20;display:flex;align-items:center;gap:8px;padding:7px 12px 7px 10px;border-radius:8px;background:#1e1400e0;border:1px solid rgba(251,191,36,.35);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);color:#fbbf24;font-size:12px;font-weight:500;line-height:1.4;pointer-events:auto;max-width:calc(100% - 32px);text-align:center}.smv-largeModelBanner__icon{flex-shrink:0}.smv-largeModelBanner__dismiss{flex-shrink:0;display:flex;align-items:center;justify-content:center;margin-left:4px;padding:2px;background:none;border:none;border-radius:4px;color:#fbbf24b3;cursor:pointer;transition:color .15s ease,background .15s ease}.smv-largeModelBanner__dismiss:hover{color:#fbbf24;background:#fbbf241f}.smv-root[data-reduced-motion] *,.smv-root[data-reduced-motion] *:before,.smv-root[data-reduced-motion] *:after{transition-duration:0s!important;transition-delay:0s!important}
@@ -1,10 +1,31 @@
1
1
  import * as react from 'react';
2
- import { M as ModelFormat, a as ViewerModelInfo } from './modelInfo-CEmmvVCH.mjs';
3
- export { V as ViewerModelBounds } from './modelInfo-CEmmvVCH.mjs';
2
+ import { c as ModelViewerLabels, R as ReducedMotionSetting } from './materialVariants-DnZe9eMC.mjs';
3
+ export { D as DEFAULT_MODEL_VIEWER_LABELS, M as MaterialVariantGroup, a as MaterialVariantOption, b as ModelViewerLabelKey } from './materialVariants-DnZe9eMC.mjs';
4
+ import { V as ViewerLoading, c as ViewerReveal, M as ModelFormat, b as ViewerModelInfo } from './modelInfo-BYebMPdZ.mjs';
5
+ export { a as ViewerModelBounds } from './modelInfo-BYebMPdZ.mjs';
4
6
  import { PresetsType } from '@react-three/drei/helpers/environment-assets';
5
7
 
6
8
  type SimpleModelViewerProps = {
9
+ /** Plain-text overrides for built-in viewer text. */
10
+ labels?: ModelViewerLabels;
7
11
  modelUrl: string;
12
+ /**
13
+ * An image shown in the viewer's place until its 3D view is drawn: a render
14
+ * or photo of the model, best framed as the viewer frames it.
15
+ */
16
+ poster?: string;
17
+ /**
18
+ * `"lazy"` (default) starts the 3D view when the viewer is on or near the
19
+ * screen. When the page needs room for viewers in view, one scrolled away
20
+ * from goes back to a picture of itself, and comes back as it was. `"eager"`
21
+ * starts at once and keeps its view.
22
+ */
23
+ loading?: ViewerLoading;
24
+ /**
25
+ * `"interaction"` keeps the poster, with a "View in 3D" button, until the
26
+ * visitor asks for the 3D view. Default `"auto"`.
27
+ */
28
+ reveal?: ViewerReveal;
8
29
  /** Explicit format for extensionless or signed model URLs. */
9
30
  modelFormat?: ModelFormat;
10
31
  /** Directory containing the DRACO decoder files, or false. */
@@ -21,6 +42,13 @@ type SimpleModelViewerProps = {
21
42
  highlightOnHover?: boolean;
22
43
  showLoadingOverlay?: boolean;
23
44
  refitOnResize?: boolean;
45
+ /**
46
+ * Cuts motion the viewer starts on its own: auto-rotate doesn't start, and
47
+ * camera moves jump instead of gliding. `"user"` (default) follows the
48
+ * visitor's `prefers-reduced-motion` setting; `"always"` and `"never"`
49
+ * override it.
50
+ */
51
+ reducedMotion?: ReducedMotionSetting;
24
52
  backgroundEnabled?: boolean;
25
53
  autoRotate?: boolean;
26
54
  envPreset?: PresetsType;
@@ -33,6 +61,6 @@ type SimpleModelViewerProps = {
33
61
  onModelLoaded?: (model: ViewerModelInfo) => void;
34
62
  onLoadError?: (error: unknown) => void;
35
63
  };
36
- declare function SimpleModelViewer({ modelUrl, modelFormat, dracoDecoderPath, ktx2TranscoderPath, meshopt, backgroundColor, showSceneObjectsPanel, showSceneSettingsPanel, enableModelUpload, zoomOnSelected, highlightOnHover, showLoadingOverlay, refitOnResize, backgroundEnabled, autoRotate, envPreset, exposure, ambientIntensity, ambientColor, directionalIntensity, directionalColor, onModelLoaded, onLoadError, }: SimpleModelViewerProps): react.JSX.Element;
64
+ declare function SimpleModelViewer(props: SimpleModelViewerProps): react.JSX.Element;
37
65
 
38
- export { ModelFormat, SimpleModelViewer, type SimpleModelViewerProps, ViewerModelInfo };
66
+ export { ModelFormat, ModelViewerLabels, ReducedMotionSetting, SimpleModelViewer, type SimpleModelViewerProps, ViewerLoading, ViewerModelInfo, ViewerReveal };
@@ -1,10 +1,31 @@
1
1
  import * as react from 'react';
2
- import { M as ModelFormat, a as ViewerModelInfo } from './modelInfo-CEmmvVCH.js';
3
- export { V as ViewerModelBounds } from './modelInfo-CEmmvVCH.js';
2
+ import { c as ModelViewerLabels, R as ReducedMotionSetting } from './materialVariants-DnZe9eMC.js';
3
+ export { D as DEFAULT_MODEL_VIEWER_LABELS, M as MaterialVariantGroup, a as MaterialVariantOption, b as ModelViewerLabelKey } from './materialVariants-DnZe9eMC.js';
4
+ import { V as ViewerLoading, c as ViewerReveal, M as ModelFormat, b as ViewerModelInfo } from './modelInfo-XfUQO7C_.js';
5
+ export { a as ViewerModelBounds } from './modelInfo-XfUQO7C_.js';
4
6
  import { PresetsType } from '@react-three/drei/helpers/environment-assets';
5
7
 
6
8
  type SimpleModelViewerProps = {
9
+ /** Plain-text overrides for built-in viewer text. */
10
+ labels?: ModelViewerLabels;
7
11
  modelUrl: string;
12
+ /**
13
+ * An image shown in the viewer's place until its 3D view is drawn: a render
14
+ * or photo of the model, best framed as the viewer frames it.
15
+ */
16
+ poster?: string;
17
+ /**
18
+ * `"lazy"` (default) starts the 3D view when the viewer is on or near the
19
+ * screen. When the page needs room for viewers in view, one scrolled away
20
+ * from goes back to a picture of itself, and comes back as it was. `"eager"`
21
+ * starts at once and keeps its view.
22
+ */
23
+ loading?: ViewerLoading;
24
+ /**
25
+ * `"interaction"` keeps the poster, with a "View in 3D" button, until the
26
+ * visitor asks for the 3D view. Default `"auto"`.
27
+ */
28
+ reveal?: ViewerReveal;
8
29
  /** Explicit format for extensionless or signed model URLs. */
9
30
  modelFormat?: ModelFormat;
10
31
  /** Directory containing the DRACO decoder files, or false. */
@@ -21,6 +42,13 @@ type SimpleModelViewerProps = {
21
42
  highlightOnHover?: boolean;
22
43
  showLoadingOverlay?: boolean;
23
44
  refitOnResize?: boolean;
45
+ /**
46
+ * Cuts motion the viewer starts on its own: auto-rotate doesn't start, and
47
+ * camera moves jump instead of gliding. `"user"` (default) follows the
48
+ * visitor's `prefers-reduced-motion` setting; `"always"` and `"never"`
49
+ * override it.
50
+ */
51
+ reducedMotion?: ReducedMotionSetting;
24
52
  backgroundEnabled?: boolean;
25
53
  autoRotate?: boolean;
26
54
  envPreset?: PresetsType;
@@ -33,6 +61,6 @@ type SimpleModelViewerProps = {
33
61
  onModelLoaded?: (model: ViewerModelInfo) => void;
34
62
  onLoadError?: (error: unknown) => void;
35
63
  };
36
- declare function SimpleModelViewer({ modelUrl, modelFormat, dracoDecoderPath, ktx2TranscoderPath, meshopt, backgroundColor, showSceneObjectsPanel, showSceneSettingsPanel, enableModelUpload, zoomOnSelected, highlightOnHover, showLoadingOverlay, refitOnResize, backgroundEnabled, autoRotate, envPreset, exposure, ambientIntensity, ambientColor, directionalIntensity, directionalColor, onModelLoaded, onLoadError, }: SimpleModelViewerProps): react.JSX.Element;
64
+ declare function SimpleModelViewer(props: SimpleModelViewerProps): react.JSX.Element;
37
65
 
38
- export { ModelFormat, SimpleModelViewer, type SimpleModelViewerProps, ViewerModelInfo };
66
+ export { ModelFormat, ModelViewerLabels, ReducedMotionSetting, SimpleModelViewer, type SimpleModelViewerProps, ViewerLoading, ViewerModelInfo, ViewerReveal };