@liveroom-tech/react-immersive 2.2.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -48,13 +48,17 @@ var init_bvhWorkerCode = __esm({
48
48
  var index_exports = {};
49
49
  __export(index_exports, {
50
50
  BindingBuilder: () => BindingBuilder,
51
+ DEFAULT_SCENE_CONFIG: () => DEFAULT_SCENE_CONFIG,
51
52
  MATERIAL_BLENDING_MODES: () => MATERIAL_BLENDING_MODES,
52
53
  MATERIAL_SIDES: () => MATERIAL_SIDES,
53
54
  ModelViewer: () => ModelViewer,
54
55
  SimpleModelViewer: () => SimpleModelViewer,
56
+ defineObjectBindings: () => defineObjectBindings,
55
57
  material: () => material,
56
58
  patchBindings: () => patchBindings,
57
59
  patchSceneConfig: () => patchSceneConfig,
60
+ selectBindingKeys: () => selectBindingKeys,
61
+ selectBindings: () => selectBindings,
58
62
  useObjectBinding: () => useObjectBinding,
59
63
  useObjectBindingIds: () => useObjectBindingIds,
60
64
  useObjectBindings: () => useObjectBindings,
@@ -63,6 +67,8 @@ __export(index_exports, {
63
67
  useViewerActions: () => useViewerActions,
64
68
  useViewerAnimations: () => useViewerAnimations,
65
69
  useViewerCamera: () => useViewerCamera,
70
+ useViewerConnection: () => useViewerConnection,
71
+ useViewerEffects: () => useViewerEffects,
66
72
  useViewerHover: () => useViewerHover,
67
73
  useViewerModel: () => useViewerModel,
68
74
  useViewerSelection: () => useViewerSelection
@@ -221,7 +227,7 @@ function disposeSceneBVH(root) {
221
227
  var import_fiber12 = require("@react-three/fiber");
222
228
  var import_xr4 = require("@react-three/xr");
223
229
  var import_qrcode = require("qrcode.react");
224
- var import_three20 = require("three");
230
+ var import_three21 = require("three");
225
231
 
226
232
  // #style-inject:#style-inject
227
233
  function styleInject(css, { insertAt } = {}) {
@@ -2063,6 +2069,26 @@ function Controls({ controlsRef, onCameraChange }) {
2063
2069
  // src/components/Lights.tsx
2064
2070
  var import_react9 = require("react");
2065
2071
  var import_fiber5 = require("@react-three/fiber");
2072
+ var import_three10 = require("three");
2073
+
2074
+ // src/utils/resolveBindingKey.ts
2075
+ function resolveBindingKey(objectBindings, objectId) {
2076
+ if (objectBindings[objectId]) {
2077
+ return objectId;
2078
+ }
2079
+ const idMatch = Object.entries(objectBindings).find(
2080
+ ([, binding]) => binding.id === objectId
2081
+ )?.[0];
2082
+ if (idMatch) {
2083
+ return idMatch;
2084
+ }
2085
+ const modelObjectMatch = Object.entries(objectBindings).find(
2086
+ ([, binding]) => binding.modelObjectId === objectId
2087
+ )?.[0];
2088
+ return modelObjectMatch ?? null;
2089
+ }
2090
+
2091
+ // src/components/Lights.tsx
2066
2092
  var import_jsx_runtime7 = require("react/jsx-runtime");
2067
2093
  function rotatePositionY(pos, deg) {
2068
2094
  if (!deg) return pos;
@@ -2142,21 +2168,42 @@ function LightInstance({
2142
2168
  shadows,
2143
2169
  shadowsEnabled,
2144
2170
  environmentRotation,
2145
- modelBounds
2171
+ modelBounds,
2172
+ objectBindings,
2173
+ nodeRefs
2146
2174
  }) {
2147
2175
  const dirRef = (0, import_react9.useRef)(null);
2148
2176
  const pointRef = (0, import_react9.useRef)(null);
2149
2177
  const spotRef = (0, import_react9.useRef)(null);
2150
2178
  const camera = (0, import_fiber5.useThree)((s) => s.camera);
2179
+ const rootScene = (0, import_fiber5.useThree)((s) => s.scene);
2180
+ const attachedPosition = (0, import_react9.useMemo)(() => new import_three10.Vector3(), []);
2151
2181
  const position = (0, import_react9.useMemo)(
2152
- () => light.attachedToCamera ? light.position : rotatePositionY(light.position, environmentRotation),
2153
- [light.position, light.attachedToCamera, environmentRotation]
2182
+ () => light.attachedToCamera || light.attachment ? light.position : rotatePositionY(light.position, environmentRotation),
2183
+ [
2184
+ light.position,
2185
+ light.attachedToCamera,
2186
+ light.attachment,
2187
+ environmentRotation
2188
+ ]
2154
2189
  );
2155
2190
  (0, import_fiber5.useFrame)(() => {
2156
- if (!light.attachedToCamera) return;
2157
- if (dirRef.current) dirRef.current.position.copy(camera.position);
2158
- if (pointRef.current) pointRef.current.position.copy(camera.position);
2159
- if (spotRef.current) spotRef.current.position.copy(camera.position);
2191
+ const instance = dirRef.current ?? pointRef.current ?? spotRef.current;
2192
+ if (!instance) return;
2193
+ if (light.attachedToCamera) {
2194
+ instance.position.copy(camera.position);
2195
+ return;
2196
+ }
2197
+ const attachment = light.attachment;
2198
+ if (!attachment) return;
2199
+ const bindingKey = objectBindings ? resolveBindingKey(objectBindings, attachment.objectId) : null;
2200
+ const binding = bindingKey ? objectBindings?.[bindingKey] : void 0;
2201
+ const modelObjectId = binding?.modelObjectId ?? attachment.objectId;
2202
+ const target = (bindingKey ? nodeRefs?.current[bindingKey] : void 0) ?? rootScene.getObjectByName(modelObjectId);
2203
+ if (!target) return;
2204
+ attachedPosition.fromArray(attachment.offset);
2205
+ target.localToWorld(attachedPosition);
2206
+ instance.position.copy(attachedPosition);
2160
2207
  });
2161
2208
  const castShadow = shadowsEnabled && shadows.enabled && light.castShadow;
2162
2209
  const fit = (0, import_react9.useMemo)(
@@ -2202,6 +2249,8 @@ function LightInstance({
2202
2249
  color: light.color,
2203
2250
  intensity: light.intensity,
2204
2251
  position,
2252
+ distance: light.distance,
2253
+ decay: light.decay,
2205
2254
  castShadow,
2206
2255
  "shadow-mapSize-width": shadows.mapSize,
2207
2256
  "shadow-mapSize-height": shadows.mapSize,
@@ -2227,7 +2276,9 @@ function Lights({
2227
2276
  shadowsEnabled = true,
2228
2277
  environmentRotation = 0,
2229
2278
  environmentConfig,
2230
- modelBounds
2279
+ modelBounds,
2280
+ objectBindings,
2281
+ nodeRefs
2231
2282
  }) {
2232
2283
  const lighting = lightingConfig ?? DEFAULT_LIGHTING;
2233
2284
  const shadows = shadowConfig ?? DEFAULT_SHADOWS;
@@ -2248,7 +2299,9 @@ function Lights({
2248
2299
  shadows,
2249
2300
  shadowsEnabled,
2250
2301
  environmentRotation,
2251
- modelBounds
2302
+ modelBounds,
2303
+ objectBindings,
2304
+ nodeRefs
2252
2305
  },
2253
2306
  light.id
2254
2307
  )),
@@ -2269,7 +2322,9 @@ function Lights({
2269
2322
  shadows,
2270
2323
  shadowsEnabled,
2271
2324
  environmentRotation: 0,
2272
- modelBounds
2325
+ modelBounds,
2326
+ objectBindings,
2327
+ nodeRefs
2273
2328
  }
2274
2329
  )
2275
2330
  ] });
@@ -2283,7 +2338,7 @@ var import_fiber7 = require("@react-three/fiber");
2283
2338
  var import_drei3 = require("@react-three/drei");
2284
2339
  var import_fiber6 = require("@react-three/fiber");
2285
2340
  var import_react10 = require("react");
2286
- var import_three10 = require("three");
2341
+ var import_three11 = require("three");
2287
2342
  var import_three_stdlib2 = require("three-stdlib");
2288
2343
  function createUnlitEmissiveTexturePlugin(parser) {
2289
2344
  return {
@@ -2309,7 +2364,7 @@ function createUnlitEmissiveTexturePlugin(parser) {
2309
2364
  [...wanted].map(async ([name, index]) => {
2310
2365
  const texture = await parser.getDependency("texture", index);
2311
2366
  if (texture) {
2312
- texture.colorSpace = import_three10.SRGBColorSpace;
2367
+ texture.colorSpace = import_three11.SRGBColorSpace;
2313
2368
  textureByName.set(name, texture);
2314
2369
  }
2315
2370
  })
@@ -2367,7 +2422,7 @@ function useConfiguredGLTF(url, config = {}) {
2367
2422
  }
2368
2423
 
2369
2424
  // src/components/Model.tsx
2370
- var import_three14 = require("three");
2425
+ var import_three15 = require("three");
2371
2426
  var import_three_stdlib3 = require("three-stdlib");
2372
2427
 
2373
2428
  // src/utils/animationClips.ts
@@ -2387,7 +2442,7 @@ function resolveAnimationClips(clips) {
2387
2442
  }
2388
2443
 
2389
2444
  // src/components/model/materialUtils.ts
2390
- var import_three11 = require("three");
2445
+ var import_three12 = require("three");
2391
2446
  function isMeshStandardMaterial(material2) {
2392
2447
  return Boolean(
2393
2448
  material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
@@ -2429,28 +2484,28 @@ var SRGB_TEXTURE_KEYS = /* @__PURE__ */ new Set([
2429
2484
  "specularColorMap"
2430
2485
  ]);
2431
2486
  function colorSpaceForTextureKey(key) {
2432
- return SRGB_TEXTURE_KEYS.has(key) ? import_three11.SRGBColorSpace : import_three11.NoColorSpace;
2487
+ return SRGB_TEXTURE_KEYS.has(key) ? import_three12.SRGBColorSpace : import_three12.NoColorSpace;
2433
2488
  }
2434
2489
  function getMaterialTexturePath(material2, key) {
2435
2490
  return material2?.[key]?.path;
2436
2491
  }
2437
2492
  function sideFromConfig(side) {
2438
- if (side === "back") return import_three11.BackSide;
2439
- if (side === "double") return import_three11.DoubleSide;
2440
- return import_three11.FrontSide;
2493
+ if (side === "back") return import_three12.BackSide;
2494
+ if (side === "double") return import_three12.DoubleSide;
2495
+ return import_three12.FrontSide;
2441
2496
  }
2442
2497
  function blendingFromConfig(blendingMode) {
2443
- if (blendingMode === "additive") return import_three11.AdditiveBlending;
2444
- return import_three11.NormalBlending;
2498
+ if (blendingMode === "additive") return import_three12.AdditiveBlending;
2499
+ return import_three12.NormalBlending;
2445
2500
  }
2446
2501
  function toSafeVector2(value, fallbackX = 1, fallbackY = fallbackX) {
2447
2502
  if (value && typeof value === "object") {
2448
2503
  const vectorLike = value;
2449
2504
  if (typeof vectorLike.x === "number" && Number.isFinite(vectorLike.x) && typeof vectorLike.y === "number" && Number.isFinite(vectorLike.y)) {
2450
- return new import_three11.Vector2(vectorLike.x, vectorLike.y);
2505
+ return new import_three12.Vector2(vectorLike.x, vectorLike.y);
2451
2506
  }
2452
2507
  }
2453
- return new import_three11.Vector2(fallbackX, fallbackY);
2508
+ return new import_three12.Vector2(fallbackX, fallbackY);
2454
2509
  }
2455
2510
  function clampAnimationSpeed(speed) {
2456
2511
  return Number.isFinite(speed) && speed > 0 ? speed : 1;
@@ -2465,12 +2520,12 @@ function getClipLoopMode(clipConfig) {
2465
2520
  }
2466
2521
 
2467
2522
  // src/components/model/materialFactory.ts
2468
- var import_three12 = require("three");
2523
+ var import_three13 = require("three");
2469
2524
  function toStandardMaterial(material2) {
2470
2525
  if (isMeshStandardMaterial(material2)) return material2;
2471
2526
  if (!material2 || typeof material2 !== "object") return null;
2472
2527
  const src = material2;
2473
- const std = new import_three12.MeshStandardMaterial();
2528
+ const std = new import_three13.MeshStandardMaterial();
2474
2529
  if (typeof src.name === "string") std.name = src.name;
2475
2530
  std.metalness = 0;
2476
2531
  std.roughness = 1;
@@ -2478,17 +2533,17 @@ function toStandardMaterial(material2) {
2478
2533
  if (isUnlit) {
2479
2534
  std.color.set(0);
2480
2535
  std.emissiveIntensity = 1;
2481
- if (src.map instanceof import_three12.Texture) {
2536
+ if (src.map instanceof import_three13.Texture) {
2482
2537
  std.emissiveMap = src.map;
2483
2538
  std.emissive.set(16777215);
2484
- } else if (src.color instanceof import_three12.Color) {
2539
+ } else if (src.color instanceof import_three13.Color) {
2485
2540
  std.emissive.copy(src.color);
2486
2541
  }
2487
2542
  } else {
2488
- if (src.color instanceof import_three12.Color) std.color.copy(src.color);
2489
- if (src.map instanceof import_three12.Texture) std.map = src.map;
2490
- if (src.emissive instanceof import_three12.Color) std.emissive.copy(src.emissive);
2491
- if (src.emissiveMap instanceof import_three12.Texture) std.emissiveMap = src.emissiveMap;
2543
+ if (src.color instanceof import_three13.Color) std.color.copy(src.color);
2544
+ if (src.map instanceof import_three13.Texture) std.map = src.map;
2545
+ if (src.emissive instanceof import_three13.Color) std.emissive.copy(src.emissive);
2546
+ if (src.emissiveMap instanceof import_three13.Texture) std.emissiveMap = src.emissiveMap;
2492
2547
  }
2493
2548
  const shared = [
2494
2549
  "aoMap",
@@ -2520,7 +2575,7 @@ function createPhysicalMaterialFromStandard(original) {
2520
2575
  if (isMeshPhysicalMaterial(original)) {
2521
2576
  return original.clone();
2522
2577
  }
2523
- const cloned = new import_three12.MeshPhysicalMaterial();
2578
+ const cloned = new import_three13.MeshPhysicalMaterial();
2524
2579
  cloned.name = original.name;
2525
2580
  cloned.color.copy(original.color);
2526
2581
  cloned.map = original.map;
@@ -2649,7 +2704,7 @@ function snapshotBaseMaterial(cloned) {
2649
2704
  }
2650
2705
 
2651
2706
  // src/components/model/applyMaterialConfig.ts
2652
- var import_three13 = require("three");
2707
+ var import_three14 = require("three");
2653
2708
  function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2654
2709
  material2.map = material2.userData.baseMap ?? null;
2655
2710
  material2.color.copy(material2.userData.baseColor);
@@ -2657,7 +2712,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2657
2712
  material2.metalnessMap = material2.userData.baseMetalnessMap ?? null;
2658
2713
  material2.roughness = material2.userData.baseRoughness ?? material2.roughness;
2659
2714
  material2.roughnessMap = material2.userData.baseRoughnessMap ?? null;
2660
- material2.emissive.copy(material2.userData.baseEmissive ?? new import_three13.Color(0, 0, 0));
2715
+ material2.emissive.copy(material2.userData.baseEmissive ?? new import_three14.Color(0, 0, 0));
2661
2716
  material2.emissiveIntensity = material2.userData.baseEmissiveIntensity ?? material2.emissiveIntensity;
2662
2717
  material2.emissiveMap = material2.userData.baseEmissiveMap ?? null;
2663
2718
  material2.opacity = material2.userData.baseOpacity ?? 1;
@@ -2666,9 +2721,9 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2666
2721
  material2.alphaTest = material2.userData.baseAlphaTest ?? 0;
2667
2722
  material2.alphaHash = material2.userData.baseAlphaHash ?? false;
2668
2723
  material2.dithering = material2.userData.baseDithering ?? false;
2669
- material2.blending = material2.userData.baseBlending ?? import_three13.NormalBlending;
2724
+ material2.blending = material2.userData.baseBlending ?? import_three14.NormalBlending;
2670
2725
  material2.depthWrite = material2.userData.baseDepthWrite ?? true;
2671
- material2.side = material2.userData.baseSide ?? import_three13.FrontSide;
2726
+ material2.side = material2.userData.baseSide ?? import_three14.FrontSide;
2672
2727
  material2.normalMap = material2.userData.baseNormalMap ?? null;
2673
2728
  material2.normalScale.copy(toSafeVector2(material2.userData.baseNormalScale));
2674
2729
  material2.bumpMap = material2.userData.baseBumpMap ?? null;
@@ -2688,7 +2743,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2688
2743
  );
2689
2744
  material2.sheen = material2.userData.baseSheen ?? material2.sheen;
2690
2745
  material2.sheenColor.copy(
2691
- material2.userData.baseSheenColor ?? new import_three13.Color(0, 0, 0)
2746
+ material2.userData.baseSheenColor ?? new import_three14.Color(0, 0, 0)
2692
2747
  );
2693
2748
  material2.sheenColorMap = material2.userData.baseSheenColorMap ?? null;
2694
2749
  material2.sheenRoughness = material2.userData.baseSheenRoughness ?? material2.sheenRoughness;
@@ -2701,14 +2756,14 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2701
2756
  material2.thickness = material2.userData.baseThickness ?? material2.thickness;
2702
2757
  material2.thicknessMap = material2.userData.baseThicknessMap ?? null;
2703
2758
  material2.attenuationColor.copy(
2704
- material2.userData.baseAttenuationColor ?? new import_three13.Color("#ffffff")
2759
+ material2.userData.baseAttenuationColor ?? new import_three14.Color("#ffffff")
2705
2760
  );
2706
2761
  material2.attenuationDistance = material2.userData.baseAttenuationDistance ?? material2.attenuationDistance;
2707
2762
  material2.reflectivity = material2.userData.baseReflectivity ?? material2.reflectivity;
2708
2763
  material2.specularIntensity = material2.userData.baseSpecularIntensity ?? material2.specularIntensity;
2709
2764
  material2.specularIntensityMap = material2.userData.baseSpecularIntensityMap ?? null;
2710
2765
  material2.specularColor.copy(
2711
- material2.userData.baseSpecularColor ?? new import_three13.Color("#ffffff")
2766
+ material2.userData.baseSpecularColor ?? new import_three14.Color("#ffffff")
2712
2767
  );
2713
2768
  material2.specularColorMap = material2.userData.baseSpecularColorMap ?? null;
2714
2769
  material2.flatShading = material2.userData.baseFlatShading ?? material2.flatShading;
@@ -2775,7 +2830,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2775
2830
  "specularColorMap"
2776
2831
  );
2777
2832
  if (texturePath) {
2778
- material2.map = getOrCreateTexture(texturePath, import_three13.SRGBColorSpace);
2833
+ material2.map = getOrCreateTexture(texturePath, import_three14.SRGBColorSpace);
2779
2834
  material2.color.set(resolvedBaseColor ?? "#ffffff");
2780
2835
  } else if (resolvedBaseColor) {
2781
2836
  material2.map = null;
@@ -2802,7 +2857,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2802
2857
  material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
2803
2858
  }
2804
2859
  if (emissiveMapPath) {
2805
- material2.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three13.SRGBColorSpace);
2860
+ material2.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three14.SRGBColorSpace);
2806
2861
  }
2807
2862
  if (configuredMaterial?.opacity !== void 0) {
2808
2863
  material2.opacity = configuredMaterial.opacity;
@@ -2890,7 +2945,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2890
2945
  if (sheenColorMapPath) {
2891
2946
  material2.sheenColorMap = getOrCreateTexture(
2892
2947
  sheenColorMapPath,
2893
- import_three13.SRGBColorSpace
2948
+ import_three14.SRGBColorSpace
2894
2949
  );
2895
2950
  }
2896
2951
  if (configuredMaterial?.sheenRoughness !== void 0) {
@@ -2943,10 +2998,10 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
2943
2998
  if (specularColorMapPath) {
2944
2999
  material2.specularColorMap = getOrCreateTexture(
2945
3000
  specularColorMapPath,
2946
- import_three13.SRGBColorSpace
3001
+ import_three14.SRGBColorSpace
2947
3002
  );
2948
3003
  }
2949
- material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !== import_three13.NormalBlending || Boolean((alphaMapPath || material2.alphaMap) && material2.alphaTest <= 0);
3004
+ material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !== import_three14.NormalBlending || Boolean((alphaMapPath || material2.alphaMap) && material2.alphaTest <= 0);
2950
3005
  if (!material2.transparent) {
2951
3006
  material2.depthWrite = true;
2952
3007
  }
@@ -2966,6 +3021,24 @@ function refreshSkinnedBounds(root) {
2966
3021
 
2967
3022
  // src/components/Model.tsx
2968
3023
  var import_jsx_runtime8 = require("react/jsx-runtime");
3024
+ function applyBindingTransform(node, transform, original) {
3025
+ if (!original) return;
3026
+ if (transform?.position) node.position.fromArray(transform.position);
3027
+ else node.position.copy(original.position);
3028
+ if (transform?.rotation) {
3029
+ node.rotation.set(
3030
+ transform.rotation[0],
3031
+ transform.rotation[1],
3032
+ transform.rotation[2],
3033
+ original.rotation.order
3034
+ );
3035
+ } else {
3036
+ node.rotation.copy(original.rotation);
3037
+ }
3038
+ if (transform?.scale) node.scale.fromArray(transform.scale);
3039
+ else node.scale.copy(original.scale);
3040
+ node.updateMatrix();
3041
+ }
2969
3042
  function firstMaterial(node) {
2970
3043
  const mat = node.material;
2971
3044
  if (!mat) return void 0;
@@ -3019,19 +3092,30 @@ function Model({
3019
3092
  });
3020
3093
  return map;
3021
3094
  }, [scene]);
3095
+ const originalNodeTransforms = (0, import_react11.useMemo)(() => {
3096
+ const transforms = /* @__PURE__ */ new WeakMap();
3097
+ scene.traverse((node) => {
3098
+ transforms.set(node, {
3099
+ position: node.position.clone(),
3100
+ rotation: node.rotation.clone(),
3101
+ scale: node.scale.clone()
3102
+ });
3103
+ });
3104
+ return transforms;
3105
+ }, [scene]);
3022
3106
  const shellNodeNames = (0, import_react11.useMemo)(() => {
3023
3107
  const shell = /* @__PURE__ */ new Set();
3024
- const sceneBox = new import_three14.Box3();
3108
+ const sceneBox = new import_three15.Box3();
3025
3109
  const meshNodes = [];
3026
3110
  for (const [name, node] of Object.entries(nodeMap)) {
3027
3111
  if (!isMeshNode(node)) continue;
3028
- const box = new import_three14.Box3().setFromObject(node);
3112
+ const box = new import_three15.Box3().setFromObject(node);
3029
3113
  if (box.isEmpty()) continue;
3030
3114
  meshNodes.push({ name, box });
3031
3115
  sceneBox.union(box);
3032
3116
  }
3033
- const sceneSize = sceneBox.getSize(new import_three14.Vector3());
3034
- const size = new import_three14.Vector3();
3117
+ const sceneSize = sceneBox.getSize(new import_three15.Vector3());
3118
+ const size = new import_three15.Vector3();
3035
3119
  for (const { name, box } of meshNodes) {
3036
3120
  box.getSize(size);
3037
3121
  const isShell = size.x > sceneSize.x * 0.7 && size.z > sceneSize.z * 0.7 && size.y > sceneSize.y * 0.4;
@@ -3071,6 +3155,43 @@ function Model({
3071
3155
  ).sort().join(""),
3072
3156
  [objectBindings]
3073
3157
  );
3158
+ const bindingTransformSignature = (0, import_react11.useMemo)(
3159
+ () => Object.entries(objectBindings).map(
3160
+ ([nodeName, binding]) => JSON.stringify([
3161
+ nodeName,
3162
+ binding.modelObjectId ?? nodeName,
3163
+ binding.transform ?? null
3164
+ ])
3165
+ ).sort().join(""),
3166
+ [objectBindings]
3167
+ );
3168
+ const transformedNodesRef = (0, import_react11.useRef)(/* @__PURE__ */ new Set());
3169
+ (0, import_react11.useEffect)(() => {
3170
+ for (const node of transformedNodesRef.current) {
3171
+ applyBindingTransform(node, void 0, originalNodeTransforms.get(node));
3172
+ }
3173
+ const nextTransformedNodes = /* @__PURE__ */ new Set();
3174
+ for (const [nodeName, binding] of Object.entries(objectBindings)) {
3175
+ if (!binding.transform) continue;
3176
+ const node = nodeMap[binding.modelObjectId ?? nodeName];
3177
+ if (!node) continue;
3178
+ applyBindingTransform(
3179
+ node,
3180
+ binding.transform,
3181
+ originalNodeTransforms.get(node)
3182
+ );
3183
+ nextTransformedNodes.add(node);
3184
+ }
3185
+ transformedNodesRef.current = nextTransformedNodes;
3186
+ scene.updateMatrixWorld(true);
3187
+ invalidate();
3188
+ }, [
3189
+ scene,
3190
+ nodeMap,
3191
+ originalNodeTransforms,
3192
+ bindingTransformSignature,
3193
+ invalidate
3194
+ ]);
3074
3195
  const perNodeMaterials = (0, import_react11.useMemo)(() => {
3075
3196
  const map = {};
3076
3197
  for (const [nodeName, binding] of Object.entries(objectBindings)) {
@@ -3105,7 +3226,7 @@ function Model({
3105
3226
  const map = {};
3106
3227
  if (shadingMode !== "unlit") return map;
3107
3228
  for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3108
- const basic = new import_three14.MeshBasicMaterial();
3229
+ const basic = new import_three15.MeshBasicMaterial();
3109
3230
  basic.color.copy(material2.color);
3110
3231
  basic.map = material2.map;
3111
3232
  basic.alphaMap = material2.alphaMap;
@@ -3121,7 +3242,7 @@ function Model({
3121
3242
  const map = {};
3122
3243
  if (shadingMode !== "normals") return map;
3123
3244
  for (const nodeName of Object.keys(perNodeMaterials)) {
3124
- map[nodeName] = new import_three14.MeshNormalMaterial();
3245
+ map[nodeName] = new import_three15.MeshNormalMaterial();
3125
3246
  }
3126
3247
  return map;
3127
3248
  }, [perNodeMaterials, shadingMode]);
@@ -3130,7 +3251,7 @@ function Model({
3130
3251
  if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
3131
3252
  const matcapTexture = getMatcapTexture();
3132
3253
  for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
3133
- const matcap = new import_three14.MeshMatcapMaterial();
3254
+ const matcap = new import_three15.MeshMatcapMaterial();
3134
3255
  matcap.matcap = matcapTexture;
3135
3256
  matcap.color.copy(material2.color);
3136
3257
  matcap.alphaMap = material2.alphaMap;
@@ -3304,7 +3425,7 @@ function Model({
3304
3425
  const sourceMaterials = Array.isArray(original) ? original : [original];
3305
3426
  const hasUv0 = Boolean(object.geometry.getAttribute("uv"));
3306
3427
  const diagnosticMaterials = sourceMaterials.map((source, index) => {
3307
- const material2 = new import_three14.MeshBasicMaterial({
3428
+ const material2 = new import_three15.MeshBasicMaterial({
3308
3429
  color: hasUv0 ? "#ffffff" : "#ff00ff",
3309
3430
  map: hasUv0 ? checkerTexture : null,
3310
3431
  side: source.side
@@ -3334,16 +3455,16 @@ function Model({
3334
3455
  const glbNodeName = binding.modelObjectId ?? nodeName;
3335
3456
  const node = nodeMap[glbNodeName];
3336
3457
  if (!isMeshNode(node)) continue;
3337
- const geometry = new import_three14.WireframeGeometry(
3458
+ const geometry = new import_three15.WireframeGeometry(
3338
3459
  node.geometry
3339
3460
  );
3340
- const lineMaterial = new import_three14.LineBasicMaterial({
3461
+ const lineMaterial = new import_three15.LineBasicMaterial({
3341
3462
  color: wireframeColor,
3342
3463
  transparent: true,
3343
3464
  opacity: wireframeOpacity,
3344
3465
  depthTest: false
3345
3466
  });
3346
- const line = new import_three14.LineSegments(geometry, lineMaterial);
3467
+ const line = new import_three15.LineSegments(geometry, lineMaterial);
3347
3468
  line.renderOrder = 1;
3348
3469
  node.add(line);
3349
3470
  added.push({ node, line });
@@ -3395,7 +3516,7 @@ function Model({
3395
3516
  const applyClipConfigToAction = (action, clipConfig) => {
3396
3517
  const loopMode = getClipLoopMode(clipConfig);
3397
3518
  action.setLoop(
3398
- loopMode === "once" ? import_three14.LoopOnce : import_three14.LoopRepeat,
3519
+ loopMode === "once" ? import_three15.LoopOnce : import_three15.LoopRepeat,
3399
3520
  loopMode === "once" ? 1 : Infinity
3400
3521
  );
3401
3522
  action.clampWhenFinished = loopMode === "once";
@@ -3462,7 +3583,7 @@ function Model({
3462
3583
  invalidate();
3463
3584
  };
3464
3585
  (0, import_react11.useEffect)(() => {
3465
- const mixer = new import_three14.AnimationMixer(scene);
3586
+ const mixer = new import_three15.AnimationMixer(scene);
3466
3587
  mixerRef.current = mixer;
3467
3588
  const actions = {};
3468
3589
  for (const { clip, sourceName } of resolvedAnimationClips) {
@@ -3553,17 +3674,17 @@ function Model({
3553
3674
  (0, import_fiber7.useFrame)((_, delta) => {
3554
3675
  if (!groupRef.current) return;
3555
3676
  const rotation = groupRef.current.rotation;
3556
- rotation.x = import_three14.MathUtils.lerp(
3677
+ rotation.x = import_three15.MathUtils.lerp(
3557
3678
  rotation.x,
3558
3679
  targetRotation[0],
3559
3680
  1 - Math.exp(-5 * delta)
3560
3681
  );
3561
- rotation.y = import_three14.MathUtils.lerp(
3682
+ rotation.y = import_three15.MathUtils.lerp(
3562
3683
  rotation.y,
3563
3684
  targetRotation[1],
3564
3685
  1 - Math.exp(-5 * delta)
3565
3686
  );
3566
- rotation.z = import_three14.MathUtils.lerp(
3687
+ rotation.z = import_three15.MathUtils.lerp(
3567
3688
  rotation.z,
3568
3689
  targetRotation[2],
3569
3690
  1 - Math.exp(-5 * delta)
@@ -3574,27 +3695,30 @@ function Model({
3574
3695
  const explodePartsRef = (0, import_react11.useRef)([]);
3575
3696
  const explodeStrengthRef = (0, import_react11.useRef)(0);
3576
3697
  const currentExplodeRef = (0, import_react11.useRef)(0);
3577
- const explodeTmp = (0, import_react11.useMemo)(() => new import_three14.Vector3(), []);
3698
+ const explodeTmp = (0, import_react11.useMemo)(() => new import_three15.Vector3(), []);
3578
3699
  (0, import_react11.useEffect)(() => {
3700
+ const bindingByNode = /* @__PURE__ */ new Map();
3701
+ for (const [nodeName, binding] of Object.entries(objectBindings)) {
3702
+ const node = nodeMap[binding.modelObjectId ?? nodeName];
3703
+ if (node) bindingByNode.set(node, binding);
3704
+ }
3579
3705
  for (const part of explodePartsRef.current) {
3580
- part.node.position.copy(part.restLocalPos);
3706
+ applyBindingTransform(
3707
+ part.node,
3708
+ bindingByNode.get(part.node)?.transform,
3709
+ originalNodeTransforms.get(part.node)
3710
+ );
3581
3711
  }
3582
3712
  currentExplodeRef.current = 0;
3583
3713
  scene.updateWorldMatrix(true, true);
3584
- const sceneBox = new import_three14.Box3().setFromObject(scene);
3585
- const modelCenter = sceneBox.getCenter(new import_three14.Vector3());
3586
- const radius = sceneBox.getSize(new import_three14.Vector3()).length() * 0.5;
3714
+ const sceneBox = new import_three15.Box3().setFromObject(scene);
3715
+ const modelCenter = sceneBox.getCenter(new import_three15.Vector3());
3716
+ const radius = sceneBox.getSize(new import_three15.Vector3()).length() * 0.5;
3587
3717
  explodeStrengthRef.current = Number.isFinite(radius) ? radius : 0;
3588
- const targets = [];
3589
- const targetSet = /* @__PURE__ */ new Set();
3590
- for (const [nodeName, binding] of Object.entries(objectBindings)) {
3591
- const node = nodeMap[binding.modelObjectId ?? nodeName];
3592
- if (!node || targetSet.has(node)) continue;
3593
- targetSet.add(node);
3594
- targets.push(node);
3595
- }
3718
+ const targets = [...bindingByNode.keys()];
3719
+ const targetSet = new Set(targets);
3596
3720
  const parts = [];
3597
- const partCenter = new import_three14.Vector3();
3721
+ const partCenter = new import_three15.Vector3();
3598
3722
  for (const node of targets) {
3599
3723
  if (!node.parent) continue;
3600
3724
  let ancestor = node.parent;
@@ -3607,7 +3731,7 @@ function Model({
3607
3731
  ancestor = ancestor.parent;
3608
3732
  }
3609
3733
  if (nested) continue;
3610
- const box = new import_three14.Box3().setFromObject(node);
3734
+ const box = new import_three15.Box3().setFromObject(node);
3611
3735
  if (box.isEmpty()) continue;
3612
3736
  box.getCenter(partCenter);
3613
3737
  const worldDir = partCenter.clone().sub(modelCenter);
@@ -3616,14 +3740,21 @@ function Model({
3616
3740
  parts.push({
3617
3741
  node,
3618
3742
  restLocalPos: node.position.clone(),
3619
- restWorldPos: node.getWorldPosition(new import_three14.Vector3()),
3743
+ restWorldPos: node.getWorldPosition(new import_three15.Vector3()),
3620
3744
  worldDir,
3621
- parentInverse: new import_three14.Matrix4().copy(node.parent.matrixWorld).invert()
3745
+ parentInverse: new import_three15.Matrix4().copy(node.parent.matrixWorld).invert()
3622
3746
  });
3623
3747
  }
3624
3748
  explodePartsRef.current = parts;
3625
3749
  invalidate();
3626
- }, [scene, nodeMap, nodeBindingSignature, invalidate]);
3750
+ }, [
3751
+ scene,
3752
+ nodeMap,
3753
+ nodeBindingSignature,
3754
+ bindingTransformSignature,
3755
+ originalNodeTransforms,
3756
+ invalidate
3757
+ ]);
3627
3758
  (0, import_react11.useEffect)(() => {
3628
3759
  invalidate();
3629
3760
  }, [explodeFactor, invalidate]);
@@ -3636,7 +3767,7 @@ function Model({
3636
3767
  if (current === target) return;
3637
3768
  currentExplodeRef.current = target;
3638
3769
  } else {
3639
- currentExplodeRef.current = import_three14.MathUtils.lerp(
3770
+ currentExplodeRef.current = import_three15.MathUtils.lerp(
3640
3771
  current,
3641
3772
  target,
3642
3773
  1 - Math.exp(-6 * delta)
@@ -3709,7 +3840,7 @@ function Model({
3709
3840
  var import_react12 = require("react");
3710
3841
  var import_drei4 = require("@react-three/drei");
3711
3842
  var import_fiber8 = require("@react-three/fiber");
3712
- var import_three15 = require("three");
3843
+ var import_three16 = require("three");
3713
3844
 
3714
3845
  // src/styles/AnnotationsLayer.css
3715
3846
  styleInject(".ri-annotationMarker--hovered {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n");
@@ -3722,7 +3853,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
3722
3853
  const scene = (0, import_fiber8.useThree)((state) => state.scene);
3723
3854
  const anchorNodeRef = (0, import_react12.useRef)(null);
3724
3855
  const baseLocalPosition = (0, import_react12.useMemo)(
3725
- () => new import_three15.Vector3(
3856
+ () => new import_three16.Vector3(
3726
3857
  ...annotation.anchorLocalPosition ?? annotation.localPosition ?? annotation.worldPosition
3727
3858
  ),
3728
3859
  [
@@ -3731,9 +3862,9 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
3731
3862
  annotation.worldPosition
3732
3863
  ]
3733
3864
  );
3734
- const worldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
3735
- const biasedWorldPosition = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
3736
- const toCamera = (0, import_react12.useMemo)(() => new import_three15.Vector3(), []);
3865
+ const worldPosition = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
3866
+ const biasedWorldPosition = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
3867
+ const toCamera = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
3737
3868
  (0, import_react12.useEffect)(() => {
3738
3869
  anchorNodeRef.current = null;
3739
3870
  }, [annotation.anchorNodeName]);
@@ -3756,7 +3887,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
3756
3887
  if (worldPositionsRef) {
3757
3888
  let stored = worldPositionsRef.current.get(annotation.id);
3758
3889
  if (!stored) {
3759
- stored = new import_three15.Vector3();
3890
+ stored = new import_three16.Vector3();
3760
3891
  worldPositionsRef.current.set(annotation.id, stored);
3761
3892
  }
3762
3893
  stored.copy(worldPosition);
@@ -4105,17 +4236,17 @@ function AnnotationsLayer({
4105
4236
  );
4106
4237
  (0, import_fiber8.useFrame)((_, delta) => {
4107
4238
  if (!groupRef.current) return;
4108
- groupRef.current.rotation.x = import_three15.MathUtils.lerp(
4239
+ groupRef.current.rotation.x = import_three16.MathUtils.lerp(
4109
4240
  groupRef.current.rotation.x,
4110
4241
  targetRotation[0],
4111
4242
  1 - Math.exp(-5 * delta)
4112
4243
  );
4113
- groupRef.current.rotation.y = import_three15.MathUtils.lerp(
4244
+ groupRef.current.rotation.y = import_three16.MathUtils.lerp(
4114
4245
  groupRef.current.rotation.y,
4115
4246
  targetRotation[1],
4116
4247
  1 - Math.exp(-5 * delta)
4117
4248
  );
4118
- groupRef.current.rotation.z = import_three15.MathUtils.lerp(
4249
+ groupRef.current.rotation.z = import_three16.MathUtils.lerp(
4119
4250
  groupRef.current.rotation.z,
4120
4251
  targetRotation[2],
4121
4252
  1 - Math.exp(-5 * delta)
@@ -4154,7 +4285,7 @@ function AnnotationsLayer({
4154
4285
  // src/components/ModelViewer/MeasurementLayer.tsx
4155
4286
  var import_react13 = require("react");
4156
4287
  var import_drei5 = require("@react-three/drei");
4157
- var import_three16 = require("three");
4288
+ var import_three17 = require("three");
4158
4289
  var import_jsx_runtime10 = require("react/jsx-runtime");
4159
4290
  var LINE_COLOR = "#22d3ee";
4160
4291
  var BOX_COLOR = "#facc15";
@@ -4182,8 +4313,8 @@ function DimensionsBox({ box, unit }) {
4182
4313
  );
4183
4314
  const edges = (0, import_react13.useMemo)(() => {
4184
4315
  const [w2, h2, d2] = box.size;
4185
- const geometry = new import_three16.EdgesGeometry(
4186
- new import_three16.BoxGeometry(w2 || 1e-4, h2 || 1e-4, d2 || 1e-4)
4316
+ const geometry = new import_three17.EdgesGeometry(
4317
+ new import_three17.BoxGeometry(w2 || 1e-4, h2 || 1e-4, d2 || 1e-4)
4187
4318
  );
4188
4319
  return geometry;
4189
4320
  }, [box.size]);
@@ -4229,7 +4360,7 @@ function MeasurementLayer({
4229
4360
  points.slice(1).map((point, index) => {
4230
4361
  const a = points[index].position;
4231
4362
  const b = point.position;
4232
- const distance = new import_three16.Vector3(...a).distanceTo(new import_three16.Vector3(...b));
4363
+ const distance = new import_three17.Vector3(...a).distanceTo(new import_three17.Vector3(...b));
4233
4364
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("group", { children: [
4234
4365
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_drei5.Line, { points: [a, b], color: LINE_COLOR, lineWidth: 2 }),
4235
4366
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MeasureLabel, { position: midpoint(a, b), children: formatMeasurement(distance, unit) })
@@ -4243,7 +4374,7 @@ function MeasurementLayer({
4243
4374
  var import_react14 = require("react");
4244
4375
  var import_xr3 = require("@react-three/xr");
4245
4376
  var import_fiber9 = require("@react-three/fiber");
4246
- var import_three17 = require("three");
4377
+ var import_three18 = require("three");
4247
4378
  var import_jsx_runtime11 = require("react/jsx-runtime");
4248
4379
  var TARGET_SIZE = 1;
4249
4380
  var FORWARD_DISTANCE = 1;
@@ -4280,13 +4411,13 @@ var XRModelPresenter = (0, import_react14.forwardRef)(
4280
4411
  () => box ? computeNormalization(box, scaleMode) : null,
4281
4412
  [box, scaleMode]
4282
4413
  );
4283
- const placedPosition = (0, import_react14.useRef)(new import_three17.Vector3(0, 0, -FORWARD_DISTANCE));
4414
+ const placedPosition = (0, import_react14.useRef)(new import_three18.Vector3(0, 0, -FORWARD_DISTANCE));
4284
4415
  const userScale = (0, import_react14.useRef)(1);
4285
4416
  const rotationY = (0, import_react14.useRef)(0);
4286
4417
  const placementMode = (0, import_react14.useRef)(true);
4287
4418
  const modelPlaced = (0, import_react14.useRef)(false);
4288
- const hitMatrix = (0, import_react14.useMemo)(() => new import_three17.Matrix4(), []);
4289
- const hitPosition = (0, import_react14.useRef)(new import_three17.Vector3());
4419
+ const hitMatrix = (0, import_react14.useMemo)(() => new import_three18.Matrix4(), []);
4420
+ const hitPosition = (0, import_react14.useRef)(new import_three18.Vector3());
4290
4421
  const hasHit = (0, import_react14.useRef)(false);
4291
4422
  const lastARState = (0, import_react14.useRef)(null);
4292
4423
  const gesturing = (0, import_react14.useRef)(false);
@@ -4479,7 +4610,7 @@ var XRModelPresenter_default = XRModelPresenter;
4479
4610
  // src/components/ModelViewer/CinematicCamera.tsx
4480
4611
  var import_react15 = require("react");
4481
4612
  var import_fiber10 = require("@react-three/fiber");
4482
- var import_three18 = require("three");
4613
+ var import_three19 = require("three");
4483
4614
  var ENTER_BLEND_SECONDS = 1.2;
4484
4615
  function CinematicCamera({
4485
4616
  controlsRef,
@@ -4558,14 +4689,14 @@ function CinematicCamera({
4558
4689
  const curves = (0, import_react15.useMemo)(() => {
4559
4690
  if (!hasPath) return null;
4560
4691
  return {
4561
- position: new import_three18.CatmullRomCurve3(
4562
- waypoints.map((w) => new import_three18.Vector3(...w.position)),
4692
+ position: new import_three19.CatmullRomCurve3(
4693
+ waypoints.map((w) => new import_three19.Vector3(...w.position)),
4563
4694
  loop,
4564
4695
  "catmullrom",
4565
4696
  0.5
4566
4697
  ),
4567
- target: new import_three18.CatmullRomCurve3(
4568
- waypoints.map((w) => new import_three18.Vector3(...w.target)),
4698
+ target: new import_three19.CatmullRomCurve3(
4699
+ waypoints.map((w) => new import_three19.Vector3(...w.target)),
4569
4700
  loop,
4570
4701
  "catmullrom",
4571
4702
  0.5
@@ -4581,8 +4712,8 @@ function CinematicCamera({
4581
4712
  const points = waypoints.map((waypoint) => ({
4582
4713
  azimuth: waypoint.azimuthAngle,
4583
4714
  polar: waypoint.polarAngle,
4584
- distance: new import_three18.Vector3(...waypoint.position).distanceTo(
4585
- new import_three18.Vector3(...waypoint.target)
4715
+ distance: new import_three19.Vector3(...waypoint.position).distanceTo(
4716
+ new import_three19.Vector3(...waypoint.target)
4586
4717
  )
4587
4718
  }));
4588
4719
  for (let index = 1; index < points.length; index += 1) {
@@ -4594,15 +4725,15 @@ function CinematicCamera({
4594
4725
  }, [signature, hasPath]);
4595
4726
  const elapsedRef = (0, import_react15.useRef)(0);
4596
4727
  const enterElapsedRef = (0, import_react15.useRef)(0);
4597
- const enterPos = (0, import_react15.useRef)(new import_three18.Vector3());
4598
- const enterTarget = (0, import_react15.useRef)(new import_three18.Vector3());
4728
+ const enterPos = (0, import_react15.useRef)(new import_three19.Vector3());
4729
+ const enterTarget = (0, import_react15.useRef)(new import_three19.Vector3());
4599
4730
  const startedRef = (0, import_react15.useRef)(false);
4600
4731
  const pausedElapsedRef = (0, import_react15.useRef)(0);
4601
4732
  const hasFinishedRef = (0, import_react15.useRef)(false);
4602
4733
  const orbitRef = (0, import_react15.useRef)(null);
4603
- const tmpPos = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
4604
- const tmpTarget = (0, import_react15.useMemo)(() => new import_three18.Vector3(), []);
4605
- const tmpSpherical = (0, import_react15.useMemo)(() => new import_three18.Spherical(), []);
4734
+ const tmpPos = (0, import_react15.useMemo)(() => new import_three19.Vector3(), []);
4735
+ const tmpTarget = (0, import_react15.useMemo)(() => new import_three19.Vector3(), []);
4736
+ const tmpSpherical = (0, import_react15.useMemo)(() => new import_three19.Spherical(), []);
4606
4737
  (0, import_react15.useEffect)(() => {
4607
4738
  elapsedRef.current = 0;
4608
4739
  pausedElapsedRef.current = 0;
@@ -4653,7 +4784,7 @@ function CinematicCamera({
4653
4784
  if (!hasPath) {
4654
4785
  const scene = sceneRef.current;
4655
4786
  if (scene) {
4656
- const sphere = new import_three18.Box3().setFromObject(scene).getBoundingSphere(new import_three18.Sphere());
4787
+ const sphere = new import_three19.Box3().setFromObject(scene).getBoundingSphere(new import_three19.Sphere());
4657
4788
  orbitRef.current = {
4658
4789
  center: sphere.center.clone(),
4659
4790
  radius: sphere.radius > 0 ? sphere.radius : 1
@@ -5817,7 +5948,7 @@ function consumeInitialAutoFitOverride(controls) {
5817
5948
  var import_react22 = require("react");
5818
5949
  var import_fiber11 = require("@react-three/fiber");
5819
5950
  var import_drei6 = require("@react-three/drei");
5820
- var import_three19 = require("three");
5951
+ var import_three20 = require("three");
5821
5952
 
5822
5953
  // src/assets/light-gizmos/sun.png
5823
5954
  var sun_default = "data:image/png;base64,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";
@@ -5840,7 +5971,7 @@ var RING_SCREEN_SIZE = SUN_SCREEN_SIZE * 3;
5840
5971
  var RING_COLOR = "#4d7dff";
5841
5972
  var LINE_COLOR2 = "#ffcc00";
5842
5973
  var LINE_GLOW_RADIUS = 0.02;
5843
- var UP_AXIS = new import_three19.Vector3(0, 1, 0);
5974
+ var UP_AXIS = new import_three20.Vector3(0, 1, 0);
5844
5975
  var AXIS_ARROW_LENGTH = 1.4;
5845
5976
  var AXIS_CONE_LENGTH = 0.35;
5846
5977
  var AXIS_CONE_RADIUS = 0.13;
@@ -5852,17 +5983,17 @@ var AXIS_COLORS = {
5852
5983
  z: "#4444ff"
5853
5984
  };
5854
5985
  function axisVector(axis) {
5855
- return new import_three19.Vector3(axis === "x" ? 1 : 0, axis === "y" ? 1 : 0, axis === "z" ? 1 : 0);
5986
+ return new import_three20.Vector3(axis === "x" ? 1 : 0, axis === "y" ? 1 : 0, axis === "z" ? 1 : 0);
5856
5987
  }
5857
5988
  function computeAxisDragPlaneNormal(axis, eye) {
5858
5989
  const axisVec = axisVector(axis);
5859
5990
  if (eye.lengthSq() < 1e-8) {
5860
5991
  eye = axisVec.clone();
5861
5992
  }
5862
- let normal = new import_three19.Vector3().crossVectors(axisVec, new import_three19.Vector3().crossVectors(eye, axisVec));
5993
+ let normal = new import_three20.Vector3().crossVectors(axisVec, new import_three20.Vector3().crossVectors(eye, axisVec));
5863
5994
  if (normal.lengthSq() < 1e-8) {
5864
- const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new import_three19.Vector3(1, 0, 0);
5865
- normal = new import_three19.Vector3().crossVectors(axisVec, helper);
5995
+ const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new import_three20.Vector3(1, 0, 0);
5996
+ normal = new import_three20.Vector3().crossVectors(axisVec, helper);
5866
5997
  }
5867
5998
  return normal.normalize();
5868
5999
  }
@@ -5934,18 +6065,18 @@ function LightHelpers({
5934
6065
  const shaftYRef = (0, import_react22.useRef)(null);
5935
6066
  const shaftZRef = (0, import_react22.useRef)(null);
5936
6067
  const axisDragRef = (0, import_react22.useRef)({ dragging: false, axis: null });
5937
- const axisDragPlaneRef = (0, import_react22.useRef)(new import_three19.Plane());
5938
- const dragStartPosRef = (0, import_react22.useRef)(new import_three19.Vector3());
6068
+ const axisDragPlaneRef = (0, import_react22.useRef)(new import_three20.Plane());
6069
+ const dragStartPosRef = (0, import_react22.useRef)(new import_three20.Vector3());
5939
6070
  const dragLineRef = (0, import_react22.useRef)(null);
5940
6071
  const dragLineGeomRef = (0, import_react22.useRef)(null);
5941
- const currentPosRef = (0, import_react22.useRef)(new import_three19.Vector3());
6072
+ const currentPosRef = (0, import_react22.useRef)(new import_three20.Vector3());
5942
6073
  const isDraggingRef = (0, import_react22.useRef)(false);
5943
- const dragPlaneRef = (0, import_react22.useRef)(new import_three19.Plane());
6074
+ const dragPlaneRef = (0, import_react22.useRef)(new import_three20.Plane());
5944
6075
  const onDraggingChangeRef = (0, import_react22.useRef)(onDraggingChange);
5945
6076
  (0, import_react22.useEffect)(() => {
5946
6077
  onDraggingChangeRef.current = onDraggingChange;
5947
6078
  }, [onDraggingChange]);
5948
- const lightPos = (0, import_react22.useMemo)(() => new import_three19.Vector3(position[0], position[1], position[2]), [position]);
6079
+ const lightPos = (0, import_react22.useMemo)(() => new import_three20.Vector3(position[0], position[1], position[2]), [position]);
5949
6080
  const linePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
5950
6081
  const dragLinePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
5951
6082
  const ringXPositions = (0, import_react22.useMemo)(() => makeRingPositions("x"), []);
@@ -5955,7 +6086,7 @@ function LightHelpers({
5955
6086
  const iconPath = isPointLight ? BULB_ICON_PATH : SUN_ICON_PATH;
5956
6087
  const iconScreenSize = isPointLight ? BULB_SCREEN_SIZE : SUN_SCREEN_SIZE;
5957
6088
  const sunTexture = (0, import_react22.useMemo)(() => {
5958
- const loader = new import_three19.TextureLoader();
6089
+ const loader = new import_three20.TextureLoader();
5959
6090
  return loader.load(iconPath);
5960
6091
  }, [iconPath]);
5961
6092
  const activeGizmoId = (0, import_react22.useSyncExternalStore)(subscribeActiveGizmo, getActiveGizmoSnapshot, getActiveGizmoSnapshot);
@@ -6062,7 +6193,7 @@ function LightHelpers({
6062
6193
  (0, import_react22.useEffect)(() => {
6063
6194
  const getMouseNdc = (event) => {
6064
6195
  const rect = gl.domElement.getBoundingClientRect();
6065
- return new import_three19.Vector2(
6196
+ return new import_three20.Vector2(
6066
6197
  (event.clientX - rect.left) / rect.width * 2 - 1,
6067
6198
  -((event.clientY - rect.top) / rect.height) * 2 + 1
6068
6199
  );
@@ -6104,12 +6235,12 @@ function LightHelpers({
6104
6235
  const axis = axisDragRef.current.axis;
6105
6236
  const currentPos = currentPosRef.current;
6106
6237
  const axisVec = axisVector(axis);
6107
- const eye = new import_three19.Vector3().subVectors(camera.position, currentPos);
6238
+ const eye = new import_three20.Vector3().subVectors(camera.position, currentPos);
6108
6239
  const planeNormal = computeAxisDragPlaneNormal(axis, eye);
6109
6240
  axisDragPlaneRef.current.setFromNormalAndCoplanarPoint(planeNormal, currentPos);
6110
6241
  const mouse2 = getMouseNdc(event);
6111
6242
  raycaster.setFromCamera(mouse2, camera);
6112
- const intersection2 = new import_three19.Vector3();
6243
+ const intersection2 = new import_three20.Vector3();
6113
6244
  const hitPoint2 = raycaster.ray.intersectPlane(axisDragPlaneRef.current, intersection2);
6114
6245
  if (!hitPoint2) return;
6115
6246
  const toPoint = hitPoint2.clone().sub(currentPos);
@@ -6128,7 +6259,7 @@ function LightHelpers({
6128
6259
  if (!isDraggingRef.current) return;
6129
6260
  const mouse = getMouseNdc(event);
6130
6261
  raycaster.setFromCamera(mouse, camera);
6131
- const intersection = new import_three19.Vector3();
6262
+ const intersection = new import_three20.Vector3();
6132
6263
  const hitPoint = raycaster.ray.intersectPlane(dragPlaneRef.current, intersection);
6133
6264
  if (!hitPoint) return;
6134
6265
  const newPos = [
@@ -6175,7 +6306,7 @@ function LightHelpers({
6175
6306
  setActiveTab?.("scene");
6176
6307
  setActiveSubTab?.("Lighting");
6177
6308
  }
6178
- const cameraDir = new import_three19.Vector3();
6309
+ const cameraDir = new import_three20.Vector3();
6179
6310
  camera.getWorldDirection(cameraDir);
6180
6311
  dragPlaneRef.current.setFromNormalAndCoplanarPoint(cameraDir, currentPosRef.current);
6181
6312
  isDraggingRef.current = true;
@@ -6238,7 +6369,7 @@ function LightHelpers({
6238
6369
  map: sunTexture,
6239
6370
  transparent: true,
6240
6371
  opacity: 0.9,
6241
- blending: import_three19.AdditiveBlending,
6372
+ blending: import_three20.AdditiveBlending,
6242
6373
  depthWrite: false
6243
6374
  },
6244
6375
  iconPath
@@ -6308,17 +6439,17 @@ function LightHelpers({
6308
6439
  ] }),
6309
6440
  gizmoVisible && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("group", { ref: axisGroupRef, children: axisArrows.map((axis) => {
6310
6441
  const axisColor = AXIS_COLORS[axis];
6311
- const end = new import_three19.Vector3(
6442
+ const end = new import_three20.Vector3(
6312
6443
  axis === "x" ? AXIS_ARROW_LENGTH : 0,
6313
6444
  axis === "y" ? AXIS_ARROW_LENGTH : 0,
6314
6445
  axis === "z" ? AXIS_ARROW_LENGTH : 0
6315
6446
  );
6316
- const conePos = new import_three19.Vector3(
6447
+ const conePos = new import_three20.Vector3(
6317
6448
  axis === "x" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
6318
6449
  axis === "y" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
6319
6450
  axis === "z" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0
6320
6451
  );
6321
- const shaftPos = new import_three19.Vector3(
6452
+ const shaftPos = new import_three20.Vector3(
6322
6453
  axis === "x" ? AXIS_ARROW_LENGTH / 2 : 0,
6323
6454
  axis === "y" ? AXIS_ARROW_LENGTH / 2 : 0,
6324
6455
  axis === "z" ? AXIS_ARROW_LENGTH / 2 : 0
@@ -6330,7 +6461,7 @@ function LightHelpers({
6330
6461
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6331
6462
  import_drei6.Line,
6332
6463
  {
6333
- points: [new import_three19.Vector3(0, 0, 0), end],
6464
+ points: [new import_three20.Vector3(0, 0, 0), end],
6334
6465
  color: axisColor,
6335
6466
  transparent: true,
6336
6467
  opacity: 0.9,
@@ -6358,7 +6489,7 @@ function LightHelpers({
6358
6489
  color: LINE_COLOR2,
6359
6490
  transparent: true,
6360
6491
  opacity: 0.05,
6361
- blending: import_three19.AdditiveBlending,
6492
+ blending: import_three20.AdditiveBlending,
6362
6493
  depthWrite: false,
6363
6494
  depthTest: false
6364
6495
  }
@@ -6379,7 +6510,7 @@ function LightHelpers({
6379
6510
  color: LINE_COLOR2,
6380
6511
  transparent: true,
6381
6512
  opacity: 0.7,
6382
- blending: import_three19.AdditiveBlending
6513
+ blending: import_three20.AdditiveBlending
6383
6514
  }
6384
6515
  )
6385
6516
  ] })
@@ -6419,7 +6550,7 @@ function findInfluencingBone(hit) {
6419
6550
  if (!skinIndex || !skinWeight || !position) return null;
6420
6551
  const localHit = mesh.worldToLocal(hit.point.clone());
6421
6552
  const vertices = [hit.face.a, hit.face.b, hit.face.c];
6422
- const vertex = new import_three20.Vector3();
6553
+ const vertex = new import_three21.Vector3();
6423
6554
  let nearest = vertices[0];
6424
6555
  let nearestDist = Infinity;
6425
6556
  for (const vi of vertices) {
@@ -6469,9 +6600,9 @@ function GuidedTourCameraFollow({
6469
6600
  const scene = (0, import_fiber12.useThree)((state) => state.scene);
6470
6601
  const nodeRef = (0, import_react23.useRef)(null);
6471
6602
  const hasPrev = (0, import_react23.useRef)(false);
6472
- const live = (0, import_react23.useMemo)(() => new import_three20.Vector3(), []);
6473
- const prev = (0, import_react23.useMemo)(() => new import_three20.Vector3(), []);
6474
- const goal = (0, import_react23.useMemo)(() => new import_three20.Vector3(), []);
6603
+ const live = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
6604
+ const prev = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
6605
+ const goal = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
6475
6606
  const nodeName = annotation?.anchorNodeName;
6476
6607
  const localPosition = annotation?.anchorLocalPosition;
6477
6608
  (0, import_react23.useEffect)(() => {
@@ -6510,8 +6641,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
6510
6641
  isModelPlaced: false
6511
6642
  };
6512
6643
  function computeMeasureBox(scene) {
6513
- const box = new import_three20.Box3().setFromObject(scene);
6514
- const size = new import_three20.Vector3();
6644
+ const box = new import_three21.Box3().setFromObject(scene);
6645
+ const size = new import_three21.Vector3();
6515
6646
  box.getSize(size);
6516
6647
  return {
6517
6648
  min: [box.min.x, box.min.y, box.min.z],
@@ -6530,18 +6661,18 @@ function computeUvMeshStats(scene) {
6530
6661
  return stats;
6531
6662
  }
6532
6663
  function computeModelBounds(scene) {
6533
- const box = new import_three20.Box3().setFromObject(scene);
6664
+ const box = new import_three21.Box3().setFromObject(scene);
6534
6665
  if (box.isEmpty()) return null;
6535
- const center = box.getCenter(new import_three20.Vector3());
6536
- const radius = box.getSize(new import_three20.Vector3()).length() / 2;
6666
+ const center = box.getCenter(new import_three21.Vector3());
6667
+ const radius = box.getSize(new import_three21.Vector3()).length() / 2;
6537
6668
  if (!Number.isFinite(radius) || radius <= 0) return null;
6538
6669
  return { center: [center.x, center.y, center.z], radius };
6539
6670
  }
6540
6671
  function fitClippingPlanesToScene(scene, controls) {
6541
6672
  const camera = controls?.camera;
6542
- if (!(camera instanceof import_three20.PerspectiveCamera)) return;
6543
- const box = new import_three20.Box3().setFromObject(scene);
6544
- const size = box.getSize(new import_three20.Vector3()).length();
6673
+ if (!(camera instanceof import_three21.PerspectiveCamera)) return;
6674
+ const box = new import_three21.Box3().setFromObject(scene);
6675
+ const size = box.getSize(new import_three21.Vector3()).length();
6545
6676
  if (!Number.isFinite(size) || size <= 0) return;
6546
6677
  camera.near = size / 100;
6547
6678
  camera.far = size * 100;
@@ -6726,8 +6857,8 @@ function ModelViewer({
6726
6857
  if (!controls || initialCameraStateRef.current) {
6727
6858
  return;
6728
6859
  }
6729
- const position = controls.getPosition(new import_three20.Vector3());
6730
- const target = controls.getTarget(new import_three20.Vector3());
6860
+ const position = controls.getPosition(new import_three21.Vector3());
6861
+ const target = controls.getTarget(new import_three21.Vector3());
6731
6862
  initialCameraStateRef.current = {
6732
6863
  position: [position.x, position.y, position.z],
6733
6864
  target: [target.x, target.y, target.z]
@@ -6985,7 +7116,7 @@ function ModelViewer({
6985
7116
  const controls = controlsRef.current;
6986
7117
  const scene = sceneRef.current;
6987
7118
  if (!controls) return;
6988
- const target = new import_three20.Vector3();
7119
+ const target = new import_three21.Vector3();
6989
7120
  const livePosition = annotationWorldPositionsRef.current.get(
6990
7121
  annotation.id
6991
7122
  );
@@ -7003,15 +7134,15 @@ function ModelViewer({
7003
7134
  controls.smoothTime = 0.6;
7004
7135
  const storedNormal = annotation.worldNormal;
7005
7136
  if (scene) {
7006
- const box = new import_three20.Box3().setFromObject(scene);
7007
- const modelCenter = new import_three20.Vector3();
7137
+ const box = new import_three21.Box3().setFromObject(scene);
7138
+ const modelCenter = new import_three21.Vector3();
7008
7139
  box.getCenter(modelCenter);
7009
- const modelSize = new import_three20.Vector3();
7140
+ const modelSize = new import_three21.Vector3();
7010
7141
  box.getSize(modelSize);
7011
7142
  const modelRadius = modelSize.length() * 0.2;
7012
7143
  let outwardDir;
7013
7144
  if (storedNormal) {
7014
- outwardDir = new import_three20.Vector3(
7145
+ outwardDir = new import_three21.Vector3(
7015
7146
  storedNormal[0],
7016
7147
  storedNormal[1],
7017
7148
  storedNormal[2]
@@ -7162,6 +7293,16 @@ function ModelViewer({
7162
7293
  const isControlledBindings = onObjectBindingsChange !== void 0;
7163
7294
  const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
7164
7295
  currentObjectBindingsRef.current = currentObjectBindings;
7296
+ const currentBindingTransformSignature = (0, import_react23.useMemo)(
7297
+ () => Object.entries(currentObjectBindings).map(
7298
+ ([bindingKey, binding]) => JSON.stringify([
7299
+ bindingKey,
7300
+ binding.modelObjectId,
7301
+ binding.transform ?? null
7302
+ ])
7303
+ ).sort().join(""),
7304
+ [currentObjectBindings]
7305
+ );
7165
7306
  const currentHiddenObjects = (0, import_react23.useMemo)(
7166
7307
  () => Object.fromEntries(
7167
7308
  Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
@@ -7301,7 +7442,7 @@ function ModelViewer({
7301
7442
  const fallbackY = Math.max(minOffset, (rect.height - height) / 2);
7302
7443
  const objectKey = resolveObjectKey(objectId, binding);
7303
7444
  const object = objectKey ? nodeRefsMap.current[objectKey] : null;
7304
- const clamp2 = (value, max) => {
7445
+ const clamp3 = (value, max) => {
7305
7446
  if (max <= minOffset) {
7306
7447
  return minOffset;
7307
7448
  }
@@ -7309,23 +7450,23 @@ function ModelViewer({
7309
7450
  };
7310
7451
  if (canvasContainer && cameraControls && object) {
7311
7452
  const canvasRect = canvasContainer.getBoundingClientRect();
7312
- const objectCenter = new import_three20.Vector3();
7313
- new import_three20.Box3().setFromObject(object).getCenter(objectCenter);
7453
+ const objectCenter = new import_three21.Vector3();
7454
+ new import_three21.Box3().setFromObject(object).getCenter(objectCenter);
7314
7455
  objectCenter.project(cameraControls);
7315
7456
  const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
7316
7457
  const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
7317
7458
  return {
7318
7459
  objectId,
7319
- x: clamp2(projectedX + 24, maxX),
7320
- y: clamp2(projectedY - height / 2, maxY)
7460
+ x: clamp3(projectedX + 24, maxX),
7461
+ y: clamp3(projectedY - height / 2, maxY)
7321
7462
  };
7322
7463
  }
7323
7464
  const rawX = typeof fallbackCoords?.screenX === "number" ? fallbackCoords.screenX - rect.left : fallbackX;
7324
7465
  const rawY = typeof fallbackCoords?.screenY === "number" ? fallbackCoords.screenY - rect.top : fallbackY;
7325
7466
  return {
7326
7467
  objectId,
7327
- x: clamp2(rawX, maxX),
7328
- y: clamp2(rawY, maxY)
7468
+ x: clamp3(rawX, maxX),
7469
+ y: clamp3(rawY, maxY)
7329
7470
  };
7330
7471
  },
7331
7472
  [resolveObjectKey]
@@ -7334,7 +7475,6 @@ function ModelViewer({
7334
7475
  (event) => {
7335
7476
  const { objectId, action, binding } = event;
7336
7477
  setCurrentAction(event);
7337
- onAction?.(event);
7338
7478
  switch (action.id) {
7339
7479
  case "toggle-visibility": {
7340
7480
  let targetKey = objectId;
@@ -7382,6 +7522,7 @@ function ModelViewer({
7382
7522
  default:
7383
7523
  break;
7384
7524
  }
7525
+ onAction?.(event);
7385
7526
  },
7386
7527
  [
7387
7528
  applyObjectBindings,
@@ -7522,7 +7663,8 @@ function ModelViewer({
7522
7663
  objectBindings: currentObjectBindingsRef.current,
7523
7664
  nodeRefs: nodeRefsMap.current,
7524
7665
  homeCameraState: initialCameraStateRef.current,
7525
- captureImage: (options) => captureImage(rendererCaptureRef.current, options)
7666
+ captureImage: (options) => captureImage(rendererCaptureRef.current, options),
7667
+ invalidate: () => rendererCaptureRef.current?.invalidate()
7526
7668
  });
7527
7669
  }, []);
7528
7670
  const fitScene = (0, import_react23.useCallback)(
@@ -7534,14 +7676,14 @@ function ModelViewer({
7534
7676
  }
7535
7677
  try {
7536
7678
  if (flyIn) {
7537
- const target = controls.getTarget(new import_three20.Vector3());
7538
- const position = controls.getPosition(new import_three20.Vector3());
7679
+ const target = controls.getTarget(new import_three21.Vector3());
7680
+ const position = controls.getPosition(new import_three21.Vector3());
7539
7681
  const direction = position.clone().sub(target);
7540
7682
  if (direction.lengthSq() < 1e-6) {
7541
7683
  direction.set(0, 0, 1);
7542
7684
  }
7543
7685
  direction.normalize();
7544
- const sphere = new import_three20.Box3().setFromObject(scene).getBoundingSphere(new import_three20.Sphere());
7686
+ const sphere = new import_three21.Box3().setFromObject(scene).getBoundingSphere(new import_three21.Sphere());
7545
7687
  const farDistance = Math.max(sphere.radius * 10, 20);
7546
7688
  const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
7547
7689
  await controls.setLookAt(
@@ -7853,6 +7995,13 @@ function ModelViewer({
7853
7995
  );
7854
7996
  setBindingsMismatch(!hasAnyMatch);
7855
7997
  }, [modelLoaded, currentObjectBindings]);
7998
+ (0, import_react23.useEffect)(() => {
7999
+ const scene = sceneRef.current;
8000
+ if (!modelLoaded || !scene) return;
8001
+ scene.updateMatrixWorld(true);
8002
+ setMeasureBox(computeMeasureBox(scene));
8003
+ setModelBounds(computeModelBounds(scene));
8004
+ }, [modelLoaded, currentBindingTransformSignature]);
7856
8005
  const clearMeasurements = (0, import_react23.useCallback)(() => setMeasurePoints([]), []);
7857
8006
  const handleMeasurePointerDown = (0, import_react23.useCallback)(
7858
8007
  (event) => {
@@ -7878,8 +8027,8 @@ function ModelViewer({
7878
8027
  const rect = canvas.getBoundingClientRect();
7879
8028
  const ndcX = (event.clientX - rect.left) / rect.width * 2 - 1;
7880
8029
  const ndcY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
7881
- const raycaster = new import_three20.Raycaster();
7882
- raycaster.setFromCamera(new import_three20.Vector2(ndcX, ndcY), cam);
8030
+ const raycaster = new import_three21.Raycaster();
8031
+ raycaster.setFromCamera(new import_three21.Vector2(ndcX, ndcY), cam);
7883
8032
  const hits = raycaster.intersectObject(scene, true);
7884
8033
  if (hits.length === 0) return;
7885
8034
  const { point } = hits[0];
@@ -7896,7 +8045,11 @@ function ModelViewer({
7896
8045
  );
7897
8046
  (0, import_react23.useEffect)(() => {
7898
8047
  setMeasurePoints([]);
7899
- }, [modelUrl, resolvedSceneConfig.model.rotation]);
8048
+ }, [
8049
+ modelUrl,
8050
+ resolvedSceneConfig.model.rotation,
8051
+ currentBindingTransformSignature
8052
+ ]);
7900
8053
  const handleDownloadMesh = (0, import_react23.useCallback)(() => {
7901
8054
  const matRefs = materialRefsOut.current;
7902
8055
  const scene = sceneRef.current;
@@ -8000,8 +8153,8 @@ function ModelViewer({
8000
8153
  const rect = canvas.getBoundingClientRect();
8001
8154
  const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
8002
8155
  const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1;
8003
- const raycaster = new import_three20.Raycaster();
8004
- raycaster.setFromCamera(new import_three20.Vector2(ndcX, ndcY), cam);
8156
+ const raycaster = new import_three21.Raycaster();
8157
+ raycaster.setFromCamera(new import_three21.Vector2(ndcX, ndcY), cam);
8005
8158
  const hits = raycaster.intersectObject(scene, true);
8006
8159
  if (hits.length === 0) return;
8007
8160
  const hit = hits[0];
@@ -8239,11 +8392,13 @@ function ModelViewer({
8239
8392
  shadowsEnabled: shadows,
8240
8393
  environmentRotation: resolvedSceneConfig.environment.rotation,
8241
8394
  environmentConfig: resolvedSceneConfig.environment,
8242
- modelBounds
8395
+ modelBounds,
8396
+ objectBindings: currentObjectBindings,
8397
+ nodeRefs: nodeRefsMap
8243
8398
  }
8244
8399
  ),
8245
8400
  showLightHelpers && resolvedSceneConfig.lighting.lights.map((light) => {
8246
- if (!light.visible || light.type === "none" || light.type === "hemisphere") {
8401
+ if (!light.visible || light.type === "none" || light.type === "hemisphere" || light.attachment) {
8247
8402
  return null;
8248
8403
  }
8249
8404
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
@@ -8885,7 +9040,7 @@ function ModelViewer({
8885
9040
  }
8886
9041
 
8887
9042
  // src/components/BindingBuilder/BindingBuilder.tsx
8888
- var import_react41 = require("react");
9043
+ var import_react42 = require("react");
8889
9044
  var import_lucide_react16 = require("lucide-react");
8890
9045
 
8891
9046
  // src/styles/BindingBuilder.css
@@ -8893,32 +9048,13 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
8893
9048
 
8894
9049
  // src/hooks/useViewerCamera.ts
8895
9050
  var import_react24 = require("react");
8896
- var import_three21 = require("three");
8897
-
8898
- // src/utils/resolveBindingKey.ts
8899
- function resolveBindingKey(objectBindings, objectId) {
8900
- if (objectBindings[objectId]) {
8901
- return objectId;
8902
- }
8903
- const idMatch = Object.entries(objectBindings).find(
8904
- ([, binding]) => binding.id === objectId
8905
- )?.[0];
8906
- if (idMatch) {
8907
- return idMatch;
8908
- }
8909
- const modelObjectMatch = Object.entries(objectBindings).find(
8910
- ([, binding]) => binding.modelObjectId === objectId
8911
- )?.[0];
8912
- return modelObjectMatch ?? null;
8913
- }
8914
-
8915
- // src/hooks/useViewerCamera.ts
9051
+ var import_three22 = require("three");
8916
9052
  function toTuple(vector) {
8917
9053
  return [vector.x, vector.y, vector.z];
8918
9054
  }
8919
9055
  function buildCameraState(camera, controls) {
8920
- const position = new import_three21.Vector3();
8921
- const target = new import_three21.Vector3();
9056
+ const position = new import_three22.Vector3();
9057
+ const target = new import_three22.Vector3();
8922
9058
  controls.getPosition(position);
8923
9059
  controls.getTarget(target);
8924
9060
  return {
@@ -8945,11 +9081,16 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
8945
9081
  controls.smoothTime = originalSmoothTime;
8946
9082
  }
8947
9083
  }
8948
- function useViewerCamera() {
9084
+ function useViewerCamera(options = {}) {
9085
+ const optionsRef = (0, import_react24.useRef)(options);
9086
+ optionsRef.current = options;
8949
9087
  const viewerRef = (0, import_react24.useRef)(null);
8950
9088
  const [cameraState, setCameraStateValue] = (0, import_react24.useState)(
8951
9089
  null
8952
9090
  );
9091
+ const [cameraPresets, setCameraPresets] = (0, import_react24.useState)(() => ({ ...options.presets }));
9092
+ const cameraPresetsRef = (0, import_react24.useRef)(cameraPresets);
9093
+ cameraPresetsRef.current = cameraPresets;
8953
9094
  const updateCameraState = (0, import_react24.useCallback)((nextState) => {
8954
9095
  setCameraStateValue(
8955
9096
  (previousState) => isSameCameraState(previousState, nextState) ? previousState : nextState
@@ -8963,16 +9104,56 @@ function useViewerCamera() {
8963
9104
  );
8964
9105
  const homeCameraStateRef = (0, import_react24.useRef)(null);
8965
9106
  const homeSceneRef = (0, import_react24.useRef)(null);
9107
+ const initializedControlsRef = (0, import_react24.useRef)(null);
8966
9108
  const handleViewerReady = (0, import_react24.useCallback)((viewer) => {
8967
9109
  viewerRef.current = viewer;
8968
- updateCameraState(buildCameraState(viewer.controls.camera, viewer.controls));
9110
+ const currentState = buildCameraState(
9111
+ viewer.controls.camera,
9112
+ viewer.controls
9113
+ );
9114
+ updateCameraState(currentState);
8969
9115
  if (viewer.scene !== homeSceneRef.current) {
8970
9116
  homeSceneRef.current = viewer.scene;
8971
9117
  homeCameraStateRef.current = null;
9118
+ initializedControlsRef.current = null;
8972
9119
  }
8973
9120
  if (viewer.homeCameraState && !homeCameraStateRef.current) {
8974
9121
  homeCameraStateRef.current = viewer.homeCameraState;
8975
9122
  }
9123
+ const { initialPosition, initialTarget, initialTransition = false } = optionsRef.current;
9124
+ if ((initialPosition || initialTarget) && viewer.scene && // A target-only setup should preserve ModelViewer's fitted or authored
9125
+ // camera position. Wait until that initial framing has settled instead
9126
+ // of cancelling auto-fit while the camera is still at its fallback.
9127
+ (initialPosition || viewer.homeCameraState) && initializedControlsRef.current !== viewer.controls) {
9128
+ initializedControlsRef.current = viewer.controls;
9129
+ if (!viewer.homeCameraState) {
9130
+ markInitialAutoFitOverride(viewer.controls);
9131
+ }
9132
+ const position = initialPosition ?? currentState.position;
9133
+ const target = initialTarget ?? currentState.target;
9134
+ void viewer.controls.setLookAt(
9135
+ position[0],
9136
+ position[1],
9137
+ position[2],
9138
+ target[0],
9139
+ target[1],
9140
+ target[2],
9141
+ initialTransition
9142
+ ).then(() => {
9143
+ if (viewerRef.current?.controls !== viewer.controls) {
9144
+ return;
9145
+ }
9146
+ const nextState = buildCameraState(
9147
+ viewer.controls.camera,
9148
+ viewer.controls
9149
+ );
9150
+ homeCameraStateRef.current = {
9151
+ position: nextState.position,
9152
+ target: nextState.target
9153
+ };
9154
+ updateCameraState(nextState);
9155
+ }).catch(() => void 0);
9156
+ }
8976
9157
  }, [updateCameraState]);
8977
9158
  const resetView = (0, import_react24.useCallback)(async () => {
8978
9159
  const viewer = viewerRef.current;
@@ -9068,27 +9249,33 @@ function useViewerCamera() {
9068
9249
  return false;
9069
9250
  }
9070
9251
  try {
9252
+ if (!viewerRef.current?.homeCameraState) {
9253
+ markInitialAutoFitOverride(controls);
9254
+ }
9071
9255
  await controls.setTarget(
9072
9256
  target[0],
9073
9257
  target[1],
9074
9258
  target[2],
9075
9259
  enableTransition
9076
9260
  );
9261
+ updateCameraState(buildCameraState(controls.camera, controls));
9077
9262
  return true;
9078
9263
  } catch {
9079
9264
  return false;
9080
9265
  }
9081
9266
  },
9082
- []
9267
+ [updateCameraState]
9083
9268
  );
9084
- const setCameraState = (0, import_react24.useCallback)(
9269
+ const applyCameraPreset = (0, import_react24.useCallback)(
9085
9270
  async (state, enableTransition = true) => {
9086
9271
  const controls = viewerRef.current?.controls;
9087
9272
  if (!controls) {
9088
9273
  return false;
9089
9274
  }
9090
9275
  try {
9091
- markInitialAutoFitOverride(controls);
9276
+ if (!viewerRef.current?.homeCameraState) {
9277
+ markInitialAutoFitOverride(controls);
9278
+ }
9092
9279
  const transitions = [
9093
9280
  controls.setLookAt(
9094
9281
  state.position[0],
@@ -9112,11 +9299,11 @@ function useViewerCamera() {
9112
9299
  await Promise.all(transitions);
9113
9300
  const camera = controls.camera;
9114
9301
  let projectionChanged = false;
9115
- if (state.fov !== null && "fov" in camera && typeof camera.fov === "number") {
9302
+ if (state.fov != null && "fov" in camera && typeof camera.fov === "number") {
9116
9303
  camera.fov = state.fov;
9117
9304
  projectionChanged = true;
9118
9305
  }
9119
- if ("zoom" in camera && typeof camera.zoom === "number") {
9306
+ if (state.zoom != null && "zoom" in camera && typeof camera.zoom === "number") {
9120
9307
  camera.zoom = state.zoom;
9121
9308
  projectionChanged = true;
9122
9309
  }
@@ -9131,13 +9318,94 @@ function useViewerCamera() {
9131
9318
  },
9132
9319
  [updateCameraState]
9133
9320
  );
9321
+ const setCameraState = (0, import_react24.useCallback)(
9322
+ (state, enableTransition = true) => applyCameraPreset(state, enableTransition),
9323
+ [applyCameraPreset]
9324
+ );
9325
+ const lookAt = (0, import_react24.useCallback)(
9326
+ (position, target, enableTransition = true) => applyCameraPreset({ position, target }, enableTransition),
9327
+ [applyCameraPreset]
9328
+ );
9329
+ const orbitTo = (0, import_react24.useCallback)(
9330
+ async (azimuthAngle, polarAngle, enableTransition = true) => {
9331
+ const controls = viewerRef.current?.controls;
9332
+ if (!controls) {
9333
+ return false;
9334
+ }
9335
+ try {
9336
+ if (!viewerRef.current?.homeCameraState) {
9337
+ markInitialAutoFitOverride(controls);
9338
+ }
9339
+ await controls.rotateTo(
9340
+ azimuthAngle,
9341
+ polarAngle,
9342
+ enableTransition
9343
+ );
9344
+ updateCameraState(buildCameraState(controls.camera, controls));
9345
+ return true;
9346
+ } catch {
9347
+ return false;
9348
+ }
9349
+ },
9350
+ [updateCameraState]
9351
+ );
9352
+ const dollyTo = (0, import_react24.useCallback)(
9353
+ async (distance, enableTransition = true) => {
9354
+ const controls = viewerRef.current?.controls;
9355
+ if (!controls) {
9356
+ return false;
9357
+ }
9358
+ try {
9359
+ if (!viewerRef.current?.homeCameraState) {
9360
+ markInitialAutoFitOverride(controls);
9361
+ }
9362
+ await controls.dollyTo(distance, enableTransition);
9363
+ updateCameraState(buildCameraState(controls.camera, controls));
9364
+ return true;
9365
+ } catch {
9366
+ return false;
9367
+ }
9368
+ },
9369
+ [updateCameraState]
9370
+ );
9371
+ const savePreset = (0, import_react24.useCallback)((name) => {
9372
+ const controls = viewerRef.current?.controls;
9373
+ const trimmedName = name.trim();
9374
+ if (!controls || !trimmedName) {
9375
+ return null;
9376
+ }
9377
+ const nextState = buildCameraState(controls.camera, controls);
9378
+ cameraPresetsRef.current = {
9379
+ ...cameraPresetsRef.current,
9380
+ [trimmedName]: nextState
9381
+ };
9382
+ setCameraPresets((current) => ({
9383
+ ...current,
9384
+ [trimmedName]: nextState
9385
+ }));
9386
+ updateCameraState(nextState);
9387
+ return nextState;
9388
+ }, [updateCameraState]);
9389
+ const goToPreset = (0, import_react24.useCallback)(
9390
+ (name, enableTransition = true) => {
9391
+ const preset = cameraPresetsRef.current[name];
9392
+ return preset ? applyCameraPreset(preset, enableTransition) : Promise.resolve(false);
9393
+ },
9394
+ [applyCameraPreset]
9395
+ );
9134
9396
  return {
9135
9397
  cameraState,
9398
+ cameraPresets,
9136
9399
  resetView,
9137
9400
  focusObject,
9138
9401
  fitScene,
9139
9402
  setCameraTarget,
9140
9403
  setCameraState,
9404
+ lookAt,
9405
+ orbitTo,
9406
+ dollyTo,
9407
+ savePreset,
9408
+ goToPreset,
9141
9409
  handleCameraChange,
9142
9410
  handleViewerReady
9143
9411
  };
@@ -14994,6 +15262,9 @@ function IdentityPanel({
14994
15262
  ] }) });
14995
15263
  }
14996
15264
 
15265
+ // src/components/BindingBuilder/components/BasicsPanel.tsx
15266
+ var import_react38 = require("react");
15267
+
14997
15268
  // src/components/BindingBuilder/utils/constants.ts
14998
15269
  var typeSuggestions = [
14999
15270
  "other",
@@ -15033,6 +15304,31 @@ function BasicsPanel({
15033
15304
  onToggle,
15034
15305
  updateBinding
15035
15306
  }) {
15307
+ const [groupDraft, setGroupDraft] = (0, import_react38.useState)(selectedBinding.group ?? "");
15308
+ const [tagsDraft, setTagsDraft] = (0, import_react38.useState)(
15309
+ selectedBinding.tags?.join(", ") ?? ""
15310
+ );
15311
+ (0, import_react38.useEffect)(() => {
15312
+ setGroupDraft(selectedBinding.group ?? "");
15313
+ setTagsDraft(selectedBinding.tags?.join(", ") ?? "");
15314
+ }, [selectedItem, selectedBinding.group, selectedBinding.tags]);
15315
+ const commitGroup = () => {
15316
+ updateBinding(selectedItem, (binding) => ({
15317
+ ...binding,
15318
+ group: groupDraft.trim() || void 0
15319
+ }));
15320
+ };
15321
+ const commitTags = () => {
15322
+ const tags = [
15323
+ ...new Set(
15324
+ tagsDraft.split(",").map((tag) => tag.trim()).filter(Boolean)
15325
+ )
15326
+ ];
15327
+ updateBinding(selectedItem, (binding) => ({
15328
+ ...binding,
15329
+ tags: tags.length > 0 ? tags : void 0
15330
+ }));
15331
+ };
15036
15332
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(BBSection, { title: "Basics", open, onToggle, children: [
15037
15333
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-grid", children: [
15038
15334
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
@@ -15079,6 +15375,38 @@ function BasicsPanel({
15079
15375
  children: statusOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: option, children: option }, option))
15080
15376
  }
15081
15377
  )
15378
+ ] }),
15379
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
15380
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Group" }),
15381
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
15382
+ "input",
15383
+ {
15384
+ className: "bb-input",
15385
+ placeholder: "paint",
15386
+ value: groupDraft,
15387
+ onChange: (event) => setGroupDraft(event.target.value),
15388
+ onBlur: commitGroup,
15389
+ onKeyDown: (event) => {
15390
+ if (event.key === "Enter") event.currentTarget.blur();
15391
+ }
15392
+ }
15393
+ )
15394
+ ] }),
15395
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
15396
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Tags" }),
15397
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
15398
+ "input",
15399
+ {
15400
+ className: "bb-input",
15401
+ placeholder: "exterior, configurable",
15402
+ value: tagsDraft,
15403
+ onChange: (event) => setTagsDraft(event.target.value),
15404
+ onBlur: commitTags,
15405
+ onKeyDown: (event) => {
15406
+ if (event.key === "Enter") event.currentTarget.blur();
15407
+ }
15408
+ }
15409
+ )
15082
15410
  ] })
15083
15411
  ] }),
15084
15412
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
@@ -15129,7 +15457,7 @@ function BasicsPanel({
15129
15457
  }
15130
15458
 
15131
15459
  // src/components/BindingBuilder/components/ActionsPanel.tsx
15132
- var import_react38 = require("react");
15460
+ var import_react39 = require("react");
15133
15461
 
15134
15462
  // src/components/BindingBuilder/utils/actionCatalog.ts
15135
15463
  var ACTION_CATALOG = [
@@ -15155,19 +15483,19 @@ var ACTION_CATALOG = [
15155
15483
  id: "toggle-light",
15156
15484
  label: "Toggle Light",
15157
15485
  handling: "host",
15158
- description: "Seeded on light bindings. Handle it in your onAction callback."
15486
+ description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
15159
15487
  },
15160
15488
  {
15161
15489
  id: "change-light-color",
15162
15490
  label: "Change Light Color",
15163
15491
  handling: "host",
15164
- description: "Seeded on light bindings. Handle it in your onAction callback."
15492
+ description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
15165
15493
  },
15166
15494
  {
15167
15495
  id: "set-brightness",
15168
15496
  label: "Set Brightness",
15169
15497
  handling: "host",
15170
- description: "Seeded on light bindings. Handle it in your onAction callback."
15498
+ description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
15171
15499
  }
15172
15500
  ];
15173
15501
  var CUSTOM_ACTION_ID = "__custom__";
@@ -15228,8 +15556,8 @@ function ActionsPanel({
15228
15556
  onToggle,
15229
15557
  updateBinding
15230
15558
  }) {
15231
- const [customRows, setCustomRows] = (0, import_react38.useState)(/* @__PURE__ */ new Set());
15232
- (0, import_react38.useEffect)(() => {
15559
+ const [customRows, setCustomRows] = (0, import_react39.useState)(/* @__PURE__ */ new Set());
15560
+ (0, import_react39.useEffect)(() => {
15233
15561
  setCustomRows(/* @__PURE__ */ new Set());
15234
15562
  }, [selectedItem]);
15235
15563
  const actions = selectedBinding.actions ?? [];
@@ -15345,8 +15673,8 @@ function ActionsPanel({
15345
15673
  }
15346
15674
  )
15347
15675
  ] }),
15348
- catalogEntry ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required \u2014 the button renders but can never be identified in onAction." }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
15349
- "Unrecognised ID \u2014 the viewer has no built-in behaviour for it, so your app must handle it in onAction.",
15676
+ catalogEntry ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required, the button renders but can never be identified in onAction." }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
15677
+ "Unrecognised ID, the viewer has no built-in behavior for it. Add declarative effects and execute them with useViewerActions, or handle it in onAction.",
15350
15678
  suggestion ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
15351
15679
  " Did you mean ",
15352
15680
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
@@ -15369,7 +15697,13 @@ function ActionsPanel({
15369
15697
  "?"
15370
15698
  ] }) : null
15371
15699
  ] }),
15372
- isDuplicate ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID \u2014 they will render as two identical buttons." }) : null
15700
+ action.effects?.length ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "bb-note", children: [
15701
+ action.effects.length,
15702
+ " declarative effect",
15703
+ action.effects.length === 1 ? "" : "s",
15704
+ ", execute with useViewerActions."
15705
+ ] }) : null,
15706
+ isDuplicate ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID, they will render as two identical buttons." }) : null
15373
15707
  ] }),
15374
15708
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
15375
15709
  "button",
@@ -15518,7 +15852,7 @@ function CameraPositionPanel({
15518
15852
  }
15519
15853
 
15520
15854
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
15521
- var import_react39 = require("react");
15855
+ var import_react40 = require("react");
15522
15856
 
15523
15857
  // src/components/BindingBuilder/utils/bindingFactory.ts
15524
15858
  function slugify2(value) {
@@ -15607,9 +15941,9 @@ function SceneNodesPanel({
15607
15941
  isOpen = true,
15608
15942
  onToggle
15609
15943
  }) {
15610
- const [search, setSearch] = (0, import_react39.useState)("");
15611
- const nodeKeys = (0, import_react39.useMemo)(() => Object.keys(bindings), [bindings]);
15612
- const filteredNodeKeys = (0, import_react39.useMemo)(() => {
15944
+ const [search, setSearch] = (0, import_react40.useState)("");
15945
+ const nodeKeys = (0, import_react40.useMemo)(() => Object.keys(bindings), [bindings]);
15946
+ const filteredNodeKeys = (0, import_react40.useMemo)(() => {
15613
15947
  const query = search.trim().toLowerCase();
15614
15948
  if (!query) return nodeKeys;
15615
15949
  return nodeKeys.filter((nodeKey) => {
@@ -15752,7 +16086,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
15752
16086
  }
15753
16087
 
15754
16088
  // src/components/BindingBuilder/hooks/useUndoableState.ts
15755
- var import_react40 = require("react");
16089
+ var import_react41 = require("react");
15756
16090
  var MAX_HISTORY = 100;
15757
16091
  function historyReducer(state, action) {
15758
16092
  switch (action.type) {
@@ -15791,13 +16125,13 @@ function historyReducer(state, action) {
15791
16125
  }
15792
16126
  }
15793
16127
  function useUndoableState(initial, coalesceWindowMs = 600) {
15794
- const [state, dispatch] = (0, import_react40.useReducer)(historyReducer, {
16128
+ const [state, dispatch] = (0, import_react41.useReducer)(historyReducer, {
15795
16129
  past: [],
15796
16130
  present: initial,
15797
16131
  future: []
15798
16132
  });
15799
- const lastSetAt = (0, import_react40.useRef)(0);
15800
- const setValue = (0, import_react40.useCallback)(
16133
+ const lastSetAt = (0, import_react41.useRef)(0);
16134
+ const setValue = (0, import_react41.useCallback)(
15801
16135
  (updater) => {
15802
16136
  const now = Date.now();
15803
16137
  const coalesce = now - lastSetAt.current < coalesceWindowMs;
@@ -15806,15 +16140,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
15806
16140
  },
15807
16141
  [coalesceWindowMs]
15808
16142
  );
15809
- const undo = (0, import_react40.useCallback)(() => {
16143
+ const undo = (0, import_react41.useCallback)(() => {
15810
16144
  lastSetAt.current = 0;
15811
16145
  dispatch({ type: "undo" });
15812
16146
  }, []);
15813
- const redo = (0, import_react40.useCallback)(() => {
16147
+ const redo = (0, import_react41.useCallback)(() => {
15814
16148
  lastSetAt.current = 0;
15815
16149
  dispatch({ type: "redo" });
15816
16150
  }, []);
15817
- const reset = (0, import_react40.useCallback)((value) => {
16151
+ const reset = (0, import_react41.useCallback)((value) => {
15818
16152
  lastSetAt.current = 0;
15819
16153
  dispatch({ type: "reset", value });
15820
16154
  }, []);
@@ -15838,9 +16172,9 @@ function toBindingsJson(bindings) {
15838
16172
  }
15839
16173
  function toBindingsTypeScript(bindings) {
15840
16174
  return [
15841
- 'import type { ObjectBinding } from "@liveroom-tech/react-immersive";',
16175
+ 'import { defineObjectBindings } from "@liveroom-tech/react-immersive";',
15842
16176
  "",
15843
- `export const objectBindings: Record<string, ObjectBinding> = ${JSON.stringify(bindings, null, 2)};`,
16177
+ `export const objectBindings = defineObjectBindings(${JSON.stringify(bindings, null, 2)});`,
15844
16178
  ""
15845
16179
  ].join("\n");
15846
16180
  }
@@ -16060,30 +16394,30 @@ function mergeSceneConfigSection(current, incoming) {
16060
16394
  return next;
16061
16395
  }
16062
16396
  function BindingBuilder({ licenseKey }) {
16063
- const modelInputRef = (0, import_react41.useRef)(null);
16064
- const bindingsInputRef = (0, import_react41.useRef)(null);
16065
- const sceneConfigInputRef = (0, import_react41.useRef)(null);
16066
- const [modelUrl, setModelUrl] = (0, import_react41.useState)(null);
16067
- const [modelName, setModelName] = (0, import_react41.useState)("");
16068
- const [selectedItem, setSelectedItem] = (0, import_react41.useState)(null);
16397
+ const modelInputRef = (0, import_react42.useRef)(null);
16398
+ const bindingsInputRef = (0, import_react42.useRef)(null);
16399
+ const sceneConfigInputRef = (0, import_react42.useRef)(null);
16400
+ const [modelUrl, setModelUrl] = (0, import_react42.useState)(null);
16401
+ const [modelName, setModelName] = (0, import_react42.useState)("");
16402
+ const [selectedItem, setSelectedItem] = (0, import_react42.useState)(null);
16069
16403
  const bindingsHistory = useUndoableState({});
16070
16404
  const bindings = bindingsHistory.value;
16071
16405
  const setBindings = bindingsHistory.setValue;
16072
- const [loading, setLoading] = (0, import_react41.useState)(false);
16073
- const [isExporting, setIsExporting] = (0, import_react41.useState)(false);
16074
- const [statusMessage, setStatusMessage] = (0, import_react41.useState)(
16406
+ const [loading, setLoading] = (0, import_react42.useState)(false);
16407
+ const [isExporting, setIsExporting] = (0, import_react42.useState)(false);
16408
+ const [statusMessage, setStatusMessage] = (0, import_react42.useState)(
16075
16409
  "Load the demo model or upload a GLB/GLTF model to generate bindings."
16076
16410
  );
16077
- const [statusTone, setStatusTone] = (0, import_react41.useState)("default");
16078
- const [statusBannerVisible, setStatusBannerVisible] = (0, import_react41.useState)(false);
16079
- const [metricsDraft, setMetricsDraft] = (0, import_react41.useState)("{}");
16080
- const [metadataDraft, setMetadataDraft] = (0, import_react41.useState)("{}");
16081
- const [metricsError, setMetricsError] = (0, import_react41.useState)(null);
16082
- const [metadataError, setMetadataError] = (0, import_react41.useState)(null);
16083
- const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react41.useState)(false);
16084
- const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react41.useState)(false);
16085
- const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react41.useState)(false);
16086
- const [openObjectSections, setOpenObjectSections] = (0, import_react41.useState)({
16411
+ const [statusTone, setStatusTone] = (0, import_react42.useState)("default");
16412
+ const [statusBannerVisible, setStatusBannerVisible] = (0, import_react42.useState)(false);
16413
+ const [metricsDraft, setMetricsDraft] = (0, import_react42.useState)("{}");
16414
+ const [metadataDraft, setMetadataDraft] = (0, import_react42.useState)("{}");
16415
+ const [metricsError, setMetricsError] = (0, import_react42.useState)(null);
16416
+ const [metadataError, setMetadataError] = (0, import_react42.useState)(null);
16417
+ const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react42.useState)(false);
16418
+ const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react42.useState)(false);
16419
+ const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react42.useState)(false);
16420
+ const [openObjectSections, setOpenObjectSections] = (0, import_react42.useState)({
16087
16421
  identity: true,
16088
16422
  basics: true,
16089
16423
  material: false,
@@ -16092,7 +16426,7 @@ function BindingBuilder({ licenseKey }) {
16092
16426
  metrics: false,
16093
16427
  metadata: false
16094
16428
  });
16095
- const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react41.useState)({
16429
+ const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react42.useState)({
16096
16430
  pbr: true,
16097
16431
  roughness: false,
16098
16432
  emission: false,
@@ -16108,27 +16442,27 @@ function BindingBuilder({ licenseKey }) {
16108
16442
  });
16109
16443
  const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
16110
16444
  const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
16111
- const [activeEditorTab, setActiveEditorTab] = (0, import_react41.useState)(
16445
+ const [activeEditorTab, setActiveEditorTab] = (0, import_react42.useState)(
16112
16446
  "object"
16113
16447
  );
16114
- const [activeSubTab, setActiveSubTab] = (0, import_react41.useState)("General");
16115
- const [pbrWorkflow, setPbrWorkflow] = (0, import_react41.useState)("metalness");
16116
- const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react41.useState)(createMaterialPanelEnabledState());
16117
- const materialPanelItemRef = (0, import_react41.useRef)(null);
16118
- const [zoomOnSelected, setZoomOnSelected] = (0, import_react41.useState)(false);
16119
- const [highlightOnHover, setHighlightOnHover] = (0, import_react41.useState)(true);
16120
- const [resetDialogOpen, setResetDialogOpen] = (0, import_react41.useState)(false);
16121
- const [resetTarget, setResetTarget] = (0, import_react41.useState)(
16448
+ const [activeSubTab, setActiveSubTab] = (0, import_react42.useState)("General");
16449
+ const [pbrWorkflow, setPbrWorkflow] = (0, import_react42.useState)("metalness");
16450
+ const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react42.useState)(createMaterialPanelEnabledState());
16451
+ const materialPanelItemRef = (0, import_react42.useRef)(null);
16452
+ const [zoomOnSelected, setZoomOnSelected] = (0, import_react42.useState)(false);
16453
+ const [highlightOnHover, setHighlightOnHover] = (0, import_react42.useState)(true);
16454
+ const [resetDialogOpen, setResetDialogOpen] = (0, import_react42.useState)(false);
16455
+ const [resetTarget, setResetTarget] = (0, import_react42.useState)(
16122
16456
  null
16123
16457
  );
16124
16458
  const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
16125
16459
  const sceneConfig = sceneConfigHistory.value;
16126
16460
  const setSceneConfig = sceneConfigHistory.setValue;
16127
- const [activeAnnotation, setActiveAnnotation] = (0, import_react41.useState)(null);
16128
- const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react41.useState)(null);
16461
+ const [activeAnnotation, setActiveAnnotation] = (0, import_react42.useState)(null);
16462
+ const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react42.useState)(null);
16129
16463
  const license = useLicense(licenseKey);
16130
16464
  const tier = normalizeTier(license.tier);
16131
- const bindingBuilderContextValue = (0, import_react41.useMemo)(
16465
+ const bindingBuilderContextValue = (0, import_react42.useMemo)(
16132
16466
  () => ({
16133
16467
  isInsideEditor: true,
16134
16468
  activeTab: activeEditorTab,
@@ -16139,11 +16473,11 @@ function BindingBuilder({ licenseKey }) {
16139
16473
  }),
16140
16474
  [activeEditorTab, activeSubTab, tier]
16141
16475
  );
16142
- const uploadedModelRef = (0, import_react41.useRef)(null);
16143
- const previewDragDepthRef = (0, import_react41.useRef)(0);
16144
- const loadRequestRef = (0, import_react41.useRef)(0);
16145
- const pendingLoadRef = (0, import_react41.useRef)(null);
16146
- const loadedSceneRef = (0, import_react41.useRef)(null);
16476
+ const uploadedModelRef = (0, import_react42.useRef)(null);
16477
+ const previewDragDepthRef = (0, import_react42.useRef)(0);
16478
+ const loadRequestRef = (0, import_react42.useRef)(0);
16479
+ const pendingLoadRef = (0, import_react42.useRef)(null);
16480
+ const loadedSceneRef = (0, import_react42.useRef)(null);
16147
16481
  const {
16148
16482
  cameraState,
16149
16483
  resetView,
@@ -16153,7 +16487,7 @@ function BindingBuilder({ licenseKey }) {
16153
16487
  handleCameraChange,
16154
16488
  handleViewerReady
16155
16489
  } = useViewerCamera();
16156
- const handlePreviewWaypoint = (0, import_react41.useCallback)(
16490
+ const handlePreviewWaypoint = (0, import_react42.useCallback)(
16157
16491
  (waypoint) => {
16158
16492
  setCameraState(
16159
16493
  {
@@ -16177,7 +16511,7 @@ function BindingBuilder({ licenseKey }) {
16177
16511
  position: formatVector3(cameraState.position),
16178
16512
  target: formatVector3(cameraState.target)
16179
16513
  } : null;
16180
- const handleAnnotationsChange = (0, import_react41.useCallback)(
16514
+ const handleAnnotationsChange = (0, import_react42.useCallback)(
16181
16515
  (newAnnotations) => {
16182
16516
  setSceneConfig((prev) => {
16183
16517
  const currentIds = prev.annotations.map((a) => a.id).join(",");
@@ -16193,7 +16527,7 @@ function BindingBuilder({ licenseKey }) {
16193
16527
  },
16194
16528
  []
16195
16529
  );
16196
- const handleActiveAnnotationChange = (0, import_react41.useCallback)(
16530
+ const handleActiveAnnotationChange = (0, import_react42.useCallback)(
16197
16531
  (annotation) => {
16198
16532
  setActiveAnnotation(
16199
16533
  annotation ? {
@@ -16203,7 +16537,7 @@ function BindingBuilder({ licenseKey }) {
16203
16537
  },
16204
16538
  []
16205
16539
  );
16206
- (0, import_react41.useEffect)(() => {
16540
+ (0, import_react42.useEffect)(() => {
16207
16541
  if (!selectedBinding) {
16208
16542
  setMetricsDraft("{}");
16209
16543
  setMetadataDraft("{}");
@@ -16216,7 +16550,7 @@ function BindingBuilder({ licenseKey }) {
16216
16550
  setMetricsError(null);
16217
16551
  setMetadataError(null);
16218
16552
  }, [selectedBinding]);
16219
- (0, import_react41.useEffect)(() => {
16553
+ (0, import_react42.useEffect)(() => {
16220
16554
  const derived = createMaterialPanelEnabledState(selectedMaterial);
16221
16555
  if (materialPanelItemRef.current !== selectedItem) {
16222
16556
  materialPanelItemRef.current = selectedItem;
@@ -16235,12 +16569,12 @@ function BindingBuilder({ licenseKey }) {
16235
16569
  return changed ? next : prev;
16236
16570
  });
16237
16571
  }, [selectedItem, selectedMaterial]);
16238
- (0, import_react41.useEffect)(() => {
16572
+ (0, import_react42.useEffect)(() => {
16239
16573
  return () => {
16240
16574
  uploadedModelRef.current?.cleanup();
16241
16575
  };
16242
16576
  }, []);
16243
- (0, import_react41.useEffect)(() => {
16577
+ (0, import_react42.useEffect)(() => {
16244
16578
  const handleKeyDown = (event) => {
16245
16579
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
16246
16580
  return;
@@ -16261,7 +16595,7 @@ function BindingBuilder({ licenseKey }) {
16261
16595
  window.addEventListener("keydown", handleKeyDown);
16262
16596
  return () => window.removeEventListener("keydown", handleKeyDown);
16263
16597
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
16264
- (0, import_react41.useEffect)(() => {
16598
+ (0, import_react42.useEffect)(() => {
16265
16599
  if (loading || statusTone === "default" || statusTone === "success") return;
16266
16600
  setStatusBannerVisible(true);
16267
16601
  const timeout = window.setTimeout(
@@ -16270,8 +16604,8 @@ function BindingBuilder({ licenseKey }) {
16270
16604
  );
16271
16605
  return () => window.clearTimeout(timeout);
16272
16606
  }, [statusMessage, statusTone, loading]);
16273
- const nodeKeys = (0, import_react41.useMemo)(() => Object.keys(bindings), [bindings]);
16274
- const idError = (0, import_react41.useMemo)(() => {
16607
+ const nodeKeys = (0, import_react42.useMemo)(() => Object.keys(bindings), [bindings]);
16608
+ const idError = (0, import_react42.useMemo)(() => {
16275
16609
  if (!selectedItem || !selectedBinding) return null;
16276
16610
  const trimmed = selectedBinding.id.trim();
16277
16611
  if (!trimmed) return "ID is required.";
@@ -16314,7 +16648,7 @@ function BindingBuilder({ licenseKey }) {
16314
16648
  });
16315
16649
  }
16316
16650
  };
16317
- const applyBindingsFromScene = (0, import_react41.useCallback)(
16651
+ const applyBindingsFromScene = (0, import_react42.useCallback)(
16318
16652
  (scene, nextModelName) => {
16319
16653
  const nodeNames = collectNodeNames(scene);
16320
16654
  bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
@@ -16353,7 +16687,7 @@ function BindingBuilder({ licenseKey }) {
16353
16687
  bindingsHistory.reset({});
16354
16688
  setSelectedItem(null);
16355
16689
  };
16356
- const handleViewerModelLoaded = (0, import_react41.useCallback)(
16690
+ const handleViewerModelLoaded = (0, import_react42.useCallback)(
16357
16691
  (scene) => {
16358
16692
  const pending = pendingLoadRef.current;
16359
16693
  if (!pending || pending.requestId !== loadRequestRef.current) return;
@@ -16363,7 +16697,7 @@ function BindingBuilder({ licenseKey }) {
16363
16697
  },
16364
16698
  [applyBindingsFromScene]
16365
16699
  );
16366
- const handleViewerLoadError = (0, import_react41.useCallback)((error) => {
16700
+ const handleViewerLoadError = (0, import_react42.useCallback)((error) => {
16367
16701
  const pending = pendingLoadRef.current;
16368
16702
  if (!pending || pending.requestId !== loadRequestRef.current) return;
16369
16703
  pendingLoadRef.current = null;
@@ -16374,7 +16708,7 @@ function BindingBuilder({ licenseKey }) {
16374
16708
  setStatusTone("error");
16375
16709
  setLoading(false);
16376
16710
  }, []);
16377
- const handleAnimationsReady = (0, import_react41.useCallback)(
16711
+ const handleAnimationsReady = (0, import_react42.useCallback)(
16378
16712
  (controls) => {
16379
16713
  viewerAnimations.handleAnimationsReady(controls);
16380
16714
  const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
@@ -16675,7 +17009,7 @@ function BindingBuilder({ licenseKey }) {
16675
17009
  }));
16676
17010
  setError(null);
16677
17011
  };
16678
- const handleViewerSelect = (0, import_react41.useCallback)(
17012
+ const handleViewerSelect = (0, import_react42.useCallback)(
16679
17013
  (binding) => {
16680
17014
  if (!binding) {
16681
17015
  setSelectedItem(null);
@@ -16688,7 +17022,7 @@ function BindingBuilder({ licenseKey }) {
16688
17022
  },
16689
17023
  [bindings]
16690
17024
  );
16691
- const handleSceneNodeSelect = (0, import_react41.useCallback)(
17025
+ const handleSceneNodeSelect = (0, import_react42.useCallback)(
16692
17026
  async (nodeKey) => {
16693
17027
  setSelectedItem(nodeKey);
16694
17028
  setPanelHoveredBinding(null);
@@ -16696,7 +17030,7 @@ function BindingBuilder({ licenseKey }) {
16696
17030
  },
16697
17031
  [focusObject]
16698
17032
  );
16699
- const saveCameraPositionForSelectedObject = (0, import_react41.useCallback)(() => {
17033
+ const saveCameraPositionForSelectedObject = (0, import_react42.useCallback)(() => {
16700
17034
  if (!selectedItem || !cameraState) {
16701
17035
  return;
16702
17036
  }
@@ -16711,7 +17045,7 @@ function BindingBuilder({ licenseKey }) {
16711
17045
  cameraState: currentCameraState
16712
17046
  }));
16713
17047
  }, [selectedItem, cameraState]);
16714
- const goToSavedCameraPosition = (0, import_react41.useCallback)(() => {
17048
+ const goToSavedCameraPosition = (0, import_react42.useCallback)(() => {
16715
17049
  if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
16716
17050
  setCameraState(
16717
17051
  {
@@ -16723,28 +17057,28 @@ function BindingBuilder({ licenseKey }) {
16723
17057
  true
16724
17058
  );
16725
17059
  }, [savedCameraPosition, setCameraState, cameraState]);
16726
- const clearSavedCameraPosition = (0, import_react41.useCallback)(() => {
17060
+ const clearSavedCameraPosition = (0, import_react42.useCallback)(() => {
16727
17061
  if (!selectedItem) return;
16728
17062
  updateBinding(selectedItem, (binding) => ({
16729
17063
  ...binding,
16730
17064
  cameraState: void 0
16731
17065
  }));
16732
17066
  }, [selectedItem]);
16733
- const handleResetView = (0, import_react41.useCallback)(async () => {
17067
+ const handleResetView = (0, import_react42.useCallback)(async () => {
16734
17068
  await resetView();
16735
17069
  }, [resetView]);
16736
- const handleFitScene = (0, import_react41.useCallback)(async () => {
17070
+ const handleFitScene = (0, import_react42.useCallback)(async () => {
16737
17071
  await fitScene();
16738
17072
  }, [fitScene]);
16739
- const openResetDialog = (0, import_react41.useCallback)((target) => {
17073
+ const openResetDialog = (0, import_react42.useCallback)((target) => {
16740
17074
  setResetTarget(target);
16741
17075
  setResetDialogOpen(true);
16742
17076
  }, []);
16743
- const closeResetDialog = (0, import_react41.useCallback)(() => {
17077
+ const closeResetDialog = (0, import_react42.useCallback)(() => {
16744
17078
  setResetDialogOpen(false);
16745
17079
  setResetTarget(null);
16746
17080
  }, []);
16747
- const executeReset = (0, import_react41.useCallback)(() => {
17081
+ const executeReset = (0, import_react42.useCallback)(() => {
16748
17082
  if (resetTarget === "bindings") {
16749
17083
  if (!nodeKeys.length) {
16750
17084
  closeResetDialog();
@@ -17291,7 +17625,7 @@ function BindingBuilder({ licenseKey }) {
17291
17625
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
17292
17626
  "?"
17293
17627
  ] }),
17294
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
17628
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, groups, tags, transforms, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
17295
17629
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
17296
17630
  ] }),
17297
17631
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "bb-reset-footer", children: [
@@ -17303,11 +17637,11 @@ function BindingBuilder({ licenseKey }) {
17303
17637
  }
17304
17638
 
17305
17639
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
17306
- var import_react42 = require("react");
17640
+ var import_react43 = require("react");
17307
17641
  var import_drei7 = require("@react-three/drei");
17308
17642
  var import_fiber13 = require("@react-three/fiber");
17309
17643
  var import_fiber14 = require("@react-three/fiber");
17310
- var import_three22 = require("three");
17644
+ var import_three23 = require("three");
17311
17645
  var import_three_stdlib4 = require("three-stdlib");
17312
17646
 
17313
17647
  // src/styles/SimpleModelViewer.css
@@ -17335,15 +17669,15 @@ function isMeshNode2(o) {
17335
17669
  function applyClippingPlanes(scene, controls) {
17336
17670
  if (!controls) return;
17337
17671
  const camera = controls.camera;
17338
- if (!(camera instanceof import_three22.PerspectiveCamera)) return;
17339
- const size = new import_three22.Box3().setFromObject(scene).getSize(new import_three22.Vector3()).length();
17672
+ if (!(camera instanceof import_three23.PerspectiveCamera)) return;
17673
+ const size = new import_three23.Box3().setFromObject(scene).getSize(new import_three23.Vector3()).length();
17340
17674
  if (!Number.isFinite(size) || size <= 0) return;
17341
17675
  camera.near = size / 100;
17342
17676
  camera.far = size * 100;
17343
17677
  camera.updateProjectionMatrix();
17344
17678
  controls.maxDistance = size * 10;
17345
17679
  }
17346
- var ModelErrorBoundary2 = class extends import_react42.Component {
17680
+ var ModelErrorBoundary2 = class extends import_react43.Component {
17347
17681
  constructor() {
17348
17682
  super(...arguments);
17349
17683
  this.state = { hasError: false };
@@ -17375,12 +17709,12 @@ function InnerModel({
17375
17709
  meshopt: true
17376
17710
  });
17377
17711
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17378
- const [hoveredKey, setHoveredKey] = (0, import_react42.useState)(null);
17379
- const scene = (0, import_react42.useMemo)(
17712
+ const [hoveredKey, setHoveredKey] = (0, import_react43.useState)(null);
17713
+ const scene = (0, import_react43.useMemo)(
17380
17714
  () => import_three_stdlib4.SkeletonUtils.clone(rawScene),
17381
17715
  [rawScene]
17382
17716
  );
17383
- const meshMap = (0, import_react42.useMemo)(() => {
17717
+ const meshMap = (0, import_react43.useMemo)(() => {
17384
17718
  const map = {};
17385
17719
  scene.traverse((obj) => {
17386
17720
  if (!isMeshNode2(obj)) return;
@@ -17389,7 +17723,7 @@ function InnerModel({
17389
17723
  });
17390
17724
  return map;
17391
17725
  }, [scene]);
17392
- const perKeyMaterials = (0, import_react42.useMemo)(() => {
17726
+ const perKeyMaterials = (0, import_react43.useMemo)(() => {
17393
17727
  const map = {};
17394
17728
  for (const [key, { ref }] of Object.entries(meshMap)) {
17395
17729
  const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
@@ -17405,14 +17739,14 @@ function InnerModel({
17405
17739
  }
17406
17740
  return map;
17407
17741
  }, [meshMap]);
17408
- (0, import_react42.useEffect)(
17742
+ (0, import_react43.useEffect)(
17409
17743
  () => () => {
17410
17744
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
17411
17745
  },
17412
17746
  [perKeyMaterials]
17413
17747
  );
17414
- const notifiedUrl = (0, import_react42.useRef)(null);
17415
- (0, import_react42.useEffect)(() => {
17748
+ const notifiedUrl = (0, import_react43.useRef)(null);
17749
+ (0, import_react43.useEffect)(() => {
17416
17750
  if (notifiedUrl.current === url) return;
17417
17751
  notifiedUrl.current = url;
17418
17752
  scene.updateWorldMatrix(true, true);
@@ -17420,7 +17754,7 @@ function InnerModel({
17420
17754
  onMeshesReady(meshMap);
17421
17755
  onModelLoaded(scene);
17422
17756
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
17423
- (0, import_react42.useEffect)(() => {
17757
+ (0, import_react43.useEffect)(() => {
17424
17758
  const controller = new AbortController();
17425
17759
  void buildSceneBVH(scene, controller.signal);
17426
17760
  return () => {
@@ -17428,26 +17762,26 @@ function InnerModel({
17428
17762
  disposeSceneBVH(scene);
17429
17763
  };
17430
17764
  }, [scene]);
17431
- const pointerRaycastEnabled = (0, import_react42.useMemo)(
17765
+ const pointerRaycastEnabled = (0, import_react43.useMemo)(
17432
17766
  () => !exceedsBvhBudget(scene),
17433
17767
  [scene]
17434
17768
  );
17435
- (0, import_react42.useEffect)(() => {
17769
+ (0, import_react43.useEffect)(() => {
17436
17770
  for (const [key, { ref }] of Object.entries(meshMap)) {
17437
17771
  nodeRefsOut.current[key] = ref;
17438
17772
  }
17439
17773
  }, [meshMap, nodeRefsOut]);
17440
- (0, import_react42.useEffect)(() => {
17774
+ (0, import_react43.useEffect)(() => {
17441
17775
  if (!highlightOnHover && hoveredKey !== null) {
17442
17776
  setHoveredKey(null);
17443
17777
  }
17444
17778
  }, [highlightOnHover, hoveredKey]);
17445
- (0, import_react42.useEffect)(() => {
17779
+ (0, import_react43.useEffect)(() => {
17446
17780
  return () => {
17447
17781
  document.body.style.cursor = "default";
17448
17782
  };
17449
17783
  }, []);
17450
- (0, import_react42.useEffect)(() => {
17784
+ (0, import_react43.useEffect)(() => {
17451
17785
  for (const [key, { ref }] of Object.entries(meshMap)) {
17452
17786
  ref.visible = !hiddenKeys.has(key);
17453
17787
  const mat = perKeyMaterials[key];
@@ -17517,11 +17851,11 @@ function CameraFitter({
17517
17851
  refitOnResize,
17518
17852
  onInitialFitComplete
17519
17853
  }) {
17520
- const fitted = (0, import_react42.useRef)(false);
17854
+ const fitted = (0, import_react43.useRef)(false);
17521
17855
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17522
17856
  const size = (0, import_fiber13.useThree)((state) => state.size);
17523
- const resizeTimerRef = (0, import_react42.useRef)(null);
17524
- (0, import_react42.useEffect)(() => {
17857
+ const resizeTimerRef = (0, import_react43.useRef)(null);
17858
+ (0, import_react43.useEffect)(() => {
17525
17859
  fitted.current = false;
17526
17860
  invalidate();
17527
17861
  }, [invalidate, scene]);
@@ -17541,7 +17875,7 @@ function CameraFitter({
17541
17875
  }).catch(() => {
17542
17876
  }).finally(onInitialFitComplete);
17543
17877
  });
17544
- (0, import_react42.useEffect)(() => {
17878
+ (0, import_react43.useEffect)(() => {
17545
17879
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
17546
17880
  return;
17547
17881
  }
@@ -17569,7 +17903,7 @@ function CameraFitter({
17569
17903
  function ExposureController({ exposure }) {
17570
17904
  const gl = (0, import_fiber13.useThree)((state) => state.gl);
17571
17905
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17572
- (0, import_react42.useEffect)(() => {
17906
+ (0, import_react43.useEffect)(() => {
17573
17907
  gl.toneMappingExposure = exposure;
17574
17908
  invalidate();
17575
17909
  }, [gl, exposure, invalidate]);
@@ -17677,7 +18011,7 @@ function EnvironmentSection({
17677
18011
  title,
17678
18012
  children
17679
18013
  }) {
17680
- const [collapsed, setCollapsed] = (0, import_react42.useState)(false);
18014
+ const [collapsed, setCollapsed] = (0, import_react43.useState)(false);
17681
18015
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "env-section", children: [
17682
18016
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
17683
18017
  "button",
@@ -17842,8 +18176,8 @@ function SceneObjectsPanel2({
17842
18176
  onHover,
17843
18177
  onToggleVisibility
17844
18178
  }) {
17845
- const [search, setSearch] = (0, import_react42.useState)("");
17846
- const [rowHovered, setRowHovered] = (0, import_react42.useState)(null);
18179
+ const [search, setSearch] = (0, import_react43.useState)("");
18180
+ const [rowHovered, setRowHovered] = (0, import_react43.useState)(null);
17847
18181
  const entries = Object.values(meshKeys).filter(
17848
18182
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
17849
18183
  );
@@ -17967,35 +18301,35 @@ function SimpleModelViewer({
17967
18301
  onModelLoaded,
17968
18302
  onLoadError
17969
18303
  }) {
17970
- const cameraControlsRef = (0, import_react42.useRef)(null);
17971
- const nodeRefsOut = (0, import_react42.useRef)({});
17972
- const fileInputRef = (0, import_react42.useRef)(null);
17973
- const uploadedModelRef = (0, import_react42.useRef)(null);
17974
- const dragDepthRef = (0, import_react42.useRef)(0);
17975
- const [loadedScene, setLoadedScene] = (0, import_react42.useState)(null);
17976
- const [largeModelTriangles, setLargeModelTriangles] = (0, import_react42.useState)(
18304
+ const cameraControlsRef = (0, import_react43.useRef)(null);
18305
+ const nodeRefsOut = (0, import_react43.useRef)({});
18306
+ const fileInputRef = (0, import_react43.useRef)(null);
18307
+ const uploadedModelRef = (0, import_react43.useRef)(null);
18308
+ const dragDepthRef = (0, import_react43.useRef)(0);
18309
+ const [loadedScene, setLoadedScene] = (0, import_react43.useState)(null);
18310
+ const [largeModelTriangles, setLargeModelTriangles] = (0, import_react43.useState)(
17977
18311
  null
17978
18312
  );
17979
- const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react42.useState)(false);
17980
- const [meshKeys, setMeshKeys] = (0, import_react42.useState)({});
17981
- const [hiddenKeys, setHiddenKeys] = (0, import_react42.useState)(/* @__PURE__ */ new Set());
17982
- const [selectedKey, setSelectedKey] = (0, import_react42.useState)(null);
17983
- const [panelHoveredKey, setPanelHoveredKey] = (0, import_react42.useState)(null);
17984
- const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react42.useState)(null);
17985
- const [uploadedModelName, setUploadedModelName] = (0, import_react42.useState)(
18313
+ const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react43.useState)(false);
18314
+ const [meshKeys, setMeshKeys] = (0, import_react43.useState)({});
18315
+ const [hiddenKeys, setHiddenKeys] = (0, import_react43.useState)(/* @__PURE__ */ new Set());
18316
+ const [selectedKey, setSelectedKey] = (0, import_react43.useState)(null);
18317
+ const [panelHoveredKey, setPanelHoveredKey] = (0, import_react43.useState)(null);
18318
+ const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react43.useState)(null);
18319
+ const [uploadedModelName, setUploadedModelName] = (0, import_react43.useState)(
17986
18320
  null
17987
18321
  );
17988
- const [modelLoadFailed, setModelLoadFailed] = (0, import_react42.useState)(false);
17989
- const [isResolvingUpload, setIsResolvingUpload] = (0, import_react42.useState)(false);
17990
- const [initialFitComplete, setInitialFitComplete] = (0, import_react42.useState)(false);
17991
- const [isDragActive, setIsDragActive] = (0, import_react42.useState)(false);
17992
- const [isPanelOpen, setIsPanelOpen] = (0, import_react42.useState)(
18322
+ const [modelLoadFailed, setModelLoadFailed] = (0, import_react43.useState)(false);
18323
+ const [isResolvingUpload, setIsResolvingUpload] = (0, import_react43.useState)(false);
18324
+ const [initialFitComplete, setInitialFitComplete] = (0, import_react43.useState)(false);
18325
+ const [isDragActive, setIsDragActive] = (0, import_react43.useState)(false);
18326
+ const [isPanelOpen, setIsPanelOpen] = (0, import_react43.useState)(
17993
18327
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17994
18328
  );
17995
- const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react42.useState)(
18329
+ const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react43.useState)(
17996
18330
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17997
18331
  );
17998
- const [envSettings, setEnvSettings] = (0, import_react42.useState)({
18332
+ const [envSettings, setEnvSettings] = (0, import_react43.useState)({
17999
18333
  backgroundEnabled,
18000
18334
  backgroundColor,
18001
18335
  autoRotate,
@@ -18007,7 +18341,7 @@ function SimpleModelViewer({
18007
18341
  directionalColor
18008
18342
  });
18009
18343
  const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
18010
- const resetModelState = (0, import_react42.useCallback)(() => {
18344
+ const resetModelState = (0, import_react43.useCallback)(() => {
18011
18345
  setLoadedScene(null);
18012
18346
  setLargeModelTriangles(null);
18013
18347
  setLargeModelNoticeDismissed(false);
@@ -18018,11 +18352,11 @@ function SimpleModelViewer({
18018
18352
  setModelLoadFailed(false);
18019
18353
  setInitialFitComplete(false);
18020
18354
  }, []);
18021
- const revokeUploadedModelUrl = (0, import_react42.useCallback)(() => {
18355
+ const revokeUploadedModelUrl = (0, import_react43.useCallback)(() => {
18022
18356
  uploadedModelRef.current?.cleanup();
18023
18357
  uploadedModelRef.current = null;
18024
18358
  }, []);
18025
- const clearUploadedModel = (0, import_react42.useCallback)(() => {
18359
+ const clearUploadedModel = (0, import_react43.useCallback)(() => {
18026
18360
  revokeUploadedModelUrl();
18027
18361
  setUploadedModelUrl(null);
18028
18362
  setUploadedModelName(null);
@@ -18051,7 +18385,7 @@ function SimpleModelViewer({
18051
18385
  resetModelState,
18052
18386
  revokeUploadedModelUrl
18053
18387
  ]);
18054
- const handleUploadedModel = (0, import_react42.useCallback)(
18388
+ const handleUploadedModel = (0, import_react43.useCallback)(
18055
18389
  async (file) => {
18056
18390
  setIsResolvingUpload(true);
18057
18391
  try {
@@ -18070,7 +18404,7 @@ function SimpleModelViewer({
18070
18404
  },
18071
18405
  [onLoadError, resetModelState, revokeUploadedModelUrl]
18072
18406
  );
18073
- const handleFileSelection = (0, import_react42.useCallback)(
18407
+ const handleFileSelection = (0, import_react43.useCallback)(
18074
18408
  (files) => {
18075
18409
  const file = files?.[0];
18076
18410
  if (!file) return;
@@ -18078,29 +18412,29 @@ function SimpleModelViewer({
18078
18412
  },
18079
18413
  [handleUploadedModel]
18080
18414
  );
18081
- (0, import_react42.useEffect)(() => {
18415
+ (0, import_react43.useEffect)(() => {
18082
18416
  return () => {
18083
18417
  revokeUploadedModelUrl();
18084
18418
  };
18085
18419
  }, [revokeUploadedModelUrl]);
18086
- const handleEnvSettingChange = (0, import_react42.useCallback)(
18420
+ const handleEnvSettingChange = (0, import_react43.useCallback)(
18087
18421
  (key, value) => {
18088
18422
  setEnvSettings((prev) => ({ ...prev, [key]: value }));
18089
18423
  },
18090
18424
  []
18091
18425
  );
18092
- (0, import_react42.useEffect)(() => {
18426
+ (0, import_react43.useEffect)(() => {
18093
18427
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
18094
18428
  }, [backgroundColor]);
18095
- (0, import_react42.useEffect)(() => {
18429
+ (0, import_react43.useEffect)(() => {
18096
18430
  resetModelState();
18097
18431
  }, [activeModelUrl, resetModelState]);
18098
- const handleMeshesReady = (0, import_react42.useCallback)((meshes) => {
18432
+ const handleMeshesReady = (0, import_react43.useCallback)((meshes) => {
18099
18433
  setMeshKeys(meshes);
18100
18434
  setHiddenKeys(/* @__PURE__ */ new Set());
18101
18435
  setSelectedKey(null);
18102
18436
  }, []);
18103
- const handleModelLoaded = (0, import_react42.useCallback)(
18437
+ const handleModelLoaded = (0, import_react43.useCallback)(
18104
18438
  (scene) => {
18105
18439
  setLoadedScene(scene);
18106
18440
  setModelLoadFailed(false);
@@ -18111,14 +18445,14 @@ function SimpleModelViewer({
18111
18445
  },
18112
18446
  [onModelLoaded]
18113
18447
  );
18114
- const handleLoadError = (0, import_react42.useCallback)(
18448
+ const handleLoadError = (0, import_react43.useCallback)(
18115
18449
  (error) => {
18116
18450
  setModelLoadFailed(true);
18117
18451
  onLoadError?.(error);
18118
18452
  },
18119
18453
  [onLoadError]
18120
18454
  );
18121
- const focusMesh = (0, import_react42.useCallback)(
18455
+ const focusMesh = (0, import_react43.useCallback)(
18122
18456
  (key) => {
18123
18457
  setSelectedKey(key);
18124
18458
  if (!zoomOnSelected) return;
@@ -18134,14 +18468,14 @@ function SimpleModelViewer({
18134
18468
  },
18135
18469
  [zoomOnSelected]
18136
18470
  );
18137
- const handleToggleVisibility = (0, import_react42.useCallback)((key) => {
18471
+ const handleToggleVisibility = (0, import_react43.useCallback)((key) => {
18138
18472
  setHiddenKeys((prev) => {
18139
18473
  const next = new Set(prev);
18140
18474
  next.has(key) ? next.delete(key) : next.add(key);
18141
18475
  return next;
18142
18476
  });
18143
18477
  }, []);
18144
- const handleDragEnter = (0, import_react42.useCallback)(
18478
+ const handleDragEnter = (0, import_react43.useCallback)(
18145
18479
  (event) => {
18146
18480
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18147
18481
  return;
@@ -18153,7 +18487,7 @@ function SimpleModelViewer({
18153
18487
  },
18154
18488
  [enableModelUpload]
18155
18489
  );
18156
- const handleDragOver = (0, import_react42.useCallback)(
18490
+ const handleDragOver = (0, import_react43.useCallback)(
18157
18491
  (event) => {
18158
18492
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18159
18493
  return;
@@ -18165,7 +18499,7 @@ function SimpleModelViewer({
18165
18499
  },
18166
18500
  [enableModelUpload]
18167
18501
  );
18168
- const handleDragLeave = (0, import_react42.useCallback)(
18502
+ const handleDragLeave = (0, import_react43.useCallback)(
18169
18503
  (event) => {
18170
18504
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18171
18505
  return;
@@ -18179,7 +18513,7 @@ function SimpleModelViewer({
18179
18513
  },
18180
18514
  [enableModelUpload]
18181
18515
  );
18182
- const handleDrop = (0, import_react42.useCallback)(
18516
+ const handleDrop = (0, import_react43.useCallback)(
18183
18517
  (event) => {
18184
18518
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18185
18519
  return;
@@ -18272,7 +18606,7 @@ function SimpleModelViewer({
18272
18606
  raycaster: { firstHitOnly: true },
18273
18607
  style: { background: envSettings.backgroundColor },
18274
18608
  camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
18275
- children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react42.Suspense, { fallback: null, children: [
18609
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react43.Suspense, { fallback: null, children: [
18276
18610
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18277
18611
  "ambientLight",
18278
18612
  {
@@ -18290,7 +18624,7 @@ function SimpleModelViewer({
18290
18624
  }
18291
18625
  ),
18292
18626
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
18293
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react42.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18627
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react43.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18294
18628
  import_drei7.Environment,
18295
18629
  {
18296
18630
  preset: envSettings.envPreset,
@@ -18309,7 +18643,7 @@ function SimpleModelViewer({
18309
18643
  ModelErrorBoundary2,
18310
18644
  {
18311
18645
  onLoadError: handleLoadError,
18312
- children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react42.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18646
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react43.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18313
18647
  InnerModel,
18314
18648
  {
18315
18649
  url: activeModelUrl,
@@ -18408,18 +18742,18 @@ function SimpleModelViewer({
18408
18742
  }
18409
18743
 
18410
18744
  // src/hooks/useViewerSelection.ts
18411
- var import_react43 = require("react");
18745
+ var import_react44 = require("react");
18412
18746
  function useViewerSelection(initialSelectedObjectBinding = null) {
18413
- const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react43.useState)(
18747
+ const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react44.useState)(
18414
18748
  initialSelectedObjectBinding
18415
18749
  );
18416
- const handleObjectSelect = (0, import_react43.useCallback)(
18750
+ const handleObjectSelect = (0, import_react44.useCallback)(
18417
18751
  (binding) => {
18418
18752
  setSelectedObjectBinding(binding);
18419
18753
  },
18420
18754
  [setSelectedObjectBinding]
18421
18755
  );
18422
- const clearSelection = (0, import_react43.useCallback)(() => {
18756
+ const clearSelection = (0, import_react44.useCallback)(() => {
18423
18757
  setSelectedObjectBinding(null);
18424
18758
  }, []);
18425
18759
  return {
@@ -18431,9 +18765,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
18431
18765
  }
18432
18766
 
18433
18767
  // src/hooks/useObjectBinding.ts
18434
- var import_react44 = require("react");
18768
+ var import_react45 = require("react");
18435
18769
  function useObjectBinding(objectBindings, objectId) {
18436
- return (0, import_react44.useMemo)(() => {
18770
+ return (0, import_react45.useMemo)(() => {
18437
18771
  if (!objectId) {
18438
18772
  return null;
18439
18773
  }
@@ -18448,16 +18782,16 @@ function useObjectBinding(objectBindings, objectId) {
18448
18782
  }
18449
18783
 
18450
18784
  // src/hooks/useObjectBindingIds.ts
18451
- var import_react45 = require("react");
18785
+ var import_react46 = require("react");
18452
18786
  function useObjectBindingIds(objectBindings) {
18453
- return (0, import_react45.useMemo)(
18787
+ return (0, import_react46.useMemo)(
18454
18788
  () => Object.values(objectBindings).map((binding) => binding.modelObjectId),
18455
18789
  [objectBindings]
18456
18790
  );
18457
18791
  }
18458
18792
 
18459
18793
  // src/hooks/useObjectBindings.ts
18460
- var import_react46 = require("react");
18794
+ var import_react47 = require("react");
18461
18795
 
18462
18796
  // src/utils/deepMerge.ts
18463
18797
  function isPlainObject2(value) {
@@ -18475,6 +18809,27 @@ function deepMerge(base, patch) {
18475
18809
  return target;
18476
18810
  }
18477
18811
 
18812
+ // src/utils/selectBindings.ts
18813
+ function isSelector(target) {
18814
+ return typeof target === "object" && !Array.isArray(target);
18815
+ }
18816
+ function matchesSelector(binding, selector) {
18817
+ if ("group" in selector) return binding.group === selector.group;
18818
+ if ("tag" in selector) return binding.tags?.includes(selector.tag) ?? false;
18819
+ return binding.type === selector.type;
18820
+ }
18821
+ function selectBindingKeys(bindings, target) {
18822
+ if (isSelector(target)) {
18823
+ return Object.entries(bindings).filter(([, binding]) => matchesSelector(binding, target)).map(([bindingKey]) => bindingKey);
18824
+ }
18825
+ const ids = typeof target === "string" ? [target] : target;
18826
+ const keys = ids.map((id) => resolveBindingKey(bindings, id)).filter((key) => key !== null);
18827
+ return [...new Set(keys)];
18828
+ }
18829
+ function selectBindings(bindings, target) {
18830
+ return selectBindingKeys(bindings, target).map((key) => bindings[key]).filter((binding) => binding !== void 0);
18831
+ }
18832
+
18478
18833
  // src/utils/patchBindings.ts
18479
18834
  function isBindingEditArray(value) {
18480
18835
  return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
@@ -18483,10 +18838,7 @@ function patchBindings(bindings, objectIdOrEdits, patch) {
18483
18838
  const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
18484
18839
  let next = bindings;
18485
18840
  for (const edit of edits) {
18486
- const ids = Array.isArray(edit.ids) ? edit.ids : [edit.ids];
18487
- for (const id of ids) {
18488
- const bindingKey = resolveBindingKey(next, id);
18489
- if (!bindingKey) continue;
18841
+ for (const bindingKey of selectBindingKeys(next, edit.ids)) {
18490
18842
  const binding = next[bindingKey];
18491
18843
  if (!binding) continue;
18492
18844
  const patchedBinding = deepMerge(binding, edit.patch);
@@ -18501,14 +18853,44 @@ function material(value) {
18501
18853
  }
18502
18854
 
18503
18855
  // src/hooks/useObjectBindings.ts
18856
+ var TEXTURE_PROPERTY_BY_CHANNEL = {
18857
+ baseColor: "texture",
18858
+ metalness: "metalnessMap",
18859
+ roughness: "roughnessMap",
18860
+ emissive: "emissiveMap",
18861
+ alpha: "alphaMap",
18862
+ normal: "normalMap",
18863
+ bump: "bumpMap",
18864
+ ao: "aoMap",
18865
+ displacement: "displacementMap",
18866
+ clearcoat: "clearcoatMap",
18867
+ clearcoatRoughness: "clearcoatRoughnessMap",
18868
+ clearcoatNormal: "clearcoatNormalMap",
18869
+ anisotropy: "anisotropyMap",
18870
+ transmission: "transmissionMap",
18871
+ thickness: "thicknessMap",
18872
+ sheenColor: "sheenColorMap",
18873
+ sheenRoughness: "sheenRoughnessMap",
18874
+ specularIntensity: "specularIntensityMap",
18875
+ specularColor: "specularColorMap"
18876
+ };
18877
+ function cloneMaterial(value) {
18878
+ if (!value) return void 0;
18879
+ return Object.fromEntries(
18880
+ Object.entries(value).map(([key, field]) => [
18881
+ key,
18882
+ field && typeof field === "object" ? { ...field } : field
18883
+ ])
18884
+ );
18885
+ }
18504
18886
  function getHiddenObjects(objectBindings) {
18505
18887
  return Object.fromEntries(
18506
18888
  Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
18507
18889
  );
18508
18890
  }
18509
18891
  function useObjectBindings(initialBindings) {
18510
- const [objectBindings, setObjectBindings] = (0, import_react46.useState)(initialBindings);
18511
- const updateObjectBindings = (0, import_react46.useCallback)(
18892
+ const [objectBindings, setObjectBindings] = (0, import_react47.useState)(initialBindings);
18893
+ const updateObjectBindings = (0, import_react47.useCallback)(
18512
18894
  (objectIdOrEdits, patchValue) => {
18513
18895
  setObjectBindings(
18514
18896
  (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
@@ -18520,68 +18902,138 @@ function useObjectBindings(initialBindings) {
18520
18902
  },
18521
18903
  []
18522
18904
  );
18523
- const setMaterial = (0, import_react46.useCallback)(
18524
- (objectId, value) => updateObjectBindings(objectId, material(value)),
18905
+ const setMaterial = (0, import_react47.useCallback)(
18906
+ (target, value) => updateObjectBindings(target, material(value)),
18525
18907
  [updateObjectBindings]
18526
18908
  );
18527
- const setBaseColor = (0, import_react46.useCallback)(
18528
- (objectId, color) => setMaterial(objectId, { baseColor: color }),
18909
+ const setBaseColor = (0, import_react47.useCallback)(
18910
+ (target, color) => setMaterial(target, { baseColor: color }),
18529
18911
  [setMaterial]
18530
18912
  );
18531
- const resetMaterial = (0, import_react46.useCallback)(
18532
- (objectId) => updateObjectBindings(objectId, { style: { material: void 0 } }),
18913
+ const setTexture = (0, import_react47.useCallback)(
18914
+ (target, texture, channel = "baseColor") => {
18915
+ const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
18916
+ const slot = typeof texture === "string" ? { path: texture } : texture;
18917
+ updateObjectBindings(target, {
18918
+ style: { material: { [property]: slot } }
18919
+ });
18920
+ },
18533
18921
  [updateObjectBindings]
18534
18922
  );
18535
- const getMaterial = (0, import_react46.useCallback)(
18536
- (objectId) => {
18537
- const bindingKey = resolveBindingKey(objectBindings, objectId);
18538
- return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
18923
+ const clearTexture = (0, import_react47.useCallback)(
18924
+ (target, channel = "baseColor") => {
18925
+ const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
18926
+ updateObjectBindings(target, {
18927
+ style: { material: { [property]: void 0 } }
18928
+ });
18539
18929
  },
18540
- [objectBindings]
18541
- );
18542
- const hideObject = (0, import_react46.useCallback)(
18543
- (objectId) => updateObjectBindings(objectId, { visible: false }),
18544
18930
  [updateObjectBindings]
18545
18931
  );
18546
- const showObject = (0, import_react46.useCallback)(
18547
- (objectId) => updateObjectBindings(objectId, { visible: true }),
18932
+ const copyMaterial = (0, import_react47.useCallback)(
18933
+ (source, target) => {
18934
+ setObjectBindings((current) => {
18935
+ const sourceKey = resolveBindingKey(current, source);
18936
+ if (!sourceKey) return current;
18937
+ const sourceMaterial = cloneMaterial(
18938
+ current[sourceKey]?.style?.material
18939
+ );
18940
+ const targetKeys = selectBindingKeys(current, target);
18941
+ if (targetKeys.length === 0) return current;
18942
+ let next = current;
18943
+ for (const targetKey of targetKeys) {
18944
+ const binding = current[targetKey];
18945
+ if (!binding) continue;
18946
+ if (next === current) next = { ...current };
18947
+ next[targetKey] = {
18948
+ ...binding,
18949
+ style: {
18950
+ ...binding.style,
18951
+ material: cloneMaterial(sourceMaterial)
18952
+ }
18953
+ };
18954
+ }
18955
+ return next;
18956
+ });
18957
+ },
18958
+ []
18959
+ );
18960
+ const resetMaterial = (0, import_react47.useCallback)(
18961
+ (target) => updateObjectBindings(target, { style: { material: void 0 } }),
18962
+ [updateObjectBindings]
18963
+ );
18964
+ const setTransform = (0, import_react47.useCallback)(
18965
+ (target, transform) => updateObjectBindings(target, { transform }),
18966
+ [updateObjectBindings]
18967
+ );
18968
+ const resetTransform = (0, import_react47.useCallback)(
18969
+ (target) => updateObjectBindings(target, { transform: void 0 }),
18548
18970
  [updateObjectBindings]
18549
18971
  );
18550
- const toggleObjectVisibility = (0, import_react46.useCallback)(
18972
+ const updateMetadata = (0, import_react47.useCallback)(
18973
+ (target, metadata) => updateObjectBindings(target, { metadata }),
18974
+ [updateObjectBindings]
18975
+ );
18976
+ const selectObjectBindings = (0, import_react47.useCallback)(
18977
+ (target) => selectBindings(objectBindings, target),
18978
+ [objectBindings]
18979
+ );
18980
+ const selectObjectBindingIds = (0, import_react47.useCallback)(
18981
+ (target) => selectBindingKeys(objectBindings, target).map(
18982
+ (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
18983
+ ),
18984
+ [objectBindings]
18985
+ );
18986
+ const getMaterial = (0, import_react47.useCallback)(
18551
18987
  (objectId) => {
18988
+ const bindingKey = resolveBindingKey(objectBindings, objectId);
18989
+ return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
18990
+ },
18991
+ [objectBindings]
18992
+ );
18993
+ const hideObject = (0, import_react47.useCallback)(
18994
+ (target) => updateObjectBindings(target, { visible: false }),
18995
+ [updateObjectBindings]
18996
+ );
18997
+ const showObject = (0, import_react47.useCallback)(
18998
+ (target) => updateObjectBindings(target, { visible: true }),
18999
+ [updateObjectBindings]
19000
+ );
19001
+ const toggleObjectVisibility = (0, import_react47.useCallback)(
19002
+ (target) => {
18552
19003
  setObjectBindings((current) => {
18553
- const objectIds = Array.isArray(objectId) ? objectId : [objectId];
18554
- const edits = objectIds.reduce((acc, id) => {
18555
- const bindingKey = resolveBindingKey(current, id);
18556
- const binding = bindingKey ? current[bindingKey] : null;
18557
- if (!bindingKey || !binding) return acc;
18558
- acc.push({
18559
- ids: bindingKey,
18560
- patch: { visible: binding.visible === false }
18561
- });
18562
- return acc;
18563
- }, []);
19004
+ const edits = selectBindingKeys(current, target).reduce(
19005
+ (acc, bindingKey) => {
19006
+ const binding = current[bindingKey];
19007
+ if (!binding) return acc;
19008
+ acc.push({
19009
+ ids: bindingKey,
19010
+ patch: { visible: binding.visible === false }
19011
+ });
19012
+ return acc;
19013
+ },
19014
+ []
19015
+ );
18564
19016
  return patchBindings(current, edits);
18565
19017
  });
18566
19018
  },
18567
19019
  [setObjectBindings]
18568
19020
  );
18569
- const clearHiddenObjects = (0, import_react46.useCallback)(() => {
19021
+ const clearHiddenObjects = (0, import_react47.useCallback)(() => {
18570
19022
  setObjectBindings(
18571
19023
  (current) => patchBindings(current, Object.keys(current), { visible: true })
18572
19024
  );
18573
19025
  }, [setObjectBindings]);
18574
- const hiddenObjects = (0, import_react46.useMemo)(
19026
+ const hiddenObjects = (0, import_react47.useMemo)(
18575
19027
  () => getHiddenObjects(objectBindings),
18576
19028
  [objectBindings]
18577
19029
  );
18578
- const hiddenObjectIds = (0, import_react46.useMemo)(
19030
+ const hiddenObjectIds = (0, import_react47.useMemo)(
18579
19031
  () => Object.keys(hiddenObjects).map(
18580
19032
  (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
18581
19033
  ),
18582
19034
  [hiddenObjects, objectBindings]
18583
19035
  );
18584
- const isObjectHidden = (0, import_react46.useCallback)(
19036
+ const isObjectHidden = (0, import_react47.useCallback)(
18585
19037
  (objectId) => {
18586
19038
  const bindingKey = resolveBindingKey(objectBindings, objectId);
18587
19039
  return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
@@ -18594,8 +19046,16 @@ function useObjectBindings(initialBindings) {
18594
19046
  updateObjectBindings,
18595
19047
  setMaterial,
18596
19048
  setBaseColor,
19049
+ setTexture,
19050
+ clearTexture,
19051
+ copyMaterial,
18597
19052
  resetMaterial,
18598
19053
  getMaterial,
19054
+ setTransform,
19055
+ resetTransform,
19056
+ updateMetadata,
19057
+ selectObjectBindings,
19058
+ selectObjectBindingIds,
18599
19059
  hiddenObjects,
18600
19060
  hiddenObjectIds,
18601
19061
  hideObject,
@@ -18606,8 +19066,13 @@ function useObjectBindings(initialBindings) {
18606
19066
  };
18607
19067
  }
18608
19068
 
19069
+ // src/utils/defineObjectBindings.ts
19070
+ function defineObjectBindings(bindings) {
19071
+ return bindings;
19072
+ }
19073
+
18609
19074
  // src/hooks/useSceneConfig.ts
18610
- var import_react47 = require("react");
19075
+ var import_react48 = require("react");
18611
19076
 
18612
19077
  // src/utils/patchSceneConfig.ts
18613
19078
  function patchSceneConfig(sceneConfig, patch) {
@@ -18615,36 +19080,154 @@ function patchSceneConfig(sceneConfig, patch) {
18615
19080
  }
18616
19081
 
18617
19082
  // src/hooks/useSceneConfig.ts
19083
+ var DEFAULT_LIGHT = {
19084
+ type: "point",
19085
+ color: "#ffffff",
19086
+ intensity: 1,
19087
+ position: [0, 0, 0],
19088
+ distance: 0,
19089
+ decay: 2,
19090
+ attachedToCamera: false,
19091
+ castShadow: false,
19092
+ shadowBias: -5e-4,
19093
+ visible: true
19094
+ };
18618
19095
  function useSceneConfig(initialSceneConfig) {
18619
- const initialSceneConfigRef = (0, import_react47.useRef)(null);
18620
- const [sceneConfig, setSceneConfig] = (0, import_react47.useState)(() => {
19096
+ const initialSceneConfigRef = (0, import_react48.useRef)(null);
19097
+ const [sceneConfig, setSceneConfig] = (0, import_react48.useState)(() => {
18621
19098
  const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
18622
19099
  initialSceneConfigRef.current = initial;
18623
19100
  return initial;
18624
19101
  });
18625
- const updateSceneConfig = (0, import_react47.useCallback)((update) => {
19102
+ const updateSceneConfig = (0, import_react48.useCallback)((update) => {
18626
19103
  setSceneConfig((current) => {
18627
19104
  const patch = typeof update === "function" ? update(current) : update;
18628
19105
  return patchSceneConfig(current, patch);
18629
19106
  });
18630
19107
  }, []);
18631
- const resetSceneConfig = (0, import_react47.useCallback)(() => {
19108
+ const resetSceneConfig = (0, import_react48.useCallback)(() => {
18632
19109
  const initial = initialSceneConfigRef.current;
18633
19110
  if (initial) setSceneConfig(initial);
18634
19111
  }, []);
19112
+ const updateLight = (0, import_react48.useCallback)(
19113
+ (id, patch) => {
19114
+ updateSceneConfig((current) => ({
19115
+ lighting: {
19116
+ lights: current.lighting.lights.map(
19117
+ (light) => light.id === id ? { ...light, ...patch } : light
19118
+ )
19119
+ }
19120
+ }));
19121
+ },
19122
+ [updateSceneConfig]
19123
+ );
19124
+ const addLight = (0, import_react48.useCallback)(
19125
+ (light) => {
19126
+ updateSceneConfig((current) => {
19127
+ if (current.lighting.lights.some((item) => item.id === light.id)) {
19128
+ return {};
19129
+ }
19130
+ return {
19131
+ lighting: {
19132
+ lights: [
19133
+ ...current.lighting.lights,
19134
+ { ...DEFAULT_LIGHT, ...light }
19135
+ ]
19136
+ }
19137
+ };
19138
+ });
19139
+ },
19140
+ [updateSceneConfig]
19141
+ );
19142
+ const removeLight = (0, import_react48.useCallback)(
19143
+ (id) => {
19144
+ updateSceneConfig((current) => ({
19145
+ lighting: {
19146
+ lights: current.lighting.lights.filter((light) => light.id !== id)
19147
+ }
19148
+ }));
19149
+ },
19150
+ [updateSceneConfig]
19151
+ );
19152
+ const setLightIntensity = (0, import_react48.useCallback)(
19153
+ (id, intensity) => updateLight(id, { intensity }),
19154
+ [updateLight]
19155
+ );
19156
+ const setLightColor = (0, import_react48.useCallback)(
19157
+ (id, color) => updateLight(id, { color }),
19158
+ [updateLight]
19159
+ );
19160
+ const showLight = (0, import_react48.useCallback)(
19161
+ (id) => updateLight(id, { visible: true }),
19162
+ [updateLight]
19163
+ );
19164
+ const hideLight = (0, import_react48.useCallback)(
19165
+ (id) => updateLight(id, { visible: false }),
19166
+ [updateLight]
19167
+ );
19168
+ const toggleLight = (0, import_react48.useCallback)(
19169
+ (id) => {
19170
+ updateSceneConfig((current) => ({
19171
+ lighting: {
19172
+ lights: current.lighting.lights.map(
19173
+ (light) => light.id === id ? { ...light, visible: !light.visible } : light
19174
+ )
19175
+ }
19176
+ }));
19177
+ },
19178
+ [updateSceneConfig]
19179
+ );
19180
+ const attachLight = (0, import_react48.useCallback)(
19181
+ (id, attachment) => updateLight(id, { attachment, attachedToCamera: false }),
19182
+ [updateLight]
19183
+ );
19184
+ const detachLight = (0, import_react48.useCallback)(
19185
+ (id) => updateLight(id, { attachment: void 0 }),
19186
+ [updateLight]
19187
+ );
19188
+ const lights = (0, import_react48.useMemo)(
19189
+ () => ({
19190
+ items: sceneConfig.lighting.lights,
19191
+ get: (id) => sceneConfig.lighting.lights.find((light) => light.id === id),
19192
+ add: addLight,
19193
+ update: updateLight,
19194
+ remove: removeLight,
19195
+ setIntensity: setLightIntensity,
19196
+ setColor: setLightColor,
19197
+ show: showLight,
19198
+ hide: hideLight,
19199
+ toggle: toggleLight,
19200
+ attach: attachLight,
19201
+ detach: detachLight
19202
+ }),
19203
+ [
19204
+ sceneConfig.lighting.lights,
19205
+ addLight,
19206
+ updateLight,
19207
+ removeLight,
19208
+ setLightIntensity,
19209
+ setLightColor,
19210
+ showLight,
19211
+ hideLight,
19212
+ toggleLight,
19213
+ attachLight,
19214
+ detachLight
19215
+ ]
19216
+ );
18635
19217
  return {
18636
19218
  sceneConfig,
18637
19219
  setSceneConfig,
18638
19220
  updateSceneConfig,
18639
- resetSceneConfig
19221
+ resetSceneConfig,
19222
+ lights
18640
19223
  };
18641
19224
  }
18642
19225
 
18643
19226
  // src/hooks/useViewerHover.ts
18644
- var import_react48 = require("react");
19227
+ var import_react49 = require("react");
18645
19228
  function useViewerHover() {
18646
- const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react48.useState)(null);
18647
- const handleHoveredObject = (0, import_react48.useCallback)(
19229
+ const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react49.useState)(null);
19230
+ const handleHoveredObject = (0, import_react49.useCallback)(
18648
19231
  (binding) => {
18649
19232
  setHoveredObjectBinding(binding);
18650
19233
  },
@@ -18657,23 +19240,23 @@ function useViewerHover() {
18657
19240
  }
18658
19241
 
18659
19242
  // src/hooks/useViewerModel.ts
18660
- var import_react49 = require("react");
18661
- var import_three23 = require("three");
19243
+ var import_react50 = require("react");
19244
+ var import_three24 = require("three");
18662
19245
  function toTuple2(vector) {
18663
19246
  return [vector.x, vector.y, vector.z];
18664
19247
  }
18665
19248
  function useViewerModel() {
18666
- const [isLoading, setIsLoading] = (0, import_react49.useState)(true);
18667
- const [isReady, setIsReady] = (0, import_react49.useState)(false);
18668
- const [error, setError] = (0, import_react49.useState)(null);
18669
- const [bounds, setBounds] = (0, import_react49.useState)(null);
18670
- const [objectCount, setObjectCount] = (0, import_react49.useState)(0);
18671
- const handleModelLoaded = (0, import_react49.useCallback)((scene) => {
18672
- const box = new import_three23.Box3().setFromObject(scene);
19249
+ const [isLoading, setIsLoading] = (0, import_react50.useState)(true);
19250
+ const [isReady, setIsReady] = (0, import_react50.useState)(false);
19251
+ const [error, setError] = (0, import_react50.useState)(null);
19252
+ const [bounds, setBounds] = (0, import_react50.useState)(null);
19253
+ const [objectCount, setObjectCount] = (0, import_react50.useState)(0);
19254
+ const handleModelLoaded = (0, import_react50.useCallback)((scene) => {
19255
+ const box = new import_three24.Box3().setFromObject(scene);
18673
19256
  const min = box.min.clone();
18674
19257
  const max = box.max.clone();
18675
- const center = new import_three23.Vector3();
18676
- const size = new import_three23.Vector3();
19258
+ const center = new import_three24.Vector3();
19259
+ const size = new import_three24.Vector3();
18677
19260
  let nextObjectCount = 0;
18678
19261
  scene.traverse((child) => {
18679
19262
  if ("isMesh" in child && child.isMesh) {
@@ -18693,14 +19276,14 @@ function useViewerModel() {
18693
19276
  setIsLoading(false);
18694
19277
  setIsReady(true);
18695
19278
  }, []);
18696
- const handleLoadError = (0, import_react49.useCallback)((nextError) => {
19279
+ const handleLoadError = (0, import_react50.useCallback)((nextError) => {
18697
19280
  setError(nextError);
18698
19281
  setBounds(null);
18699
19282
  setObjectCount(0);
18700
19283
  setIsLoading(false);
18701
19284
  setIsReady(false);
18702
19285
  }, []);
18703
- const resetModelState = (0, import_react49.useCallback)(() => {
19286
+ const resetModelState = (0, import_react50.useCallback)(() => {
18704
19287
  setIsLoading(true);
18705
19288
  setIsReady(false);
18706
19289
  setError(null);
@@ -18720,62 +19303,774 @@ function useViewerModel() {
18720
19303
  }
18721
19304
 
18722
19305
  // src/hooks/useViewerActions.ts
18723
- var import_react50 = require("react");
18724
- function useViewerActions(objectBindings, options = {}) {
18725
- const { onAction, onObjectBindingsChange } = options;
18726
- const getActionsForObject = (0, import_react50.useCallback)(
19306
+ var import_react51 = require("react");
19307
+ function useViewerActions({
19308
+ bindings,
19309
+ scene,
19310
+ camera,
19311
+ animations,
19312
+ onAction
19313
+ }) {
19314
+ const {
19315
+ objectBindings,
19316
+ updateObjectBindings,
19317
+ showObject,
19318
+ hideObject,
19319
+ toggleObjectVisibility
19320
+ } = bindings;
19321
+ const updateSceneConfig = scene?.updateSceneConfig;
19322
+ const focusObject = camera?.focusObject;
19323
+ const fitScene = camera?.fitScene;
19324
+ const resetView = camera?.resetView;
19325
+ const setCameraTarget = camera?.setCameraTarget;
19326
+ const setCameraState = camera?.setCameraState;
19327
+ const playAnimation = animations?.play;
19328
+ const pauseAnimation = animations?.pause;
19329
+ const stopAnimation = animations?.stop;
19330
+ const setAnimationSpeed = animations?.setSpeed;
19331
+ const seekAnimation = animations?.seek;
19332
+ const getActionsForObject = (0, import_react51.useCallback)(
18727
19333
  (objectId) => {
18728
19334
  const bindingKey = resolveBindingKey(objectBindings, objectId);
18729
- if (!bindingKey) {
18730
- return [];
18731
- }
18732
- return objectBindings[bindingKey]?.actions ?? [];
19335
+ return bindingKey ? objectBindings[bindingKey]?.actions ?? [] : [];
18733
19336
  },
18734
19337
  [objectBindings]
18735
19338
  );
18736
- const runAction = (0, import_react50.useCallback)(
18737
- (objectId, actionId) => {
18738
- const bindingKey = resolveBindingKey(objectBindings, objectId);
18739
- if (!bindingKey) {
18740
- return null;
19339
+ const handleAction = (0, import_react51.useCallback)(
19340
+ async (event) => {
19341
+ for (const effect of event.action.effects ?? []) {
19342
+ if ("objectBindings" in effect) {
19343
+ updateObjectBindings(effect.target, effect.objectBindings);
19344
+ continue;
19345
+ }
19346
+ if ("sceneConfig" in effect) {
19347
+ updateSceneConfig?.(effect.sceneConfig);
19348
+ continue;
19349
+ }
19350
+ if ("visibility" in effect) {
19351
+ if (effect.visibility === "show") showObject(effect.target);
19352
+ else if (effect.visibility === "hide") hideObject(effect.target);
19353
+ else toggleObjectVisibility(effect.target);
19354
+ continue;
19355
+ }
19356
+ if ("camera" in effect) {
19357
+ const action2 = effect.camera;
19358
+ if (action2.type === "focus-object") {
19359
+ await focusObject?.(action2.objectId ?? event.objectId);
19360
+ } else if (action2.type === "fit-scene") {
19361
+ await fitScene?.();
19362
+ } else if (action2.type === "reset") {
19363
+ await resetView?.();
19364
+ } else if (action2.type === "set-target") {
19365
+ await setCameraTarget?.(action2.target, action2.transition ?? true);
19366
+ } else {
19367
+ await setCameraState?.(action2.state, action2.transition ?? true);
19368
+ }
19369
+ continue;
19370
+ }
19371
+ const action = effect.animation;
19372
+ if (action.type === "play") playAnimation?.(action.clip);
19373
+ else if (action.type === "pause") pauseAnimation?.();
19374
+ else if (action.type === "stop") stopAnimation?.();
19375
+ else if (action.type === "set-speed") {
19376
+ setAnimationSpeed?.(action.speed);
19377
+ } else {
19378
+ seekAnimation?.(action.time);
19379
+ }
18741
19380
  }
19381
+ await onAction?.(event);
19382
+ },
19383
+ [
19384
+ fitScene,
19385
+ focusObject,
19386
+ hideObject,
19387
+ onAction,
19388
+ pauseAnimation,
19389
+ playAnimation,
19390
+ resetView,
19391
+ seekAnimation,
19392
+ setAnimationSpeed,
19393
+ setCameraState,
19394
+ setCameraTarget,
19395
+ showObject,
19396
+ stopAnimation,
19397
+ toggleObjectVisibility,
19398
+ updateObjectBindings,
19399
+ updateSceneConfig
19400
+ ]
19401
+ );
19402
+ const runAction = (0, import_react51.useCallback)(
19403
+ async (objectId, actionId) => {
19404
+ const bindingKey = resolveBindingKey(objectBindings, objectId);
19405
+ if (!bindingKey) return null;
18742
19406
  const binding = objectBindings[bindingKey];
18743
- if (!binding) {
18744
- return null;
18745
- }
18746
- const action = (binding.actions ?? []).find(
18747
- (entry) => entry.id === actionId
18748
- );
18749
- if (!action) {
18750
- return null;
18751
- }
19407
+ const action = binding?.actions?.find((entry) => entry.id === actionId);
19408
+ if (!binding || !action) return null;
18752
19409
  const event = {
18753
19410
  objectId: bindingKey,
18754
19411
  action,
18755
19412
  binding
18756
19413
  };
18757
- if (action.id === "toggle-visibility" && onObjectBindingsChange) {
18758
- onObjectBindingsChange({
18759
- ...objectBindings,
18760
- [bindingKey]: {
18761
- ...binding,
18762
- visible: binding.visible === false
18763
- }
18764
- });
18765
- }
18766
- onAction?.(event);
19414
+ await handleAction(event);
18767
19415
  return event;
18768
19416
  },
18769
- [objectBindings, onAction, onObjectBindingsChange]
19417
+ [handleAction, objectBindings]
18770
19418
  );
18771
19419
  return {
18772
19420
  getActionsForObject,
19421
+ handleAction,
18773
19422
  runAction
18774
19423
  };
18775
19424
  }
18776
19425
 
19426
+ // src/hooks/useViewerConnection.ts
19427
+ var import_react52 = require("react");
19428
+ function getReadyHandler(consumer) {
19429
+ if (typeof consumer === "function") {
19430
+ return consumer;
19431
+ }
19432
+ return consumer?.handleViewerReady ?? null;
19433
+ }
19434
+ function useViewerConnection(...consumers) {
19435
+ const viewerRef = (0, import_react52.useRef)(null);
19436
+ const consumersRef = (0, import_react52.useRef)(consumers);
19437
+ consumersRef.current = consumers;
19438
+ const handleViewerReady = (0, import_react52.useCallback)((viewer) => {
19439
+ viewerRef.current = viewer;
19440
+ const invoked = /* @__PURE__ */ new Set();
19441
+ for (const consumer of consumersRef.current) {
19442
+ const handler = getReadyHandler(consumer);
19443
+ if (handler && !invoked.has(handler)) {
19444
+ invoked.add(handler);
19445
+ handler(viewer);
19446
+ }
19447
+ }
19448
+ }, []);
19449
+ const getViewer = (0, import_react52.useCallback)(() => viewerRef.current, []);
19450
+ const captureImage2 = (0, import_react52.useCallback)((options) => {
19451
+ const viewer = viewerRef.current;
19452
+ if (!viewer) {
19453
+ return Promise.reject(new Error("ModelViewer is not ready yet."));
19454
+ }
19455
+ return viewer.captureImage(options);
19456
+ }, []);
19457
+ const invalidate = (0, import_react52.useCallback)(() => {
19458
+ const viewer = viewerRef.current;
19459
+ if (!viewer) {
19460
+ return false;
19461
+ }
19462
+ viewer.invalidate();
19463
+ return true;
19464
+ }, []);
19465
+ return {
19466
+ handleViewerReady,
19467
+ getViewer,
19468
+ captureImage: captureImage2,
19469
+ invalidate
19470
+ };
19471
+ }
19472
+
19473
+ // src/hooks/useViewerEffects.ts
19474
+ var import_react53 = require("react");
19475
+ var import_three25 = require("three");
19476
+ var NUMERIC_MATERIAL_KEYS = [
19477
+ "emissiveIntensity",
19478
+ "opacity",
19479
+ "metalness",
19480
+ "roughness",
19481
+ "transmission",
19482
+ "clearcoat",
19483
+ "clearcoatRoughness",
19484
+ "ior",
19485
+ "thickness"
19486
+ ];
19487
+ var nextEffectId = 1;
19488
+ function stringList(target) {
19489
+ return typeof target === "string" ? [target] : target;
19490
+ }
19491
+ function clamp2(value, min = 0, max = 1) {
19492
+ return Math.min(max, Math.max(min, value));
19493
+ }
19494
+ function randomBetween(min, max) {
19495
+ return min + Math.random() * (max - min);
19496
+ }
19497
+ function interpolate([from, to], level) {
19498
+ return from + (to - from) * level;
19499
+ }
19500
+ function interpolateVector([from, to], level) {
19501
+ return [
19502
+ from[0] + (to[0] - from[0]) * level,
19503
+ from[1] + (to[1] - from[1]) * level,
19504
+ from[2] + (to[2] - from[2]) * level
19505
+ ];
19506
+ }
19507
+ function ease(value, easing) {
19508
+ const t = clamp2(value);
19509
+ switch (easing) {
19510
+ case "ease-in":
19511
+ return t * t;
19512
+ case "ease-out":
19513
+ return 1 - (1 - t) * (1 - t);
19514
+ case "ease-in-out":
19515
+ return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
19516
+ default:
19517
+ return t;
19518
+ }
19519
+ }
19520
+ function materialList(node) {
19521
+ const value = node.material;
19522
+ if (!value) return [];
19523
+ return (Array.isArray(value) ? value : [value]).filter(
19524
+ (entry) => entry instanceof import_three25.Material
19525
+ );
19526
+ }
19527
+ function readMaterial(material2) {
19528
+ const snapshot = {
19529
+ opacity: material2.opacity,
19530
+ transparent: material2.transparent
19531
+ };
19532
+ if (material2.color) snapshot.baseColor = `#${material2.color.getHexString()}`;
19533
+ if (material2.emissive) snapshot.emissive = `#${material2.emissive.getHexString()}`;
19534
+ for (const key of NUMERIC_MATERIAL_KEYS) {
19535
+ const value = material2[key];
19536
+ if (typeof value === "number") snapshot[key] = value;
19537
+ }
19538
+ return snapshot;
19539
+ }
19540
+ function applyMaterial(material2, patch) {
19541
+ if (patch.baseColor !== void 0 && material2.color) {
19542
+ material2.color.set(patch.baseColor);
19543
+ }
19544
+ if (patch.emissive !== void 0 && material2.emissive) {
19545
+ material2.emissive.set(patch.emissive);
19546
+ }
19547
+ for (const key of NUMERIC_MATERIAL_KEYS) {
19548
+ const value = patch[key];
19549
+ if (value !== void 0 && typeof material2[key] === "number") {
19550
+ material2[key] = value;
19551
+ }
19552
+ }
19553
+ if (patch.transparent !== void 0) material2.transparent = patch.transparent;
19554
+ material2.needsUpdate = true;
19555
+ }
19556
+ function readTransform(node) {
19557
+ return {
19558
+ position: [node.position.x, node.position.y, node.position.z],
19559
+ rotation: [node.rotation.x, node.rotation.y, node.rotation.z],
19560
+ scale: [node.scale.x, node.scale.y, node.scale.z],
19561
+ visible: node.visible
19562
+ };
19563
+ }
19564
+ function applyTransform(node, patch) {
19565
+ if (patch.position) node.position.set(...patch.position);
19566
+ if (patch.rotation) node.rotation.set(...patch.rotation);
19567
+ if (patch.scale !== void 0) {
19568
+ const scale = typeof patch.scale === "number" ? [patch.scale, patch.scale, patch.scale] : patch.scale;
19569
+ node.scale.set(...scale);
19570
+ }
19571
+ if (patch.visible !== void 0) node.visible = patch.visible;
19572
+ node.updateMatrix();
19573
+ node.updateMatrixWorld(true);
19574
+ }
19575
+ function useViewerEffects(options = {}) {
19576
+ const optionsRef = (0, import_react53.useRef)(options);
19577
+ optionsRef.current = options;
19578
+ const viewerRef = (0, import_react53.useRef)(null);
19579
+ const sceneRef = (0, import_react53.useRef)(null);
19580
+ const frameRef = (0, import_react53.useRef)(null);
19581
+ const effectsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
19582
+ const lightsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
19583
+ const materialSnapshotsRef = (0, import_react53.useRef)(
19584
+ /* @__PURE__ */ new Map()
19585
+ );
19586
+ const transformSnapshotsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
19587
+ const resolveObject = (0, import_react53.useCallback)((target) => {
19588
+ const viewer = viewerRef.current;
19589
+ if (!viewer) return null;
19590
+ const bindingKey = resolveBindingKey(viewer.objectBindings, target);
19591
+ return viewer.nodeRefs[target] ?? (bindingKey ? viewer.nodeRefs[bindingKey] : null) ?? viewer.scene?.getObjectByName(target) ?? null;
19592
+ }, []);
19593
+ const resolveTargets = (0, import_react53.useCallback)(
19594
+ (target) => {
19595
+ const viewer = viewerRef.current;
19596
+ if (!viewer) {
19597
+ return typeof target === "string" ? [target] : Array.isArray(target) ? target : [];
19598
+ }
19599
+ if (typeof target === "object" && !Array.isArray(target)) {
19600
+ return selectBindingKeys(viewer.objectBindings, target);
19601
+ }
19602
+ return stringList(target).map(
19603
+ (id) => resolveBindingKey(viewer.objectBindings, id) ?? id
19604
+ );
19605
+ },
19606
+ []
19607
+ );
19608
+ const invalidate = (0, import_react53.useCallback)(() => {
19609
+ viewerRef.current?.invalidate();
19610
+ }, []);
19611
+ const setMaterialInternal = (0, import_react53.useCallback)(
19612
+ (target, patch) => {
19613
+ let changed = false;
19614
+ for (const id of resolveTargets(target)) {
19615
+ const node = resolveObject(id);
19616
+ if (!node) continue;
19617
+ for (const entry of materialList(node)) {
19618
+ if (!materialSnapshotsRef.current.has(entry)) {
19619
+ materialSnapshotsRef.current.set(entry, readMaterial(entry));
19620
+ }
19621
+ applyMaterial(entry, patch);
19622
+ changed = true;
19623
+ }
19624
+ }
19625
+ return changed;
19626
+ },
19627
+ [resolveObject, resolveTargets]
19628
+ );
19629
+ const setMaterial = (0, import_react53.useCallback)(
19630
+ (target, patch) => {
19631
+ const changed = setMaterialInternal(target, patch);
19632
+ if (changed) invalidate();
19633
+ return changed;
19634
+ },
19635
+ [invalidate, setMaterialInternal]
19636
+ );
19637
+ const setTransformInternal = (0, import_react53.useCallback)(
19638
+ (target, patch) => {
19639
+ let changed = false;
19640
+ for (const id of resolveTargets(target)) {
19641
+ const node = resolveObject(id);
19642
+ if (!node) continue;
19643
+ if (!transformSnapshotsRef.current.has(node)) {
19644
+ transformSnapshotsRef.current.set(node, readTransform(node));
19645
+ }
19646
+ applyTransform(node, patch);
19647
+ changed = true;
19648
+ }
19649
+ return changed;
19650
+ },
19651
+ [resolveObject, resolveTargets]
19652
+ );
19653
+ const setTransform = (0, import_react53.useCallback)(
19654
+ (target, patch) => {
19655
+ const changed = setTransformInternal(target, patch);
19656
+ if (changed) invalidate();
19657
+ return changed;
19658
+ },
19659
+ [invalidate, setTransformInternal]
19660
+ );
19661
+ const getWorldPosition = (0, import_react53.useCallback)(
19662
+ (target) => {
19663
+ const node = resolveObject(target);
19664
+ if (!node) return null;
19665
+ node.updateWorldMatrix(true, false);
19666
+ const position = node.getWorldPosition(new import_three25.Vector3());
19667
+ return [position.x, position.y, position.z];
19668
+ },
19669
+ [resolveObject]
19670
+ );
19671
+ const removePointLight = (0, import_react53.useCallback)(
19672
+ (id) => {
19673
+ const runtime = lightsRef.current.get(id);
19674
+ if (!runtime) return false;
19675
+ runtime.light.removeFromParent();
19676
+ runtime.light.dispose();
19677
+ lightsRef.current.delete(id);
19678
+ invalidate();
19679
+ return true;
19680
+ },
19681
+ [invalidate]
19682
+ );
19683
+ const addPointLight = (0, import_react53.useCallback)(
19684
+ (lightOptions) => {
19685
+ const target = resolveObject(lightOptions.target);
19686
+ if (!target) return false;
19687
+ const existing = lightsRef.current.get(lightOptions.id);
19688
+ const light = existing?.light ?? new import_three25.PointLight();
19689
+ if (existing && existing.target !== lightOptions.target) {
19690
+ light.removeFromParent();
19691
+ }
19692
+ light.name = `react-immersive-runtime-light:${lightOptions.id}`;
19693
+ light.color.set(lightOptions.color ?? "#ffffff");
19694
+ light.intensity = lightOptions.intensity ?? 1;
19695
+ light.distance = lightOptions.distance ?? 0;
19696
+ light.decay = lightOptions.decay ?? 2;
19697
+ light.castShadow = lightOptions.castShadow ?? false;
19698
+ light.position.set(...lightOptions.offset ?? [0, 0, 0]);
19699
+ if (light.parent !== target) target.add(light);
19700
+ lightsRef.current.set(lightOptions.id, {
19701
+ light,
19702
+ target: lightOptions.target
19703
+ });
19704
+ invalidate();
19705
+ return true;
19706
+ },
19707
+ [invalidate, resolveObject]
19708
+ );
19709
+ const updatePointLight = (0, import_react53.useCallback)(
19710
+ (id, patch) => {
19711
+ const runtime = lightsRef.current.get(id);
19712
+ if (!runtime) return false;
19713
+ if (patch.target && patch.target !== runtime.target) {
19714
+ const target = resolveObject(patch.target);
19715
+ if (!target) return false;
19716
+ runtime.light.removeFromParent();
19717
+ target.add(runtime.light);
19718
+ runtime.target = patch.target;
19719
+ }
19720
+ if (patch.color !== void 0) runtime.light.color.set(patch.color);
19721
+ if (patch.intensity !== void 0) {
19722
+ runtime.light.intensity = patch.intensity;
19723
+ }
19724
+ if (patch.distance !== void 0) runtime.light.distance = patch.distance;
19725
+ if (patch.decay !== void 0) runtime.light.decay = patch.decay;
19726
+ if (patch.offset !== void 0) {
19727
+ runtime.light.position.set(...patch.offset);
19728
+ }
19729
+ if (patch.castShadow !== void 0) {
19730
+ runtime.light.castShadow = patch.castShadow;
19731
+ }
19732
+ invalidate();
19733
+ return true;
19734
+ },
19735
+ [invalidate, resolveObject]
19736
+ );
19737
+ const applyChannels = (0, import_react53.useCallback)(
19738
+ (channels, levels) => {
19739
+ const levelFor = (index) => levels[index % levels.length] ?? 0;
19740
+ for (const channel of channels.materials ?? []) {
19741
+ resolveTargets(channel.targets).forEach((target, index) => {
19742
+ const level = levelFor(index);
19743
+ const patch = {};
19744
+ for (const key of NUMERIC_MATERIAL_KEYS) {
19745
+ const range = channel.values[key];
19746
+ if (range) patch[key] = interpolate(range, level);
19747
+ }
19748
+ setMaterialInternal(target, patch);
19749
+ });
19750
+ }
19751
+ for (const channel of channels.transforms ?? []) {
19752
+ resolveTargets(channel.targets).forEach((target, index) => {
19753
+ const level = levelFor(index);
19754
+ const patch = {};
19755
+ if (channel.values.position) {
19756
+ patch.position = interpolateVector(channel.values.position, level);
19757
+ }
19758
+ if (channel.values.rotation) {
19759
+ patch.rotation = interpolateVector(channel.values.rotation, level);
19760
+ }
19761
+ if (channel.values.scale) {
19762
+ patch.scale = interpolateVector(channel.values.scale, level);
19763
+ }
19764
+ setTransformInternal(target, patch);
19765
+ });
19766
+ }
19767
+ for (const channel of channels.lights ?? []) {
19768
+ stringList(channel.lights).forEach((id, index) => {
19769
+ const runtime = lightsRef.current.get(id);
19770
+ if (runtime) {
19771
+ runtime.light.intensity = interpolate(
19772
+ channel.intensity,
19773
+ levelFor(index)
19774
+ );
19775
+ }
19776
+ });
19777
+ }
19778
+ },
19779
+ [resolveTargets, setMaterialInternal, setTransformInternal]
19780
+ );
19781
+ const scheduleFrameRef = (0, import_react53.useRef)(() => void 0);
19782
+ const runFrameRef = (0, import_react53.useRef)(() => void 0);
19783
+ runFrameRef.current = (now) => {
19784
+ frameRef.current = null;
19785
+ let rendered = false;
19786
+ for (const [id, effect] of effectsRef.current) {
19787
+ if (effect.startedAt === null) effect.startedAt = now;
19788
+ const previous = effect.lastFrameAt ?? now;
19789
+ effect.lastFrameAt = now;
19790
+ const override = effect.override && effect.override.until > now ? effect.override.level : null;
19791
+ if (effect.override && override === null) effect.override = null;
19792
+ const keepRunning = effect.update(
19793
+ now,
19794
+ now - effect.startedAt,
19795
+ Math.min(100, now - previous),
19796
+ override
19797
+ );
19798
+ rendered = true;
19799
+ if (!keepRunning) {
19800
+ effectsRef.current.delete(id);
19801
+ effect.onComplete?.();
19802
+ }
19803
+ }
19804
+ if (rendered) invalidate();
19805
+ if (effectsRef.current.size > 0) scheduleFrameRef.current();
19806
+ };
19807
+ scheduleFrameRef.current = () => {
19808
+ if (frameRef.current !== null || typeof requestAnimationFrame === "undefined") {
19809
+ return;
19810
+ }
19811
+ frameRef.current = requestAnimationFrame((now) => runFrameRef.current(now));
19812
+ };
19813
+ const registerEffect = (0, import_react53.useCallback)((effect) => {
19814
+ effectsRef.current.set(effect.id, effect);
19815
+ scheduleFrameRef.current();
19816
+ return effect.id;
19817
+ }, []);
19818
+ const transition = (0, import_react53.useCallback)(
19819
+ (channels, transitionOptions = {}) => {
19820
+ const id = transitionOptions.id ?? `transition-${nextEffectId++}`;
19821
+ const duration = Math.max(1, transitionOptions.duration ?? 400);
19822
+ const easing = transitionOptions.easing ?? "ease-in-out";
19823
+ return registerEffect({
19824
+ id,
19825
+ startedAt: null,
19826
+ lastFrameAt: null,
19827
+ override: null,
19828
+ onComplete: transitionOptions.onComplete,
19829
+ update: (_now, elapsed, _delta, override) => {
19830
+ const progress = override ?? ease(elapsed / duration, easing);
19831
+ applyChannels(channels, [progress]);
19832
+ return elapsed < duration;
19833
+ }
19834
+ });
19835
+ },
19836
+ [applyChannels, registerEffect]
19837
+ );
19838
+ const pulse = (0, import_react53.useCallback)(
19839
+ (channels, pulseOptions = {}) => {
19840
+ const id = pulseOptions.id ?? `pulse-${nextEffectId++}`;
19841
+ const duration = Math.max(1, pulseOptions.duration ?? 1e3);
19842
+ const minLevel = pulseOptions.minLevel ?? 0;
19843
+ const maxLevel = pulseOptions.maxLevel ?? 1;
19844
+ const repeat = pulseOptions.repeat ?? Infinity;
19845
+ const synchronized = pulseOptions.synchronized ?? true;
19846
+ const unitCount = Math.max(
19847
+ 1,
19848
+ ...[...channels.materials ?? [], ...channels.transforms ?? []].map(
19849
+ (channel) => resolveTargets(channel.targets).length
19850
+ ),
19851
+ ...(channels.lights ?? []).map(
19852
+ (channel) => stringList(channel.lights).length
19853
+ )
19854
+ );
19855
+ return registerEffect({
19856
+ id,
19857
+ startedAt: null,
19858
+ lastFrameAt: null,
19859
+ override: null,
19860
+ onComplete: pulseOptions.onComplete,
19861
+ update: (_now, elapsed, _delta, override) => {
19862
+ const cycles = elapsed / duration;
19863
+ const levels = Array.from(
19864
+ { length: synchronized ? 1 : unitCount },
19865
+ (_, index) => {
19866
+ if (override !== null) return override;
19867
+ const phase = cycles + (synchronized ? 0 : index / unitCount);
19868
+ const wave = 0.5 - Math.cos(phase % 1 * Math.PI * 2) / 2;
19869
+ return minLevel + (maxLevel - minLevel) * wave;
19870
+ }
19871
+ );
19872
+ applyChannels(channels, levels);
19873
+ return repeat === Infinity || cycles < repeat;
19874
+ }
19875
+ });
19876
+ },
19877
+ [applyChannels, registerEffect, resolveTargets]
19878
+ );
19879
+ const timeline = (0, import_react53.useCallback)(
19880
+ (steps, timelineOptions = {}) => {
19881
+ const id = timelineOptions.id ?? `timeline-${nextEffectId++}`;
19882
+ const repeat = timelineOptions.repeat ?? 1;
19883
+ const normalizedSteps = steps.map((step) => ({
19884
+ ...step,
19885
+ duration: Math.max(1, step.duration),
19886
+ hold: Math.max(0, step.hold ?? 0)
19887
+ }));
19888
+ const cycleDuration = Math.max(
19889
+ 1,
19890
+ normalizedSteps.reduce(
19891
+ (total, step) => total + step.duration + step.hold,
19892
+ 0
19893
+ )
19894
+ );
19895
+ return registerEffect({
19896
+ id,
19897
+ startedAt: null,
19898
+ lastFrameAt: null,
19899
+ override: null,
19900
+ onComplete: timelineOptions.onComplete,
19901
+ update: (_now, elapsed, _delta, override) => {
19902
+ if (normalizedSteps.length === 0 || repeat <= 0) return false;
19903
+ const cycles = elapsed / cycleDuration;
19904
+ if (repeat !== Infinity && cycles >= repeat) {
19905
+ for (const step of normalizedSteps) {
19906
+ applyChannels(step.channels, [1]);
19907
+ }
19908
+ return false;
19909
+ }
19910
+ const cycleTime = elapsed % cycleDuration;
19911
+ let cursor = 0;
19912
+ for (const step of normalizedSteps) {
19913
+ const transitionEnd = cursor + step.duration;
19914
+ const stepEnd = transitionEnd + step.hold;
19915
+ if (cycleTime >= transitionEnd) {
19916
+ applyChannels(step.channels, [override ?? 1]);
19917
+ cursor = stepEnd;
19918
+ continue;
19919
+ }
19920
+ if (cycleTime >= cursor) {
19921
+ const progress = override ?? ease(
19922
+ (cycleTime - cursor) / step.duration,
19923
+ step.easing ?? "ease-in-out"
19924
+ );
19925
+ applyChannels(step.channels, [progress]);
19926
+ break;
19927
+ }
19928
+ }
19929
+ return true;
19930
+ }
19931
+ });
19932
+ },
19933
+ [applyChannels, registerEffect]
19934
+ );
19935
+ const flicker = (0, import_react53.useCallback)(
19936
+ (channels, flickerOptions = {}) => {
19937
+ const id = flickerOptions.id ?? `flicker-${nextEffectId++}`;
19938
+ const synchronized = flickerOptions.synchronized ?? true;
19939
+ const minLevel = flickerOptions.minLevel ?? 0.15;
19940
+ const maxLevel = flickerOptions.maxLevel ?? 1;
19941
+ const interval = flickerOptions.interval ?? [45, 160];
19942
+ const rawIntervalRange = typeof interval === "number" ? [interval, interval] : interval;
19943
+ const intervalRange = [
19944
+ Math.max(1, Math.min(...rawIntervalRange)),
19945
+ Math.max(1, Math.max(...rawIntervalRange))
19946
+ ];
19947
+ const response = clamp2(flickerOptions.response ?? 0.7, 0.01, 1);
19948
+ const blackoutChance = clamp2(flickerOptions.blackoutChance ?? 0.08);
19949
+ const flashChance = clamp2(flickerOptions.flashChance ?? 0.12);
19950
+ const flashLevel = flickerOptions.flashLevel ?? 1.15;
19951
+ const unitCount = Math.max(
19952
+ 1,
19953
+ ...[...channels.materials ?? [], ...channels.transforms ?? []].map(
19954
+ (channel) => resolveTargets(channel.targets).length
19955
+ ),
19956
+ ...(channels.lights ?? []).map(
19957
+ (channel) => stringList(channel.lights).length
19958
+ )
19959
+ );
19960
+ const count = synchronized ? 1 : unitCount;
19961
+ const levels = Array.from({ length: count }, () => minLevel);
19962
+ const targets = Array.from({ length: count }, () => minLevel);
19963
+ const nextChanges = Array.from({ length: count }, () => 0);
19964
+ return registerEffect({
19965
+ id,
19966
+ startedAt: null,
19967
+ lastFrameAt: null,
19968
+ override: null,
19969
+ onComplete: flickerOptions.onComplete,
19970
+ update: (now, elapsed, delta, override) => {
19971
+ for (let index = 0; index < count; index += 1) {
19972
+ if (override !== null) {
19973
+ targets[index] = override;
19974
+ } else if (now >= nextChanges[index]) {
19975
+ const roll = Math.random();
19976
+ targets[index] = roll < blackoutChance ? randomBetween(0, Math.min(0.04, minLevel)) : roll < blackoutChance + flashChance ? flashLevel : randomBetween(minLevel, maxLevel);
19977
+ nextChanges[index] = now + randomBetween(...intervalRange);
19978
+ }
19979
+ const frameResponse = 1 - Math.pow(1 - response, Math.max(1, delta) / 16.67);
19980
+ levels[index] += (targets[index] - levels[index]) * frameResponse;
19981
+ }
19982
+ applyChannels(channels, levels);
19983
+ return flickerOptions.duration === void 0 || elapsed < flickerOptions.duration;
19984
+ }
19985
+ });
19986
+ },
19987
+ [applyChannels, registerEffect, resolveTargets]
19988
+ );
19989
+ const overrideEffect = (0, import_react53.useCallback)(
19990
+ (id, level, duration) => {
19991
+ const effect = effectsRef.current.get(id);
19992
+ if (!effect) return false;
19993
+ const now = typeof performance === "undefined" ? Date.now() : performance.now();
19994
+ effect.override = { level, until: now + Math.max(0, duration) };
19995
+ scheduleFrameRef.current();
19996
+ return true;
19997
+ },
19998
+ []
19999
+ );
20000
+ const stopEffect = (0, import_react53.useCallback)((id) => {
20001
+ return effectsRef.current.delete(id);
20002
+ }, []);
20003
+ const stopAllEffects = (0, import_react53.useCallback)(() => {
20004
+ effectsRef.current.clear();
20005
+ if (frameRef.current !== null && typeof cancelAnimationFrame !== "undefined") {
20006
+ cancelAnimationFrame(frameRef.current);
20007
+ frameRef.current = null;
20008
+ }
20009
+ }, []);
20010
+ const restoreRuntimeValues = (0, import_react53.useCallback)(() => {
20011
+ for (const [entry, snapshot] of materialSnapshotsRef.current) {
20012
+ applyMaterial(entry, snapshot);
20013
+ }
20014
+ materialSnapshotsRef.current.clear();
20015
+ for (const [node, snapshot] of transformSnapshotsRef.current) {
20016
+ node.position.set(...snapshot.position);
20017
+ node.rotation.set(...snapshot.rotation);
20018
+ node.scale.set(...snapshot.scale);
20019
+ node.visible = snapshot.visible;
20020
+ node.updateMatrix();
20021
+ node.updateMatrixWorld(true);
20022
+ }
20023
+ transformSnapshotsRef.current.clear();
20024
+ for (const runtime of lightsRef.current.values()) {
20025
+ runtime.light.removeFromParent();
20026
+ runtime.light.dispose();
20027
+ }
20028
+ lightsRef.current.clear();
20029
+ invalidate();
20030
+ }, [invalidate]);
20031
+ const resetRuntime = (0, import_react53.useCallback)(() => {
20032
+ stopAllEffects();
20033
+ restoreRuntimeValues();
20034
+ }, [restoreRuntimeValues, stopAllEffects]);
20035
+ const handleViewerReady = (0, import_react53.useCallback)(
20036
+ (viewer) => {
20037
+ if (sceneRef.current && sceneRef.current !== viewer.scene) {
20038
+ restoreRuntimeValues();
20039
+ }
20040
+ viewerRef.current = viewer;
20041
+ sceneRef.current = viewer.scene;
20042
+ optionsRef.current.onViewerReady?.(viewer);
20043
+ },
20044
+ [restoreRuntimeValues]
20045
+ );
20046
+ (0, import_react53.useEffect)(() => {
20047
+ return () => {
20048
+ resetRuntime();
20049
+ viewerRef.current = null;
20050
+ sceneRef.current = null;
20051
+ };
20052
+ }, [resetRuntime]);
20053
+ return {
20054
+ handleViewerReady,
20055
+ setMaterial,
20056
+ setTransform,
20057
+ getWorldPosition,
20058
+ addPointLight,
20059
+ updatePointLight,
20060
+ removePointLight,
20061
+ transition,
20062
+ timeline,
20063
+ pulse,
20064
+ flicker,
20065
+ overrideEffect,
20066
+ stopEffect,
20067
+ stopAllEffects,
20068
+ resetRuntime
20069
+ };
20070
+ }
20071
+
18777
20072
  // src/hooks/useShareableViewerState.ts
18778
- var import_react51 = require("react");
20073
+ var import_react54 = require("react");
18779
20074
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
18780
20075
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
18781
20076
  return {
@@ -18795,8 +20090,8 @@ function useShareableViewerState({
18795
20090
  debounceMs = 400,
18796
20091
  autoSync = true
18797
20092
  }) {
18798
- const lastWrittenRawRef = (0, import_react51.useRef)(null);
18799
- const buildHash = (0, import_react51.useCallback)(() => {
20093
+ const lastWrittenRawRef = (0, import_react54.useRef)(null);
20094
+ const buildHash = (0, import_react54.useCallback)(() => {
18800
20095
  const state = serializeState(
18801
20096
  cameraState,
18802
20097
  objectBindings,
@@ -18804,12 +20099,12 @@ function useShareableViewerState({
18804
20099
  );
18805
20100
  return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
18806
20101
  }, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
18807
- const getShareUrl = (0, import_react51.useCallback)(() => {
20102
+ const getShareUrl = (0, import_react54.useCallback)(() => {
18808
20103
  const url = new URL(window.location.href);
18809
20104
  url.hash = buildHash();
18810
20105
  return url.toString();
18811
20106
  }, [buildHash]);
18812
- const syncUrl = (0, import_react51.useCallback)(() => {
20107
+ const syncUrl = (0, import_react54.useCallback)(() => {
18813
20108
  const url = new URL(window.location.href);
18814
20109
  url.hash = buildHash();
18815
20110
  window.history.replaceState(null, "", url.toString());
@@ -18817,7 +20112,7 @@ function useShareableViewerState({
18817
20112
  paramKey
18818
20113
  );
18819
20114
  }, [buildHash, paramKey]);
18820
- const latestStateRef = (0, import_react51.useRef)({
20115
+ const latestStateRef = (0, import_react54.useRef)({
18821
20116
  cameraState,
18822
20117
  objectBindings,
18823
20118
  selectedObjectBinding
@@ -18827,9 +20122,9 @@ function useShareableViewerState({
18827
20122
  objectBindings,
18828
20123
  selectedObjectBinding
18829
20124
  };
18830
- const [restoreAttempted, setRestoreAttempted] = (0, import_react51.useState)(false);
18831
- const lastSyncedHashRef = (0, import_react51.useRef)(null);
18832
- (0, import_react51.useEffect)(() => {
20125
+ const [restoreAttempted, setRestoreAttempted] = (0, import_react54.useState)(false);
20126
+ const lastSyncedHashRef = (0, import_react54.useRef)(null);
20127
+ (0, import_react54.useEffect)(() => {
18833
20128
  if (!autoSync || !restoreAttempted) return;
18834
20129
  const tick = () => {
18835
20130
  const current = latestStateRef.current;
@@ -18851,9 +20146,9 @@ function useShareableViewerState({
18851
20146
  const id = window.setInterval(tick, debounceMs);
18852
20147
  return () => window.clearInterval(id);
18853
20148
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
18854
- const lastRestoredRawRef = (0, import_react51.useRef)(null);
18855
- const restoringRawRef = (0, import_react51.useRef)(null);
18856
- const restoreFromUrl = (0, import_react51.useCallback)(async () => {
20149
+ const lastRestoredRawRef = (0, import_react54.useRef)(null);
20150
+ const restoringRawRef = (0, import_react54.useRef)(null);
20151
+ const restoreFromUrl = (0, import_react54.useCallback)(async () => {
18857
20152
  setRestoreAttempted(true);
18858
20153
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
18859
20154
  const raw = new URLSearchParams(rawHash).get(paramKey);
@@ -18906,7 +20201,7 @@ function useShareableViewerState({
18906
20201
  setCameraState,
18907
20202
  setSelectedObjectBinding
18908
20203
  ]);
18909
- (0, import_react51.useEffect)(() => {
20204
+ (0, import_react54.useEffect)(() => {
18910
20205
  const handleHashChange = () => {
18911
20206
  void restoreFromUrl();
18912
20207
  };
@@ -18920,13 +20215,17 @@ function useShareableViewerState({
18920
20215
  // Annotate the CommonJS export names for ESM import in node:
18921
20216
  0 && (module.exports = {
18922
20217
  BindingBuilder,
20218
+ DEFAULT_SCENE_CONFIG,
18923
20219
  MATERIAL_BLENDING_MODES,
18924
20220
  MATERIAL_SIDES,
18925
20221
  ModelViewer,
18926
20222
  SimpleModelViewer,
20223
+ defineObjectBindings,
18927
20224
  material,
18928
20225
  patchBindings,
18929
20226
  patchSceneConfig,
20227
+ selectBindingKeys,
20228
+ selectBindings,
18930
20229
  useObjectBinding,
18931
20230
  useObjectBindingIds,
18932
20231
  useObjectBindings,
@@ -18935,6 +20234,8 @@ function useShareableViewerState({
18935
20234
  useViewerActions,
18936
20235
  useViewerAnimations,
18937
20236
  useViewerCamera,
20237
+ useViewerConnection,
20238
+ useViewerEffects,
18938
20239
  useViewerHover,
18939
20240
  useViewerModel,
18940
20241
  useViewerSelection