@liveroom-tech/react-immersive 2.2.0 → 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,22 +5948,30 @@ 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");
5952
+
5953
+ // src/assets/light-gizmos/sun.png
5954
+ var sun_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N15mFxllfjx76nqTmclewJJSEIWsgBh3xcNi0mqExHUjMO46wwz/lSYcQHFJTqKqCgoKu4CMipBBQzpEAJE9kWQnSydzr7va3d6qTq/P97ukD1Vdd9b997q83mefgKm6r1Huuq+577LecEYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYY00qnk446BhOcRB2AMcaY+NP76EEVnwX+BTgeqAAWocyggltkAiuijdAUyhIAY4wxh6UPch4p7gUGHOIluxD+n0zizlLGZYKxBMAYY8wh6SzejXIP0PFIL0X4mCUByWEJgDHGmIPSGj4M/AY33J+PXWQZK1NYHmJYxpNU1AEYY4yJH63hWuAO8u/8AbqQ5rPhRGR8sxGAGNMahgP/hXIpwgBgO/APhF/LJB6NODxjTJnSmfwvwleKfPsbkuEkrwGZUFgCEFNawxeBbwJVh3jJX2jkk3IFW0sYljGmjKkizOKHwLUBmtkmGXr4ismEx6YAYkhruAn4Lofu/AHeSxXP6GyOK1FYxpgypi9SySz+QLDOH6DeRzwmfJYAxIw+xKXAdXm+fAxZntOZnB1mTMaY8qZz6ch6/gJ8IHBjwqvBIzKlYAlA3GT5WoHv6IfwuM7y8MU1xrQ7Opsu1DMDmOKnQf7qpR0TOlsDECP6ID1JsYnifi854DrJcLPnsIwxZUrn0J1mZgLne2pyG2kGygR2eWrPhMhGAOKkguMoPilLAd/Xmfxc5xa0bccY0w7pg/Skmdn46/wB7rDOPzksAYiTFpoCtyFczW5m6Fy6eojIGFOG9AH6k+Lv4HX9kCLc7rE9EzJLAOKkC4uA3YHbUSaym7/rA/QPHpQxppzoDAZTyZPAOM9Nz5FJLPDcpgmRrQGIGa1hOvB+T80tJstEmUKtp/ZMQukcutPCxcAolBTCcoRHZSJroo7NlI7OYijKo8Aw740Ll8sk/ua9XRMaSwBiRh/iQnI84bHJzcC7JcPTHts0CaE1VAFfA64Buuz31y3A7+nAF+RSNpU8OFNSOpvRZHkEGBhC88vZyTCZSjaEtk1IbAogZmQiTyI847HJXsDDWuNpi49JDJ1BZ2A28GUO7PzB1Xj/GE08qw9xTEmDMyWlNZxKlicIp/MH+Jl1/sljCUA8+d7K1xm4T2fyn57bNXGW5hbgHXm8ciRZ7lG1EcFypLM4F3gM6BvSJRrI8puQ2jYhsgQgjiZyPzDPc6tphNu1hpvsRl/+dAYjgU/k/QbhQmYxObyITBR0JhehzIZQa/PfLVPYGGL7JiSWAMSQCArcElLz1zGL3+mLVIbUvomDCt4LpAt6j/Kv4QRjoqA1TEJ4COgW5mXI8eMQ2zchsgQgvu6C0FZof4T1zNQHQr0xmCjluKDg9wjv9B+IiYLOZCLwV6BTyJeaI5N5I+RrmJBYAhBTkqER5bYQL3EZlTyqs+kX4jVMVIRTinjXMTqDwd5jMSWlM5mIcB/QMfSLCT8K/RomNJYAxFkTtwPbQ7zCmWR5WmsYHuI1TInpI/Sm2NXeKc7xG40ppZJ2/rCQ53moBNcxIbEEIMbkCrZC6KU1RwDP64OcF/J1TKk0cXLR703Z0dJJVeLOH+BWmUauRNcyIbAEIO6auQVoCPkqvUkxWx/i0pCvY0ojSIlXGwFIoAg6/y2kuatE1zIhsQQg5uRy1gG/LsGlupJjptbwvhJcy4RJA4wAKKe3Vg80CRFB5w/wSzv1L/ksAUiG74GHkwKPrAPwJ51VwP5xEz8SaASgilSABMKUVESdfwtpflrC65mQWAKQAJJhJcrvS3S5NMqvtIbPl+h6xiOdSwUwNlAjOVsHkAQRdf4Af5YJrCjxNU0ILAFICuE7uMNbSnM1+L7WcFOJrmd8aWQUwTsESwBiLsLOH7Ctf+XCEoCEkAx1KPeW+LLX6Ux+qtPsc5IYWS/D97YQMMYi7vyflQzPRXBdEwK7sSeJ8C0o8bYb4VOcxe+tdHBCBJv/bzNcH6C/h3aMZzqLCRF2/mBP/2XFEoAEkQxvIcyI4NJXsZ6/6vTQy4qaoMTTAr5KzvLSjvFGZzEB5X6i6/xX0Y+/RnRtEwJLAJJG+CagEVx5Ml2ZpTUcFcG1Tb6CbAHcVyLXAegSOupCxuj88jrnQmt4F8oDRNf5A9wmZ9Ac4fWNZ5YAJIxM5J/AnIgu/w6Ex7QmtHPFTQA6gz7AMX4aS1YCoCvopIu4lSzbSfEWFWzVOqbr4uRPZWgN7wDug0jrM+wkxa8ivL4JgSUASaR8O8Jrn47yhNYwKLIYQqRLOFoXcorWJnCkIxVw+9/ehNNVEW/thW03PwGugT1rVVIo7yfHY7oiuVNXWsM5wAygc7SB8CuZyOZIYzDeWQKQQFLNEyhPRhcAo4GndAYjI4vBM13IGF3E38myhhQvI6zXRXxPlXTUseUtxRiPrfXkIYZ4bC80uoiLET5+iL8eSxMfKWlAnuhDnAzMhMinM5rJcWvEMZgQWAKQXDdGfP0hpHlcazw+dUZE5zOAFI8B79jrf64CvsBivhdRWIVTRntu8TTP7YVD+Pph/145tUSReKOzGEWO2UCvqGNB+T+ZwvKowzD+WQKQUFLNQ8CLEYdxDPD31ieV5Krg/wFHH/TvlP/WxQlZES+eE4Bc/DtOrWUCykVHeFmizjbQGYxEmQuxWL+gpLk56iBMOCwBSDKJfBQAoC85HtOZnBF1IAGccpi/E3Lcmoj5cPU6BQDEPwEgxTfyeNXK0OPwRGcwmDRz8LWYM7gZMpE3ow7ChMMSgCSbyP3AP6IOA+iF8Ig+yHlRB1KkI22rPJfFXFGSSIqkM+gMHOu1UYl3Uqe1TMlzt8Ky0IPxQGsYRJq5EKu1F8mZAjMFswQgwURQ4H+jjqNVd1I8rDVcHHUgRag74iuU72ttjIeS04zC//e5vz4UmyfRfagiSF5P/6DxTwB0Nv2Ah4FhUceyh/KkZHg66jBMeCwBSDjJMAN4Puo4WnUBZuhMLos6kIIoC/J41TBS/GfosRTL//B/m3guBFzEu8l3iiLF0lBjCUhn0Icsj0Jov8PipPlu1CGYcFkCUA6UaVGHsJfOCDN0JpdHHUjehHl5vU75qi6hR8jRFMf3AsA22ZiuAxBuyPOVSof4Hl2rc+hOmlnAiVHHsp95PMesqIMw4bIEoAxINQ9FWhfgQFUI03UWV0YdSF5a8hoBAOhNji+GGkvxwkkAYrgQUOuYDJyZ58vXy7E0hBlPsXQ2XWhiBsRwrYVwo0wr8cFjpuQsASgX+a2GLqUOKNN1Fh+MOpAjkdGsBrbm9WLlWq2NZRXEcIaPJYZTADm+WsCrj7y+IwI6g85kmYlwYdSxHMRy+nJP1EGY8FkCUCZkEo8Cf486jv2kUe7QWXw06kCOSPIeBeiE8LVQYymQTicNjAip+aGtZwzEgtaRQQqoyyAsDjGcouiLVJLmXvYtPBUfwg/s0J/2wRKAciIFPRmVShrltzqT/xd1IIeVK2iv88e1NkYVEI/iOMI8Ja4iRoWelC8X9PpcvEYAdBop1nMXkIk6lkPYTEd+G3UQpjQsASgjMomnUB6JOo6DEITbdCbXRB3IYRSSAKSR2Gy/BBgVaut62EJJJaO1TADOL+hNcRsBOIsfAh+IOoxDEn4s49kZdRimNCwBKDepeA1P70UQbtEaro06kEMotNrZlbooJoWPcqEfynRCyO3nR/hKEe+KzQiAzuJGiHUSXE8LP406CFM6lgCUGZnEsxDb7TsC3KI1XBd1IAfI8UYR77rJexzFGR5y+yeF3P4R6WIuAy4o+I0V8RgB0Bo+h/KlqOM4gl/JFDZGHYQpHUsAypFbJX2k8rZRuiluSYCMYhVa8HnnF2od1aEEVJiwE4CxOi3ie4UW9fRfzxDWeo+lQFrDh4HvRx3HETSSjX2MxjNLAMqQTOYlYEbUcRzBd3QWn406iH0IbxX8HuU7qqRDiKYQYe0AaNOZc6IrUauLuDiPE/8OZnFruezIaA3vAX4DMT9MSvmNTGFV1GGY0rIEoFy5J6Y4F/IQlFtjlgQUc+rZSdRFV+ugdQtg+IfHaKTTAMU8/UPE8/+t52L8CaiIMo48NJOyp//2yBKAMiXVvA7cF3UcR+CSgBo+E3UgrYpZBwDwjcgOCurOYKBD6NfRaErVah1nAuOLenOEOwD0Ic4CHoAYHyD1tt/JpHifl2DCYQlAefsakI06iCMQ4EcxqRNQ7LnnQxD+3Wsk+WoJffi/TTQjAIXu+9/3vZEkAPogJ5JjFtA1iusXqJl0bBazmhKzBKCMSYa3gLuijiMPrk5A1CMBWV4P8O4v62o6e4slf2EvAGxT8hEAXcJo4N0Bmij5FIA+yDBSzAZ6lfraRbpbJrAk6iBMNCwBKHdZvgrxPAxlP24koIZPRxbAKDYC64t8+zHU8ymf8eRFSpYAjNS5IVYbPJgs1xPkHiUs8hfMkels+pFiFjCglNcNIIva0397ZglAmZMprEK4Peo48iTAj6NMAih+HQDAdTqfbt4iyU+ppgAqqA/txMED6CKOBf41QBNNLC/dk60+SE+yPAIcX6prBqb8UapZGHUYJjqWALQHWb4FbIk6jDy5JCC6NQFBpgH6UFHySm+lSgAgVdJpgC8QbHFjnYynxVcwh6Nz6YjwADEomFSAHMp3ow7CRMsSgHZAJrMF+EHUcRSg7eyAKJKAVwK+//O6ojTzv6oIcFwpruUuWJoEQOfRG+HjAZvJ93THQHQ6aRq4O6bH+h7OdJkcaLTLlAFLANqLNLdC9FXRCuCSgFn8R0mvmuLlgC10p5H/9hLLkczhGKBLSa7llOYJt5Jr0YD/v5T5nqI5vK7cAry3JNfyR1FujDoIEz1LANoJmcCumJ1glw9B+bnWlHCL3WbeAnYHbOVaXUQ/H+EcVksJn/4BlDGhX8KtoQg+8iPhjwDoLL4OsalhUYi/ttYJMe2cJQDtSV9+BaVdGe2BALfrTK4qycXOoBmKKAm8r664OeywDS3BNd4mDNEZIW91rOA/gZ6B29FwEwCt4d9RpoV5jZAoOb4TdRAmHiwBaEfkDJoRvhp1HEVII9ylM5lakqtp4HUAAJ/WBQz00M7hhF8CeF8pKsM7eri1mqKf46JbwlvdrjOZDPwsrPZDNqP1rBBjLAFodyZyD5LIG0Aa4fetN99wSeB1AAAdqQj9+NdSJwCQC3ErYIp/w88e+g0yhk0e2jmAzuRsJBH1/Q9O+FbUIZj4SOaH2BRNBNVZ3AA8FHUsReiAMF1rmCwZHgvtKsIrXs6QUz6ptdwkI1npobWDGQICVcdAp8FQ2evtnw69oLL32/+eqgCpgHRrmYJUB0jvtc6uZTtoa9XoltYdo9nd0Lx5358Ox7xf9YatwDpgpYh4OT9eFaHO2xbKUIb/tYaxQA2lXXjpjzJTMvwj6jBMfMT7iEoTGq3hEeCSqOMoUj2QkQyPh9G4vklXqtiGnxGy22REsBMPVbUbMAY4AffUPxQYTMOyC6g6ppJU+GcBHUY9sBRYDixr/XMxbh3FfBFpyqcRrSODMtNLRMJvZDif9NJWK53BQNI8TRSjLn4owtkyyRIA8zZLANopncWZKM+T3M/AdlJcJhN5IYzGdRELwct8926yjJBRRz5rXVUrcDX3x+E6+xOBsbhOJ4m/pxagFnfIUtvPqyJywPy8LuJR4GJP1/2ijPB3vK3OoTtNPI5wsq82I3CfZLgy6iBMvCTxpmI80Rr+TPL2MO9tGzkuCWNRk9YxHeX9fhrjVhl5YG0AVT0GOAM4HTgfOA8iOVCo1LbjKi4+BTzN2ndvZ9eMv3trXblcRvI3L03NpSP1PJzAQj97U1KcKhN5NepATLxYAtCO6SxGobxBsteCbADe2XryoTday5cRvu2pud20MJxRWgFc2vpzAXCsp/YTTpWmecLuJ6F+DjTMhdzmAM0xQkYGPwlQp5OmK/eQ7CQZ4B7J8IGogzDxYwlAO6cz+SkSwSl2fq0nzTtkgr/qb7qISbgFX8VLdYGqc6HzpdD1A+uoGNIP+87lIQeNL0PDI1D/COx+ErQx3zfXM5xuIuSCRqE13A78Z9B2IpYlx0kymXlRB2Lix25G7ZzW0Bc3T9s96lgCWkmai3ydba619EWKOBq4YiB0eQ90uRI6XuRW3ptgcrug4SHY+Veonwm5bYd79YsygjODXlJruA7K4Khc5W6p5kNRh2HiyRIAg87ierQsqoMtR7lIqlnmozFdxFLyWfVdMcR1+l3fDx3PxcprhCkLu5+DnffCzj9Bdt3+L7hDRvCxIFfQWbwf5U8k/xeZRThBJpXmYCSTPEn/gBsfdvAj3PatpBuMMFtne6rDL4fZMpXuBz3+Gwa9BEOWQp9boeP52FcqbGn337nPrTBkJQyYA90+BLJn7eSbQVrXWVyAchfl8Yu80zp/czjl8CE3AclUGhBuiDoOT0aRZY4+6KWe/H4JQBo6XQr9p7vOp/cPoeq0wJcxRZIK9/vodxcMXeP+7HVLpaoWNbKpNQxH+SvQ0XOkUWgm520RqylTNgVggNZKbLN4HoLPn8bEs6S5TCawq9gGdBHjgcfocAJ0+zh0+6B78jdxtxC4E7hDRFbn8wadQR/SPIOf2g9x8HPJ8F9RB2HizRIAs4fO5CIknOp6kVAeQZgsGfJeQr7nraopsksm0/jW/XTOiH1VEqkZuB/4kYg8fagX6XQ60ZVHgXNLFlm4GkkzUiawIupATLzZFIDZQ6p5AuGBqOPwRrgU+KPOzb/Ogap2U9X/AN4gfdwDdK62zj+5KoH3A0+p6ouq+mFVrdz7BTqNFF24m/Lp/EH5hXX+Jh+WAJh95fgi7smpXFxBA79WPXwvrqpDVPVHwCrgF7ja+6Z8nI6bFliiql9SVbft9Sx+iJRVidzd5Phe1EGYZLAEwOxDqlmI6wDLyUeo4daD/cVeHf8C4LNAt5JGFrmWqAMotYHAjcAK3ThnNpW9fJ1AGBc/kSlHPnfCGLCxTXMQ+hC9yFEL9Io6Fs9ukAw3AqjqUOC/gauBqiiDCqYF2NX60wA0tf407vfPOd7u7Fta/31/6dYfcNWh00CH1p+qvf6swi2U79L6kz6gpcTI7oSVv4W6G6HxgJoCSbMTGCYZNkQdiEkGSwDMQZVNJbT99Z5wI2c9NAi4isScgZDDnZ+zrfXPnbhTeHdB4esbQ7B3MtAFV1SyO24wJSG3mJYdsOwnsOQH0Lwp6miKo3xDqpkWdRgmORLy7TSlptPpQFfeAoZHHYsXlb1g2Bdh6LWQivMDfwuwufVnW+vPDg7+xB53Kd5OBrrjBpR6EusRg+atsPgmWPojyO2OOppCbASGS4btUQdiksMSAHNIOpOrEP4v6jgCkUoY8ikYMQ0qe0QdzUHsxnX2m3D38M0ks7PPlwA9gD64ZKAvsTwBuWE51H4VVv0e0KijOTLhf2QSt0QdhkkWSwDMIaki1PAcwllRx1KUflNgzC3QOU6DGC3AemBt60/RdYrKyFHA0a0/fYjVCMG2F2De52HLk1FHcjjLgFHF1Lsw7ZslAOaw9EHOI8VTJOmz0m0cnHA79Dwv6kha7QDW4Dr8DZT3E35QaaAfLhk4BremIGoKq/8I8z8HjWujDuZAwsdkEndEHYZJnuTc1E1ktIa7gX+LOo4jSneCYdfBsC9BqkPEwewCVgMrcUP7pjhHAYNwhzJ2jTaU5q2waBosuw00NkncAjpxooxvf/s5TXCWAJgj0hkMJM0C4vE4dnC9x8MJP4cux0cYxE5gBa7T3xphHOWqNy4ZOBboFF0YW56BN6+GHW9EF8PbrpAM90cdhEkmSwBMXrSGrwLfjDqOA1QdDaO+BwM/FFEAOdyT/mIg8fvIE0Jw0wTDgAFEUs9MW2D5z2DBDa6WQBSUF8hwjkgSVimaOLIEwORF59KRBuYBQ6OOZY8BV8HYn0a0un8HsBRYQjz24rdXHXEjAsNw0wUlVr8IXv0wbH229NeGSyTDY1Fc2JQHSwBM3nQmUxHuiToOKrrD2NsieOpX3FEBC3Hb9ky89AOOxy0eLCHNwpKbofZrkGsq1VVnSYZMqS5mypMlAKYgWsPfgXdEFkDvS2Hc76DjoBJetAVYjuv4d5TwuqY4XYERuFGBEm4p3PEavPoh92e4lBRnyET+GfaFTHmzBMAURGdyCsKLlHqzdqojjJwGx30BpFRzvg3AIqCO8jogsb3oiEsEhuPOMSiB3G6onQZLvh/eTgHlj1LNVeE0btoTSwBMwbSGXwD/UbILdh0Lp97r/iyJRtzTfi2QLdE1TXgqcInAKEqWCGyY5UYD/J8r0EyKsTKRRb4bNu2PJQCmYFpDX1wPGf7qu/7vgXF3uHn/0DXhOv1a7Im/HLUlAqOByvAvt3sFvDwVtj7ns9XbJcOnfDZo2i9LAExRdCb/g/CD0C4gFXD8t1xhn9A1AwtwHb/VUyl/VbjRgBGEPpOVa4QF17nDhYJrIMtImcIqH40ZYwmAKYq+SCXreR13J/Wr40A45R7oeb73pveluMV9r+EO5Um4bD00bnAV61p2tP7sfPufybnXaA5Q989t0q01nqQC0h1B0lDRDSq6QsVRb/9Z2QOq+ro1GYnXCTgJV2UwZKvvhjf+E7KBzn74tmT4iq+QjLEEwBRNZ3IZwsNeG+31TjjlT1DV32uzB9oAvELiKvZlG6BhBTQsg4bV0LTBdfpN66GlhAcLVXSDqn7up0Nf6DgAOg+BToNiftzywfQCTsFVGgzRjlfhn++F+rpi3r0BGBGn4351ESeifBDhdJR1CL+VEVaXIEksATCBaA01wCQvjQ36GJz4C3eEb2h24Z74V4Z4DU9adsGuhbBzIdQvd0fUNq4n1sfTSsolb52GuISg6/HQZYQ7pyHWBFfj6kTc7oGQNG+Gf14Jmx8v9J2fkgy3hxFSobSWsQhfB97HgWUYvyoj+FYEYZkiWAJgAtHZjCbLawRaVSUw8usw4uve4jqQ4rb0vUFs5/kb18OOebBzvuv0G1YS684+X5KCToOh62iXEBx1AlT2jDqqQ6gETsCtDwjp9phrhNc/6aYF8rOAfpwkZ0S7MlXncxwVXA98gsMtnlDOlZF4XflowmEJgAlMa/g+8Pmi3pyqgpN+68r6hmYb8CKwOcRrFCHX7Dr77a/B9tdh1+KoIyqdqv7Q4zTocQZ0G+PWHsRKH+AMoFtI7Sss+ibUfoMjJnnC5TKJv4UUyBHpQoaRYhpwFfmtmrxNRvDZcKMyPlgCYALTB+hGJfOAgQW9sbI3nH4f9LwwnMDIAvNbf2JyfGvLLtj6Amx5Hra/6Z4G27t0JzhqHPQ6xyUEsVlDkMatcR1DaAcOrbkHXvuoKyB0cE9IJprKmzqfAaT5CsInKWSET/mLjOR94UVmfLEEIAF0Hr2p5BPACJQ1pPmDDGNB1HHtTWfxAZQ/5v2GLiPhjIeg87CQItoEvIA7ojdiuUbY+k/Y9ARse9WdJGcOLlXpkoGe50Cvs2Oy26AHcDahHTa0aS788wpo2bb/3yjC+TKJkp401Hq/uQ74NMWcuyx8XYbH8ORQcwBLAGJOF3I2KR5i36I7OeBvKN+TkaW9ORyO1vAocPERX9h1LJw1B6oGhBEFbk//m0T71K+w/Q3Y8AhseRHUCgsVLFUFvc6FvpdAV/+7TQuTBsbiRgRCuG1u/yf8Y6Lb1dGmxCV/9VW60JVPo1xP8UW+NpJljIxio8/YTDgsAYgxXUEnGqnj8MebPYnwXYZRE/W54FrDWNzeukMPF3Y/3T35d+gTQgT1wPMQ5b2neStsfNx1/I3roouj3HQcAH3GQ9/xrh5BZPoDZ1LMg/ER7ZoPL1wKu1eBK0s5VjIUtWewELqEjrTwKYTrgb4BmtoNVNtWwOSwBCDGtI5/RflDni9/A+EmlnOPjI9umbvWcDPwuYP+Zc8L4YwHQ7qBLwNeJrISvjsXwtoHYes/3PGwJhxSCb3Pg/6T3TbDSFThkoAQjh1uWOqSgPq6myXDF/xf4G06lwqO5SqEaSjHBWwui/IBGcmfvQRnSsISgBjTWr6EcGOBb6sDvksjd8oJlOxw8jaHXBDYezyc/jdId/V8xRzwT2CJ53bzoW5uf12NW8VvSqvrKDjmPW43QSS3spHAOLwvEGxal2PnmxdK70ue8duwo4qwiPcjfAv3fyJwk8DHZAR3emjLlJAlADGmdVyJ8pci374O4RY6cZsMoP7IL/dHZ+03ctH/clfa1/vq7nrgWUq+vU9bYMNcWPs3G+aPg06D4ZjLofcFlP6W1hc4hxCKB60DLhMRr5ml1nIOwveBCzw2+3kZEeK5ICY0lgDEmNZShfAirjxZsTai/JQKbpXjSlf3VmfxGMp4+lwGp88IofPfgOv8S7mNTmHzc7DyD9F3/Cnc4XZtu7KFfb/Nbesfs7jnsxbKoqbQYXUaCEe/B3pf6IoPle7CwLmEUEp4A/AOEZkXtCFdwmiy3ARcHjysffyvjOBrnts0JWIJQMzpYsaRZS5Cr4BNbQVuo5kfyRi8H1K+P61hLD0veJUzH6rYc9CMN/Nwq/xL1aO1dvyr/gS715Tmkmnc0fUdcFPOHXBLKytaf4rp31pwCUFz60/jXj/ltEmh07Ew4H2urkDJD2brhgAAIABJREFUbnEp4FTA+7bWVcCFIlLUHJcuoA9pvgp8CvfJ8enHMoJrPLdpSsgSgATQWk5DeATwUT91J/ALKviBDCW03kxVzybX+ASpqg7+Ws3hKvot89fkkWx/E1bcAfUhXjOFG0Hu3PpnJ1yHX0o53BruBtxxCfW4ZCHJugyHwR8t8RbCkcDJeL611gEXicjqfN+gq+lMA59B+TJhFDBQfscIPhH1ziMTjCUACaELOYUUj+BvnLEJuIMWviGjyfvGkg9VHQfMhcCjFntpAp7BjYqWQONaWPlH2BxSmYVOQBdcpdlOxPOb2IRLF3fgkoJE3urFjQQc+6GQtp4ezEBc4aB8qubmbSEuCTjs3JMqKer4IHAToWxTAIS7GcZHROJSXtMUK463HXMIISQB4AaA7/SVCKjqSOAJ4OjAke2xC3gKSnESarYBVv/Frez3WbFPcB1+d1yn77VvKIEcLhnYjksIknbrT3WEAVfA0ZNDPm2yTS/gfDwvDnwVGC8iWw72l1pHNcr3cBWLwqH8hZV8IMqtxsYfSwASRhdxOjAHP9MBe6sHbge+JyNYX0wDqjoQeA4Y5C+szbjOvwSL/ba9DEt/BU0eCwm1dfpHkbxO/1ByuERgKy43S5Kqo2Ho1e5EwtB1wS229zoC/yRud8CeL0TrAr+bgWqfFzqA8DA53i0jS7ry1oTIEoAEah0JmIM7sswvYRfKb0jzHTmOtXnHpNoVd3M6xV8wG3Cdf8gPGy073cr+DY/4aS+NK6Tai9LP5ZdaE7AFl6clZlRA3E6BwR+FCt91KfbXAbgQr7NhMB34ACulJ418nXAW+O3vKXYxUU5OXMpnDsMSgITShYwhxaOEN8/nEoEUN8owjjDvqGngL3jdYrQGt80v5JVoGx+HFXdBy47gbXXCTc50I7TD42Irh6vAvInkJAIdesGQf4cep4d8oQrcdEA/f03Wz36QNRPPIYyHgAM9RwvvktF4+JKYOLEEIMG0lrGtuwPCSQKcHcBtVHKzDOHgc4+qPwY+4++SK3An+YXYk2R3wdJfw+ang7fVBXcbDvthMgmagNUka2qg90Uw9JMhnzyYxiUB/T21p7D+Y7Aj5OJ7yqt05GI5ttTVtkwpWAKQcFrH8SiP4nXe/aB2oPwMuFFGvr0aT1WvBW7xd5llwD8Idcn59tdhyc+gKWA5hC64QnC+yxyUg43AepKzc6CqHwz7TMhbBlO4qoEDj/TC/GgzrJkEDY/6ae9Ab5LlnXayX/myBKAM6BKGkuMxDwd65GMjws2k+BFD9TLgPrwtb1sMvOSnqYPRZre1b+1MAvVMHXCjud09xVWudgHLSc6UgKRh4FQ4+vIQKwl6TgJyW2DludC8wE97b5tHivFHmv4zyWYJQJnQOgajPAYML8kFO4xZw6BXeiAdPJ2LGvKTf9MmWPRD2FVbfBsVuCf+ntg3J1+7gaUkq6hQtxNg+LVQGVaGlwLOw9vMXfNCWHkm5Lxtk11IC+N91wcx8WO3sTKiizgWeBQ/J3wdWqoLDHweOvjaSrUKt+AvpM5/x1tQdws0byu+jZ64ygbtbXGfD424wxqTlAR06A0j/ge6hPVVSuO2CHpaGLjrflh7JR6+Q7W08E7r/NsHSwDKjC6mPzkeBcLb6NzvTuj2YU+NrcNt9QtjnFhh3SxY8XvQInufDsAAbJ4/qHrcSEBS1gSAKxg05OPQ95KQLpAGLsLbQv6N/w3bbg3SwqLWOf9VfgIycWcJQBlqTQIeIdgpggfX/TPQ58eeGluP6/xDeDTMNcGSnxZfyldwW7f7YU/9vmyFRHYt/Sa4mgESRiWnSuCduMIRAWkLrL4Ydj9ZzLuXkeadchxLgwdiksISgDKly+hJMzNx55T60fFsGPAEiI/qNtuBxwjlGLqWHVD7fdg5v7j321N/eFYCAWZiInPUOBjxOUh7WvKyj47AJbjToALKroUVp0G2oHO+rPNvp+zZpkzJELawi8sAP+Xt0n2g/589df67cUUDQ+j8G9fCvK8U3/n3wi2jtM4/HMeQzJLI21+DeV/1WyZ6D4/fh/TR0P//KOA/8nJaGG+df/tkCUAZk5PZhTIZuD9wY31+BhU+Sg204G529R7a2s+Ot+CtL8PuIk45TgNDcB2UfSvCk8ZrQbySaljukoBQjobeDjyNl7UwncZDj8/n88plpHmHjGZJ8IuaJLJbXZmTkTSygvcDdxXdSLePQdf3e4hGgedxk8Gebf0nLPy2q+tfqM64p36r5FcaPXFT30nUtAnmfx12et93jzv7wlMdjF7/C1VnHu4V9uRvbA1Ae6FKmjpuB/69oDdWHgeDXoGUjxPNXgYWeWhnP5ufhcU/Lm6lf29cdVb7JpTWRkh0iZlUFYz4PHQ/OYTGTwTGBG+maR6sPB20Yf+/WYZyiYykLvhFTJLZCEA7IUKW4VwN/CD/d6Wg7+88df7LCKXz3/RkcZ1/CleM7Wis84+Ch0Xvkco1Qu13YcsLITT+Ju4wrIA6jIHeN+7/vy6lhfHW+RuwBKBdEUFlBJ9HuD6vN/T4InR6h4crbyWUEr/rZ8PinxTe+VcCQ0l+J5RkFbjTE5NMW1yBqc3P+G4YdxhWEdNZ++t+DXSe2PZvC8hygc35mzb27NNOaS2fQriNQyWBHcbBoBdAqgJeqQm3EcHz8XAb58KSn1NwZZnOwGCSuRK93KzFHR+cdJKCYddAL387bp3uuO2BAT+sLSth+Sm1NG+yCn9mHxVB3qw1TAI+DJyGq5K+GVfdZSOwEWUdKdaTYyPCRmAtKdaynfUyNVGFQcuOjORnuohdwK854HOQgr4/99D5ty3689z5b3qyuM7/KNywv417xUPQj1dcaA4W3+bWBfQ4zWPD24AXgbODNVMxCIYsfloqulvnb/ZR1AiA1nAUcDcwpcjrZoH1CKtR1gBrUFaTav1TWEuaVVSyXsbTUuQ1TB60jsko97B3FZLun4Y+t3lo/Q1gnod29rLlBTfsWuiwf2/cfL+Jj524pSHlItUBjv+SO0zIq1OBEUEbyQEXiojv+QqTYAUnADqdDnTlUdxJFmHL4UYUXGIAaxFWIawClpFiGR1YKuPZXYJYypYu5GxSPAj0IX0MDH4LUkEnyDcCf8dr8fdtL7sKf1pgTng0LgEw8bILym4TWroTjPoqdAncYe8lBVyKh/On3wBOE5EQKnCZJCo8AajhOuCmEGIJYi2wDGEZOZZB60+aJVSxTMb7WE1T3rSWExBmc/SfB9LlvQFbawYexmuxn/olMO/rkCsg1xNcSV9b7BdP24EVUQcRgopuMOZb0NHTcb+Am7+6FA+LV64Xke8Gj8eUg4ISAH2RStazArdzOkk20ZYUKEtb/3kpaRZRRZ2NIDja8NRH6Hj+HcFbeg6vd/amzTDvBleEJV/W+cdf0msBHE5Vfxj7bajwsYW2zUjglKCNNAAnisji4PGUD51NF5o5DmEoMAShP4qQYhEpZskE1kcdYxgKSwBmcS5KOc4hbUFYDLyF8ibCYrIspgsL2svogap2wW1AHhKspaXAP4IH1CbbAPO/Vlj5VQGOBbr5C8OEYAVuFKBcdRsDo77ijhX25gJcvepAZolIxkMwiaIPM4Asw4DhKMNQhiEMB4Zx+IfaZuBndOL6cntYLCwBqOHDwJ0hxRJX7SI5UNVpwNeDtbILN/Tvad2mZmHhjbD99fzfY51/MuSABXgpfR9rvS+AYZ/B347rjsC78LCFolpEaoLHEx86nQ50ZxjKcHKtHb3r3Nv+uWPASzxFlgkyJYyDTKJRWAIwkw8hAWrKlxcFVqIsQJgHzCPHfLK8JZcna2BTVQcACwl8Bt4TeB3TXfYbV+wnXwIMwk2XmnjbjJdid4kw6Co45j0eGxwCnBW0kfnASSKSuF1Weh896MAoYCzCaGA0rnbycQTc2p6HX0qGq0O+RskUmgCcjfBcWMGUkS0I88gxD2E+wltkmc+LLJVp8XvmUdXfAh8L1soS3J5lTzY96fZWF2IQHhZKm9DlgFq8DRTFn7jtgd0Dz9/v5XzcIpdA/ktEfu4hmFBoDYNIMZoco4GxwChcR+9zdWWhcihjpJqFEcbgTWEJwHTSdGUJbpDVFG43LvOeD8xrTRLms4sFMpWmKAJS1ZNxdXoDLC/eDcwGX/8X6pfBvK+4euv5GoA7Zc7EX7lUACxERVcY+x23ONCLzsAEAj7wrgdGikikKzF0Nv1QxqGchHIS7jSk0cR1Ik/5hlQzLeowfCh8G+AsPoHy6zCCaceacUPwr6O8hvAGwusyKfxd0qr6MHBZsFaeBVb6CAdadsFb10NjAVMJfUnuGfPtzQ5gedRBRKTzELc9MOWrBKKXXQE3isgNHoI5Ip1LRxoZu1dHP671J2m7yh6UTNFF8GKluEqAM/k1wid8B2MOsB3hDXK8TorXyPEGTbwmV7DVR+OqmgFmBmtlFXjbGKJQ+z3YWsDBQT1w5X1N/O3Cdf6xmwQrob6XwdDCTuQ+NAEuBnoFaWQ3MFpEvNZk1FkMBcahnIhwcmuHP5Lw5+hL4QnJ4OOUtMgV98v4B//B2SxA+SbBV1aaQzsK5TyE81Dc970KtIYVuKperwGvk+IN+vCWnEHeFb5UVYBvBgsvC7wSrIm9rZ9dWOffFQ/ToKYktgGrad+dP8CGOXDUiZ4ODlLc7N2lBNhl0BG4AfiPoiKYRoozGUGK08hxGsJpwGnoXhNyHouBxkSiFnkfTqC9KTqbY8nxbZQPBm3LBNYCLER4iRwvkeIlUrwsEw5+Eo+qXg7cH+ySb+FKB3jQsNIN/efyXEdQhdvcYwf7xFsLbs5/W9SBxEhFNzjxZqj0tWjlDNwC+KI1A6NE5LDHBOt00hzFaHKcjjug4LTWP+M5Vx8W5dNSzU+jDsMHL522zuJMcvwA4UIf7Rmv1gAvobyE8BJpnpcJrFfVF4Azi2+2AXgIL0u5tRneugHql+b3+hSu8y+X0+TKUQtuq98m7Kn/YI4aB6NuwM8tuAqYBAQqOPRzEfmvtn/RuVRQz4mkOH2vJ/tx7H1oWPu0iUqGy2XlkdJ6fWrXGqYAt+CKLpi4OvrKzZz6l0ATh+6YX0+ruVb8HtbOyO+1AgzGDf+beMnhTvjbhlvsV35Dv34N/gj0r/bU2Chc/1wk1RZemnwtG2uGoZyFe7pv7539/nLAv0iGP0cdiC9eB1AlwwzgBOAL4GehmgnBsOsCdv6b8Nb571oE6wpYh9gP6/zjIodb2LcBVwF6Pm+X97XO/8hW/hEa13pqrBaXdRVJpIK+mZ+g/A+u3rB1/vuqR/loOXX+EMIMqmRolAw304ERCD8ECtjMbULXdxJ0D1pF7FUvoaAtsOR20DzHiLsCffxc2hShBdfHrMPVfZqP6/jX4xIB6/QLk2uCJb/Az3+4HFBAyeyDOfaT0HGQh1jKjPIIyqlSze+jDsW30JZQyaVskkl8jjQjgV/iloybqA0PuuV3Nd6quKy5DxryPDWwAtvuV2otuOH8tcBiXO3+5bhT/OqxDt+HHW/Cxr97amwVbuFFkVJVcNznPMVSFlYiTJVqLiuXyn/7K9nKfZ3FOJTvAO3uFKrY6H46nBe0XO8cvMzu7F4Nb34BcnnuXBxK4JMKzBE04Z7w61t/2k2p3oilO8NJt3jaFXAMbgS/SC07YO6x0FIWa9yK1YTyc1r4ilweZF4l/kq2iUom8ZpkqG7d0/5kqa5r9jL0fwI2sBI/SzsUlvw8/86/D9b5hyGH6/DX4KaQa3FP+9uxzr+UsvWw3NcZa2twQzRFqugGgwIeC5JsD5JjjFRzTbl3/hDBLmqZxLNSzUWkuIzAk1Ymb1XHwNHvC9CA4vb9e7DpGdg5P7/XdsTK/PrUgpvBWYqbw1+OGzWO5CQKs8fmp2HHPE+NBazNMfQakABHgyTTWyjvkgxTZDKLow6mVCIroyITeYQXOAX4CG5i2YRp8Kcg1SFAAyvwUs0l1wQr/5DfawVX6c9KTAWTA7bgFu4twD3l26K9+Fl+R/4LYg9rPW5rRpE6DYW+vrYnxt5W4Hp2cqpUMyfqYEotFrdWnU0XslyL2z5oB7r6lqqCdy4LeBLZbNzYcECr74VV9+b32j4k75iQOGnAdfzbsGI8SdAC9LoERvk4br4fBClXv+kxeOESD3HEliLcTRNfkMvLp7RvoWJRSFUmsEsyfJsUw4Dv4g6oML4c868BO/81eOn8mzbDmr/l99oOuFP+TOF24Ib4F+MSAOv8k6ERWDkXmn2czrse98svUu+LodtJHuKIIeEZcpwpk/hwe+78ISYJQBuZyGbJcD3CGOBObCmSH4ODPlEs8BIGq+6BXJ5lIY4hZp/OmFPck/4i3Lz+QU+AMLHWDGRzMP8nnhoMuHPt2KLOB4qzNQhX8zwXymQKOHWsfMXyFiuTWCoZPkqa47EaAsF0OR56nB2ggS0Emk9ss3sNbHoiv9cehVX7y1cOt+h7IW6ThpXdSqZm3l6Tse5VaPDxYLoCt5+zSAOuctOHydcA3EgnjpdJ/FKm2ZhYm1gmAG1kAkskw9XAOITp2GBm4QZ9gmBLPTzVv1g1HTSPPE6wVf/52obburcOGytLur0Tt5zCgp95aFRxH5AiVfaCfu/2EEdkFLgXYaxkuEHGszPqgOIm1glAG8nwlkziX0gxDrgXW7+cH6mAgR8M0EA97rEyoIYVsPmZ/F7bGzvl70h2AXW4X411/MmnHPh73DAP6j1891iCG14o0qCPeoghEo8Bp0uGqTKJpVEHE1eJSADayETelAxTUU7GEoEj6zMBqgYEaGAxXgZdVk0nr19VGlv4dzgNuPv5UmyZbDnZe/i/jQLzfBw530ygg7sC30NKbgHCVMlwiWR4Oepg4i5RCUAbqeZ1yTCVFKfiEgFzMIEqeimwLHgMDStgywv5vbYPCf1EhizH2wfwBJjSNTF1qHUbm+ugwcdpgXXFv1XSAUcRS2YjyrV04kSZZH1CvhJ9u5WJvCoZpuKKXz8WdTyxUtkL+k0O0MAavPQ2ax8k76f/gIcUl6UduJX9G7HxrnKU49DTOArU/sbDRbYRaEvgwI94iCE09bit48Olmh/JeJsUK0SiE4A2kuFpyXAJlgi8rf/lAVfweqiG2bwNNj+V32vt6X9fLbjD3ZYTaArXxNyRSjBveM2dFRBYgO9z17HQ9QQPMXiVA35HluMlw/WS8VGopP0pq1vunkRAeVe7P3Co/5UB3tyAqxcb0Lqa/A78saf/fW3FLd72ce6SibcjbdvMKtTd6eFCywm0YvTo93qIwZs5pDhNMnxcprAq6mCSrKwSgDZSzRyp5iKEC4EHo46n5Cq6QZ9LAzSwhMDjzblG2PBIfq/tQZl+EguUw+Vdq7ANr+1BM/n9nlc95eGMgBZcXYAiHR3kgcIT5QWESyXDu2Qir0YdTjko69uuTOIpyTAF3bNYsH3MovadDKmOARoIcKNos+lJd7Z4Puzp3z0JLsGd1Gfah3yLNjU3w8oZHi4YYDdAt5Oh83APMRRlHsJUMpwjk3g0qiDKUVknAG2kmldatw+eBvyFcn++CpStb8VL3f98n/674er+t2fbcVO0trWv/chR2NqOZTUeLroBN71XpEDTikVZivBRdnKSTOJekXbyAFdC7SIBaNOaCLwP9pQYLr8Vo+lO0HdSgAY8PP3XL4NdeS46as9P/4rbbLGCck9Jzf4KLdlcvwV2LQ14UYUgU+alWwewEbieToyRSdwpU60UfFjaVQLQRjLUSYarSXM8yo8pp2ev3pdCukuABjxUH9vwcH6vq6D91vzP4Tr+zVEHYiJxpNX/B7P4Tx4uHCDB73EWVB3jIYZD2knblr4M35XxZXRfjql2mQC0kQkskWquIcuo1kQgwPhYTPS5LMCbt0DQctm5JtiUZ9nf7sEulVgtuPn+PJdImDLTRHEjPhtfI/gypk0Uf5sT6H1JwOsfVBPwS9IMty19pdWuE4A2MoXlUs01pBiO8EOSfJhqoATAw/D/5mchm+d/vqOCXy5xdmPz/e1dsSc2NrfA2qBr4JRAo3yB7i8HaAZ+SZZhkuFqmcB6n42bI7MEYC8ykTUyic+RZSjKN0jamuyOg6DL6AANrA4eQ76H/lQCnYNfLlF24+r4W2Gf9itLsJVHKx7yEESA73mfdxHsdFHAjX/cS5YTJMPVtpc/OpYAHIRMYaNUMw0YCHyEQGdqllCfdwV48y4Cj0m37ILtr+f32vb29N+A6/xtOVP7VuzTf5ttK1yNjUA2UnQWUnW0qwxYHNfxK2Mkw1SZkpD7ahmzBOAwJEOjZLiLFxgNvBt4MeqYDivQ8JyHyn9bngPN88bSnhb/7cadq2Sdf/uWo7jFf/u0obAiaG2zHAQZbS/8PuM6/jQnSIapUs3C4i9ufLIEIA8yjZxkmCEZzoxvdUGB3hcHeP+a4CFsfja/1wntZ/h/N27Bn3X+phE/pcjWPuGjkeLfmn8CoLh75RmSYapMYH7xFzVhsASgQHtVFzwHuI+47ODuOhY69CvyzTlckZAAWnbAjjfze20n2scnr+0o9nh8QkyUlODD/212rAUNupAkQMLf80J3TPChKXA/KU6VDFMkw8vFX8yEqT3chkMh1TwvGa6EPbUEot1C2PPcAG/eQOCaSNteAc3zMTdImYKkyOKG/W3BnwF/T//gpgHWBR0FqKfoip8V3Q59OqDyCMpZkuEKq9cff5YABCQZ6qSaa0jv2TkQ4ODtALqfHeDNHnbfbCsgye8U/HKxlsM9+ft64jPJ5/uzsPZxD40E+N73OGf//6WGFGdLNZdJdczXSpk9LAHwRCawXqqZRiXHAdfhpaReAQ78QhZgY8CLa/6r/wGqAl4u7tbgHrCMAbcOxPc00NY8S20fVoDvfY+zoW2OP8XZkqFaJvKCh6BMCVkC4JlcxjbJ8D12MhS3cyDPlXEBVHSDrmOKfHOOwIMWuxZB87b8XpuivA//2YQ7T8mYNkFX/h9Mc1P+520cUoAEoM+l21FOlgxTrONPLksAQiJTybbuHDgP5R3A/YS1HKz7WUdalHMYWwi8RH3rK/m/tpyf/uuBdVEHYWKliXB2gCiwcnbARhooeqiq4+BuZLS0o5zGO0sASkCqeUIyXEGOkbjDLvJ8XM5TjyDz/0GH/4Edb+T/2nJNAFpwlZTtwFLTRgm35POmAr53h1T091+AMz0EYCJkCUAJyWQWS4brgcEo1+KWigV31KkB3hyw2rE2uymAfFUGu1xsraQcD5c2QTQTbv2HBh+VygM9AJziIQATIUsAIiAZtks1P6IfIxCmAs8FavBQW3LyEvAmsmsx5ArY61bsTEWcbSTJx0eZsIR94FM2BzvrAjYS6Psf5MZjYsASgAjJGTTLJO6VDOe2Vhi8l0KfGVIdoMvIIiNoJPBdameBVT3LLQHYjZddlKbMNFKa6o/rngrYwHYCLE2yBCDhLAGIidYKg1PJMgb4CfmezNNlNEhFkVf1sFx9Z4HVPYsNNY5yuKF/m/c3+yvVcc9b3grYQA7YWeybx6iq9SEJZr+8mJEp1EqGz9DMQISrgcPX1w00/O9hLWKhIwDl9IlbjxX7MQcKY9//oez0cIZH8feBzsBQDwGYiJTT7bisyOXskEn8UjKceNjpgW4RJgBNm/Pf/19udgObow7CxE7YK//317zbw/HAgb7DNg2QYJYAJMCe6QEYhdtG+HbXE+UIQMOyYO9PKgVWY0P/5kC7Ke3nQoFNLwVsJNBU4IkBL24iZAlAgkiGOslwPZ0YCHwEeJ3OI4psTSn6MJA29UXsYiyHTnMzUR/9ZOLI54l/hdgStB5AoAeBYQEvbiJUTkuy2g0Zz27gLlV+j47eiZuLK1A9gZcpNxSRACT9aNxmbNW/ObhSP/232Rm0nEjbvaCoLTpDAl7cRMhGABJNuyMVRXT+4OW0mvoipgCSPgKwjuQnMca/HNEtCK33kZEWfT+wBCDBLAFItgBfvoCVazQHu1cX/r4kV8urx3cRZ1MudhFdctuc347hwyv6fjBYVcVDACYClgAkW3QJQPNm0CJ68wKKBsbO2qgDMLHURLSJbXMWckGPHCz6ftAR6Bfw4iYilgAk2+Di3xowAWjcUNz7kjoCsA1b+GcOpMTjcxG4JHCgKUGbBkgoSwCSLcIEoMh5xyQmAIot/DMH10A81oRsqw3YQKD7gSUACWUJQLL1L/6tAR9bmtpRArAFN8xrzN5aiE8lyJ1LAzYQaAQgwH3IRMm2ASZbr+LfGrBcWXuZAsgR8MRUU5YULxtpvGko8vu4R4BMZutN39Ra/g1hCTnqSDOPHM/LSILOS5iQWQKQbEUmAFkC1wBo3lLc+1pwnWpSxp62kOyFiyYcuynNaX/5agm6EyBAAiAdeyKcA5xDCpccCWgditKIKzW4BliKMg94mSzPyGiK2EZkfLIEINl6F/c2D+PZzQGqCDbh1g7HXQ4I+mBlyk8Lpa33n4/moMMRzRSdmacOcRtSBPdNP7r151SEKwCoAK1FERpwtTXXAHWkeJ0sT9PEs3KCTbyFzRKAZCtyBMDDxGWQJ45GkpEAbCJeT3kmekrg9bOhyPpYjFBkZp4u8jYkCK6KaWdgEHAmOT6AAFWgi2hG2YGwGmEhyssoTzKCp0USN5kYS5YAJFRr8Y2exb3bQ2IdJAHYDXQPHkKocrgEwJi9lfKo30K0+JinKjIBSAVYinR4lQi9gF4oJwJXIkAd6CIaUDYByxFeR3mKNHNkGOvCCqYcWQKQIFrLUSgXIZzH0r6nMnRDkb+/gE8LuaZgR5DGYd/0kdjTv9lfM/Eb+m+T8/FhLfLBoNgRgGA6IQzCjRych3A1OdBFtODWHCxHeIBO3CwDYrVcM1YsAYghreN4clwGnAucgDAIpQdCBXuKbgb51QUcPQu64KiBPQuFYsme/s3+csRz6L+NqqvMKUHuC0WOIkinANf0rgLoA/R+Yh0YAAAgAElEQVRBOY0GvqQLeaccz/NRBxZHlgBESJdyHDkuRjmfHOOAIQg9UdIHdI4H/HuHAFcOOIYZ5Om/7fK7gVjdN/ayEXv6N/vaSbwPslKgaTtUBXkaL/K+IFUBrhkypSMpZqvSSySWkzeRsgSgBHQRxwKXI1xEjpMQBgDdaNmrWy/0aTjSBMDDfONO4pkAZLGnf7OvBpKREGaDzq0VmwAEuReVRHdqmQTMjDqQuLEEwCNV0szjYiqZgHAWMBI3HOX+O/sc9o4yASjmEKD9bQX6Bm/Guw3Ec5GXiUac5/33FzgxL/aDH/sEANKcgSUAB7AEoEhaS18quIIs4xHGkWMwi+hasu9CoAQg4OOMjwSgCbcWMU6jh024HcnGgPuaxHnef38aNAEo8r4Q/xEAaOG1qEOII0sA8qBv0osq/g24GGUcMBChas/3JZIFbQkfAQB3wl6cDhJdR7zneU3ptO33T9LnIfCRwMVOAVTiCgjFduhsJ8fzQNRBxJElAIehtVSR4nGUs2jr4uOycl0qA7w54F0t5ykB2IKbIIlDWeCdQIDihqbM7CIZ8/57C/y9DNCBSwfQGM6VKM1UkbEFgAcXh1tvfKV4FeVs4tPt7yXCz7N4+ti07diNWg5XiNQYcIv+2uX5D0Fuc7HKlnYDdeT4JRX0k8E8GXVAcWUjAIegC7gEZVTUcRySBhnuC9iBB9prvJ+NuHqGUaZY67Hjfo3TSHIW/e1P0gEbKPa+oB7WHxR3WYR6YCXwCvAIWe6TUXZ+Z74sATiUCjKxnv+LMgFIefzYNOOmAiIpJoYb+rdtfwbcZzHJNeOiSgDUxzkER5TFLdGtQ3mJCuZQwcNybCLqisaWJQCHojFf/xsoAQh4o/A5AgDuCfwoSv9pbAFWlfiaJp5acMlgkqWDnrBV5H0h0L3ooBoR1gK1ZHmONA/IcF70fRFjCcChCXcDX2k90jKGymQKAFxuvwoY4rfZw8oBKwhcFdmUgSzJ7/wBKroGbKDYEYCi70UNuL03ixBeQnmcNHPluMROwiSOJQCHIMNZqHX8Ebgq6lgOKtIpgBD2/e7ErQfo47/pg1pNsod7jR854l/mN1+VUSUAh50CUIR6lLXAQuAlKpjLEp6Q8ZZ+R80SgMOQ4fyb1rIQ+GzrsZTxEWjeLeAUQLpLsPcfynpcYaBu4TS/x1pcDQLTvrV1/uWwQUyAdNDa2gGmAJQmhC3ASoQFKC+T40lG8qJIvLYImLdZAnAEMpJvAN9QpYJFnA+8kxRnoBwPHAN0JYo17LltFF+BKOATfEUXtxVQPd85FTcsfyzhJQFrsUV/5u3Ov1y6Ji9bc4u8L1QOfUVGcqqHAEyJWQKQJxFagMdbf/ah6zmanVxIlnMRTgKG4QazuxFWcqDNkNsJqWJ6yqBD+ALprtASQuWctiRgANDDc7ursCd/U36dP0Aq6A4AKP6+IJZSJ5QlAB5IP9YC97b+7KFKikWcjHAhcDopRpFjMG7nexVBk4Pc5iITAA8F+Cu6hZMAwNud9S7cGEvQh5sm3E5h2zBkcsAOymPYf29pH+tyir4v2AkaCWUJQIhay0++3PpzAF3P0WzjLIQzEE4AhqH0B7r///buPEyuomr8+Pd0z54ZErIBSSA7JOyyLwIGgslMgryigiLugguKvqAGccM94E8FUV5xQcE9CopkJkFAVGQPOyGBbIQQCAkJk3XW7vP7o7qTSZjp6e5bt293z/k8Tz8zkL5VlUnPrXNrOYU7LDdzgJDYBBX5LJ33cLOoCHuiHpclcBvuvIAh5BcubcEFE+V2wze5K9fOH6Ay6Px/jADdgQUAJcoCgAilRg7+nnrtRpU4z3MocY4DDgcmAqOB4SiDEWpJbszz2bgS15sGWPpcuVf+1+aiG7difwNu3GQw/ccvirvRv4Y99RunnDt/gKqgOwACjQraFECJsgCgSKVWzj6ZevVKk6feApyTR+m4XjTAToKqEflfm48u3C6B9bim16W+VuAeXrpxc7ptuO195XqjN7lLJ/kph61+fakKukkp0Kjg6wErNxGxAKCUSdX6/C+uIVAAUF3gAKCnTix3v8nOQOj8Aer2DVhAoCyCNgVQouw0wNIWIJFtwL38UQYAxmSjEzfsXy6df6agt/6AgIUHuh+8FLByExELAErb6vwvrQtWc9XIYNcbE6Z2KPLTPHKTIHPa6sFTA1YQKAAIcB8yUbIAoLS9mP+lQUcALAAwRWo75bf4czt9361FoHa/gBXkfT9IZ+4wJcgCgNIWIPIOGADEa6FycLAyjPEpvdK/3NaHtOGe/vvK9VPpIa9H/veDDSJip2qUKAsAStta8s5n5iGff23QeUdjPOnGdf7ldrxMAnaejddXAFDjI2VmnlOCmrSn/xJmAUAJE5Eu3C75PHgIAOoKeX6vMX3ooHwO9dnTdtwge4y+79Z1owJWUkHeuwDW33a4NnOtNhf0MG/jiQUApe+F/C6rxCUbDMBGAEyUFNdB7qB8Vvr3lB76h8wH9e09JWBFAZJ67VhZiXAJwkpt4XZdyHTVCA5HM3mxAKD0Lc3/0oBz+DYCYKKSoDzn+9O62X0hY6aMLcOODVhZgPvAtiXp72LAbJLcyQKWaDOf0Tt8DDOaMFkAUPoW539pwLnDmjEgPk4hMyYHHbgzHsrpNL+eFDel0VNfAUA8BnWjA1YYJADo9fZzEMI1JFirzVyrdzA+/wpMmCwAKH0BAoCAIwCxSpsGMIWTPsa33Nec95bKus8FgD524uRbhvYcAei9YOESEizXFm6z6YHiYwFA6YsuAABoCDr/aEwWunBP/V1RNyRknbwxQ3emBYANPqbh8rwPtL8E3ZuzeWcMeFtqemCpNvMZnRd0AZLxwQKAEicir+DOvctDA4E/AvUHBbvemEzSw+EDIZ9/kt6zF1ZmuGb4UQErrSHvkwC35vXscSDCNdTzgrYwVxcSNIORCcACgPLwbH6XxQg8ClBvIwAmJAPlqT9tC70HOZkCgJGnBqx07/wv7X3+P1sjgTkkWaULuFkXckiQwkx+LAAoD8/kf+nwYDVXDYWqgGUY01OCXU/95bi3vzeZ/q59LQCsqYOKgGd6BPn9DxYApFWjvI8kT2szd2oLZ9k6gcKxAKA8PJz/pcOC194Q9CASY1LaGVhP/eC2+/W1nTFO33fpvXzM/wcIAFof8lD/ToIwHfg7C3hCF3CRrRMInwUA5WD9X5blf7GHp/fBRwYvwwxs6eH+cjvEpz9dZP47Zxr+H3FSwMpj5D0F0L0ZtgdIQZLZ4Sg3UM8L2syVeruPm5TpjQUAJUzvYKS2MJdHz72Tro15llJL4KOBBx8JNmpn8tFzuL9c9/X3pZs37vffU18BgAD7nRawAXuTOcVgBq0PgYY+PzMS4WvEeVGb+akuwFYce2YBQAnS2zlAW7ieBC8Cc0DraH0kQIkBA+yKBhg0IVgZZmBR3H73gTbcn5Y+uTDTzgah7wBg0FCI5Zm/f6cgw/8PBqw7J7UIH0N5Vlu4XecTdOjDpFgAUEJ2PvHHeQ74BD3372wOMh9n0wCmQBQ3z7+ZN+53HyiS9L3iv6dMw//DjvDQkJIJANJcuuEY92kL/7UFg8FZAFACtIUR2sJcErwAzKG3o7sC/ULuG+DalMFB9yObsqa4Dn8zbs673Pf09yWd1yCb0fOqDH82pilgQ2K4nXj5UGgNsO7Yj5NxCwaf1AVcoIsyhkumDxYAFDG9neHawlzY2fH3vSq29UHQfCdRB+GSAgVQP8ltCTSmp55P/OV6al8utrPrhL/+9NWlVdfCoKA7AIaT+YShDLY/R/5rjrw7DOU3rGeZHUCUOwsAipDexTBt5krirMB1/P2v0utqhS2PBag16CiAwN4nBizDlA3FPemnV/YP9I4f3JN/tqcXVtH3utphPnLmBPh9f+0uD/V7N3a3A4gsw2BWLAAoIrqQodrMlXSyAuFr5HpQ92t3Bqjdw+/LUAsABrwErsPfjHvyHyiJfPqzndyOLu4rO68AY98evD1Bft8D3WdC5w4gSmcYvJ3JUTeomFkAUAT0Nhq0hTkkd3b8+eXnDfSLOYK8hwTT6idD1YhgZZjS1I3r5LbgOn574t9lO7kteIzR969idR00BO3Tasn12WIn7YZN/w5Yf0G4DINxlmoLt2szx0TdoGJkAUCE9B7qtZkvU8lqYC4wJFCBrfdDor+NxX2J4YKAIASGnhCwDFMy0gv7tuC2tOXyhDtQbCP33Q6Zhv9H+lhsG+Dpv/WhbE8ALBZu54DwsLbQrAuwYcoeLACIgC6iUhdwEW08j/BNAp3I0UOyEzbdG6CAMcHbUHdwbgudTOlJ4Bb0pRf2DbQEPtlIr/bPJyjqa3u/AGPflXeTdgnwe76xKOf/syFAE8r96S2EUTeoGFgAUEB6JTFdwLtYzxKUG/Ay8b6HQNMAo8k7M1jasKOBGvdEaE+F5aPn0/6W1Pc2zN87Jfc5/7RK+r4r1w2F2qC3jGry3/5Hsc//Z+tk4O+6gPsGei4BCwAKRBcyneN4FGUeMDG0ijYsCHBxJbBP8Dbse7z7mp4XbsUWhJUixWXp24Y97WcrneEv38C3r8V/APvPzLPQnsaQd9rurk3FsP/fH+Uk4O+08Lgu4F0DMRCwACBk2sJp2sL9JLkTCD9d3valsO3ZAAXsH7wNEy/Y/R6T3hK2GdeZdGFPj8VKcZ1XutO3f6/spTv/fKe/YvS9978iDvv7GLUO8Pu9/u+gZZi3WTgCZR4LeEpbeL/OCzoMWjosAAiJzudQbWEe8C8o8MKTdbcEuHgUgacBKgfD4D7mGXs+UW7HOpdi0LPTb8X+XfLRjZsaCTJCUkPfD+fDDwMJ2i/VECj9b6D7Skk4FLiJQTyjzbxP7wm6Lar4WQDgmS5gnLZwAzGeBHys2Mndq7cGuLgCL6mBJ5yX+c/3fNLcgbuJWqdTGAnctMxWdu/0Te66cD/HoFNcmfb+T3pfwMIh0PB/YluxJgDyT5iCcDNtqeyC9/S5LLPkWQDgiTazrzbzU5TngYuI8me75QnYsSJAAeODt2H48VBbn9170wvMtrJrZKATWzPgUwL3M06vyUhn6LPdGsGkg6iggWump/+GfaHOw9Qc4/K/dP18SLZ7aENJGYdwDW08py18qhwDAQsAAtI7GKQtfBVhGcLHyHyGV+EEGgXYl2yyD/frgMbcr0mPDGzHBQOtuFGCdmyEIBfduJ9Zzw5/B+5naz9DP7bhfqY+ZOpaJvt4+h9CoN3G5T/8n8kBwHW0sVpbmFNOgYAFAHnauaUvwWLg60CWj7sFsi5IACAEelpIG/sOqAgYD6VXorexa7g63ZlZUOAkcB37DtzPphX3s2rDOvwwJHHBqa8trtVk2Po3GIYd66GSCflfmmiDDQs9tKHkjQTmpkYELiyHEwgtAMjDHlv6gh7LFY7WhzxMAwTdFROD0acGLKMX6eHsnkFBesV6W+rPyjEwSHf06Sf7dGe/hV3pZhOU39+7mHTiPmu+tkMKmc74hAnneKgkjnuIzdP62wJkGC1LBwA/Yz3LdAEXlfJiQQsAcqDzmaotzCvYlr5AFNbeFOD6OrzkBJj0QYgX4GOWxI0UtOOehNOBQc8Ocgeuk+xiV0dZLJ2l4v4O3ezq5NN/j83A6+z6e6Sf7K2zLxzF/Xtsw+/PPNPTf3UtjPKx939/As1MBrqPlLWxKDewg+d1AReV4vbBAZf4IB+6kP1QvoHyIQLvkSugmjHwlhcCbB9aC9wfvB1LroU19wUvJwzp34BY6vv0154v9vi+t/9O6xlUCLs6dnp8Tf8/3eN7U5wS+FnlvyfBHfvVVwAw5d1wgI8RgNOBYfld2r4W/jUW1DJAZWEJMJdt/E7OLY2UWTYCkIHeTl3qlL6lKB+llDp/gPaXYNM9AQoYhZelDQd+DOJF+qNLd8IJdj19d+CewNtwT307cE/e23q8trIrLW7P19Y93rOtRxntqVd6FKIbe4ovdukh/zB2pNSS+en/AB/H/g4l784fYO3N1vlnbypwE/U8pi38TylkFrQAoBc6j7g281HiLMed0pfn2ZlF4KVfB7hYgEnB2xCvgTEhrAUwJizprH5hTX3HyJz2d9K5+BmgPSjY5Wtv9tCGAedw4K8s4MliTzFsAcAedCHHUc99CD8njMN6Cu3VW6GrNUABE3DnkwY0+UKoLNm1MmYg6cCtHQkzMVIdfffvtfUwuslDJYNwB3zl6fX7XWpxk6/DUimGH9AW3hp1Y3pjAUCK3s5obeF3JHkQOD7q9niTaIN1fwpQQJxAW4jSYhUw1seCJmNCkmDXQsswVZA5pj7wA/h5+j8wWDlrf+2hDQbXn9yROn3w5Kgb09OADwB0EZXazGeIswQ4n3JcGPnCdQSbaJ6Ml+UPE97n5jaNKTbpw6oKkRkxU46thpGwz2keKqkiUC6Prtfh5d97aIfZyZ0+eK+28Edd6GNuNbgBHQDoQqaznicRrgEaom5PaLYtho3/DFBADYH2Ee8kMPUjHsoxxpNO3HB/W4Hqq4E+d43HgMM+66miSRkqysKaGyAR9lDIgCTAeSR5Vlu4QZt9HLySvwEZAOjtTNZm5qf280+Nuj0F8cK1AQs4GC8fl5Gnwl6lv7TClLhudi3yK9SZEzEyJ/3Z5wio9/FgWIkbtcuTdsPq6z20w2RQCVyEsFxbmKst0Sw0H1ABgN5GgzZzNXGeQZgVdXsKav182P5cgALq8JIeGODQS8txosWUgiSu099C4U8/zLTwryIOU309/R9EoIW7626B9jWe2mL6MQiYA6zQFuZoS8a9Id4NiABAFdEFvItKFiN8Hi/L2kuNeojqD8bLWoD6sbDfccHLMSZb6Ux+PnP456KKzHediW+HikGeKgo4ihB4tNDkYThuy/kz2sy5hdo6WPbPYTqfo4lxHXBi1G2JXEUDTFsDFYMDFPIEsCx4W5Ld8J8PQ+eAO2LUFFo6AVNUx0vHcBn/+jzudxic+H+eKjucQHv/Nz8K9x/jqS0mgEXAHGkiyOKtfpXtCIDOZ29t5ifEeAjr/J3urbDmZwELmYKXUYBYBRx2yQAIQU1k0gv8dhBd5w+Zh/5jAofN8VRRNTAxWBErr/LSEhPYMcDd2syd2sKbwqqkLAMAXcDbiLEU4ZOUWvresK28OuDJXjW4/cUeDDsGhgfMVGbMnjrYdTpklB0/uD4509D/2DOgfpynyg4h0Mr/bc/Cq7d4aovxQpgOLNIWfq0tjPFdfNkFANrChSh/w53dbPbU+dpa1t22PFghU8m8mTkHh18BlSV/rLaJmuKG+VtxiXyKIX19nMy/JnUNLkOmF3sROGHX8m+CRh0xmV7EgA8Az2kL39A78LFYBCizAVhdyBEkeYRAZ1+WlY0ojwOLiPEw3TwsZ7FWVfcFVhCoF18NPOynlRvuhyeusUNxTO7SHX87xfX5EVyf3Nf4Y0zghG9BfYDters5lUDHd2vXMu5q+BxdHUchHIwbTjjYU+OMX68gXMlWfhn01MHyCgBa+AvwjqjbEZFXgMdRHiPGYyR5TGaxuq83q+o1wGeCVXkP8FqwItKe+hase8pPWab8JdnV8Rejevoe+hdg4lkuM6YXo4GTghZygYj8ruf/0L8yhBoOJcnRxDga5WjcIqCyGzkuUc8ifF4aacm3gLIJAHQeVdTzOt7GpovaK8CjKI8iPEqSxTKblbkUoKr74UYBAuTmfR24Gy+PXpqEez8C7ZZ9zGTQhZvjj2IrX7ZqyHwXGrIfHOdrq10MmEHAY7uXA1NFpN9EyHong+niSOBNwGG4bQcHMzDuu8XqduJcLDPIOXlD+QQALUzEfZDLSRewFHgaeArlMap5TKaz0UfhqnotcEmwUh6GvgcacrN9FTxwOSSLaSzXRC6J+01opzjm9jOpIPPh4RUV8OafQNXeniqcguuHA3mfiPw234t1HnFqmUCMw4HDEA4FjsAtSrDRgsJoBWZLE/flclH5BADzmUCMFVG3I4C1CE+jPAU8hfA0W1kq54b3rKOqQ3Gb+ofmX0onsBD3WObBC3+EZbcW13yuiUY3u572S+HzEMN1/n11eQIceQmMeLOnCgcBbyXQyn+X2ONoEfG++k9vp44KDgEORzmUXSMGI3zXZQDYjHJEpqnfPZVPAFA6UwDbURYT4yngaRI8TQVPykw2RdEYVf08cHWwUjwuCAR49Euw0UOyIVN6krgOP8rEPfkQ3HFimfricafDgR/3WGnAhX/OdBG520Njsqa3sQ/VHEaCw4kxFWUqbihjWCHbUab+KE28J9s3l00AAKAt3Aq8Pep2pGwClqAsRVia+rqEh1klVxbPrU1Vq4BnCZxB5D7gZQ8twmUJvO9j0LbVT3mm+HX2eJWiTIv+APYeDcf+AH+33PG4XDGB3CYi/+OhMV5oCyOAgxGmpIKCdGDg4yjSgaKLLobJ2WR18yy3AOAEXE9UqHmnJMpqYjyH60SfI+k6emliQ4HaEJiqngf8MVgpO4A78Hagets6eOB/oTvDpG8Hbmi4mjL7JA8AipvX70x9LYUh/r7U4Rb+9aW6Gk7+qadc/6Qqm0HAI026gSNE5FkvTQqR3kM92zmIeGq0QJmCMBV36IFt+d6TcIo08t/s3lpmtIVvA1d4LvZlYCVu1fxK4DmU59jOc3JuwU4SD42qCnAvcHKwkpbhphQ9ySY/wDZcJ1KFCwTsdlC80p1+B677KeVOP62WzPto4jE4/jtQHzBJz25OhOBJ4X4kIgG3AUdLF1HJRibSzUEIk3ABweTU1/0ZuAsQm6SJBdm8sfwCAEVo4WsIXyL71THtuI59JZr6KqkOv5aVMq1odxt7o6onAf8l0GdCgf8A6/00CmDl72H53zJXuZ1dQ8cxXBBQTcC1UcaLBK6zTz/tl5NqyJiTTYAjPgkj3+Kx0jF4ONqkFZgsIp6SeBSf1LG6E1Em7wwOlEmp7w+gnIODGEfLTB7L5q1lFwCk6XymInwc4c24Ve6duCf51SgvEGMVwkq6WMlsXhYpi+eRQFT1RuBDwUppA/6B17v94v8Ha/tZZLiFN84+pIOBKlwwULaf9iKiuH+HztTXYt+2l6/+On+Ag86Bse/2WGkdcCbBTzNPflIk7uv4wZKjLVSTZAJxJpPcGRRMBMamXpkmdIpdKyMZKcfQlc2b7ZZodkptC1xC4HMU1gL3e2hRD4vmwKZVff95EthK3x2O4IKB9Kt84//C62b3Tr/cVdF/3p0xx8PBl3msVIDTCLyDruNheOmkm1iT+KhMGxD/WjnTZvYlHQwIB6S+H8euACFTpodoCT+QRrL+4FkAYHajqh8Afh28pEVAhg47V9oN918M21/P8B5cEJDNbS2Gu5FXYqMDuUiya1g//RpIY2fZdP7DJ8BR38Xvhyqdnj8A7Ya1x0LHE6DMZxDnySh2eGneAKLz2RthLDEOAMaRTAUHLlgYjXuAiuIU2hdJcqTMJsNNcnd22zNvoKp3AWcEK6UbuAuy242SncQOuP/TmbcHKm5hYFYDYD3EU6+KHi/jOvsE7ufZRWntzfetGjcKn+muOXgfOO4aEJ/3/6HANAIPW7VeBRsv3/XfwkN0Mkum+sksahydR5y9GAnsR4JRwL5I6qsLEPbBLeYYib9lyy8T460yk8W5XGQBgHkDVZ0MPEXgubDNuLMCPE4Ed22G+y+BjgybL/INAnqKpV7pYCCe+u9y/I1RXMeeZNe8fSL13wPp6T6TbOb8G4bB8T+CmM+tKFXA9Cwq70f3alhzCCTfcNbGsygzZDIvBavA5EoV4e+MpKpHQJBkDMJIYATCPijDcfM+w+n9sWQrym8Qrsxn63k53s5MQKrE2fyzRQy+8Mjgpb0IPBS8mJ46NsIDn4XODOmH99wd4IOkXhXsCg5iPV7FLt3JJ9h9KF+xjj6T/g73ARg0GE78EcQCnK31BgK8GffgGNArs2BHn4fGrUY4UyZi6TeLmN7FMBKMIMlwhBq6eZ0Yz0hT/nnYLQAwu1Elzkp+DRUXMPp+qD7WQ6mP4/2cprZX4MHPQ1c/PfwOCndkbAw3UiDsHhhIj/+X/j6odIedfkpPv5J7/L90R28dfH76S/IDUNvgOn9viX7SDsMlwgto642w/iP9vWsdMWbIBOxM7gHEAgCzky6ikiH8ETgHgKqpMOZRkKBPNUlcfgDPyRHb18NDn4OOfnr4dii6pU7S46vgOuhMowjpzjz9vQmX4Ebd+9txV7cXnPBDqGjw3IBRBM7LBdC1Cl46EpJbsnn3RoRGmcgjwSs2paAUBi5NAehiqhjCPNKdP0DnEtj4RQ+lx4AT8L69tmYkHP9DqOonQKnBrdwupnB3zyf09Px7X68ENlRfKOmDffrr/BuGwknXhdD5NwDHeSgnCRs+lG3nDzAM5Z+6gukeKjclwAIAgy6jmmr+CrzxYJDNP4IdWWWV7EcNbj7T8/L6mmFw8nVQ189NuAp3X7VPvMmkAhhM/x/TIfvC8dd5nvMH90E9GS+Lw1+fC23/zvWqepT5uryXe4EpO8X0TGQioIupopo/A2/r800Vo2D/pyE21EONr+DOa/L8KJvYAQ9dCtv6OVU5iVscGGSHgClP2az0Bxg2Ho76juetfuCi01MInIcLoONxWHsCaN6rYLtQLpDJzAveGFOs7HloANPFVFHDX8jU+QN0vwwbLvZU637AEZ7K6iFeByf+BPYel/l9MdxIQCkn+zT+1dF/5y/AqCPh6LkhdP7gjvf10PlrG6y/IEjnD1CJ8HtdxkeDN8gUKwsABihdRCXV/AnlrKwu2PZH2HKDp9onp16eSRyOvRrGnNT/e+voP6ObKX8xXGLX/gJCASbNhkOvIJyB08NwWWY92PAp6PRyym8c4We6PPvUsqa02BTAAKRKnBX8DjgvpwulGkbf62lroAIP4M4NCMHqP8Hzt/Q/05DEJQ2yrOgDTxXuqb+/u6/E06cAACAASURBVGBc4JALYd+w1sZNAI72U9SWX8CGC/2U1ZNwpUzk6/4LNlGyAGCASXX+vwXyO6as4gC3NTA+3ENrkrj1AOs8lNWLDffB0z+G7iwyEbbhtgvaKvvyJ7gRoOos3ltVA0d/BRpCGLECXAK4E/ByK+58El460U0BhON7MokvhFW4KTwLAAYQVeIs52aE8wMVVHsGjLoDP+ddJIB78Z4jIK19PSy6AnZksRWqGzcaMJDz3Ze7CtxTfzYf3SH7usV+FWHNFY0CTsTLTGzydXjpGOhaGbysTITrmcCn7Pj08mBrAAaIVOd/U+DOH6Dtbnj9Wx5aBe5OfDLuwJMQ1IyEk2+AkQf3/94KYAi2QLAcpRP77EX/nb8A+58Ix10bYuc/Evfk7+MWrLD+w+F3/q6qT7KC/1O1vqMc2AjAAJAa9v8N8B5/pcZgv/lQ1+ipvC7gX0Crp/J6sfpP8PytoFk8vCRw2wVtbUDpqwJqye6pvzIOB38c9jktxAYNA07FX06M5LdZHn8SYS5uQUEh/JyJfFzExstKmQUAZU6VGCu5CeUC74XHGmD0f6HqcE8FtuNSBm/2VF4vtr0Aj38D2rZl9/5OXBphu82Vnhhurr+/jH5pew2HN30dqkeE2KhhuL3+3k4M/AtwnogkUzk9Pgh8HS8nCPVD+T2TeL+Iz+M+TSFZAFDGVBFW8H/Ax0KrpGI0jH4QKsZ4KrAT+C+EekR5Ehb/AF5+OLtFf0ncIsG8z9wyBSW4BX79neCXFgPGTYdJFxLuLXEEnrNhLgJOE5HdTrrQxdRTzWeBy3CTWmH6DRP5kAUBpckCgDKmy7ka+HzoFVUfBaP/m0RqPc0LduN2B6z3U1xfNtwHz1wPXVmmBUzgAgGfRwwbv6pwHX+2n8TaQXDE5bDXQSE2ClwCrBPxs3AWgBeAE0Tk1b7eoEtpoJJPolxOuIHAPFq5QI6x/JqlxgKAMqXL+Sbw5QJU9TrCVUzYshQabsHbHS6BCwL6vL95qqYdnvkerH86+y2AXbhpAXvmKR4VuI4/24drERhzAkz5NIjn8yneYH/c4T7e1s1tAU4WkWeyeXNBAgHhdpK8SybbOFkpsQCgDOkKPovyw5Cr6QZuRPmyTHZ7+FT1UuD7/qpIAg8SWrKgnl57GJ75MXT2c7RwT524ZQu2UDA6lbhdG7lMqQ8aDEfMgfpJITWqpwnAUXi81XYBTSJyV64X6hqG0sGlwCW4hNi+3UYH58ohNkZWKiwAKDO6nE8B14VcTQtxLpPxLH1D/arXAJ/xW91iwEtq08w0AUuug7UPZLdTIK0bNyJggUDhVOBW9ufS8cdjMH4WTLiAwtz6DgYO8VlgEvigiPwmSCG6hqG0cwnCZ3FnH/q0AOXtNhJQGiwAKCO6nPcDvyK8/A6LEebIRJr7bIOqAD8FLvJb9UrgMQqSqm/HS/D092HL2tyq68KNCNhMaHiqU69cRu0FGH4gHPo5qAx7TRy4X79jgQN8F3ypiHgb2dMlDKOCT4cQCLSgnGNBQPGzAKBM6ArOQfkTHpcY97AG+BYT+WU2q31VNQb8Fq95B8ClDH6Agj1qb7gflvwc2rfndl0SFwh0YtsHfUiv6q8ht9BWgLq94dDPwOAsEkF5UQWchFvx79UXRWSu70IB9DmGE+NzCJ/E39TAX2nlPFsYWNwsACgDuowZCLeRXXbzXLgFfjGulfHkMDkOqloJ3AJZnjaYQ5Pc4sDQ8p3vQWHV72FVM3TnGHgoLgjowKYH8lGJ+0RXkvudqqYWJp8P+83w364+NeC2+XnOHrjqh6+x6dJRYXemuoy9iPEJlM/jEhYEdSutvNuCgOJlAUCJ0xVMR7kdvwlsO4DrqOQ7MpbX8y1EVauA24CZ3loGuOY9SOjbBHvSJKz8DbywEBJ5LP9P4JptowKZxdg1zJ/PRFZVFYxtgnHvBilkttr9gOPxmODHWf1jePbToFwms/iB38J7l8oj8HHgUtxfLH/C9TKRi700zHhnAUAJ05UcR5K78ffIoQh/IcHlciBeEour6iDgDlzCf48UeAbeuA4xXMlOWP4rWPOv/AIBcKMBHbi1AhYMuI2jlbjR83wnsCorYeyZMP79Be74AaYAh+L9drrmF/DMRaQWomwlwVQ5qxBbYhxdRQ3dfBjh88C4fItBOF4m8ojHphlPLAAoUbqMQxD+jZ+hOoC7Ub4gk3nMU3k7qeogurf8g4q9TvJdtluesIiCj7Enu9zUwJo7oTPArqdu3KhANwNrmqCCXR1+kFUrVTVwwFth/HtAvCXZybZy3FN/CFl3X/wpPHuxG3na5ffSxHv9V5aZLqKSIbwXuBzIPWOScoVM5rveG2YCswCgBOly9sfly/WxzPhplG/KZP7soaw+6T/q5nP472axz/+EUPpm3OLArSGUnYWXmmHVX6EtiyOHM0myezBQTqMDMdxTfkXqa5CHdAHqhsD4c2CU59mlrA3FZfbLNt9wDlZeBc9d3vufJTldZnOP/0r7p0qM5cxC+Apum0O2viCT+F5Y7TL5swCgxOgyRiDcSz6R+O6WoHyNSfwl7LO9tYXTgH8hFXDYjTD6fSHUkgCeBpaFUHaWNi6CVX+C11/MLY9AX5LsCga6KJ3Mg4Lr4NOdfQV+NqbGBIZOgskfgIYDPRSYDwEmAYcTym7bTJ2/s5iRvCnKhXWpM0ZmAl8A3pLFJcfIJB4Nt1UmHxYAlJDUaV8P4FKL5UdYhXIlE/ldIQ7wUEVYwMPAMa7+OBx6A4z5SEg1rgMegdw2LfjVtd1ND7xyH3Ts6P/92VJcEJBMfU0Q7UhBuqOP9/hagcd09yk1dTDqVBh/PsR9rnXNVR0upW8YpwUqLLkMXshim79wqTSGnukzK/o8xxPjC8D/0FtEpPxKJvPhgjfMZMUCgBKiy/giwnfyvPwVlG/Tyc8LmapTF3AByh6ZywQOvhbGfjqkWjtwQcArIZWfg9an4IVbYdNz0B1ivJXs8Ur0+F7ZlcxIyS5YEHZ17vT4Pv0S/D3V96WyAoZOhYnvhvrJIVaUrTHA0WR/tnAONOEW+710Y7ZXbKWCKfJWXvbfmPzoCg5E+Rzwbtx+yFaEa5jAt+ykwOJlAUAJ0RU8iHJ8bhexCbiOBN+XKYWdJNd7qKGNpcDYXt8w7jMw5QchrtpegZsWKIZtyArr74OX7oDXl+e/g6CcVVTAkPFwwCwYHsJ60bxUA0cSQlY/J7Ednjgf1v89t+uU38oswphLC0TvoYJJ7CX7synqtpj+WQBQQnQ5TwGHZfn2bcA1xPm+jKc1xGb1SVuYA2TOXrbPOXDEbyAewmIqwE0FPA68FFL5+VB49T/wyj3QuhK62guS4bjoCFA9yD3pjzoThr4p6hbtYQxuts13fq2Ujlfg0bNgc57T48I0aeRfXttkBhQLAEqILuenwMf6edsO4Cco30uf0hcFXchQkizDLZfObMjxcNRtUL1PiC16GXeWQKEyCOag7RVYuwA2PA47NkCinJb/9yBAvBIa9oXhR8OoGVDtaxerT4Nww/0hfh63Pg2Pzoa2F4OU8jS1HCXTBtQGUuORBQAlRFcyliSPA3v38sdtCD9FuEom8Gqh27YnbeFHQPaT/LXj4Zj5UB9mzvZO3JTAKor6kXv7Knj1v7DxGdj+SumOEAhQWQ2D9oEhh8Co02FQ77NBxSEGTMad4BdiToHX/gGPvwu6A24bBRAulkauD16QGYgsACgx+jxHEuNGID1euhm4iQrmyrhiWPUGOp8JxFhCriumKofAm/4Cw84Ip2E7tQJPQHQDJLlJtMOmx2HjE7D1BWh/DTq2Q7JIRgoEt0Wvsg7qRkDDeNj7UBh2bMSr9nOxD26uf69wq1n9E1jyGbfwz4/XqWKyTGejrwLNwGEBQAlSRVjJJJLU8xJPF9sQoLbwF+AdeV0scZj0FZj4lQKkdH0ZFwjkeNpfsWjfAJuXwNaV0LYO2jdCx2boboNElwsQfI0cxOMQr4KqWqjcC2qHQd1oaJgEQ6ZCpe9j5QulATiCoCnv+5Vsdzn91/wijNKvkyYuCaNgU94sADBe6QJORLmPoJ+tkbPh8JuhsrfZDp8SwPO4MwWKKo7yI9EG7etcsNDd4Tqi7jZIdrsV6JqEijqIpRa6xWsgXgtVDVA93L1K5ik+F1W4of6JhH4bbFvthvw3h5YOvxvlKJnF02FVYMqTBQDGK13AP1GmeSmsbhIcdQs0HO6luMw6cVkEn6csAwGTUoHL5DcF7yf39Wb9fHjq/dCV96Ga2bpbmpgediWmvFgAYLzRBTShNHstNFYDB18H+3/Ua7F968AFAcsondy7pn/pjv8gQknmsydNwIpvwfJv7HmgT3iEs6WRHBMKmIHMAgDjRSrl76PsWpzo15gPw9RroKIhlOLfqA03LbAKCwRKWSVumL9AHT+4If+n3g+b/lOY+nZZARwiTXQUumJTmiwAMF5oM+cj/C7USmrHweE3wdBTQ61mdx24IGA5RZlDwPShBpiA29ZXoI4fYN2f4ZmPFWLIvy9zpImro6rclBYLAExguohK1vMsbow1XBJzZwgcdDXECnhjJwmswY0KeNi/bUIyBDgQ2J9wDyvYQ1crPPspeDncGDgLW4lxkMwsji3BprhZAGAC0wV8EuUnBa204TA44rcFWiDYk+JOHFyJO2yoFDP0lJsYMBr3xD+y8NVvvAue+hC0F0266RulibCO2zRlxAIAE4jOo5Z6luHuwIUVr4XJ34Jxl4BUFLx6d87AC7gpgm0R1D/QNQDjgPGElq8/k+4t8PwVsPp6iiwQTCKcII2Etu/QlAcLAEwg2sIVwLcjbUTDEXDoDe5MgUgosB4XDLyMbSMMUyXukJ5xwPDomrF+Piz+JLSvia4NmT1AIyeLFFdkYoqLBQAmbzqfvYmxgt7PJigsicGYj8KU70FFyOlcM0oAr+JOH1yLBQM+xHFD++OAURR0bn9P7WtdKt91t0TXhmwJ50sjf4i6GaZ4WQBg8qbNXI3w+ajbsZvq/eCgq2B0MRyV3o0bEXgJFxRYMJC9KmBf3NP+voR6OE82tBtevB6e/zJ0b422LdlbQ4IpchY7om6IKU4WAJi86D8YRTfLgLqo29KrEbPcaED91KhbkqK4Q4hexi0ejGybWJES3Ar+fXBP+yOI9Em/p413wZLLYOtTUbckd8rXZRZXRt0MU5wsADB50RZuAC6Kuh0ZSYVLIDT5G1Ad4tnueWnD7SZ4LfUaiIsI98LN44/AdfwRLOTLZPtz8PxX3N7+0tVGnINkBkW7WMFExwIAkzNt5kCExbj8qsUvXg8TPgfjv+B2DhSldmAjLhjYiBshKJLjfr2I45aKDE+9hlHQBD256NoIy7/pju7Vspi2sW2BplcWAJicaQvzgHdF3Y6c1Y6FA78N+72nAEcNB6XADmAzLhjY0uNV7Gpwnf1g3FP+3rgte8V+u9E2Vl2TYPmV9XSXws85a0liHCszeSzqhpjiUuy/kabI6HyOJsYjlPJnp24CTJjjpgciyR8QRBewHRccbN/j1Q6hp4EX3JN7LTBoj1cdUE/kC/Zytx34JTCXZhlGjH8DQyNuk2//lCbOiLoRpriU7k3cREJbaAaaom6HF7XjYeLlsP9HkxAr9iGBHHTgjjfu7PF9Ehc8aOqVHtpOsmuxXQXulhBLfR/HdfZVuPn59PdlYxtwI/BdEVmX/p+6kONIchdu2KJ8KI0yi4VRN8MUDwsATNZ0AceiPEQ5fW6Ehczc8FkYPge4gIIcEm8ithm4DrhGRDb29gZt4XSgGTefUS6WUMvhMs32oxqnjJ56TOiSfIdy6vzhGSp4t8iI50Tkw8ABwNdxK/FM+VkJXA6MF5Gv9NX5A0gT/0Q4j/JK3jCVNj4UdSNM8Sinm7kJkS7gzSj3Rt0Oj14mzgm9bY9S1WrgPOAyoNCnDRn/7gOuBW4VkUQuF+oCLkC5ifJ5WFpPF5PkbEomm5EJT7l8qE3YlG9G3QSPthKjqa+90SLSISI3i8gRwHTg75TXk2D5S26DLb+ANYfDC/JREflzrp0/gDTyW5RLwmhiREZSyeeiboQpDjYCYPqlC5lOkjujbocnXcBsaeIfuVykqvsC55Hc9FliQ8eF0jITXMejsOVnsO0PkEw95Crvk8n8NkixuoCvoWWTUc+SAxnARgBMNhJ8NeomeKIIF+Xa+QOIyDoRuZaVw25izaHQehUkbKlAUehe6/49XpwMLx3jAoBkjxFu4digVUgjXwe+H7ScIlFLomyCGROAjQCYjLSFRqAl6nZ44SEvuq7gzyjvBEBqoG4W1L/DfY1FeQrhANP9Mmz/K2y/Bdr+TT9ZEx+QSZwUtEpVhAX8Avhw0LKKQIIYR8tMnoy6ISY6FgCYjLSZhxCOi7odgSl/oIn3Bj0fXZexBGHKG/5AqqHmFBh0FtSfB/FiO3ugDHS/CNv/Btv+DO33k0Oq5HZa2UuOoStoE/RKYhzHzcB7g5YVOeEeaeT0qJthomMBgOmTNnM2wt+ibocHd7ONJjmXziCF6DKqEbbR3xkIUgE1p7lRgbrpUHVYkGoHLu2Gjodgx52w43boCJDJNsmb5ECe8NKsecSp53e4nSKlrkmaWBB1I0w0LAAwvUoNdz4GHBl1WwJ6mg5OlbfTGrQgXcnhJPMYMo2PhNrToHY61DVCxf5Bm1LOVgJ3seW6LjZ+9WKSgf/ZHOEjMpEb/RQGOo8q6rkFmO2rzIg8wzaOlHPJeYeEKX2llgjdFMpC3kmpd/7KKuLM8NH5p8o7JK/rEuvdsPW21LGyVYdAzZthr4v/Q/VhI4ApDMxgvAt4AngQeAj4j4isAVAlzgbeCkz2UpNyhJdyUuRcOnUe76Cev1LaqbEPZRDvB34VdUNM4Q3Em47pR2qe8wmglMeuX0N4szTynK8CdRlfR7ztiHiJVibIMXSp6hDgBOD41OtQoNyGCRK4p/sncR3+g8BjItLW1wW6gg+j/NJT/ffKJE71VNZOOo9a6mkB3uK77AJayzYmy7n0+W9hypMFAOYNtJnzEX4XdTsC2IEwXRp5wGehupw/Aef6KYzLZDI/6POPVQcDB+OCgYOBQ4CpwGiK+/e2C1gNLAae7fF1iYi051KQLqKSISwFJnho1xYmMiToItDe6B0MopsFCKf4LrtglMtkVt+fR1OeivlGYiKg84gziGd6XeleGrqAs8NY2KTLeALxMpS8BeEAmcjmnNugWoUbHRiLO7tgXOr70cAIOl45hIqGSuL1Hpq5h65W6NoEXRuhcwO0r4G21aCxf3LQt76K6/hfySfjXl90ORcCP/NTGJNkMiu8lLVn0XcymC7uAo4Jo/wCeJ0kE2U2r0fdEFM4tgbA7K6e90LJdv6KcKE0htD5K8IKT/PRcEM+nT+AiHQCK1KvN9AWbgXeTqwKKoemXntDrNa9oaLB7VIQgYohqYsS0L3FfZ9sh0RqJDixtUenv8m9r9dKuUumfPu+fP4+/Wrl1wzhClygE0yMI+nj5xaUnMlm/StnUs0/gTeFUUfI9ka4FPhK1A0xhWMBgNlJ5xEHroi6HQF8URq5KZSSV7I/UOehpC7cUbRhWQK8nWQndKxzr7DFeCGsouUYunQZ30L4ReDClCOBW4K3qnfydlq1hRnAv3DTNqVF+F9t5icyiwJ8aEwxsFTAZpcGzgUOiroZeVGulyauCq38pLdRkd/LpFBzsHtb9Ji1BKtDLX8Sv8bH30vD39UiTWygi9OJ4t8huEHYCMCAYgGAAVJD3MoXo25Hnv7EI3w61BrES2CkKN/zUE7fYiwNtfzeaLgBgAgJxMNplFKYba1yNq8S50wIb2QkNMKFupBJUTfDFIYFAMZp4RxKc9vfv4APyJXZ54XNi58A4G8ymcUeyulbkqXgf6V7Bh08yiuh1zKBPwBPBSxljC5npI/m9EdmsIYYZwIvF6I+jypJ8I2oG2EKwwIA457+YyX59P80HbxdmugIvSb1EACE/fQPSBNboIDHvCqrQg++ABGSiIeOKeY3IVAmMpPlJHgLsLZQdXohvFsXclTUzTDhswDAwELOQjk66mbkRFlFzGOWv/4FXQNwt0z2m5egT8rTBanHCWVVfa8mcCvKw4HKSBY2u6WcxTISTKO0ggAhyXeiboQJnwUABpQvR92EHG0AZsrMAgw9A/okg3D77PMnfNdPa7Kq65kC1rW8YFUJSoyvByuEwz01J/sqdwUBpTQdMENb7KTAcmcBwACnLTQCx0bdjhxsIUmjzOL5gtVYx4EES5r1iEzkbl/NyUJ5jgAAMpEWhP/kXUC+5zkEJGexDC25IGCuqiWLK2cWAJhS2vffBrxNZvNoQWsVDgx4/VxPLcm2vsIFAFLYACBVZ5ARq6mqxL21JQcyi+dLLAg4loW8PepGmPBYADCA6QLOAN4cdTuylEC4QJr4d8FrViYGuHYpE/ibx9b0bytLcQmHwpcs3BRAmkzgXuCfeV5ew8oA/54BlVwQoHxX77GEceXKAoCBTEsm6YeiXCiN3BpJ7cL4ANfOFQl/lfxuVZ5LJ7CsAFUlkZCTAPUtyGf3UG+tyIPM4nninAElkXHvQHbwvqgbYcJhAcAApfM5CTgt6nZkRbhMZkV6Xnm+T4xr6OAPXluSvUJMA7xYkC2YvZBJ3A/8Pa+LI1oH0JPMYCnCWyiFkQDhazqPqqibYfyzAGCgkoCrqQvnq9LIDyNuQ77H0X5PDqHTa0uyV4gAoPDz/z3FmQN053Fl5AEAgDTyHMLpFH8QMJYGPhh1I4x/FgAMQNrCCQjTo25HFn4sTR5SwAagi6gExuRx6TqqPRxgky8twFZALfz8f08ynqUoN+dxaaRTAD31CAIKsqU1b8qXtIXqqJth/LIAYGAqhbn/m2jkkqgbwd6MgzxWjQtzZX/avLcnW4VIBhSLeAQAIMlXgR05XnWgLi6eIe1UEDCN4g4CDkD4SNSNMH5ZADDA6EKOABqjbkc//k4tHxUpaE773uW3A+BVavm597bkYhargK0h1xJ5ACAHsRbN+XjlSqoDbu30rCSCAOXLOo/aqJth/LEAYKBJcjnBktqES7iHWs6TaXnN7fqnecz/K1fLqJyfSr1KBU/hHjwURQ6A3lQwF9iY41VFsQ6gJ2nkOWA6xRsE7McgLoq6EcYfCwAGEL2D8cA7o25HBo/QydkyjfaoG7KT5BwAvEYnPwulLbkLenpeJkm6CrLVsF8ynlbgqhwvK7oAAECaeJY4J6OsirotvRIu19upi7oZxg8LAAaSbj4HRZvU43nizJazQx+2zlWuAcDVcgjbQmlJrpTHQyx7tZwV7SjHbuJcR26nIBbt0dcyg1XANIpgiqUX+xLnE1E3wvhhAcAAoXcwEinSrTzKKhKcLjNYH3VT3iC3KYCNdPPT0NqSqziPhVa2sDS0svMg42lH+FrWF2jg0x1DJbNYTZxpFCahU64u19toiLoRJjgLAAaKBJdAUQ7dvYQyXc4q0uNSc5kCEP6fTCmiEYxqniKslMDCklDKDWICN6E8mdV7hYmpLZ5FS2awhi5OgQKe7pid4VTyyagbYYKzAGAA0DsYBHw86nb0Yj1J3iqzWRl1Q3qjSxgGWT/pbKSLn4TZnlyl1lKE86SeLL4AIJVyOdstrpUMi+5MgGzJ2bxKnDOyDmwK5/Pawl5RN8IEYwHAQNDNx4BhUTdjD6+R5AyZXXwdyU4VjM36vcoPiurpf5dwpgFixTUFkCaTuR2yPDAqUdzTAGkyg/Uo01AejrotPQxDiyBPhwnEAoAyp4uoRPhM1O3Yw2aURplddEObuxMOyOp9yibgx+E2Jk9hLQTsLs4AAIAkn4UsDmASDgq/MX7IbF6nircCD0Tdlp2Ey3Q+e0fdDJM/CwDK3atcAFl2ZIWxnRhnySwWRd2QfknWIwDfl8lsCbUt+QpnIeAGOYvXQijXCzmQJ4Df9fvGZOkEAAByJpuJcyb5H4Xs25AifLgwObAAoIypIgiXRd2OHtoQZstM7o26IVnKJgB4jUTOmegKp5rHyeZpODfFO22zy5egn1TMMYYXpin+yAy2E+dtwN1RtwUA4bO6kKFRN8PkxwKActbC2yiehCedCO+URv4VdUOylsxq5GRukc79AyDT2IbvrWRa/AGATGIN8IOMb1JaC9Mav2QG24FZ5Hscsl+DUT4ddSNMfiwAKGcxvhB1E1K6EN4ljbRE3ZCcCIP7eccr1PF/BWlLMH7XARRZDoA+dXMVZNxe+odCNcU3aaKDbbwL+GvUbUG5xPIClCYLAMqUNnMqyklRtwNIIHxAGoviaSVX/c1zfzfqnP9Z8h0AFP0IAIBMYSsxzkHY3ssff08msaDgjfJIzqWTkZwH/DHipgylqii3GZt+WABQroQ5UTcBUJSPS2OJPmkJv83wZ6vQosn5n1nM80LA7tIIAABkAg+TZArw/4Bm4Oco02RS0YyOBSLH0EUj5wPXRtoQ5TI7KbD0FO+pcCZvOp9DifEU0f77KsInpJEbImxDYLqcq4HP7/4/2YTSJAfyUDStyo0uZChJXsPP52EbjexVFEc1m91oC3OAuZE1QLhYGrk+svpNzmwEoBzF+F+iDu6EOaXe+QOknhRPBq4D/ojwNZIcVCqdP4DMZBP+DpZ52jr/4iRNXJXalhfNv49yuc6jKpK6TV6K9WQ4kydtYQRwfsTNmCONfC/iNngjk7gfuD/qdgSiPIgwyUNJxZaS1vQgjfxIW2gFfknh7+/708D5wK8LXK/Jk40AlJ9PADUR1v9laeLqCOs3vXvQUzlPeSrHhESauBnlnUB7wStXvqTziBe8XpMXCwDKSGr4LcrVuF+SJr4dYf2mL+opAEjaCEApkFncRpImKHiOikkM4h0FrtPkyQKAclLPu4H9Iqr9S9LEdyKq2/RnX56CwFsWlViRn99gdpLZ3INwBrCxwFVfoWoLzEuBBQDl5VMR1WudhvXiewAABXBJREFUf5GTY+hCeTRgMSulqUjPPDC9kkYeAU4lc0Ikz5VyBHfQVLD6TN4sACgT2sypwLERVG2df+kIOg1g8/8lSJp4ljinAMsLVmmSrxSsLpM3CwDKRxSnclnnX0piAQMAsfn/UiUzWIVyCoUL4o7X+UwrUF0mTxYAlAFtZizC2YWtlCus8y8xEvAsebUAoJTJLNaR5C0Q8HOQrRhfKkg9Jm8WAJSDGJdAAbfeKFfILL5bsPqMFzKTV4AX8y4gaVMApU5m8zq1vBX4RwGqO0NbOKEA9Zg8WQBQ4vQ2GlA+UrgKrfMvaZL3NMBWZrHKa1tMJGQa2xjJbODmAlR3RQHqMHmyAKDUVfEh6PfYWj+s8y99ybxTGD9lKYDLR+oQoQ+ifD3kqmbrQg4JuQ6TJwsASpgqgvLJwlRmnX9ZiOU5/2vz/2VHBJVZXAl8BOgOqxqSXBZS2SYgCwBK2QJmAweFXIsiXGqdf5mo4XHySRHr+0hhUzSkiRtTqYODJorqywV6B/uHVLYJwAKA0vbZkMtX4DPSyA9DrscUiEyjHbgrx8uSKHeE0R5THGQWt6GcDmwIofhKuvl0COWagCwAKFHazGEQ6j7bbpQPSBPXhViHiYJwTY5X/FmaeCmUtpiiIbN4CDiRMBIGCR/TvzLEe7kmEAsASpXwaQgt33Yn8B6ZxW9CKt9ESBq5G/hZlm9/GQ19pMkUCWliBcopSOC00Xvai2ou8lymCcgCgBKUiqTPD6n4DpRzpYm/hFS+KQa1XAxcBxlX9j9DjNNkFusK1CpTBGQW66jhLcACz0X/r94T6VHlZg8WAJSiaj4IDAqh5O3EmC2zuC2Esk0RkWl0SxOXoBwH/ApYCbQBrwJ3Ah9hJEfJzALmjzdFI5Ur4Gz85grYl3bO81ieCciObCwxqggLWIL/1f+tCE3SWKA0ocaYoqeKsJBvo3zRS4HCPdLI6V7KMoFZAFBidAEzUBZ6LnYTwszU0aHGGLMbbeHDwA1ARcCi2tlGvZxLwkOzTEA2BVBqlIs9l7iOJKdZ52+M6Ys0cSNwDrA9YFE1VNLgoUnGAxsBKCF6OwcQZyX+Dv5ZTYzpNs9rjMmGLuQoktwOjMqziA5G0iDH0OWzXSY/NgJQSmJ8An+d//PEOcU6f2NMtmQmjxHnBMj7ZMhHrPMvHhYAlAhVBOG9nop7lgqmyQzWeCrPGDNApO4bpyB5rEUSrvffIpMvCwBKxUIOBC/5tB8hxinyVl72UJYxZgCSJrZQw1nAT7O/iIXM5I/htcrkygKAUpHIe86tp7vp4gyZySYPZRljBrBULolPAJ+j/9ME/4Vynh0pXVwsACgVwtZA1yu3ArPk7IDlGGNMD9LE94FTgH/38scvI1xKLWdKE1sK3DTTD9sFUCK0hWpgE1CX88XCL9jKx23vrTEmTNrCGJQjiVGFsoqHeVKuJBl1u0zvLAAoIdrCz4ALc7zsKhr5og29GWOM6cmmAEpJF18B1mf5bkX4gjRxuXX+xhhj9mQBQAmRs3kVZRb9BwEdKB+RRr5XiHYZY4wpPRYAlBiZxSLgaOCP0MucvnA/yptlFr8qdNuMMcaUDlsDUMK0mX0RTkMYRZJWlAdlNkuibpcxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMaZo/X/BUewfDO2dLgAAAABJRU5ErkJggg==";
5955
+
5956
+ // src/assets/light-gizmos/bulb.png
5957
+ var bulb_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAC4gAAAuIAHVHB4bAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAIABJREFUeJzt3Xd4HOXVBfBzZ1eSe6djYqoNwloZDJgaJ0AgIZ2IAJINIcS00D5CCASCCaQBAULHAWIsyQYEhEAILQ6mmRI3ych0MGCae5VVdud+f0gQbFyk3Zm5U87veXgSZM3MAaSZszPzvq+AiBJD761IYVjxN+DK4RDZFnBbAXkb6jwk5ZNmW+cjouCIdQAiCobWj9kPcO8ApHQj3/IYnOw4GX73B4EGIyITLABECaD1Y74O6CMAum3mWxchp6Nlr9p5QeQiIjssAEQxp40VA5AteQPAwE5u8jbWtAyXA+rW+pmLiGw51gGIyGe5bmej8xd/ANgZPYpP9ysOEYUDCwBR3Kn+sMvbiHOcD0mIKERYAIhiTBUCYGgeW5Z2bEtEMcUCQBRnDWN6ACjKY8tuHdsSUUyxABDFWbo5/0/xhWxLRKHHAkBERJRALABEREQJxAJARESUQCwARERECcQCQERElEAsAERERAnEAkBERJRALABEREQJxAJARESUQCwARERECcQCQERElEAsAERERAnEAkBERJRALABEREQJxAJARESUQCwARERECcQCQERElEAsAERERAnEAkBERJRALABEREQJxAJARESUQCwARERECcQCQERElEAsAERERAmUtg5A9rSxohjZ4uMAORaCDFRKIPo2VP8NOH+VTPW71hmJqGu04fidoHIyRA6Dys4QbYFiDoC7kW65W0rrWq0zki2xDkC2dO6xg6Hp+6HYZyPf0gLgtyir+YMINMhsVDhtrOiFbMmqvDZOt/SW0rrVHkcin6lCUF91MQS/BlCy4e+SlwH3aMnULgg0HIUKHwEkmDZWDICbfnITF3+g/QTyOzRU3hxULiIqQH3VrRD8Fhu9+AOA7gvIE9pwfP/AclHosAAkWVvJ1QCGdu6b5VSdU3mSr3mIqCA6p/IkCMZ18tt3hzpX+hqIQo0FIKG0vnJ7CMZ2aSNxrtJXxw70KRIRFUAbju8PkT91cbOTdO6xg30JRKHHApBYcjSAVNe20QFo1Yt9iUNEhdHUbwAM6uJWDnKp7/sRh8KPBSC5hue3mZ6h9VWdfGxAREFo/53UM/La2JFSj+NQRLAAJJYW57lhEQR/9jQKERWm/XeyKK9tXeR7LqCIYwFIKpH5eW+rOErrq470LgwR5UvrKw+F4qgC9jDfszAUKSwASaV4ssAdXK1PjeZEUkSGOn4H/1LYTjDVmzQUNSwACSWZmmcBmVnAHkoxcPAp3iUioi4bOPgUoJBn+DJTRtQ+710gihIWgCRz3bOBAmb3U72cwwKJbGjD8f2hOr6gnQjO8yYNRRELQILJiNrnIagrYBf9OSyQyIg6l6Lrw/6+QO6RsuqnPctDkcMCkHSq5wFYU8AOfq5zTuAwIqIAdQzFPb2AXaxFLneBV3komlgAEq5jMZCrC9hFGk6uq7OPEVFhrkG+w/7aXSl7TX7PqzAUTSwABKxp+ROA/E8GHBZIFBitrzwUwLcK2MUCQK7yKg9FFwsAQQ6oWwvgwgJ3c43OGFfIJxIi2gy9tyIFyHWF7UXOl0x1AY/9KC5YAAgAIJmaKQCeKWAXu6N4bWdXISOifAwrOQ3Annlvr5iOsup7vAtEUcYCQP+jzjkAcvlvz2GBRH5pH/aHSwvYhYuUnC1SwNBfihUWAPqclE+aDeBvBeyCwwKJ/FL4sL87ZHj1DM/yUOSxANB65NcAVuS/PYcFhsrCRc0A3Dy2zHVsSyGgDccNQ2HD/lYhnfqNV3koHlgAaB2SqV4I1csL2AWHBYaIfG1aFsBHeWz6Xse2FAaayn+1PwBQjJfSiZ94F4jigAWAvizb83oAr+e9PYcFhs3jeWzzsOcpKC8FD/tTvIWe/W/yLhHFBQsAfYmMnNAGt+A5wjksMDyuAtCVT/NrkXau8SsMdZ4nw/4cOVt2vaHFo0gUIywAtEEyouYRAI8WsAsOCwwJydS8DnT67XGF6jgpnfS+n5mokwof9veklFX/y7tAFCcsALRxaZwFoDXv7TksMDQkU/N7iFwAoG0T37YMqkdLeW1NULlo4zwY9tcGwZmeBaLYYQGgjZLSmregKOTZIYcFhoiUVV8J1y0F9HoI5qF9EajFAF4E5CKIu7OU1/7dOCZ9ptBhf4IbOu7+EG2QWAegcNMXK/ugu7wOYOs8d5GFpsql/K5GL3MRxZk2HDcMmmpA/m/+L4SbHiojJi73MhfFC+8A0CbJqNqVECnkNiSHBRJ1VaHD/kQv5sWfNocFgDZv+M63A8h/BjEOCyTqNA9W+5uD11rv9CoPxRcLAG2WyHgXrp4DFDSHOIcFEm2GPjU6XfCwP5Fz5Ji6/Nf0oMRgAaBOkRG1z0NQV8Audkdx0ymeBSKKo/7bFTbsD3KPlFU/7VkeijUWAOo81fPQ/uZ4ntvjtxwWSLRh2nB8f4gUMl//WuRyF3gWiGKPBYA6TTK1CwBcXcAuOCyQaGMKXu0PV8pek9/zKg7FHwsAdc2alj8BKOAkw9UCidbnwWp/CwC5yqs8lAwsANQlckDdWgAXFrALDgskWl+hw/4g50umOv/Hc5RInAiI8qL1VU8DOKSAXXxTMjWPeZWHKKrah/3Jv/PfAaYjU3OQSEGjdCiBeAeA8qPOOQAKGWrEYYGUeB4M+3ORkrN58ad8sABQXqR80mwAfytgFxwWSFT4sL87ZHh1/pN0UaKxAFAB5NcAVuS9OYcFUoJ5MOxvFdKpQranhGMBoLxJpnohgCsK2EV/tLqXeJWHKFJUxqOQYX+ql0npxE88y0OJwwJAhUm3XA+gkCVHz+CwQEqa9mF/clr+O8Bb6DngRg8jUQKxAFBBpLSuFS7OK2AXHBZIyVPosD9HzpZdb2jxLhAlEYcBkie0vupfAL6Z9w7EOVzKJuU/FIooInRu1eFw8UQBu3hUMjWFrBZIBIB3AMgrkvs/AG15b6/upd6FIQoxVy8rYOu2jt81ooLxDgB5RudUXQPBuflujnR6W77UlB+dUdEX3Uu2QFYGQjAQLgZC3IFQDIRICRR9AQCCEgA9OjbrjfbS1tzx9yugcCHIQbES0CY4zhIolsDRJci6i5B2l6ClzxIZOaEp+H/K6NNZx26LVPrDvHcguEbKagp55Eb0ubR1AIoRTf8Wkq0CsEUeWwvaWncHwAKwAfpiZR+UYChSzhCoDgEwBMCOX/jfbsgCgAKKjmov/6v4na3663y/ANoxv4wLwHEA1wGKmqD1VcsAzIdgPlzMh8h8AO8CeBfp5jektK61gH/c+EoX7wF18916EXLpy72MQ8nGAkCekRETl2vDmIuhelt+OxA+ksJnnxKL9gZ0bwB7QFAKxTAAzucXZHv9AfSHYkR7WfhCrmxJVuur3gdkHqAzIdII15mHzF3zEj9jnZtzIHneeBW5WEZMXO5tIEoyFgDy1mvNd2BoyWkAyvPYupDhhJGkr44diJbc/nCcA6F6IIC9AfRY54IavUtmGsBOgO4E4NtQBSQHNFQt03q8AOh0uPI8cj1eTtyjBFdfRyqvAjAHrzXf4XUcSja+A0Ce04bjD4E6T3dxs5ckUzPKl0AhonOP2wpu6ggABwM4EMAwJPf3MAvBbCimQ/VpNGOqjKpdaR3Kb9pQ9TIU+3RpI3G/KmWTn/EpEiVUUk885DOtr5oI4ITOfjugh0umdqqPkUzovRUpDCsqB5zDoPgOgP3B0TcbkwNkDqD/hLgPY/jkWXF8ZJDHMMC7JFNzol95KLlYAMgXWj+mJ6AvolMLnchFkqn+g++hAqKNFb2QLfkOgB8AOAztz8up6z4C8ChEHkBr9ydl5IT8h5mGTMe7Mp15oa8BkAMkU73G91CUOCwA5BttOL4/1KkBsJFJS2QpgDMkU313kLn8oO+e2A0rc4cDbgUgPwDQyzpTzCwH9GHAqUNb98fiUAa0vuo4QG4EdMAGv0HwCHLpKr74R35hASBfqUJQX3k4HDkVigyAAYC8DXWnQpw/dywoFEl6b0UKQ7t9C9DjAXwHQE/rTAmxGIoHIKiRTM2z1mEKofVjtgT0fABfB7ATgKUQ1ENwC/as+XccH4FQeLAAEHWRzhm7HRy3CorTAHzFOk/CvQHBnVD5W5TLJJEFFgCiTmh/ma/716DuOLQ/2+cQ2nBpBeQJCCbhteYH5Ji6nHUgorBjASDaBG2sGIBst1MgeiYU21jnoU5QvAXRa9HWc2Li5hkg6gIWAKIN0PoxOwLuOYD8FHy2H1UrAL0LwFWSqV1gHYYobFgAiL5A6ytHAfILAN8HkLLOQ55ogUgtsu6fZa/aedZhiMKCBYAIgM4dWwbXvRhAhXUW8o0L4H4Al0imJnHTThOtjwWAEk3rx+wJ6G8A/Aj8fUiK9iLgur+WEZPftA5DZIUnPEoknTtmd7h6GXjhT7I2AHch7VwupZPetw5DFDSe+ChR2mcnlPGAnA4O5aN2rYDeipKSi2XYnauswxAFhQWAEkGfGp3GwMEnQfUKAFtY56FQ+ggil2H4zreLjHetwxD5jQWAYk/rKw8F5FoAw62zUBTITKicI+WTnrNOQuQnFgCKLZ0zdjvAvRGC71tnochRqN6FotbzpLRuqXUYIj+wAFDsqELQUDWm/VP/RlZaI+qchQDOl0zNJOsgRF5jAaBY0bkn7Aw3NwHtq6sReeVfSDuncbQAxQkLAMWCPjU6jQHbXQDIxQC6WeehWFoFwUUYXnMTl+mlOGABoMjT2ScOgZOtBnCQdZZQSfcGUr06/uoGON3bv+4UAVLc/v+1FWj5BGh6B+A1rbOmQp0TpHzSh9ZBiArBAkCRpg1jKqA6AUA/6yyBcoqBkm2A4i2A4i2gRYOAokFA8ZZAug+Q7oUu/XpnV0AWPQIseQJQjoDrhOWAnCaZ6rutgxDliwWAIklnVPRFuuQmCCqts/gu3RfosRPQbQdoyfZA98FA8daAON4fa/U8yHvXAW6z9/uOJa1GuvV0Ka1bbZ2EqKtYAChytL7qYEBqAR1sncV7AnTbDui5G7T7rkDPXds/1Qdp5WzIe9cGe8xoewPAcZKpmWUdhKgrWAAoUrRhzDio3gigyDqLZ9K9gV7Dob2GA73L2v/emLx/PbBihnWMKGkB8HPJ1NxuHYSos1gAKBJ0ekV39Ci+GSInWmfxRMnWQN/9oL3L22/vh+1XcVU9ZP6frVNEkFZjTespckDdWuskRJsTsrMO0ZdpY9UuyOIBRH0q3+ItgH6joH32Bbp/xTrNpuWaIPNOtU4RVbPgpo+WERPnWwch2hQWAAo1nVP5bYjUAOhrnSUvTkn7Rb//V4Eeu1in6RJ55SeA5qxjRNUiuDhGRtRMsw5CtDEsABRaHc/7b0IUl+3tPgTafzTQb38g1d06TdfxDoAXslD8XMprbrMOQrQhLAAUOh1z+V8K4FLrLF0iDtB3H+jAI4EeO1unKQxHAnhIr0dZ7TmcPZDChgWAQkXrx/QE3FpAvmedpdOcbsCAQ6ADj2h/zh8DMv/PwKp66xhxUoc+6bGy40ROsEChwQJAoaGNJ26NXPYhKPaxztIpqR7tF/1B3wBSPa3TeGf5S5APbrJOEUdPI93yQy4vTGHBAkChoI1jd0CbOxWC8L8p53QDBn0DOujI9nn242Tl7PaLv9tqnSSuXoU6h3MdAQoDFgAyp/VjdgR0KoAdrbNskqSBQYdDB307FJP1eKp1IWTRw8DSZ8BFgXz3LsQ9TMomv2MdhJKNBYBM6dwxu8PVfwPY1jrLJvXZG7rNccFPy5sPtxVoWwJkVwG51UB2NZBbBcmtaf9zzQJuS/v/z64AWhcCzQvs8ibT+3Ddw2TE5Detg1BysQCQGa2v2gvA4wAGWWfZqG6DodscD/QqtU6yLs21L+Pb/AGk5QOgdVHHX4vbL+oUBZ/Ccb4hwyc1WAehZGIBIBNaXzkKkMcQ1gl+nG7QrX4EDDzMn1X3uqp1IbDmDUjTm8Dad9s/sWvWOhUVbknHOwGzrYNQ8rAAUOD0laoMcvIfQAdYZ9mg3hnoticAxYY3JtqWAqsaIKtfAZreANqW22Uhvy2GpkZL+V2N1kEoWVgAKFBaXzUUwNMAtrLO8iXp3tCtjwf6H2hwcAWa3oasnNU+/r75A4MMZGghgEMkU/O6dRBKDhYACkzHoj5PI4wv/PUZAd3up0C6T7DHXfsuZMVLwPKX2l/cowSTD+CmDuEiQhSUyBQAnXvcVlDpiVzxUhkxkfdDI0bnHjsYbuoZQIYYR1mXUwzd+jhg4NcR2K9DdiWw7FnIsqfbX+Qj+oziLbjZr8ped39kHYW6RuvHbAnJ9UJr2xIZWReJN3FDXQD0xco+6I5fATIWwHYdX3YBNEDkFnTvd5fsekOLYUTqBJ1x3CAUpaYD2NU6yzq6fwU6+DSgJIgbEgqsngdZNg1YMZMv8NGmvIK2loOichFJMn3zzBKsXfYzqJwB6LAv/NFciNyGJR/cJl+bFtpf9tAWAJ1VuQdSzmOADt7Et70NwS+krObBwIJRl+i7J3bDiuxUCA6wzrKOgV+HblPVPrmPnzQLLH8RsvhRPtenzhNMQ/f+R/IDTnhpQ9X3obgawKZW/pqBNnxXRtZ8HFSurghlAdBXxw5EmzsXim06uclUiJ4rZbVzfQ1GXdK+qt+YGkCPt87yOSlqf8N/wCH+HsdtBpZMhSx5Amhb5u+xKK6moKymkqsIhos2VA6HyrUADu3kJq9jre4ro2pX+pkrH+EsAPVjJgD6sy5ulgXwVxQ7l8juk/g2VQhofdWVAM63zvG5ooHQHX7u71K9mm1/vv/pA5yQh7wwXjI1l1mHoI4Ppq3u5QB+BqCrtw5vlkzNGT7EKkjoCoA2nrg1stn3ABTnuYtlAC5DW4+bZeSENg+jURdofdXJAP5qneNzPYdBdzjTvzn8NQssndY+nz4/8ZN3FNCfSKb2LusgSaUzxhWhqOl0AJcC6J/nblZhTctWckDdWg+jFSwEU5ytp63tMOR/8Qfa/wNdh6KmV3RO5bc9SkVdoHOrDgdwi3WOz/XdF7rj+f5d/Fc3Qt66BPLRJF78yWsCyAStrzrYOkgSacPYw1DUNAvAdcj/4g8AvdG9eC+PYnkmfAXAkd082tNuEHlYG6r+2TH5DAVAG8fuABeT0fVbZP4YdAR0hzMAKfJ+3y2fQN67FvLun4Bmru5KvikGUKdzxm632e8kT2h91VBtqPon1H0SwJ6e7DQlQzzZj4fCVwDU44nXFUcBmKtzqq7R2Sf283TftA5998RuyLr3IwyL+0gKut3J0G0q4fmTLm2DfHof5M0LgZWcwp0CsRXEvU/fPLPEOkic6ewT++mcqmsAzO24dsRa+AoA9A0fdloEwblwsm/onKpT9N6KlA/HoFW5mwCMtI4BSUMHn+HPm/5Nb0LevARY+FD7inxEwRmFpmXXWIeII723IqVzqk6Bk30DgnMBeH/L0HVDt/Rz+ApAsfMIAL/Gvm4Bwa0YWjJX66uO9OkYiaT1ladD9STrHHCKoV85B+jrcQ9xWyEfTYK8fQXQwknayMzpOqfS/vcsRnR21WjsVjITglsBbOHTYRbgjbaZPu07b6EbBQAAOqfqmo4W5jP5JyR3tpRNfsf/Y8WXzjl+X4jzLAp7ebNwTkn7xb9Xqbf7bV4A+eAWTuRDYbEWKewve9bUWweJso7pyX8HyBj/D6Y/lfLaO30/TheFswDMGNcDRU2PAzgogMM1A3ot0q2/l9K61QEcL1a0fkxPQGcB8Orlzfw43aA7/hLosYuHO1Vg0b8gn97PqXspXATz0NpjHxk5ock6StRoY0UvZIsvAuRcAN0COOTtKKsZF8YJncL3CACAjJzQhHTLNwH5K+D7v7RugFyIXMkbWl95gur4UP47CS3VG2B+8S+GDvk/by/+udWQ+VdDPrmHF38KH8UeSK+50jpGlKiOd7S+8gTkSt4A5EL4f/FfDdWzwnrxB0J6B+CLdHblPnDwF0D2D+aIMhMq50j5pOeCOV506ZwxP4To/aYhJA3d4SygT7l3+1z7HuT964HWRd7tk8h7CuD7kql5yDpI2Onsyn0gcl1Aa5IogPuQdn4hpZPeD+B4eQt9AQA+m1O+agyAPwHYOpijyj/hps7k2twbpnPGbgdx6wEMNAshDnTw6UDffb3b5/LpkA/vBNxW7/ZJ5J9FaEMmrIvNWGs/T+X+AEgVgrnezYI6Z0flA2QkCsBnOp43nw/gVwCCGA/bBMUNKGq5gu8H/I/qeAcNbz0J4OumObY72cOhfgr55F5g0SMe7Y8oMI+hrOZbYb3NbEGnV3RHr5KzoPg1AJ+mAF3HEgCX4/WWG+WYusiMD45UAfiMNlbtgix+D6AioEN+COAilNVU85cM0IYxZ0D1RtMQW30fuuUPvdmXZiELbgeWT/dmf0SBk5MlU32HdYow0Pox3wHc6wEZEsDh2gC9BW2tv5GRdZFb/SuSBeAzWl95KCDXwaupGjdLXobIOVI26YVgjhc+OuvYbZFKzwPQ1yxEv1HQwafBkx/f3GrIe9cDa14rfF9EdlZAnVIpn5TYOam1vmovtM/ZH9S6CVMBOUcy1a8EdDzPRboAAIA+NTqNgYNPgurvEMwUtApoDdJFv5TSiZ8EcLxQ0fqqfwD4rlmAnkOhO14AiAdLDWRXdMzjv6DwfRHZu18yNT+yDhE0nXHcIBQ5lwByBoAgZnl9GyIXSll1XQDH8lXkC8BntLFiALLFlwb4Q7AGwNXok/6j7DixOYDjmdOGyuOhUmsWoGggdJfferOqX9uS9ot/S+I6HMWZytFSXv2AdYwgfL5Mr+C3UPQJ4JBNAK6K0zk/NgXgMzp3zO5w3WsBOSKgQ74P4BLJ1EwK6Hgm9NWxA9HqzgOwpUkAKYLufDHQfcfC99W6GPLuH4HWhYXviyhcPoG4e0jZ5FivS93+nF+vBbBzEIdDRIb1dVXsCsBnOn5A/gLAgytGZ8hTcOQcGT6pIZjjBUvrqyYCOMHs+Nv/DOjvwaO91oWQd34HtMX6/EhJJrhNympOtY7hB204bhg0dQ2AbwZ0yEgN6+uq2BYAANDGimJkS04DcDmCGQriAloLOL+QTHVsPl5qw/F7Q52XYTVz5MBDodt60D3alkHeuYIT/FDcuRB3XymbHLrFZ/L1hUe8pwPw4AWgzYrksL6uinUB+IzWV24POH8C9DgE88+8DILfYHjNTVEfNtgxCdOzAA40CdD9K9CdLy38pb/sCsg7vwdaOF8KJcLzKKs5OPrnn/EO6t88AyKXAegfwCHbILgBrS2/jeKwvq5KRAH4jM6tOgAu/oKg1qxXPIie/Y+VXW/wa3lj3+mcMWMgavN+g1Pc/tJfybaF7Se3tv22f3OsHt8RbZrIMVF+U11nVPRFUcn9AA4N6IiPQ9xzpGxKYsYEJ2rhGxleMx1lu+wH1Z8C8P/1b8H3sWZZZCfn0BnjekD0CrPjb31c4Rd/zUE+uJEXf0oexZ91xrge1jHyoU+NTqOo5CEEcfFXvAXIdyVTe2SSLv5AwgoAAIiMd6W89k6s1aEArgLg76TvgkqdXXWUr8fwS3rNhQB2MDl27wwwsPCZhuXjGmDVXA8CEUWNDkbx2v+zTpGXAdufAcCreb43ZjVUL0TP/ntKpvphn48VSol6BLAhAU0r/B/J1AR0G8sbHTP+vQWge+AHT/eG7vrHwsf7L/pn+/z+RMm1GpCdo/RScsd7R28B2MmvQyCmw/q6KnF3ANYnpTVvSabmGEAPA+DXlI6jtbGil0/79oeTuggWF38Auk1l4Rf/1a9APr3Pm0BE0dULohdYh+iSOcfvAv8u/rOgziGSqTkm6Rd/gAXgc5KpnYq2HnsBOAeA14PEHWS7DfF4n77RxrE7QORkk4P3zgD9Clyyu3Ux5P2bAXW9yUQUZYrT20dCRYSTGuzDXj8F5GSU7bJPXMf054MF4Atk5IQ2ydT8BW253SC4DYB34z/Vic5wnKz7GwSz3PK6nBLotmML24e2Qd6/Hshx9WaiDt2g+JV1iE5z1MtpdtsguAZtLUMlU32HyHh+KviCxL8DsCkeTivchj7pPlGYP7rjnYh5AIoCP/Y2VcCgbxS0D1nwV2DZsx4lIoqNNkCGSqb6Xesgm6OvndQbLa3LUPiaLlOhqbOl/K5GL3LFEe8AbIIMr35VMrVHAvJdAIX84jwWhYs/ACCHS2Fw8Ue37YCBBb4nueK/vPgTbVgRoBdZh+gMGXbnKgBP5r0DxVsQOUYyNYfx4r9pLACdIJnqh7GmpRQil6B9FcCuaIW6ZmPpu0LnnrAzFMeZHHubSkAKKPxtSyEf/s27QETxc6I2jrUZ1ttV4v4B6PIshv8b1hfhCZCCxALQSXJA3Vopq74C0GGATEZnfzgVZ0n55Jf9TeeRXPZcBLOU8rr6jgR67VnADhSyYAKf+xNtWhpt7jnWITpDyiY/A8G1nfx2BbQauexQKa/9Y5RnXg0a3wHIUyemFV4FkZ9GpYm2L7ZR8j6AnoEeWNLQ3f4IFBewyvDixyAfT/YuE1F8rYKb3kFGTFxuHWRzVCGorzoXgj9h4wsAzYA4Z0nZpBeCzBYXQayqFEsyvGa6KvZFQ9VBUFRBkEH7uPn3AEyFuJMitSZ3rtvpgAZ78QfaX/or5OLfuhjy6f3e5SGKt95IZU8B8CfrIJvTvpBRzTVaX3kvICcDOgoig6FYDKAR6kxGZtLzUV/wyBLvABD0zTNLsHbZu1BsE+iBnW7QoVcD6T5570LmX8Wpfom65iOkW3aU0jp/p0Gn0OM7AAQ0LRsT+MUfAAYdUdDFH8ue48WfqOu2RbbY5GVfChcWAAKAMwM/YqoHdFAB0yu4zZBP7vEuD1GiyNnWCcgeC0DCaX3lKABlgR934BFAKv/lEWThg0B2hYeJiBJlhDYcv7d1CLLFApB04vws8GM63dpv/+erdSGw+Anv8hAlksHvPoUKC0CC6Wsn9YZm1+SxAAAgAElEQVSqn8sgb9iA0UCqR96by8eTAc16l4coiRSV+tpJBS67SVHGApBkzW2VAII9AYgDHXhY/ts3vQmsnOVdHqLk6oWWtmOsQ5AdFoAkEw3+FmDffQsa988x/0ReMjgHUGiwACSUzj6+HMBegR934JH5b7x6XvtfROSV/bR+TCHzcFOEsQAklUjw44C77wj02CnvzWXhgx6GISIAgLo/to5ANlgAkkrk6KAPqQNG57/x6nnAmtc8y0JEHSw+DFAosAAkkNaP2Q/AzoEe1OkG9B2V9+ay5DEPwxDRF+ys9VWBPw4keywASaQa/C2/fvsDqe75bdvyMbCy3ts8RPQ/FucEMscCkDCqEAiCv/3f/5C8t5XFjwFc8IvIPyLHqnJxuKRhAUiaOZUHANgh0GMWbwn0yPOJQ241sPx5b/MQ0fp2QMOYfa1DULBYAJJG5DuBH7Pf/vlvu+x5wOWqpUS+U/fb1hEoWCwASSP4ZtCH1L75f7CQZU97mISINkqcwM8NZIsFIEF0RtU2AIYHetCSbYBug/PbtulNoHmBt3mIaCN0r45zBCUEC0CSFMm3gIBf9Om3X96byrJnPAxCRJshKMLh1iEoOCwAiaLB3/7vVZ7nhjlgxUxvwxDR5vAxQIKwACSEPjU6DeDQQA+a7g302DG/bVc1tI8AIKIgHaH3VqSsQ1AwWACSYtD2+wLoF+gxe5ch3ycOsvJlb7MQUWf0x25Fe1uHoGCwACSF4uDAD9krz/cNNQusnO1tGCLqJOcg6wQUDBaApFAcGOwBBci3AKx+Fcg1eRuHiDpHNOBzBVlhAUiAjik+81+JJx/dtm9/ByAPsnqux2GIqPOEdwASggUgCeYeNxTAFoEes+du+W+7igv/EBnaUhurdrEOQf5jAUgCTQd+S0977Jrfhq2L2lf/IyI7Od4FSAIWgCQQHBD4MfMtAKt4+5/InPI9gCRgAUgC1X0CPV5Rf6A4vycOsvZNj8MQUR5GWgcg/7EAxJw2VhQDGBroQbvnOfkPAKxhASAKgd11xrgi6xDkLxaAuNOS3QEUB3rM7l/Jb7vsCqB1obdZiCgfJUi3FPAmL0UBC0DcZaUs6ENqyXb5bbjmDW+DEFH+JBf4uYOCxQIQd6LB/xJ32yGvzaR5vrc5iKgAGuzS4RQ4FoC4EwRbAJxioHjL/LZd+763WYgofxL83UMKFgtA3Cn2DPR4JdsBkuePVfMCb7MQUf6UBSDuWABiTOvH9ASwbaAHzXP4H3KrgbYl3mYhogLo9vruid2sU5B/WADiTJ0hgR+zeFB+2zV/6G0OIiqUYIWb3ws9FAksALGWLWBAfn60KM8CwOF/RCGkgZ9DKDgsAHHmyJDAj5nvI4DWRd7mICIvsADEGAtAnKkMCfyYed4BED7/JwohlwUgxlgAYs3gl7eoX37b8Q4AUfiIsADEGAtArDl5zsmbJ3GAVI/8tm1b6m0WIiqcYIh1BPIPC0Cs6VaBHi7VE4Dkt212padRiMgDijxn9aIoYAGIt4GBHi3VO7/tNAu4Ld5mISIvBHsOoUCxAMSUzhjXA0D3QA+a7pXfdtnVANTTKETkiV6cDCi+WADiqntzngPyC5Dv8//cam9zEJF3Vri8CxBTLABx1WZw606K8tvObfY2BxF5J8XHAHHFAhBXKTf4OwCSzm87t83bHETkHVeDP5dQIFgA4iqH/oEfM98CoFlvcxCRh9wB1gnIHywAcSUa7AuAAAsAURwp+BJgTLEAxJU4JcEfM5XfdiwAROFlcS6hQLAAxJYWB37IfF/my67yNgcRechlAYgpFoC4chF8AWj5JK/NpPUjj4MQkYeCP5dQIFgA4kok+Na+9h2gbVnXttEcsGKmP3mIqHAW5xIKBAtAbBk8AlAXsugfXdtm8eMAlwImCi9XWQBiigUgtvKdladAS54Clr/Yue9d/Qrk0/v8zUNEhRGHjwBiKs9xWxR+Vq/WK2TBrdDWT4Etjtrw0EDNAosfg3x6f/sjACIKMeVMXTHFAhBfrWZHVrf94r50GtBvFLTbkPaFgrKrIGvfAVa8DLQtNYtHRF2gyqU6Y4oFIK5UWyBim6FtCbDoERinIKJCOA4LQEzxHYD4srsDQETxoS4LQEyxAMQVWzsReYLnkrhiAYgrVa6xS0SFE55L4ooFIK5U+ZYdERXOlcXWEcgfLABxJeAvLREVTnSRdQTyBwtAXDlp/tISUeGcLM8lMcUCEFctTbwDQESFW5XjuSSmOEQ7xrS+qhkA5/EmonytlkxNb+sQ5A/eAYi3T60DEFGkfWwdgPzDAhBngnesIxBRpL1tHYD8wwIQa8ICQET5U7xlHYH8wwIQZ6ps70SUP+E5JM5YAGKNdwCIqAC8AxBrLABx5vCXl4gK4Lg8h8QYC0CcrWpuBJC1jkFEkdSE17JvWocg/7AAxJgcULcWwGvWOYgoghRz5Ji6nHUM8g8LQPzNsg5ARBHkyEzrCOQvFoDY09nWCYgoglyeO+KOBSDuRHkHgIi6Ls27h3HHAhB3rb1mAGixjkFEkbIc81pesQ5B/mIBiDkZOaEJwH+tcxBRhCim8QXA+GMBSALVadYRiChCRJ6yjkD+YwFIAsE06whEFCn/sQ5A/mMBSII1rdPB9wCIqHMWoqy60ToE+Y8FIAHaJwTiYwAi6gSRf4pArWOQ/1gAEkP+YZ2AiCJA8aB1BApGenPfoLNP7Adky5GSLYII5DlVF8BCQGZJpnqNdRwzuew/kErfBECsoxBRaK3GmuZ/W4ewpDOqtkFah8Nx+lpnyY+2QeVjLP1gpnxt2ibXgtnoxUDnjN0N4v4OwPcAFHkd0UAzFPcjlbpUht+VyDWutaHqZSj2sc5BRKF1n2RqKqxDWNA5Yw/quOYdjHh8UFoByO1IN/9eSuuWbugbNvgIQOeMOQLizgDwI8Tj4g8A3SCohJubrXMqv20dxgRv7RHRpoj+3TqCBZ1TeSbEnQbgEMTj4g8AfQE9D9mSWTp3bNmGvuFLBUDnjtkdovcB6O17PBu9IVKn9WP2sw4SODc9GeDLPUS0QauRan3IOkTQtL7quxD5C4CUdRaffAWu+6jOOnbb9f/gy3cAXFwJoFcQqQx1A/RW1fGJeglSRkycD+A56xxEFEIi90pp3WrrGEHSGeOKAFyL+Hzq35htkUr9cf0vrnMB1PoxWwL6reAymSrHK2+Nsg4RONFq6whEFEa5u6wTBK6kaTSAnaxjBEOO1caKAV/8yvqfgPfbwNfiK6eHWEcIXBPuAbDWOgYRhYnOx/DJz1qnCFwOSfoQWIRc8TqPvte92KsOCjSONXG2so4QNBlVuxKQRL7oQ0QbIc4diZz8RzDQOkKw1r3mrVsAHCwLNIs5d7F1AhOO3mQdgYhCowWS/at1CCPJuuYplnzxb9ctANnsy0jSW+Kq060jWJDhNdMBzLDOQUShcLcMn/KpdQgTbqLOgzkAL33xC+sUANnr7o8AmRZkIkNvYNlHyXvm9RnVW6wjEFEIuAm+I9gvPRWCj61jBELwkGSqF37xS19+4c/BLwG0BpXJSA7inLG5aRJjral1CrDu7SAiShp9QUbU/tc6hRXZcWIzXLnAOkcAlgHuL9b/4pcKgAyvngHRnyC+JaAVIidL2aREz3ctB9SthUhymz8RAZAvjQ1PGimvrgbkd9Y5fLQEwPekbPI76//BBof8SVntZKh7MIDn/U4WsKkQ9wApq55oHSQUUs1/AZCoiT+I6DPaiLJd/mmdIgwkU30xVH8I4A3rLB7KQVGLtLOXZGo2+Lh7s7Mf6dwTdobmRgGyJaDF3mf0maIZqp8glZsuw+/+wDpO2OicqmsgONc6BxEFTPVYKa+9xzpGmKhC0FA1AsCeALaERHJ64DWA+wFSbc9ubBGgz8R9+kPaDJ0zdjuI+zaAEussRBQQxVt4o2WYHFOXs45CdpIz6x9tkJRP+hDQv1nnIKIg6WW8+BMLAAG53OUAmqxjEFEg5iKz62TrEGSPBYA65n/AzdY5iCgAqheJjHetY5A9FgBqV+z8EYKV1jGIyFcvIVP7iHUICgcWAAIAyO6TlsDF1dY5iMg3CnV+kchFf2iDWADof5pargbwvnUMIvKDTJHySc9Zp6DwYAGgz8kBdWuh+mvrHETkudVQ+aV1CAoXFgBaV6a2FvGbAZIo2QRXtA/5JfofFgBahwi0Y0EoPickioc3kGq51joEhQ8LAH2JDK+ZDqDaOgcReUDkXCmti+viblQAFgDaCDkfwDLrFERUCP2HlFX/yzoFhRMLAG2QZKoXQuQS6xxElLcWuHq+dQgKLxYA2rjXmm8FMMM6BhHlQXCljJj8pnUMCi8WANqojsVCTgGQtc5CRF3yOnqnf28dgsKNBYA2STI1swC9yjoHEXWaC+BnsuPEZusgFG4sALR5PQZcBsE86xhE1AmKmyVT86x1DAo/FgDaLNn1hhao/hQA1w8nCrf30K34IusQFA0sANQpkql9EcCN1jmIaBNUTpFhd66yjkHRwAJAndfW4yIAb1vHIKINUPxNyqsft45B0cECQJ0mIyc0Qd2fgdMEE4XNJ3Dc86xDULSwAFCXSPnkpwC53ToHEX2ByhlSNpkzd1KXsABQ17U1nw/IB9YxiAiA4F4pr37AOgZFDwsAdZmMrFsBuD8BHwUQWVsElTOtQ1A0sQBQXiRTOxXQG6xzECWayqmSqV5oHYOiiQWA8ten6AJAG61jECVS+1v/vPVPeWMBoLy1TzUqYwG0WWchSpgP+dY/FYoFgArSvlYAuOgIUXAUwMl8658KxQJAhVu64ApAXraOQZQIqjdLpuYx6xgUfSwAVDD52rQsJHsCgLXWWYhi7l0Utf7KOgTFAwsAeULKprwG4ELrHEQx5kKdsVJat9o6CMUDCwB5p6zmeiietI5BFEuKq6R80nPWMSg+WADIMyJQaHocAK5GRuQpbUTf9HjrFBQvLADkKRkxcT6AC6xzEMVIC5zU8e3Dbom8wwJA3iuruRVQLktK5I2LZfikBusQFD8sAOS59kcBqZ8CWG6dhSjinsPrLddah6B4YgEgX0j5pA8h+kvrHESRJVgJNz1GjqnLWUeheGIBIN9IWe1fATxqnYMoklTP6ninhsgXLADkL3V+BoBTlhJ1if5DMrV3WaegeGMBIF9J+aQPofoL6xxEEbIIjnuKdQiKPxYA8p2U194JxYPWOYgiQfUkGT7lU+sYFH8sABQQ5+fgowCizblDymv/aR2CkoEFgALR/igAXL+caOM+hJvm4zIKjFgHoGTRhqrHofiGdQ6iEPoml/mlIPEOAAUr644DwNXMiNZ1By/+FDQWAAqU7DX5PUAvsc5BFCIfQdzzrUNQ8rAAUPDKdr0ewPPWMYhCQfRkKZvMF2QpcCwAFDiR8S5yOg5Aq3UWIltaLWW1nC2TTLAAkAnZq3YeIFdZ5yAytARw+NY/mWEBIDs9+l0O4HXrGEQ29DzJVC+0TkHJxWGAZErnVH4DIo9b5yAK2NMoq/maCNQ6CCUX7wCQKSmvfQLAFOscRAFqgeRO5cWfrLEAkD0ndy44TTAlhl4uZVNes05BxAJA5joWPvm1dQ6iALyOdCtffqVQYAGgcCjb5TYI/msdg8hXoudKaR2Hv1IosABQKIiMdyE4B+BzUYorfYBj/ilMWAAoNGR4zXQoJlvnIPLBWo75p7BhAaBwKUr/AoKV1jGIPKX4g2Sq37WOQfRFLAAUKlI68ROo/NE6B5GH3kHfNF/8o9BhAaDw6dHvGijeso5B5AnBebLjxGbrGETrYwGg0JFdb2iB4FfWOYg88KyU1TxoHYJoQ1gAKJQkU3M/uGQwRZvCYZGl8GIBoBDTX4DDAimy5F4ZXjPdOgXRxrAAUGhJpvZFKP5hnYMoD61I68XWIYg2hQWAwq39XYA26xhEXaK4SUpr+CIrhRoLAIWaZGpeB/QO6xxEXbAcJc7vrEMQbQ4LAIVfuugyAGusYxB1iuIa2X3SEusYRJvDAkChJ6UTPwFwi3UOok5YDk3fYB2CqDNYACga2nJ/ArDaOgbRZlwlIyYutw5B1BksABQJMnLKYvAuAIXbEpQU89M/RQYLAEVHW+5KAKusYxBtkOrVMuxO/nxSZLAAUGTIyCmLocq7ABRGi9Gt5CbrEERdwQJA0eIWXw2+C0BhI3IVP/1T1LAAUKTIXn9bBL4LQOGyAk3urdYhiLqKBYCiJ5e9DkCrdQwiAIDiVhlVu9I6BlFXsQBQ5Mhed38EYIp1DiIAbRC90ToEUT5YACii5GpwpUCyplormdoF1jGI8sECQJEkmepXADxmnYMSLi3XWUcgyhcLAEWYXG2dgBLtUdmzpt46BFG+WAAosiRT/R8As6xzUFKxgFK0sQBQ1PEkTBYaOgooUWSxAFC0tfW4D8BH1jEocTjunyKPBYAiTUZOaAMw0ToHJcpqrNVa6xBEhWIBoOhLO7cByFnHoKTQGk78Q3HAAkCRJ6WT3odwSCAFRf5qnYDICywAFA+u8pksBUBfkEwNR55QLLAAUDxkdv0XoPOtY1DcCYsmxQYLAMWCyHgX4txhnYNibXnHqBOiWGABoPhIpW4HkLWOQTGlqJGRE5qsYxB5hQWAYkNKJ34C4AnrHBRTIjXWEYi8xAJA8SIcn00+ULyFsuqXrWMQeYkFgOKlteeDAFZZx6CYEdSIcPlpihcWAIqVjme0D1jnoJhRZ4p1BCKvsQBQ/DjgYwDykL4g5ZPesE5B5DUWAIqfPXeZCmCBdQyKCXFYKCmWWAAodkTGuwDuts5BsdCG1uw91iGI/MACQPGUAodskReelJFTFluHIPIDCwDFkuxZUw/gdescFHXCF0optlgAKL5U/2EdgSLNRTr1iHUIIr+wAFB8OakHrSNQpD3fMbskUSyxAFB8Dd/pJQg+to5BEcU7SBRzLAAUWx2jAR6yzkERpcqfHYo1FgCKOX6Ko7zMlRGT37QOQeQnFgCKt+4D/gPBSusYFDECvj9CsccCQLEmu97QApVHrXNQ1Li8c0SxxwJAScBPc9QVH2H45FnWIYj8xgJA8Se5xwHkrGNQZDzJpX8pCVgAKPakbPIyALOtc1BEqEy1jkAUBBYASgh90joBRYX8xzoBURBYACgZlAWAOkMbpXzSh9YpiILAAkDJUNT2PIA11jEo7BwWRUoMFgBKBCmtawXwrHUOCjl1+fyfEoMFgJJD9d/WESjUsmjGM9YhiILCAkDJkUrx9i5tyosyqpazRlJisABQcuw5aS4ALu9KG8Pb/5QoLACUGB2TuzxlnYPCSnj7nxKFBYCSRTDdOgKFUg7p5petQxAFiQWAEsZ9wToBhVKDlNattg5BFCQWAEqWJR/VA+CJntbHYkiJwwJAiSJfm5YFMNM6B4WMKgsAJQ4LACWQ8j0AWlcqzQJAicMCQAnk8GRPX7RQht/1tnUIoqCxAFDytGVfALjeO32Gt/8pmVgAKHFk5JTFAN6yzkEhIcICQInEAkBJxZM+deDQUEomFgBKqhetA1AouEi1zbIOQWSBBYCSSZw51hEoFN7mBECUVCwAlEyqDQBc6xhkrt46AJEVFgBKJMlUr4HiHescZEx1rnUEIissAJRcwk9/iedIg3UEIissAJRc7Y8BKMlUWAIpsVgAKLkc5ck/2VahrHq+dQgiKywAlFy5YhaAZGsQ4YyQlFwsAJRc5RPfA7DcOgaZ4SMgSjQWAEqsjk9/r1jnICPKFwAp2VgAKNmU7wEkVoovgVKysQBQwgnHgSdVNj3POgKRJRYASjaRN60jkIlFMmIi3/+gRGMBoGRLC5cFTiTlf3dKPBYASrY9dloAYK11DAocCwAlHgsAJZrIeBeCd61zUNDkbesERNZYAIhU+R5A0gjvABCxABDB4cUgaVyXpY8SjwWASPlpMHGK2vjfnBKPBYBI+GkwYZZJad1S6xBE1lgAiHIcEpYsnPuBCGABIAJG7PYBOBQwSVj4iMACQMShgEkjyiGARGABIPoMC0BSqLxnHYEoDNLWAYiCUlFRkZq/0h0GV7ZOOSpf/LNH/pvFln1lY5tSjDz3mrvFqCOOPuyLX3PVdVWcT4f0cV6rq6vLWWUjChLPeBR7Bx11VP+2tuILIfITKAZZ56FQWwjVW9dKrysbnqheYx2GyE8sABRr+x7xg4yoPAxgsHUWig4BXtEUvvnSow8ssM5C5BcWAIqtA771w6/ksngZwJbWWSiKZI6zyjnghRfqOEKEYokvAVJs5bJyC3jxp7xpudvbPds6BZFfWAAolkZ+4wd7AvpN6xwUdXoWeKeUYooFgGIpLTjKOgPFwjb7H/a9Pa1DEPmBBYDiyXWGWEegeMilUjtYZyDyAwsAxZRmrRNQPIjrtFlnIPIDCwDFkuvI69YZKB5S6SynDqZYYgGgWBJHHwTAT25UqMbpjz7IAkCxxAJAsdQxgctN1jko2kTkt9YZiPzCAkCxNSC19lcKPG2dg6JJBbe/+Pj991rnIPILCwDF1qOPPtrSo7XPkRC5CQBfCqTOahXgspf3LzvFOgiRnzjBBSXCfocfvSscHAfV/QBsCciXf/YdZ0sRh2sGxFku+4oCLV/+A1UACwUyHU6u9sXHHpwfdDSioLEAEHU4+MfjzoHKtdY5yD850V2m3zOBL/URgY8AiIiIEokFgIiIKIFYAIiIiBKIBYCIiCiBWACIiIgSiAWAiIgogVgAiIiIEogFgIiIKIFYAIiIiBKIBYCIiCiBOBUwJcb+R1TsklP3QKhu5Tj6pZ99KSo5yEkVfdsiGwUj29J0lai7ZP2vu64oHP0Emn725Sfq3rXIRhQ0FgCKvX0PP3qkiP4ZwCHWWSgCBI/nJHX2jMfqXreOQuQnFgCKtf0OP7pKRW8XoMQ6C0XKCkf1ey88+fenrYMQ+YUFgGJrn8N/dKgj7mMA0tZZKJKWuMiN+O8T//jAOgiRH/gSIMXS3nuPK3LEvRW8+FP+BjpIXWEdgsgvLAAUS6mBi44AsIt1Doo2BX584He/29s6B5EfWAAolhyRg60zUPQJUNLanB5hnYPIDywAFE+KgdYRKB4c0f7WGYj8wAJA8ST41DoCxYOjqUXWGYj8wAJAsSTQZ60zUCysbS1pmWMdgsgPLAAUS62Lt5iqwNvWOSjiFPfNfPjhJusYRH5gAaBYmjlzQpuonAXAtc5CUaUrVVKXWqcg8kvKOgCRXz5859U3B+88bDkgR4CTXlEXKNDiwDnupSfue8k6C5FfWAAo1ha8/dpLg3ce+l9A9gU4MoA6Q+bC0R+99PgDU62TEPmJn4ooEUaPHp1eUzToqyLuQY7K1ir6pcdfTio9HKn0/hb5KBhutvVuuO7K9b8uKq46+FhEnhncW56tq6vLWeQjChILAFGHg3887hyoXGudg/yTE91l+j0T+HIoEfgSIBERUSKxABARESUQCwAREVECsQAQERElEAsAERFRArEAEBERJRALABERUQKxABARESUQCwAREVECsQAQERElEKcCpkQ46Kij+re2dfsuoPs5IoM29D2SSu0KJ1UedDYKjra1PQro6g39mau6WFSmI9300EuPPvql9QKI4oYFgOJORh3xg1+q4iJA+liHoUhYAsgvX3ri/jutgxD5icsBU3yNH+/sVzxgCiDnAFJiHYcioweA72238+748O1Xn7YOQ+QXFgCKrf2KBlwMwZnWOSiaBPjq9jsPm/7h26+9Y52FyA98CZBiae/R3xkEwa+sc1CkCSCXW4cg8gsLAMVSUVHR99F+K5eoEPvu980fbm8dgsgPLAAUSyoy3DoDxYKIK8OsQxD5gQWA4qq7dQCKCeXPEsUTCwDFkkDft85AMSH6oXUEIj+wAFAsKdxHrTNQDAgWD+6TqreOQeQHFgCKpZeeeHAmBI9b56Cokxvq6upy1imI/MACQLGlmjoNwKfWOSiaVDA7W9x6tXUOIr+wAFBsvfxE3bvi6tcAvGadhSLnBU2lvjXz4YebrIMQ+YUFgGLtxX///dUBqbXlUP05gJcAuNaZKLRcBaYrcPIOfVMH//dfdZ9YByLyExcDokSpqKhIvbMyt6WTk40O7Srq02+848iYIHORv9xc20ltq1ZvdF5/N6Vrd+qTWsjn/ZQkLABE6znkx6f8UBX3W+cgz7S5zW0Dn3/ozlXWQYjChI8AiNbTrbjpcQAbXDOeIulJXvyJvowFgGg9T1RXr4Ho361zkDcUqLHOQBRGLABEG+C4Um2dgTyxpkdJ00PWIYjCiAWAaAOeLt1mKoAPrHNQoeS+J6qr11inIAojFgCiDRk/3hXRSdYxqDDqYKJ1BqKwYgEg2gjHwXUA+Okxul5+7u5bp1mHIAorFgCijZg2ZcJigdxhnYPyo8AV1hmIwowFgGgTnGz2KgCt1jmoixSNz+2xzSPWMYjCLGUdgCjM5r86a+WQPUcOBrC3dRbqPAHOeu/mP79inYMozHgHgGgzWpzcbwAst85BnaQy/Zm62+61jkEUdiwARJvx0pTbP4XgN9Y5qFNyksLPAah1EKKwYwEg6oSt3aU3QzHbOgdt1s3P3H0r/zsRdQILAFEn1NXV5VzHOR1cTji0FPg01drCOzVEncSXAIk66YPGGQuG7Ll3MSCHWGehL1FHncqnH7i93joIUVTwDgBRFzyz+7a/AfAf6xy0HsHVz9Tdwjn/ibqABYCoK8aPd7MqYwEsto5Cn5H/LnWXXmydgihqWACIuuiFuls/VMUYADnrLIQlKQfHNNbVcbImoi7iOwBEeXh/3sy3dthj5Cci+I51lgRbq6pHPXPvbQ3WQYiiiAWAKE/vz5s5c4fSvSGQ0dZZEiinKsc+V3fbE9ZBiKKKBYCoAO83znx6SOk+AwDsZ50lQVQU456tu22ydRCiKOM7AEQF2kqX/J8KuGpgMFQU5z5Tdxv/fRMVSKwDEMWEHHLMqZcq9FLrIDGWVcgpz9176wgWYhIAAAIFSURBVJ3WQYjigI8AiDzyXuOMaV/Zc+RSAEeA5dprTQL88Nl7ucgPkVdYAIg89H7jzJeH7DHydQiOBFBsnScmFqijRz17z4Rp1kGI4oSfUoh8cGDFaUMd5O6FSJl1lkhTTG1N5ypfmnL7p9ZRiOKGdwCIfPDBvBlLdtp3+ES0FfUBRwjkIyeQy58t3ebkD2+8ZrV1GKI44h0AIp8d9ONxFaK4DpBtrbNExFxx3FOfufuv062DEMUZ7wAQ+ez9xpnzhu21x225XFEKwChw+O3GNAnk90t16ZgZ91bPtw5DFHe8A0AUoAMqflaeEucGAAdZZwkRFch9cJzznrn75g+swxAlBQsAkYGDKsYdJJALIPi2dRZDLhT/gqOXPnvPhFnWYYiShgWAyNDBx5x6IKAXATgSyXk00ALI3a7KH56vu+V16zBEScUCQBQC+1ecul0R9Ecq+AmAjHUen8wURbWT1tppUyYstg5DlHQsAEQhc3DFqfvC0R9BcSiAckT3zkALIC+q4sk0nHum1d38lnUgIvofFgCiEDv0B2cMbC3KjRbo11WxNwRDAfSzzrURHyvwGkReSrn6n9XN+vzMhyc0WYciog1jASCKmAN+cOqWqXRud4Uz1IEOgEg/AL1VtLeo083fo7urAGc1xF3lqqxyRBYIcm80p7u9/lLtDSv9PTYReen/AbeNKKToOb16AAAAAElFTkSuQmCC";
5958
+
5959
+ // src/components/BindingBuilder/components/LightHelpers.tsx
5821
5960
  var import_jsx_runtime19 = require("react/jsx-runtime");
5822
5961
  var POSITION_MIN = -100;
5823
5962
  var POSITION_MAX = 100;
5824
5963
  var SUN_SCREEN_SIZE = 0.03;
5825
5964
  var BULB_SCREEN_SIZE = 0.03;
5826
5965
  var MIN_LINE_DISTANCE = 0.5;
5827
- var SUN_ICON_PATH = "/sun.png";
5828
- var BULB_ICON_PATH = "/bulb.png";
5966
+ var SUN_ICON_PATH = sun_default;
5967
+ var BULB_ICON_PATH = bulb_default;
5829
5968
  var RING_RADIUS = 0.4;
5830
5969
  var RING_SEGMENTS = 64;
5831
5970
  var RING_SCREEN_SIZE = SUN_SCREEN_SIZE * 3;
5832
5971
  var RING_COLOR = "#4d7dff";
5833
5972
  var LINE_COLOR2 = "#ffcc00";
5834
5973
  var LINE_GLOW_RADIUS = 0.02;
5835
- var UP_AXIS = new import_three19.Vector3(0, 1, 0);
5974
+ var UP_AXIS = new import_three20.Vector3(0, 1, 0);
5836
5975
  var AXIS_ARROW_LENGTH = 1.4;
5837
5976
  var AXIS_CONE_LENGTH = 0.35;
5838
5977
  var AXIS_CONE_RADIUS = 0.13;
@@ -5844,17 +5983,17 @@ var AXIS_COLORS = {
5844
5983
  z: "#4444ff"
5845
5984
  };
5846
5985
  function axisVector(axis) {
5847
- 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);
5848
5987
  }
5849
5988
  function computeAxisDragPlaneNormal(axis, eye) {
5850
5989
  const axisVec = axisVector(axis);
5851
5990
  if (eye.lengthSq() < 1e-8) {
5852
5991
  eye = axisVec.clone();
5853
5992
  }
5854
- 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));
5855
5994
  if (normal.lengthSq() < 1e-8) {
5856
- const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new import_three19.Vector3(1, 0, 0);
5857
- 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);
5858
5997
  }
5859
5998
  return normal.normalize();
5860
5999
  }
@@ -5926,18 +6065,18 @@ function LightHelpers({
5926
6065
  const shaftYRef = (0, import_react22.useRef)(null);
5927
6066
  const shaftZRef = (0, import_react22.useRef)(null);
5928
6067
  const axisDragRef = (0, import_react22.useRef)({ dragging: false, axis: null });
5929
- const axisDragPlaneRef = (0, import_react22.useRef)(new import_three19.Plane());
5930
- 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());
5931
6070
  const dragLineRef = (0, import_react22.useRef)(null);
5932
6071
  const dragLineGeomRef = (0, import_react22.useRef)(null);
5933
- const currentPosRef = (0, import_react22.useRef)(new import_three19.Vector3());
6072
+ const currentPosRef = (0, import_react22.useRef)(new import_three20.Vector3());
5934
6073
  const isDraggingRef = (0, import_react22.useRef)(false);
5935
- const dragPlaneRef = (0, import_react22.useRef)(new import_three19.Plane());
6074
+ const dragPlaneRef = (0, import_react22.useRef)(new import_three20.Plane());
5936
6075
  const onDraggingChangeRef = (0, import_react22.useRef)(onDraggingChange);
5937
6076
  (0, import_react22.useEffect)(() => {
5938
6077
  onDraggingChangeRef.current = onDraggingChange;
5939
6078
  }, [onDraggingChange]);
5940
- 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]);
5941
6080
  const linePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
5942
6081
  const dragLinePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
5943
6082
  const ringXPositions = (0, import_react22.useMemo)(() => makeRingPositions("x"), []);
@@ -5947,7 +6086,7 @@ function LightHelpers({
5947
6086
  const iconPath = isPointLight ? BULB_ICON_PATH : SUN_ICON_PATH;
5948
6087
  const iconScreenSize = isPointLight ? BULB_SCREEN_SIZE : SUN_SCREEN_SIZE;
5949
6088
  const sunTexture = (0, import_react22.useMemo)(() => {
5950
- const loader = new import_three19.TextureLoader();
6089
+ const loader = new import_three20.TextureLoader();
5951
6090
  return loader.load(iconPath);
5952
6091
  }, [iconPath]);
5953
6092
  const activeGizmoId = (0, import_react22.useSyncExternalStore)(subscribeActiveGizmo, getActiveGizmoSnapshot, getActiveGizmoSnapshot);
@@ -6054,7 +6193,7 @@ function LightHelpers({
6054
6193
  (0, import_react22.useEffect)(() => {
6055
6194
  const getMouseNdc = (event) => {
6056
6195
  const rect = gl.domElement.getBoundingClientRect();
6057
- return new import_three19.Vector2(
6196
+ return new import_three20.Vector2(
6058
6197
  (event.clientX - rect.left) / rect.width * 2 - 1,
6059
6198
  -((event.clientY - rect.top) / rect.height) * 2 + 1
6060
6199
  );
@@ -6096,12 +6235,12 @@ function LightHelpers({
6096
6235
  const axis = axisDragRef.current.axis;
6097
6236
  const currentPos = currentPosRef.current;
6098
6237
  const axisVec = axisVector(axis);
6099
- const eye = new import_three19.Vector3().subVectors(camera.position, currentPos);
6238
+ const eye = new import_three20.Vector3().subVectors(camera.position, currentPos);
6100
6239
  const planeNormal = computeAxisDragPlaneNormal(axis, eye);
6101
6240
  axisDragPlaneRef.current.setFromNormalAndCoplanarPoint(planeNormal, currentPos);
6102
6241
  const mouse2 = getMouseNdc(event);
6103
6242
  raycaster.setFromCamera(mouse2, camera);
6104
- const intersection2 = new import_three19.Vector3();
6243
+ const intersection2 = new import_three20.Vector3();
6105
6244
  const hitPoint2 = raycaster.ray.intersectPlane(axisDragPlaneRef.current, intersection2);
6106
6245
  if (!hitPoint2) return;
6107
6246
  const toPoint = hitPoint2.clone().sub(currentPos);
@@ -6120,7 +6259,7 @@ function LightHelpers({
6120
6259
  if (!isDraggingRef.current) return;
6121
6260
  const mouse = getMouseNdc(event);
6122
6261
  raycaster.setFromCamera(mouse, camera);
6123
- const intersection = new import_three19.Vector3();
6262
+ const intersection = new import_three20.Vector3();
6124
6263
  const hitPoint = raycaster.ray.intersectPlane(dragPlaneRef.current, intersection);
6125
6264
  if (!hitPoint) return;
6126
6265
  const newPos = [
@@ -6167,7 +6306,7 @@ function LightHelpers({
6167
6306
  setActiveTab?.("scene");
6168
6307
  setActiveSubTab?.("Lighting");
6169
6308
  }
6170
- const cameraDir = new import_three19.Vector3();
6309
+ const cameraDir = new import_three20.Vector3();
6171
6310
  camera.getWorldDirection(cameraDir);
6172
6311
  dragPlaneRef.current.setFromNormalAndCoplanarPoint(cameraDir, currentPosRef.current);
6173
6312
  isDraggingRef.current = true;
@@ -6230,7 +6369,7 @@ function LightHelpers({
6230
6369
  map: sunTexture,
6231
6370
  transparent: true,
6232
6371
  opacity: 0.9,
6233
- blending: import_three19.AdditiveBlending,
6372
+ blending: import_three20.AdditiveBlending,
6234
6373
  depthWrite: false
6235
6374
  },
6236
6375
  iconPath
@@ -6300,17 +6439,17 @@ function LightHelpers({
6300
6439
  ] }),
6301
6440
  gizmoVisible && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("group", { ref: axisGroupRef, children: axisArrows.map((axis) => {
6302
6441
  const axisColor = AXIS_COLORS[axis];
6303
- const end = new import_three19.Vector3(
6442
+ const end = new import_three20.Vector3(
6304
6443
  axis === "x" ? AXIS_ARROW_LENGTH : 0,
6305
6444
  axis === "y" ? AXIS_ARROW_LENGTH : 0,
6306
6445
  axis === "z" ? AXIS_ARROW_LENGTH : 0
6307
6446
  );
6308
- const conePos = new import_three19.Vector3(
6447
+ const conePos = new import_three20.Vector3(
6309
6448
  axis === "x" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
6310
6449
  axis === "y" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
6311
6450
  axis === "z" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0
6312
6451
  );
6313
- const shaftPos = new import_three19.Vector3(
6452
+ const shaftPos = new import_three20.Vector3(
6314
6453
  axis === "x" ? AXIS_ARROW_LENGTH / 2 : 0,
6315
6454
  axis === "y" ? AXIS_ARROW_LENGTH / 2 : 0,
6316
6455
  axis === "z" ? AXIS_ARROW_LENGTH / 2 : 0
@@ -6322,7 +6461,7 @@ function LightHelpers({
6322
6461
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
6323
6462
  import_drei6.Line,
6324
6463
  {
6325
- points: [new import_three19.Vector3(0, 0, 0), end],
6464
+ points: [new import_three20.Vector3(0, 0, 0), end],
6326
6465
  color: axisColor,
6327
6466
  transparent: true,
6328
6467
  opacity: 0.9,
@@ -6350,7 +6489,7 @@ function LightHelpers({
6350
6489
  color: LINE_COLOR2,
6351
6490
  transparent: true,
6352
6491
  opacity: 0.05,
6353
- blending: import_three19.AdditiveBlending,
6492
+ blending: import_three20.AdditiveBlending,
6354
6493
  depthWrite: false,
6355
6494
  depthTest: false
6356
6495
  }
@@ -6371,7 +6510,7 @@ function LightHelpers({
6371
6510
  color: LINE_COLOR2,
6372
6511
  transparent: true,
6373
6512
  opacity: 0.7,
6374
- blending: import_three19.AdditiveBlending
6513
+ blending: import_three20.AdditiveBlending
6375
6514
  }
6376
6515
  )
6377
6516
  ] })
@@ -6411,7 +6550,7 @@ function findInfluencingBone(hit) {
6411
6550
  if (!skinIndex || !skinWeight || !position) return null;
6412
6551
  const localHit = mesh.worldToLocal(hit.point.clone());
6413
6552
  const vertices = [hit.face.a, hit.face.b, hit.face.c];
6414
- const vertex = new import_three20.Vector3();
6553
+ const vertex = new import_three21.Vector3();
6415
6554
  let nearest = vertices[0];
6416
6555
  let nearestDist = Infinity;
6417
6556
  for (const vi of vertices) {
@@ -6461,9 +6600,9 @@ function GuidedTourCameraFollow({
6461
6600
  const scene = (0, import_fiber12.useThree)((state) => state.scene);
6462
6601
  const nodeRef = (0, import_react23.useRef)(null);
6463
6602
  const hasPrev = (0, import_react23.useRef)(false);
6464
- const live = (0, import_react23.useMemo)(() => new import_three20.Vector3(), []);
6465
- const prev = (0, import_react23.useMemo)(() => new import_three20.Vector3(), []);
6466
- 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(), []);
6467
6606
  const nodeName = annotation?.anchorNodeName;
6468
6607
  const localPosition = annotation?.anchorLocalPosition;
6469
6608
  (0, import_react23.useEffect)(() => {
@@ -6502,8 +6641,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
6502
6641
  isModelPlaced: false
6503
6642
  };
6504
6643
  function computeMeasureBox(scene) {
6505
- const box = new import_three20.Box3().setFromObject(scene);
6506
- const size = new import_three20.Vector3();
6644
+ const box = new import_three21.Box3().setFromObject(scene);
6645
+ const size = new import_three21.Vector3();
6507
6646
  box.getSize(size);
6508
6647
  return {
6509
6648
  min: [box.min.x, box.min.y, box.min.z],
@@ -6522,18 +6661,18 @@ function computeUvMeshStats(scene) {
6522
6661
  return stats;
6523
6662
  }
6524
6663
  function computeModelBounds(scene) {
6525
- const box = new import_three20.Box3().setFromObject(scene);
6664
+ const box = new import_three21.Box3().setFromObject(scene);
6526
6665
  if (box.isEmpty()) return null;
6527
- const center = box.getCenter(new import_three20.Vector3());
6528
- 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;
6529
6668
  if (!Number.isFinite(radius) || radius <= 0) return null;
6530
6669
  return { center: [center.x, center.y, center.z], radius };
6531
6670
  }
6532
6671
  function fitClippingPlanesToScene(scene, controls) {
6533
6672
  const camera = controls?.camera;
6534
- if (!(camera instanceof import_three20.PerspectiveCamera)) return;
6535
- const box = new import_three20.Box3().setFromObject(scene);
6536
- 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();
6537
6676
  if (!Number.isFinite(size) || size <= 0) return;
6538
6677
  camera.near = size / 100;
6539
6678
  camera.far = size * 100;
@@ -6611,7 +6750,8 @@ function ModelViewer({
6611
6750
  showAnnotationNavigation = true,
6612
6751
  showAnnotationOnHover = false
6613
6752
  }) {
6614
- const { activeTab, activeSubTab } = useBindingBuilderContext();
6753
+ const { isInsideEditor, activeTab, activeSubTab } = useBindingBuilderContext();
6754
+ const showLightHelpers = isInsideEditor && activeTab === "scene" && activeSubTab === "Lighting";
6615
6755
  const license = useLicense(licenseKey);
6616
6756
  const resolvedSceneConfig = (0, import_react23.useMemo)(
6617
6757
  () => sceneConfig ?? DEFAULT_SCENE_CONFIG,
@@ -6717,8 +6857,8 @@ function ModelViewer({
6717
6857
  if (!controls || initialCameraStateRef.current) {
6718
6858
  return;
6719
6859
  }
6720
- const position = controls.getPosition(new import_three20.Vector3());
6721
- 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());
6722
6862
  initialCameraStateRef.current = {
6723
6863
  position: [position.x, position.y, position.z],
6724
6864
  target: [target.x, target.y, target.z]
@@ -6976,7 +7116,7 @@ function ModelViewer({
6976
7116
  const controls = controlsRef.current;
6977
7117
  const scene = sceneRef.current;
6978
7118
  if (!controls) return;
6979
- const target = new import_three20.Vector3();
7119
+ const target = new import_three21.Vector3();
6980
7120
  const livePosition = annotationWorldPositionsRef.current.get(
6981
7121
  annotation.id
6982
7122
  );
@@ -6994,15 +7134,15 @@ function ModelViewer({
6994
7134
  controls.smoothTime = 0.6;
6995
7135
  const storedNormal = annotation.worldNormal;
6996
7136
  if (scene) {
6997
- const box = new import_three20.Box3().setFromObject(scene);
6998
- const modelCenter = new import_three20.Vector3();
7137
+ const box = new import_three21.Box3().setFromObject(scene);
7138
+ const modelCenter = new import_three21.Vector3();
6999
7139
  box.getCenter(modelCenter);
7000
- const modelSize = new import_three20.Vector3();
7140
+ const modelSize = new import_three21.Vector3();
7001
7141
  box.getSize(modelSize);
7002
7142
  const modelRadius = modelSize.length() * 0.2;
7003
7143
  let outwardDir;
7004
7144
  if (storedNormal) {
7005
- outwardDir = new import_three20.Vector3(
7145
+ outwardDir = new import_three21.Vector3(
7006
7146
  storedNormal[0],
7007
7147
  storedNormal[1],
7008
7148
  storedNormal[2]
@@ -7153,6 +7293,16 @@ function ModelViewer({
7153
7293
  const isControlledBindings = onObjectBindingsChange !== void 0;
7154
7294
  const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
7155
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
+ );
7156
7306
  const currentHiddenObjects = (0, import_react23.useMemo)(
7157
7307
  () => Object.fromEntries(
7158
7308
  Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
@@ -7292,7 +7442,7 @@ function ModelViewer({
7292
7442
  const fallbackY = Math.max(minOffset, (rect.height - height) / 2);
7293
7443
  const objectKey = resolveObjectKey(objectId, binding);
7294
7444
  const object = objectKey ? nodeRefsMap.current[objectKey] : null;
7295
- const clamp2 = (value, max) => {
7445
+ const clamp3 = (value, max) => {
7296
7446
  if (max <= minOffset) {
7297
7447
  return minOffset;
7298
7448
  }
@@ -7300,23 +7450,23 @@ function ModelViewer({
7300
7450
  };
7301
7451
  if (canvasContainer && cameraControls && object) {
7302
7452
  const canvasRect = canvasContainer.getBoundingClientRect();
7303
- const objectCenter = new import_three20.Vector3();
7304
- new import_three20.Box3().setFromObject(object).getCenter(objectCenter);
7453
+ const objectCenter = new import_three21.Vector3();
7454
+ new import_three21.Box3().setFromObject(object).getCenter(objectCenter);
7305
7455
  objectCenter.project(cameraControls);
7306
7456
  const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
7307
7457
  const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
7308
7458
  return {
7309
7459
  objectId,
7310
- x: clamp2(projectedX + 24, maxX),
7311
- y: clamp2(projectedY - height / 2, maxY)
7460
+ x: clamp3(projectedX + 24, maxX),
7461
+ y: clamp3(projectedY - height / 2, maxY)
7312
7462
  };
7313
7463
  }
7314
7464
  const rawX = typeof fallbackCoords?.screenX === "number" ? fallbackCoords.screenX - rect.left : fallbackX;
7315
7465
  const rawY = typeof fallbackCoords?.screenY === "number" ? fallbackCoords.screenY - rect.top : fallbackY;
7316
7466
  return {
7317
7467
  objectId,
7318
- x: clamp2(rawX, maxX),
7319
- y: clamp2(rawY, maxY)
7468
+ x: clamp3(rawX, maxX),
7469
+ y: clamp3(rawY, maxY)
7320
7470
  };
7321
7471
  },
7322
7472
  [resolveObjectKey]
@@ -7325,7 +7475,6 @@ function ModelViewer({
7325
7475
  (event) => {
7326
7476
  const { objectId, action, binding } = event;
7327
7477
  setCurrentAction(event);
7328
- onAction?.(event);
7329
7478
  switch (action.id) {
7330
7479
  case "toggle-visibility": {
7331
7480
  let targetKey = objectId;
@@ -7373,6 +7522,7 @@ function ModelViewer({
7373
7522
  default:
7374
7523
  break;
7375
7524
  }
7525
+ onAction?.(event);
7376
7526
  },
7377
7527
  [
7378
7528
  applyObjectBindings,
@@ -7513,7 +7663,8 @@ function ModelViewer({
7513
7663
  objectBindings: currentObjectBindingsRef.current,
7514
7664
  nodeRefs: nodeRefsMap.current,
7515
7665
  homeCameraState: initialCameraStateRef.current,
7516
- captureImage: (options) => captureImage(rendererCaptureRef.current, options)
7666
+ captureImage: (options) => captureImage(rendererCaptureRef.current, options),
7667
+ invalidate: () => rendererCaptureRef.current?.invalidate()
7517
7668
  });
7518
7669
  }, []);
7519
7670
  const fitScene = (0, import_react23.useCallback)(
@@ -7525,14 +7676,14 @@ function ModelViewer({
7525
7676
  }
7526
7677
  try {
7527
7678
  if (flyIn) {
7528
- const target = controls.getTarget(new import_three20.Vector3());
7529
- 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());
7530
7681
  const direction = position.clone().sub(target);
7531
7682
  if (direction.lengthSq() < 1e-6) {
7532
7683
  direction.set(0, 0, 1);
7533
7684
  }
7534
7685
  direction.normalize();
7535
- 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());
7536
7687
  const farDistance = Math.max(sphere.radius * 10, 20);
7537
7688
  const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
7538
7689
  await controls.setLookAt(
@@ -7844,6 +7995,13 @@ function ModelViewer({
7844
7995
  );
7845
7996
  setBindingsMismatch(!hasAnyMatch);
7846
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]);
7847
8005
  const clearMeasurements = (0, import_react23.useCallback)(() => setMeasurePoints([]), []);
7848
8006
  const handleMeasurePointerDown = (0, import_react23.useCallback)(
7849
8007
  (event) => {
@@ -7869,8 +8027,8 @@ function ModelViewer({
7869
8027
  const rect = canvas.getBoundingClientRect();
7870
8028
  const ndcX = (event.clientX - rect.left) / rect.width * 2 - 1;
7871
8029
  const ndcY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
7872
- const raycaster = new import_three20.Raycaster();
7873
- 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);
7874
8032
  const hits = raycaster.intersectObject(scene, true);
7875
8033
  if (hits.length === 0) return;
7876
8034
  const { point } = hits[0];
@@ -7887,7 +8045,11 @@ function ModelViewer({
7887
8045
  );
7888
8046
  (0, import_react23.useEffect)(() => {
7889
8047
  setMeasurePoints([]);
7890
- }, [modelUrl, resolvedSceneConfig.model.rotation]);
8048
+ }, [
8049
+ modelUrl,
8050
+ resolvedSceneConfig.model.rotation,
8051
+ currentBindingTransformSignature
8052
+ ]);
7891
8053
  const handleDownloadMesh = (0, import_react23.useCallback)(() => {
7892
8054
  const matRefs = materialRefsOut.current;
7893
8055
  const scene = sceneRef.current;
@@ -7991,8 +8153,8 @@ function ModelViewer({
7991
8153
  const rect = canvas.getBoundingClientRect();
7992
8154
  const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
7993
8155
  const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1;
7994
- const raycaster = new import_three20.Raycaster();
7995
- 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);
7996
8158
  const hits = raycaster.intersectObject(scene, true);
7997
8159
  if (hits.length === 0) return;
7998
8160
  const hit = hits[0];
@@ -8230,33 +8392,28 @@ function ModelViewer({
8230
8392
  shadowsEnabled: shadows,
8231
8393
  environmentRotation: resolvedSceneConfig.environment.rotation,
8232
8394
  environmentConfig: resolvedSceneConfig.environment,
8233
- modelBounds
8395
+ modelBounds,
8396
+ objectBindings: currentObjectBindings,
8397
+ nodeRefs: nodeRefsMap
8234
8398
  }
8235
8399
  ),
8236
- (() => {
8237
- const currentConfig = sceneConfig || DEFAULT_SCENE_CONFIG;
8238
- const activeLights = currentConfig.lighting.lights.filter(
8239
- (light) => light.visible === true
8400
+ showLightHelpers && resolvedSceneConfig.lighting.lights.map((light) => {
8401
+ if (!light.visible || light.type === "none" || light.type === "hemisphere" || light.attachment) {
8402
+ return null;
8403
+ }
8404
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8405
+ LightHelpers,
8406
+ {
8407
+ light,
8408
+ position: light.position,
8409
+ color: light.color,
8410
+ intensity: light.intensity,
8411
+ visible: light.visible,
8412
+ onPositionChange: (newPosition) => handleLightPositionChange(light.id, newPosition)
8413
+ },
8414
+ light.id
8240
8415
  );
8241
- return activeLights.map((light) => {
8242
- if (light.type === "none" || light.type === "hemisphere") {
8243
- return null;
8244
- }
8245
- const uniqueKey = light.id;
8246
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8247
- LightHelpers,
8248
- {
8249
- light,
8250
- position: light.position,
8251
- color: light.color,
8252
- intensity: light.intensity,
8253
- visible: light.visible,
8254
- onPositionChange: (newPos) => handleLightPositionChange(light.id, newPos)
8255
- },
8256
- uniqueKey
8257
- );
8258
- });
8259
- })()
8416
+ })
8260
8417
  ] }),
8261
8418
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
8262
8419
  Controls,
@@ -8883,7 +9040,7 @@ function ModelViewer({
8883
9040
  }
8884
9041
 
8885
9042
  // src/components/BindingBuilder/BindingBuilder.tsx
8886
- var import_react41 = require("react");
9043
+ var import_react42 = require("react");
8887
9044
  var import_lucide_react16 = require("lucide-react");
8888
9045
 
8889
9046
  // src/styles/BindingBuilder.css
@@ -8891,32 +9048,13 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
8891
9048
 
8892
9049
  // src/hooks/useViewerCamera.ts
8893
9050
  var import_react24 = require("react");
8894
- var import_three21 = require("three");
8895
-
8896
- // src/utils/resolveBindingKey.ts
8897
- function resolveBindingKey(objectBindings, objectId) {
8898
- if (objectBindings[objectId]) {
8899
- return objectId;
8900
- }
8901
- const idMatch = Object.entries(objectBindings).find(
8902
- ([, binding]) => binding.id === objectId
8903
- )?.[0];
8904
- if (idMatch) {
8905
- return idMatch;
8906
- }
8907
- const modelObjectMatch = Object.entries(objectBindings).find(
8908
- ([, binding]) => binding.modelObjectId === objectId
8909
- )?.[0];
8910
- return modelObjectMatch ?? null;
8911
- }
8912
-
8913
- // src/hooks/useViewerCamera.ts
9051
+ var import_three22 = require("three");
8914
9052
  function toTuple(vector) {
8915
9053
  return [vector.x, vector.y, vector.z];
8916
9054
  }
8917
9055
  function buildCameraState(camera, controls) {
8918
- const position = new import_three21.Vector3();
8919
- const target = new import_three21.Vector3();
9056
+ const position = new import_three22.Vector3();
9057
+ const target = new import_three22.Vector3();
8920
9058
  controls.getPosition(position);
8921
9059
  controls.getTarget(target);
8922
9060
  return {
@@ -8943,11 +9081,16 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
8943
9081
  controls.smoothTime = originalSmoothTime;
8944
9082
  }
8945
9083
  }
8946
- function useViewerCamera() {
9084
+ function useViewerCamera(options = {}) {
9085
+ const optionsRef = (0, import_react24.useRef)(options);
9086
+ optionsRef.current = options;
8947
9087
  const viewerRef = (0, import_react24.useRef)(null);
8948
9088
  const [cameraState, setCameraStateValue] = (0, import_react24.useState)(
8949
9089
  null
8950
9090
  );
9091
+ const [cameraPresets, setCameraPresets] = (0, import_react24.useState)(() => ({ ...options.presets }));
9092
+ const cameraPresetsRef = (0, import_react24.useRef)(cameraPresets);
9093
+ cameraPresetsRef.current = cameraPresets;
8951
9094
  const updateCameraState = (0, import_react24.useCallback)((nextState) => {
8952
9095
  setCameraStateValue(
8953
9096
  (previousState) => isSameCameraState(previousState, nextState) ? previousState : nextState
@@ -8961,16 +9104,56 @@ function useViewerCamera() {
8961
9104
  );
8962
9105
  const homeCameraStateRef = (0, import_react24.useRef)(null);
8963
9106
  const homeSceneRef = (0, import_react24.useRef)(null);
9107
+ const initializedControlsRef = (0, import_react24.useRef)(null);
8964
9108
  const handleViewerReady = (0, import_react24.useCallback)((viewer) => {
8965
9109
  viewerRef.current = viewer;
8966
- updateCameraState(buildCameraState(viewer.controls.camera, viewer.controls));
9110
+ const currentState = buildCameraState(
9111
+ viewer.controls.camera,
9112
+ viewer.controls
9113
+ );
9114
+ updateCameraState(currentState);
8967
9115
  if (viewer.scene !== homeSceneRef.current) {
8968
9116
  homeSceneRef.current = viewer.scene;
8969
9117
  homeCameraStateRef.current = null;
9118
+ initializedControlsRef.current = null;
8970
9119
  }
8971
9120
  if (viewer.homeCameraState && !homeCameraStateRef.current) {
8972
9121
  homeCameraStateRef.current = viewer.homeCameraState;
8973
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
+ }
8974
9157
  }, [updateCameraState]);
8975
9158
  const resetView = (0, import_react24.useCallback)(async () => {
8976
9159
  const viewer = viewerRef.current;
@@ -9066,27 +9249,33 @@ function useViewerCamera() {
9066
9249
  return false;
9067
9250
  }
9068
9251
  try {
9252
+ if (!viewerRef.current?.homeCameraState) {
9253
+ markInitialAutoFitOverride(controls);
9254
+ }
9069
9255
  await controls.setTarget(
9070
9256
  target[0],
9071
9257
  target[1],
9072
9258
  target[2],
9073
9259
  enableTransition
9074
9260
  );
9261
+ updateCameraState(buildCameraState(controls.camera, controls));
9075
9262
  return true;
9076
9263
  } catch {
9077
9264
  return false;
9078
9265
  }
9079
9266
  },
9080
- []
9267
+ [updateCameraState]
9081
9268
  );
9082
- const setCameraState = (0, import_react24.useCallback)(
9269
+ const applyCameraPreset = (0, import_react24.useCallback)(
9083
9270
  async (state, enableTransition = true) => {
9084
9271
  const controls = viewerRef.current?.controls;
9085
9272
  if (!controls) {
9086
9273
  return false;
9087
9274
  }
9088
9275
  try {
9089
- markInitialAutoFitOverride(controls);
9276
+ if (!viewerRef.current?.homeCameraState) {
9277
+ markInitialAutoFitOverride(controls);
9278
+ }
9090
9279
  const transitions = [
9091
9280
  controls.setLookAt(
9092
9281
  state.position[0],
@@ -9110,11 +9299,11 @@ function useViewerCamera() {
9110
9299
  await Promise.all(transitions);
9111
9300
  const camera = controls.camera;
9112
9301
  let projectionChanged = false;
9113
- if (state.fov !== null && "fov" in camera && typeof camera.fov === "number") {
9302
+ if (state.fov != null && "fov" in camera && typeof camera.fov === "number") {
9114
9303
  camera.fov = state.fov;
9115
9304
  projectionChanged = true;
9116
9305
  }
9117
- if ("zoom" in camera && typeof camera.zoom === "number") {
9306
+ if (state.zoom != null && "zoom" in camera && typeof camera.zoom === "number") {
9118
9307
  camera.zoom = state.zoom;
9119
9308
  projectionChanged = true;
9120
9309
  }
@@ -9129,13 +9318,94 @@ function useViewerCamera() {
9129
9318
  },
9130
9319
  [updateCameraState]
9131
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
+ );
9132
9396
  return {
9133
9397
  cameraState,
9398
+ cameraPresets,
9134
9399
  resetView,
9135
9400
  focusObject,
9136
9401
  fitScene,
9137
9402
  setCameraTarget,
9138
9403
  setCameraState,
9404
+ lookAt,
9405
+ orbitTo,
9406
+ dollyTo,
9407
+ savePreset,
9408
+ goToPreset,
9139
9409
  handleCameraChange,
9140
9410
  handleViewerReady
9141
9411
  };
@@ -14992,6 +15262,9 @@ function IdentityPanel({
14992
15262
  ] }) });
14993
15263
  }
14994
15264
 
15265
+ // src/components/BindingBuilder/components/BasicsPanel.tsx
15266
+ var import_react38 = require("react");
15267
+
14995
15268
  // src/components/BindingBuilder/utils/constants.ts
14996
15269
  var typeSuggestions = [
14997
15270
  "other",
@@ -15031,6 +15304,31 @@ function BasicsPanel({
15031
15304
  onToggle,
15032
15305
  updateBinding
15033
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
+ };
15034
15332
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(BBSection, { title: "Basics", open, onToggle, children: [
15035
15333
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-grid", children: [
15036
15334
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
@@ -15077,6 +15375,38 @@ function BasicsPanel({
15077
15375
  children: statusOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: option, children: option }, option))
15078
15376
  }
15079
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
+ )
15080
15410
  ] })
15081
15411
  ] }),
15082
15412
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
@@ -15127,7 +15457,7 @@ function BasicsPanel({
15127
15457
  }
15128
15458
 
15129
15459
  // src/components/BindingBuilder/components/ActionsPanel.tsx
15130
- var import_react38 = require("react");
15460
+ var import_react39 = require("react");
15131
15461
 
15132
15462
  // src/components/BindingBuilder/utils/actionCatalog.ts
15133
15463
  var ACTION_CATALOG = [
@@ -15153,19 +15483,19 @@ var ACTION_CATALOG = [
15153
15483
  id: "toggle-light",
15154
15484
  label: "Toggle Light",
15155
15485
  handling: "host",
15156
- 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."
15157
15487
  },
15158
15488
  {
15159
15489
  id: "change-light-color",
15160
15490
  label: "Change Light Color",
15161
15491
  handling: "host",
15162
- 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."
15163
15493
  },
15164
15494
  {
15165
15495
  id: "set-brightness",
15166
15496
  label: "Set Brightness",
15167
15497
  handling: "host",
15168
- 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."
15169
15499
  }
15170
15500
  ];
15171
15501
  var CUSTOM_ACTION_ID = "__custom__";
@@ -15226,8 +15556,8 @@ function ActionsPanel({
15226
15556
  onToggle,
15227
15557
  updateBinding
15228
15558
  }) {
15229
- const [customRows, setCustomRows] = (0, import_react38.useState)(/* @__PURE__ */ new Set());
15230
- (0, import_react38.useEffect)(() => {
15559
+ const [customRows, setCustomRows] = (0, import_react39.useState)(/* @__PURE__ */ new Set());
15560
+ (0, import_react39.useEffect)(() => {
15231
15561
  setCustomRows(/* @__PURE__ */ new Set());
15232
15562
  }, [selectedItem]);
15233
15563
  const actions = selectedBinding.actions ?? [];
@@ -15343,8 +15673,8 @@ function ActionsPanel({
15343
15673
  }
15344
15674
  )
15345
15675
  ] }),
15346
- 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: [
15347
- "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.",
15348
15678
  suggestion ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
15349
15679
  " Did you mean ",
15350
15680
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
@@ -15367,7 +15697,13 @@ function ActionsPanel({
15367
15697
  "?"
15368
15698
  ] }) : null
15369
15699
  ] }),
15370
- 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
15371
15707
  ] }),
15372
15708
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
15373
15709
  "button",
@@ -15516,7 +15852,7 @@ function CameraPositionPanel({
15516
15852
  }
15517
15853
 
15518
15854
  // src/components/BindingBuilder/components/SceneNodesPanel.tsx
15519
- var import_react39 = require("react");
15855
+ var import_react40 = require("react");
15520
15856
 
15521
15857
  // src/components/BindingBuilder/utils/bindingFactory.ts
15522
15858
  function slugify2(value) {
@@ -15605,9 +15941,9 @@ function SceneNodesPanel({
15605
15941
  isOpen = true,
15606
15942
  onToggle
15607
15943
  }) {
15608
- const [search, setSearch] = (0, import_react39.useState)("");
15609
- const nodeKeys = (0, import_react39.useMemo)(() => Object.keys(bindings), [bindings]);
15610
- 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)(() => {
15611
15947
  const query = search.trim().toLowerCase();
15612
15948
  if (!query) return nodeKeys;
15613
15949
  return nodeKeys.filter((nodeKey) => {
@@ -15750,7 +16086,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
15750
16086
  }
15751
16087
 
15752
16088
  // src/components/BindingBuilder/hooks/useUndoableState.ts
15753
- var import_react40 = require("react");
16089
+ var import_react41 = require("react");
15754
16090
  var MAX_HISTORY = 100;
15755
16091
  function historyReducer(state, action) {
15756
16092
  switch (action.type) {
@@ -15789,13 +16125,13 @@ function historyReducer(state, action) {
15789
16125
  }
15790
16126
  }
15791
16127
  function useUndoableState(initial, coalesceWindowMs = 600) {
15792
- const [state, dispatch] = (0, import_react40.useReducer)(historyReducer, {
16128
+ const [state, dispatch] = (0, import_react41.useReducer)(historyReducer, {
15793
16129
  past: [],
15794
16130
  present: initial,
15795
16131
  future: []
15796
16132
  });
15797
- const lastSetAt = (0, import_react40.useRef)(0);
15798
- const setValue = (0, import_react40.useCallback)(
16133
+ const lastSetAt = (0, import_react41.useRef)(0);
16134
+ const setValue = (0, import_react41.useCallback)(
15799
16135
  (updater) => {
15800
16136
  const now = Date.now();
15801
16137
  const coalesce = now - lastSetAt.current < coalesceWindowMs;
@@ -15804,15 +16140,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
15804
16140
  },
15805
16141
  [coalesceWindowMs]
15806
16142
  );
15807
- const undo = (0, import_react40.useCallback)(() => {
16143
+ const undo = (0, import_react41.useCallback)(() => {
15808
16144
  lastSetAt.current = 0;
15809
16145
  dispatch({ type: "undo" });
15810
16146
  }, []);
15811
- const redo = (0, import_react40.useCallback)(() => {
16147
+ const redo = (0, import_react41.useCallback)(() => {
15812
16148
  lastSetAt.current = 0;
15813
16149
  dispatch({ type: "redo" });
15814
16150
  }, []);
15815
- const reset = (0, import_react40.useCallback)((value) => {
16151
+ const reset = (0, import_react41.useCallback)((value) => {
15816
16152
  lastSetAt.current = 0;
15817
16153
  dispatch({ type: "reset", value });
15818
16154
  }, []);
@@ -15836,9 +16172,9 @@ function toBindingsJson(bindings) {
15836
16172
  }
15837
16173
  function toBindingsTypeScript(bindings) {
15838
16174
  return [
15839
- 'import type { ObjectBinding } from "@liveroom-tech/react-immersive";',
16175
+ 'import { defineObjectBindings } from "@liveroom-tech/react-immersive";',
15840
16176
  "",
15841
- `export const objectBindings: Record<string, ObjectBinding> = ${JSON.stringify(bindings, null, 2)};`,
16177
+ `export const objectBindings = defineObjectBindings(${JSON.stringify(bindings, null, 2)});`,
15842
16178
  ""
15843
16179
  ].join("\n");
15844
16180
  }
@@ -16058,30 +16394,30 @@ function mergeSceneConfigSection(current, incoming) {
16058
16394
  return next;
16059
16395
  }
16060
16396
  function BindingBuilder({ licenseKey }) {
16061
- const modelInputRef = (0, import_react41.useRef)(null);
16062
- const bindingsInputRef = (0, import_react41.useRef)(null);
16063
- const sceneConfigInputRef = (0, import_react41.useRef)(null);
16064
- const [modelUrl, setModelUrl] = (0, import_react41.useState)(null);
16065
- const [modelName, setModelName] = (0, import_react41.useState)("");
16066
- 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);
16067
16403
  const bindingsHistory = useUndoableState({});
16068
16404
  const bindings = bindingsHistory.value;
16069
16405
  const setBindings = bindingsHistory.setValue;
16070
- const [loading, setLoading] = (0, import_react41.useState)(false);
16071
- const [isExporting, setIsExporting] = (0, import_react41.useState)(false);
16072
- 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)(
16073
16409
  "Load the demo model or upload a GLB/GLTF model to generate bindings."
16074
16410
  );
16075
- const [statusTone, setStatusTone] = (0, import_react41.useState)("default");
16076
- const [statusBannerVisible, setStatusBannerVisible] = (0, import_react41.useState)(false);
16077
- const [metricsDraft, setMetricsDraft] = (0, import_react41.useState)("{}");
16078
- const [metadataDraft, setMetadataDraft] = (0, import_react41.useState)("{}");
16079
- const [metricsError, setMetricsError] = (0, import_react41.useState)(null);
16080
- const [metadataError, setMetadataError] = (0, import_react41.useState)(null);
16081
- const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react41.useState)(false);
16082
- const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react41.useState)(false);
16083
- const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react41.useState)(false);
16084
- 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)({
16085
16421
  identity: true,
16086
16422
  basics: true,
16087
16423
  material: false,
@@ -16090,7 +16426,7 @@ function BindingBuilder({ licenseKey }) {
16090
16426
  metrics: false,
16091
16427
  metadata: false
16092
16428
  });
16093
- const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react41.useState)({
16429
+ const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react42.useState)({
16094
16430
  pbr: true,
16095
16431
  roughness: false,
16096
16432
  emission: false,
@@ -16106,27 +16442,27 @@ function BindingBuilder({ licenseKey }) {
16106
16442
  });
16107
16443
  const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
16108
16444
  const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
16109
- const [activeEditorTab, setActiveEditorTab] = (0, import_react41.useState)(
16445
+ const [activeEditorTab, setActiveEditorTab] = (0, import_react42.useState)(
16110
16446
  "object"
16111
16447
  );
16112
- const [activeSubTab, setActiveSubTab] = (0, import_react41.useState)("General");
16113
- const [pbrWorkflow, setPbrWorkflow] = (0, import_react41.useState)("metalness");
16114
- const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react41.useState)(createMaterialPanelEnabledState());
16115
- const materialPanelItemRef = (0, import_react41.useRef)(null);
16116
- const [zoomOnSelected, setZoomOnSelected] = (0, import_react41.useState)(false);
16117
- const [highlightOnHover, setHighlightOnHover] = (0, import_react41.useState)(true);
16118
- const [resetDialogOpen, setResetDialogOpen] = (0, import_react41.useState)(false);
16119
- 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)(
16120
16456
  null
16121
16457
  );
16122
16458
  const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
16123
16459
  const sceneConfig = sceneConfigHistory.value;
16124
16460
  const setSceneConfig = sceneConfigHistory.setValue;
16125
- const [activeAnnotation, setActiveAnnotation] = (0, import_react41.useState)(null);
16126
- 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);
16127
16463
  const license = useLicense(licenseKey);
16128
16464
  const tier = normalizeTier(license.tier);
16129
- const bindingBuilderContextValue = (0, import_react41.useMemo)(
16465
+ const bindingBuilderContextValue = (0, import_react42.useMemo)(
16130
16466
  () => ({
16131
16467
  isInsideEditor: true,
16132
16468
  activeTab: activeEditorTab,
@@ -16137,11 +16473,11 @@ function BindingBuilder({ licenseKey }) {
16137
16473
  }),
16138
16474
  [activeEditorTab, activeSubTab, tier]
16139
16475
  );
16140
- const uploadedModelRef = (0, import_react41.useRef)(null);
16141
- const previewDragDepthRef = (0, import_react41.useRef)(0);
16142
- const loadRequestRef = (0, import_react41.useRef)(0);
16143
- const pendingLoadRef = (0, import_react41.useRef)(null);
16144
- 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);
16145
16481
  const {
16146
16482
  cameraState,
16147
16483
  resetView,
@@ -16151,7 +16487,7 @@ function BindingBuilder({ licenseKey }) {
16151
16487
  handleCameraChange,
16152
16488
  handleViewerReady
16153
16489
  } = useViewerCamera();
16154
- const handlePreviewWaypoint = (0, import_react41.useCallback)(
16490
+ const handlePreviewWaypoint = (0, import_react42.useCallback)(
16155
16491
  (waypoint) => {
16156
16492
  setCameraState(
16157
16493
  {
@@ -16175,7 +16511,7 @@ function BindingBuilder({ licenseKey }) {
16175
16511
  position: formatVector3(cameraState.position),
16176
16512
  target: formatVector3(cameraState.target)
16177
16513
  } : null;
16178
- const handleAnnotationsChange = (0, import_react41.useCallback)(
16514
+ const handleAnnotationsChange = (0, import_react42.useCallback)(
16179
16515
  (newAnnotations) => {
16180
16516
  setSceneConfig((prev) => {
16181
16517
  const currentIds = prev.annotations.map((a) => a.id).join(",");
@@ -16191,7 +16527,7 @@ function BindingBuilder({ licenseKey }) {
16191
16527
  },
16192
16528
  []
16193
16529
  );
16194
- const handleActiveAnnotationChange = (0, import_react41.useCallback)(
16530
+ const handleActiveAnnotationChange = (0, import_react42.useCallback)(
16195
16531
  (annotation) => {
16196
16532
  setActiveAnnotation(
16197
16533
  annotation ? {
@@ -16201,7 +16537,7 @@ function BindingBuilder({ licenseKey }) {
16201
16537
  },
16202
16538
  []
16203
16539
  );
16204
- (0, import_react41.useEffect)(() => {
16540
+ (0, import_react42.useEffect)(() => {
16205
16541
  if (!selectedBinding) {
16206
16542
  setMetricsDraft("{}");
16207
16543
  setMetadataDraft("{}");
@@ -16214,7 +16550,7 @@ function BindingBuilder({ licenseKey }) {
16214
16550
  setMetricsError(null);
16215
16551
  setMetadataError(null);
16216
16552
  }, [selectedBinding]);
16217
- (0, import_react41.useEffect)(() => {
16553
+ (0, import_react42.useEffect)(() => {
16218
16554
  const derived = createMaterialPanelEnabledState(selectedMaterial);
16219
16555
  if (materialPanelItemRef.current !== selectedItem) {
16220
16556
  materialPanelItemRef.current = selectedItem;
@@ -16233,12 +16569,12 @@ function BindingBuilder({ licenseKey }) {
16233
16569
  return changed ? next : prev;
16234
16570
  });
16235
16571
  }, [selectedItem, selectedMaterial]);
16236
- (0, import_react41.useEffect)(() => {
16572
+ (0, import_react42.useEffect)(() => {
16237
16573
  return () => {
16238
16574
  uploadedModelRef.current?.cleanup();
16239
16575
  };
16240
16576
  }, []);
16241
- (0, import_react41.useEffect)(() => {
16577
+ (0, import_react42.useEffect)(() => {
16242
16578
  const handleKeyDown = (event) => {
16243
16579
  if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
16244
16580
  return;
@@ -16259,7 +16595,7 @@ function BindingBuilder({ licenseKey }) {
16259
16595
  window.addEventListener("keydown", handleKeyDown);
16260
16596
  return () => window.removeEventListener("keydown", handleKeyDown);
16261
16597
  }, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
16262
- (0, import_react41.useEffect)(() => {
16598
+ (0, import_react42.useEffect)(() => {
16263
16599
  if (loading || statusTone === "default" || statusTone === "success") return;
16264
16600
  setStatusBannerVisible(true);
16265
16601
  const timeout = window.setTimeout(
@@ -16268,8 +16604,8 @@ function BindingBuilder({ licenseKey }) {
16268
16604
  );
16269
16605
  return () => window.clearTimeout(timeout);
16270
16606
  }, [statusMessage, statusTone, loading]);
16271
- const nodeKeys = (0, import_react41.useMemo)(() => Object.keys(bindings), [bindings]);
16272
- const idError = (0, import_react41.useMemo)(() => {
16607
+ const nodeKeys = (0, import_react42.useMemo)(() => Object.keys(bindings), [bindings]);
16608
+ const idError = (0, import_react42.useMemo)(() => {
16273
16609
  if (!selectedItem || !selectedBinding) return null;
16274
16610
  const trimmed = selectedBinding.id.trim();
16275
16611
  if (!trimmed) return "ID is required.";
@@ -16312,7 +16648,7 @@ function BindingBuilder({ licenseKey }) {
16312
16648
  });
16313
16649
  }
16314
16650
  };
16315
- const applyBindingsFromScene = (0, import_react41.useCallback)(
16651
+ const applyBindingsFromScene = (0, import_react42.useCallback)(
16316
16652
  (scene, nextModelName) => {
16317
16653
  const nodeNames = collectNodeNames(scene);
16318
16654
  bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
@@ -16351,7 +16687,7 @@ function BindingBuilder({ licenseKey }) {
16351
16687
  bindingsHistory.reset({});
16352
16688
  setSelectedItem(null);
16353
16689
  };
16354
- const handleViewerModelLoaded = (0, import_react41.useCallback)(
16690
+ const handleViewerModelLoaded = (0, import_react42.useCallback)(
16355
16691
  (scene) => {
16356
16692
  const pending = pendingLoadRef.current;
16357
16693
  if (!pending || pending.requestId !== loadRequestRef.current) return;
@@ -16361,7 +16697,7 @@ function BindingBuilder({ licenseKey }) {
16361
16697
  },
16362
16698
  [applyBindingsFromScene]
16363
16699
  );
16364
- const handleViewerLoadError = (0, import_react41.useCallback)((error) => {
16700
+ const handleViewerLoadError = (0, import_react42.useCallback)((error) => {
16365
16701
  const pending = pendingLoadRef.current;
16366
16702
  if (!pending || pending.requestId !== loadRequestRef.current) return;
16367
16703
  pendingLoadRef.current = null;
@@ -16372,7 +16708,7 @@ function BindingBuilder({ licenseKey }) {
16372
16708
  setStatusTone("error");
16373
16709
  setLoading(false);
16374
16710
  }, []);
16375
- const handleAnimationsReady = (0, import_react41.useCallback)(
16711
+ const handleAnimationsReady = (0, import_react42.useCallback)(
16376
16712
  (controls) => {
16377
16713
  viewerAnimations.handleAnimationsReady(controls);
16378
16714
  const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
@@ -16673,7 +17009,7 @@ function BindingBuilder({ licenseKey }) {
16673
17009
  }));
16674
17010
  setError(null);
16675
17011
  };
16676
- const handleViewerSelect = (0, import_react41.useCallback)(
17012
+ const handleViewerSelect = (0, import_react42.useCallback)(
16677
17013
  (binding) => {
16678
17014
  if (!binding) {
16679
17015
  setSelectedItem(null);
@@ -16686,7 +17022,7 @@ function BindingBuilder({ licenseKey }) {
16686
17022
  },
16687
17023
  [bindings]
16688
17024
  );
16689
- const handleSceneNodeSelect = (0, import_react41.useCallback)(
17025
+ const handleSceneNodeSelect = (0, import_react42.useCallback)(
16690
17026
  async (nodeKey) => {
16691
17027
  setSelectedItem(nodeKey);
16692
17028
  setPanelHoveredBinding(null);
@@ -16694,7 +17030,7 @@ function BindingBuilder({ licenseKey }) {
16694
17030
  },
16695
17031
  [focusObject]
16696
17032
  );
16697
- const saveCameraPositionForSelectedObject = (0, import_react41.useCallback)(() => {
17033
+ const saveCameraPositionForSelectedObject = (0, import_react42.useCallback)(() => {
16698
17034
  if (!selectedItem || !cameraState) {
16699
17035
  return;
16700
17036
  }
@@ -16709,7 +17045,7 @@ function BindingBuilder({ licenseKey }) {
16709
17045
  cameraState: currentCameraState
16710
17046
  }));
16711
17047
  }, [selectedItem, cameraState]);
16712
- const goToSavedCameraPosition = (0, import_react41.useCallback)(() => {
17048
+ const goToSavedCameraPosition = (0, import_react42.useCallback)(() => {
16713
17049
  if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
16714
17050
  setCameraState(
16715
17051
  {
@@ -16721,28 +17057,28 @@ function BindingBuilder({ licenseKey }) {
16721
17057
  true
16722
17058
  );
16723
17059
  }, [savedCameraPosition, setCameraState, cameraState]);
16724
- const clearSavedCameraPosition = (0, import_react41.useCallback)(() => {
17060
+ const clearSavedCameraPosition = (0, import_react42.useCallback)(() => {
16725
17061
  if (!selectedItem) return;
16726
17062
  updateBinding(selectedItem, (binding) => ({
16727
17063
  ...binding,
16728
17064
  cameraState: void 0
16729
17065
  }));
16730
17066
  }, [selectedItem]);
16731
- const handleResetView = (0, import_react41.useCallback)(async () => {
17067
+ const handleResetView = (0, import_react42.useCallback)(async () => {
16732
17068
  await resetView();
16733
17069
  }, [resetView]);
16734
- const handleFitScene = (0, import_react41.useCallback)(async () => {
17070
+ const handleFitScene = (0, import_react42.useCallback)(async () => {
16735
17071
  await fitScene();
16736
17072
  }, [fitScene]);
16737
- const openResetDialog = (0, import_react41.useCallback)((target) => {
17073
+ const openResetDialog = (0, import_react42.useCallback)((target) => {
16738
17074
  setResetTarget(target);
16739
17075
  setResetDialogOpen(true);
16740
17076
  }, []);
16741
- const closeResetDialog = (0, import_react41.useCallback)(() => {
17077
+ const closeResetDialog = (0, import_react42.useCallback)(() => {
16742
17078
  setResetDialogOpen(false);
16743
17079
  setResetTarget(null);
16744
17080
  }, []);
16745
- const executeReset = (0, import_react41.useCallback)(() => {
17081
+ const executeReset = (0, import_react42.useCallback)(() => {
16746
17082
  if (resetTarget === "bindings") {
16747
17083
  if (!nodeKeys.length) {
16748
17084
  closeResetDialog();
@@ -17289,7 +17625,7 @@ function BindingBuilder({ licenseKey }) {
17289
17625
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
17290
17626
  "?"
17291
17627
  ] }),
17292
- /* @__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." }),
17293
17629
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
17294
17630
  ] }),
17295
17631
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "bb-reset-footer", children: [
@@ -17301,11 +17637,11 @@ function BindingBuilder({ licenseKey }) {
17301
17637
  }
17302
17638
 
17303
17639
  // src/components/SimpleModelViewer/SimpleModelViewer.tsx
17304
- var import_react42 = require("react");
17640
+ var import_react43 = require("react");
17305
17641
  var import_drei7 = require("@react-three/drei");
17306
17642
  var import_fiber13 = require("@react-three/fiber");
17307
17643
  var import_fiber14 = require("@react-three/fiber");
17308
- var import_three22 = require("three");
17644
+ var import_three23 = require("three");
17309
17645
  var import_three_stdlib4 = require("three-stdlib");
17310
17646
 
17311
17647
  // src/styles/SimpleModelViewer.css
@@ -17333,15 +17669,15 @@ function isMeshNode2(o) {
17333
17669
  function applyClippingPlanes(scene, controls) {
17334
17670
  if (!controls) return;
17335
17671
  const camera = controls.camera;
17336
- if (!(camera instanceof import_three22.PerspectiveCamera)) return;
17337
- 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();
17338
17674
  if (!Number.isFinite(size) || size <= 0) return;
17339
17675
  camera.near = size / 100;
17340
17676
  camera.far = size * 100;
17341
17677
  camera.updateProjectionMatrix();
17342
17678
  controls.maxDistance = size * 10;
17343
17679
  }
17344
- var ModelErrorBoundary2 = class extends import_react42.Component {
17680
+ var ModelErrorBoundary2 = class extends import_react43.Component {
17345
17681
  constructor() {
17346
17682
  super(...arguments);
17347
17683
  this.state = { hasError: false };
@@ -17373,12 +17709,12 @@ function InnerModel({
17373
17709
  meshopt: true
17374
17710
  });
17375
17711
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17376
- const [hoveredKey, setHoveredKey] = (0, import_react42.useState)(null);
17377
- const scene = (0, import_react42.useMemo)(
17712
+ const [hoveredKey, setHoveredKey] = (0, import_react43.useState)(null);
17713
+ const scene = (0, import_react43.useMemo)(
17378
17714
  () => import_three_stdlib4.SkeletonUtils.clone(rawScene),
17379
17715
  [rawScene]
17380
17716
  );
17381
- const meshMap = (0, import_react42.useMemo)(() => {
17717
+ const meshMap = (0, import_react43.useMemo)(() => {
17382
17718
  const map = {};
17383
17719
  scene.traverse((obj) => {
17384
17720
  if (!isMeshNode2(obj)) return;
@@ -17387,7 +17723,7 @@ function InnerModel({
17387
17723
  });
17388
17724
  return map;
17389
17725
  }, [scene]);
17390
- const perKeyMaterials = (0, import_react42.useMemo)(() => {
17726
+ const perKeyMaterials = (0, import_react43.useMemo)(() => {
17391
17727
  const map = {};
17392
17728
  for (const [key, { ref }] of Object.entries(meshMap)) {
17393
17729
  const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
@@ -17403,14 +17739,14 @@ function InnerModel({
17403
17739
  }
17404
17740
  return map;
17405
17741
  }, [meshMap]);
17406
- (0, import_react42.useEffect)(
17742
+ (0, import_react43.useEffect)(
17407
17743
  () => () => {
17408
17744
  Object.values(perKeyMaterials).forEach((m) => m.dispose());
17409
17745
  },
17410
17746
  [perKeyMaterials]
17411
17747
  );
17412
- const notifiedUrl = (0, import_react42.useRef)(null);
17413
- (0, import_react42.useEffect)(() => {
17748
+ const notifiedUrl = (0, import_react43.useRef)(null);
17749
+ (0, import_react43.useEffect)(() => {
17414
17750
  if (notifiedUrl.current === url) return;
17415
17751
  notifiedUrl.current = url;
17416
17752
  scene.updateWorldMatrix(true, true);
@@ -17418,7 +17754,7 @@ function InnerModel({
17418
17754
  onMeshesReady(meshMap);
17419
17755
  onModelLoaded(scene);
17420
17756
  }, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
17421
- (0, import_react42.useEffect)(() => {
17757
+ (0, import_react43.useEffect)(() => {
17422
17758
  const controller = new AbortController();
17423
17759
  void buildSceneBVH(scene, controller.signal);
17424
17760
  return () => {
@@ -17426,26 +17762,26 @@ function InnerModel({
17426
17762
  disposeSceneBVH(scene);
17427
17763
  };
17428
17764
  }, [scene]);
17429
- const pointerRaycastEnabled = (0, import_react42.useMemo)(
17765
+ const pointerRaycastEnabled = (0, import_react43.useMemo)(
17430
17766
  () => !exceedsBvhBudget(scene),
17431
17767
  [scene]
17432
17768
  );
17433
- (0, import_react42.useEffect)(() => {
17769
+ (0, import_react43.useEffect)(() => {
17434
17770
  for (const [key, { ref }] of Object.entries(meshMap)) {
17435
17771
  nodeRefsOut.current[key] = ref;
17436
17772
  }
17437
17773
  }, [meshMap, nodeRefsOut]);
17438
- (0, import_react42.useEffect)(() => {
17774
+ (0, import_react43.useEffect)(() => {
17439
17775
  if (!highlightOnHover && hoveredKey !== null) {
17440
17776
  setHoveredKey(null);
17441
17777
  }
17442
17778
  }, [highlightOnHover, hoveredKey]);
17443
- (0, import_react42.useEffect)(() => {
17779
+ (0, import_react43.useEffect)(() => {
17444
17780
  return () => {
17445
17781
  document.body.style.cursor = "default";
17446
17782
  };
17447
17783
  }, []);
17448
- (0, import_react42.useEffect)(() => {
17784
+ (0, import_react43.useEffect)(() => {
17449
17785
  for (const [key, { ref }] of Object.entries(meshMap)) {
17450
17786
  ref.visible = !hiddenKeys.has(key);
17451
17787
  const mat = perKeyMaterials[key];
@@ -17515,11 +17851,11 @@ function CameraFitter({
17515
17851
  refitOnResize,
17516
17852
  onInitialFitComplete
17517
17853
  }) {
17518
- const fitted = (0, import_react42.useRef)(false);
17854
+ const fitted = (0, import_react43.useRef)(false);
17519
17855
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17520
17856
  const size = (0, import_fiber13.useThree)((state) => state.size);
17521
- const resizeTimerRef = (0, import_react42.useRef)(null);
17522
- (0, import_react42.useEffect)(() => {
17857
+ const resizeTimerRef = (0, import_react43.useRef)(null);
17858
+ (0, import_react43.useEffect)(() => {
17523
17859
  fitted.current = false;
17524
17860
  invalidate();
17525
17861
  }, [invalidate, scene]);
@@ -17539,7 +17875,7 @@ function CameraFitter({
17539
17875
  }).catch(() => {
17540
17876
  }).finally(onInitialFitComplete);
17541
17877
  });
17542
- (0, import_react42.useEffect)(() => {
17878
+ (0, import_react43.useEffect)(() => {
17543
17879
  if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
17544
17880
  return;
17545
17881
  }
@@ -17567,7 +17903,7 @@ function CameraFitter({
17567
17903
  function ExposureController({ exposure }) {
17568
17904
  const gl = (0, import_fiber13.useThree)((state) => state.gl);
17569
17905
  const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
17570
- (0, import_react42.useEffect)(() => {
17906
+ (0, import_react43.useEffect)(() => {
17571
17907
  gl.toneMappingExposure = exposure;
17572
17908
  invalidate();
17573
17909
  }, [gl, exposure, invalidate]);
@@ -17675,7 +18011,7 @@ function EnvironmentSection({
17675
18011
  title,
17676
18012
  children
17677
18013
  }) {
17678
- const [collapsed, setCollapsed] = (0, import_react42.useState)(false);
18014
+ const [collapsed, setCollapsed] = (0, import_react43.useState)(false);
17679
18015
  return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "env-section", children: [
17680
18016
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
17681
18017
  "button",
@@ -17840,8 +18176,8 @@ function SceneObjectsPanel2({
17840
18176
  onHover,
17841
18177
  onToggleVisibility
17842
18178
  }) {
17843
- const [search, setSearch] = (0, import_react42.useState)("");
17844
- 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);
17845
18181
  const entries = Object.values(meshKeys).filter(
17846
18182
  (m) => m.label.toLowerCase().includes(search.toLowerCase())
17847
18183
  );
@@ -17965,35 +18301,35 @@ function SimpleModelViewer({
17965
18301
  onModelLoaded,
17966
18302
  onLoadError
17967
18303
  }) {
17968
- const cameraControlsRef = (0, import_react42.useRef)(null);
17969
- const nodeRefsOut = (0, import_react42.useRef)({});
17970
- const fileInputRef = (0, import_react42.useRef)(null);
17971
- const uploadedModelRef = (0, import_react42.useRef)(null);
17972
- const dragDepthRef = (0, import_react42.useRef)(0);
17973
- const [loadedScene, setLoadedScene] = (0, import_react42.useState)(null);
17974
- 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)(
17975
18311
  null
17976
18312
  );
17977
- const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react42.useState)(false);
17978
- const [meshKeys, setMeshKeys] = (0, import_react42.useState)({});
17979
- const [hiddenKeys, setHiddenKeys] = (0, import_react42.useState)(/* @__PURE__ */ new Set());
17980
- const [selectedKey, setSelectedKey] = (0, import_react42.useState)(null);
17981
- const [panelHoveredKey, setPanelHoveredKey] = (0, import_react42.useState)(null);
17982
- const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react42.useState)(null);
17983
- 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)(
17984
18320
  null
17985
18321
  );
17986
- const [modelLoadFailed, setModelLoadFailed] = (0, import_react42.useState)(false);
17987
- const [isResolvingUpload, setIsResolvingUpload] = (0, import_react42.useState)(false);
17988
- const [initialFitComplete, setInitialFitComplete] = (0, import_react42.useState)(false);
17989
- const [isDragActive, setIsDragActive] = (0, import_react42.useState)(false);
17990
- 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)(
17991
18327
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17992
18328
  );
17993
- const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react42.useState)(
18329
+ const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react43.useState)(
17994
18330
  () => typeof window === "undefined" ? true : window.innerWidth > 900
17995
18331
  );
17996
- const [envSettings, setEnvSettings] = (0, import_react42.useState)({
18332
+ const [envSettings, setEnvSettings] = (0, import_react43.useState)({
17997
18333
  backgroundEnabled,
17998
18334
  backgroundColor,
17999
18335
  autoRotate,
@@ -18005,7 +18341,7 @@ function SimpleModelViewer({
18005
18341
  directionalColor
18006
18342
  });
18007
18343
  const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
18008
- const resetModelState = (0, import_react42.useCallback)(() => {
18344
+ const resetModelState = (0, import_react43.useCallback)(() => {
18009
18345
  setLoadedScene(null);
18010
18346
  setLargeModelTriangles(null);
18011
18347
  setLargeModelNoticeDismissed(false);
@@ -18016,11 +18352,11 @@ function SimpleModelViewer({
18016
18352
  setModelLoadFailed(false);
18017
18353
  setInitialFitComplete(false);
18018
18354
  }, []);
18019
- const revokeUploadedModelUrl = (0, import_react42.useCallback)(() => {
18355
+ const revokeUploadedModelUrl = (0, import_react43.useCallback)(() => {
18020
18356
  uploadedModelRef.current?.cleanup();
18021
18357
  uploadedModelRef.current = null;
18022
18358
  }, []);
18023
- const clearUploadedModel = (0, import_react42.useCallback)(() => {
18359
+ const clearUploadedModel = (0, import_react43.useCallback)(() => {
18024
18360
  revokeUploadedModelUrl();
18025
18361
  setUploadedModelUrl(null);
18026
18362
  setUploadedModelName(null);
@@ -18049,7 +18385,7 @@ function SimpleModelViewer({
18049
18385
  resetModelState,
18050
18386
  revokeUploadedModelUrl
18051
18387
  ]);
18052
- const handleUploadedModel = (0, import_react42.useCallback)(
18388
+ const handleUploadedModel = (0, import_react43.useCallback)(
18053
18389
  async (file) => {
18054
18390
  setIsResolvingUpload(true);
18055
18391
  try {
@@ -18068,7 +18404,7 @@ function SimpleModelViewer({
18068
18404
  },
18069
18405
  [onLoadError, resetModelState, revokeUploadedModelUrl]
18070
18406
  );
18071
- const handleFileSelection = (0, import_react42.useCallback)(
18407
+ const handleFileSelection = (0, import_react43.useCallback)(
18072
18408
  (files) => {
18073
18409
  const file = files?.[0];
18074
18410
  if (!file) return;
@@ -18076,29 +18412,29 @@ function SimpleModelViewer({
18076
18412
  },
18077
18413
  [handleUploadedModel]
18078
18414
  );
18079
- (0, import_react42.useEffect)(() => {
18415
+ (0, import_react43.useEffect)(() => {
18080
18416
  return () => {
18081
18417
  revokeUploadedModelUrl();
18082
18418
  };
18083
18419
  }, [revokeUploadedModelUrl]);
18084
- const handleEnvSettingChange = (0, import_react42.useCallback)(
18420
+ const handleEnvSettingChange = (0, import_react43.useCallback)(
18085
18421
  (key, value) => {
18086
18422
  setEnvSettings((prev) => ({ ...prev, [key]: value }));
18087
18423
  },
18088
18424
  []
18089
18425
  );
18090
- (0, import_react42.useEffect)(() => {
18426
+ (0, import_react43.useEffect)(() => {
18091
18427
  setEnvSettings((prev) => ({ ...prev, backgroundColor }));
18092
18428
  }, [backgroundColor]);
18093
- (0, import_react42.useEffect)(() => {
18429
+ (0, import_react43.useEffect)(() => {
18094
18430
  resetModelState();
18095
18431
  }, [activeModelUrl, resetModelState]);
18096
- const handleMeshesReady = (0, import_react42.useCallback)((meshes) => {
18432
+ const handleMeshesReady = (0, import_react43.useCallback)((meshes) => {
18097
18433
  setMeshKeys(meshes);
18098
18434
  setHiddenKeys(/* @__PURE__ */ new Set());
18099
18435
  setSelectedKey(null);
18100
18436
  }, []);
18101
- const handleModelLoaded = (0, import_react42.useCallback)(
18437
+ const handleModelLoaded = (0, import_react43.useCallback)(
18102
18438
  (scene) => {
18103
18439
  setLoadedScene(scene);
18104
18440
  setModelLoadFailed(false);
@@ -18109,14 +18445,14 @@ function SimpleModelViewer({
18109
18445
  },
18110
18446
  [onModelLoaded]
18111
18447
  );
18112
- const handleLoadError = (0, import_react42.useCallback)(
18448
+ const handleLoadError = (0, import_react43.useCallback)(
18113
18449
  (error) => {
18114
18450
  setModelLoadFailed(true);
18115
18451
  onLoadError?.(error);
18116
18452
  },
18117
18453
  [onLoadError]
18118
18454
  );
18119
- const focusMesh = (0, import_react42.useCallback)(
18455
+ const focusMesh = (0, import_react43.useCallback)(
18120
18456
  (key) => {
18121
18457
  setSelectedKey(key);
18122
18458
  if (!zoomOnSelected) return;
@@ -18132,14 +18468,14 @@ function SimpleModelViewer({
18132
18468
  },
18133
18469
  [zoomOnSelected]
18134
18470
  );
18135
- const handleToggleVisibility = (0, import_react42.useCallback)((key) => {
18471
+ const handleToggleVisibility = (0, import_react43.useCallback)((key) => {
18136
18472
  setHiddenKeys((prev) => {
18137
18473
  const next = new Set(prev);
18138
18474
  next.has(key) ? next.delete(key) : next.add(key);
18139
18475
  return next;
18140
18476
  });
18141
18477
  }, []);
18142
- const handleDragEnter = (0, import_react42.useCallback)(
18478
+ const handleDragEnter = (0, import_react43.useCallback)(
18143
18479
  (event) => {
18144
18480
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18145
18481
  return;
@@ -18151,7 +18487,7 @@ function SimpleModelViewer({
18151
18487
  },
18152
18488
  [enableModelUpload]
18153
18489
  );
18154
- const handleDragOver = (0, import_react42.useCallback)(
18490
+ const handleDragOver = (0, import_react43.useCallback)(
18155
18491
  (event) => {
18156
18492
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18157
18493
  return;
@@ -18163,7 +18499,7 @@ function SimpleModelViewer({
18163
18499
  },
18164
18500
  [enableModelUpload]
18165
18501
  );
18166
- const handleDragLeave = (0, import_react42.useCallback)(
18502
+ const handleDragLeave = (0, import_react43.useCallback)(
18167
18503
  (event) => {
18168
18504
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18169
18505
  return;
@@ -18177,7 +18513,7 @@ function SimpleModelViewer({
18177
18513
  },
18178
18514
  [enableModelUpload]
18179
18515
  );
18180
- const handleDrop = (0, import_react42.useCallback)(
18516
+ const handleDrop = (0, import_react43.useCallback)(
18181
18517
  (event) => {
18182
18518
  if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
18183
18519
  return;
@@ -18270,7 +18606,7 @@ function SimpleModelViewer({
18270
18606
  raycaster: { firstHitOnly: true },
18271
18607
  style: { background: envSettings.backgroundColor },
18272
18608
  camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
18273
- 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: [
18274
18610
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
18275
18611
  "ambientLight",
18276
18612
  {
@@ -18288,7 +18624,7 @@ function SimpleModelViewer({
18288
18624
  }
18289
18625
  ),
18290
18626
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
18291
- /* @__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)(
18292
18628
  import_drei7.Environment,
18293
18629
  {
18294
18630
  preset: envSettings.envPreset,
@@ -18307,7 +18643,7 @@ function SimpleModelViewer({
18307
18643
  ModelErrorBoundary2,
18308
18644
  {
18309
18645
  onLoadError: handleLoadError,
18310
- 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)(
18311
18647
  InnerModel,
18312
18648
  {
18313
18649
  url: activeModelUrl,
@@ -18406,18 +18742,18 @@ function SimpleModelViewer({
18406
18742
  }
18407
18743
 
18408
18744
  // src/hooks/useViewerSelection.ts
18409
- var import_react43 = require("react");
18745
+ var import_react44 = require("react");
18410
18746
  function useViewerSelection(initialSelectedObjectBinding = null) {
18411
- const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react43.useState)(
18747
+ const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react44.useState)(
18412
18748
  initialSelectedObjectBinding
18413
18749
  );
18414
- const handleObjectSelect = (0, import_react43.useCallback)(
18750
+ const handleObjectSelect = (0, import_react44.useCallback)(
18415
18751
  (binding) => {
18416
18752
  setSelectedObjectBinding(binding);
18417
18753
  },
18418
18754
  [setSelectedObjectBinding]
18419
18755
  );
18420
- const clearSelection = (0, import_react43.useCallback)(() => {
18756
+ const clearSelection = (0, import_react44.useCallback)(() => {
18421
18757
  setSelectedObjectBinding(null);
18422
18758
  }, []);
18423
18759
  return {
@@ -18429,9 +18765,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
18429
18765
  }
18430
18766
 
18431
18767
  // src/hooks/useObjectBinding.ts
18432
- var import_react44 = require("react");
18768
+ var import_react45 = require("react");
18433
18769
  function useObjectBinding(objectBindings, objectId) {
18434
- return (0, import_react44.useMemo)(() => {
18770
+ return (0, import_react45.useMemo)(() => {
18435
18771
  if (!objectId) {
18436
18772
  return null;
18437
18773
  }
@@ -18446,16 +18782,16 @@ function useObjectBinding(objectBindings, objectId) {
18446
18782
  }
18447
18783
 
18448
18784
  // src/hooks/useObjectBindingIds.ts
18449
- var import_react45 = require("react");
18785
+ var import_react46 = require("react");
18450
18786
  function useObjectBindingIds(objectBindings) {
18451
- return (0, import_react45.useMemo)(
18787
+ return (0, import_react46.useMemo)(
18452
18788
  () => Object.values(objectBindings).map((binding) => binding.modelObjectId),
18453
18789
  [objectBindings]
18454
18790
  );
18455
18791
  }
18456
18792
 
18457
18793
  // src/hooks/useObjectBindings.ts
18458
- var import_react46 = require("react");
18794
+ var import_react47 = require("react");
18459
18795
 
18460
18796
  // src/utils/deepMerge.ts
18461
18797
  function isPlainObject2(value) {
@@ -18473,6 +18809,27 @@ function deepMerge(base, patch) {
18473
18809
  return target;
18474
18810
  }
18475
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
+
18476
18833
  // src/utils/patchBindings.ts
18477
18834
  function isBindingEditArray(value) {
18478
18835
  return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
@@ -18481,10 +18838,7 @@ function patchBindings(bindings, objectIdOrEdits, patch) {
18481
18838
  const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
18482
18839
  let next = bindings;
18483
18840
  for (const edit of edits) {
18484
- const ids = Array.isArray(edit.ids) ? edit.ids : [edit.ids];
18485
- for (const id of ids) {
18486
- const bindingKey = resolveBindingKey(next, id);
18487
- if (!bindingKey) continue;
18841
+ for (const bindingKey of selectBindingKeys(next, edit.ids)) {
18488
18842
  const binding = next[bindingKey];
18489
18843
  if (!binding) continue;
18490
18844
  const patchedBinding = deepMerge(binding, edit.patch);
@@ -18499,14 +18853,44 @@ function material(value) {
18499
18853
  }
18500
18854
 
18501
18855
  // src/hooks/useObjectBindings.ts
18502
- function getHiddenObjects(objectBindings) {
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
+ }
18886
+ function getHiddenObjects(objectBindings) {
18503
18887
  return Object.fromEntries(
18504
18888
  Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
18505
18889
  );
18506
18890
  }
18507
18891
  function useObjectBindings(initialBindings) {
18508
- const [objectBindings, setObjectBindings] = (0, import_react46.useState)(initialBindings);
18509
- const updateObjectBindings = (0, import_react46.useCallback)(
18892
+ const [objectBindings, setObjectBindings] = (0, import_react47.useState)(initialBindings);
18893
+ const updateObjectBindings = (0, import_react47.useCallback)(
18510
18894
  (objectIdOrEdits, patchValue) => {
18511
18895
  setObjectBindings(
18512
18896
  (current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
@@ -18518,68 +18902,138 @@ function useObjectBindings(initialBindings) {
18518
18902
  },
18519
18903
  []
18520
18904
  );
18521
- const setMaterial = (0, import_react46.useCallback)(
18522
- (objectId, value) => updateObjectBindings(objectId, material(value)),
18905
+ const setMaterial = (0, import_react47.useCallback)(
18906
+ (target, value) => updateObjectBindings(target, material(value)),
18523
18907
  [updateObjectBindings]
18524
18908
  );
18525
- const setBaseColor = (0, import_react46.useCallback)(
18526
- (objectId, color) => setMaterial(objectId, { baseColor: color }),
18909
+ const setBaseColor = (0, import_react47.useCallback)(
18910
+ (target, color) => setMaterial(target, { baseColor: color }),
18527
18911
  [setMaterial]
18528
18912
  );
18529
- const resetMaterial = (0, import_react46.useCallback)(
18530
- (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
+ },
18531
18921
  [updateObjectBindings]
18532
18922
  );
18533
- const getMaterial = (0, import_react46.useCallback)(
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
+ });
18929
+ },
18930
+ [updateObjectBindings]
18931
+ );
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 }),
18970
+ [updateObjectBindings]
18971
+ );
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)(
18534
18987
  (objectId) => {
18535
18988
  const bindingKey = resolveBindingKey(objectBindings, objectId);
18536
18989
  return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
18537
18990
  },
18538
18991
  [objectBindings]
18539
18992
  );
18540
- const hideObject = (0, import_react46.useCallback)(
18541
- (objectId) => updateObjectBindings(objectId, { visible: false }),
18993
+ const hideObject = (0, import_react47.useCallback)(
18994
+ (target) => updateObjectBindings(target, { visible: false }),
18542
18995
  [updateObjectBindings]
18543
18996
  );
18544
- const showObject = (0, import_react46.useCallback)(
18545
- (objectId) => updateObjectBindings(objectId, { visible: true }),
18997
+ const showObject = (0, import_react47.useCallback)(
18998
+ (target) => updateObjectBindings(target, { visible: true }),
18546
18999
  [updateObjectBindings]
18547
19000
  );
18548
- const toggleObjectVisibility = (0, import_react46.useCallback)(
18549
- (objectId) => {
19001
+ const toggleObjectVisibility = (0, import_react47.useCallback)(
19002
+ (target) => {
18550
19003
  setObjectBindings((current) => {
18551
- const objectIds = Array.isArray(objectId) ? objectId : [objectId];
18552
- const edits = objectIds.reduce((acc, id) => {
18553
- const bindingKey = resolveBindingKey(current, id);
18554
- const binding = bindingKey ? current[bindingKey] : null;
18555
- if (!bindingKey || !binding) return acc;
18556
- acc.push({
18557
- ids: bindingKey,
18558
- patch: { visible: binding.visible === false }
18559
- });
18560
- return acc;
18561
- }, []);
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
+ );
18562
19016
  return patchBindings(current, edits);
18563
19017
  });
18564
19018
  },
18565
19019
  [setObjectBindings]
18566
19020
  );
18567
- const clearHiddenObjects = (0, import_react46.useCallback)(() => {
19021
+ const clearHiddenObjects = (0, import_react47.useCallback)(() => {
18568
19022
  setObjectBindings(
18569
19023
  (current) => patchBindings(current, Object.keys(current), { visible: true })
18570
19024
  );
18571
19025
  }, [setObjectBindings]);
18572
- const hiddenObjects = (0, import_react46.useMemo)(
19026
+ const hiddenObjects = (0, import_react47.useMemo)(
18573
19027
  () => getHiddenObjects(objectBindings),
18574
19028
  [objectBindings]
18575
19029
  );
18576
- const hiddenObjectIds = (0, import_react46.useMemo)(
19030
+ const hiddenObjectIds = (0, import_react47.useMemo)(
18577
19031
  () => Object.keys(hiddenObjects).map(
18578
19032
  (bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
18579
19033
  ),
18580
19034
  [hiddenObjects, objectBindings]
18581
19035
  );
18582
- const isObjectHidden = (0, import_react46.useCallback)(
19036
+ const isObjectHidden = (0, import_react47.useCallback)(
18583
19037
  (objectId) => {
18584
19038
  const bindingKey = resolveBindingKey(objectBindings, objectId);
18585
19039
  return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
@@ -18592,8 +19046,16 @@ function useObjectBindings(initialBindings) {
18592
19046
  updateObjectBindings,
18593
19047
  setMaterial,
18594
19048
  setBaseColor,
19049
+ setTexture,
19050
+ clearTexture,
19051
+ copyMaterial,
18595
19052
  resetMaterial,
18596
19053
  getMaterial,
19054
+ setTransform,
19055
+ resetTransform,
19056
+ updateMetadata,
19057
+ selectObjectBindings,
19058
+ selectObjectBindingIds,
18597
19059
  hiddenObjects,
18598
19060
  hiddenObjectIds,
18599
19061
  hideObject,
@@ -18604,8 +19066,13 @@ function useObjectBindings(initialBindings) {
18604
19066
  };
18605
19067
  }
18606
19068
 
19069
+ // src/utils/defineObjectBindings.ts
19070
+ function defineObjectBindings(bindings) {
19071
+ return bindings;
19072
+ }
19073
+
18607
19074
  // src/hooks/useSceneConfig.ts
18608
- var import_react47 = require("react");
19075
+ var import_react48 = require("react");
18609
19076
 
18610
19077
  // src/utils/patchSceneConfig.ts
18611
19078
  function patchSceneConfig(sceneConfig, patch) {
@@ -18613,36 +19080,154 @@ function patchSceneConfig(sceneConfig, patch) {
18613
19080
  }
18614
19081
 
18615
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
+ };
18616
19095
  function useSceneConfig(initialSceneConfig) {
18617
- const initialSceneConfigRef = (0, import_react47.useRef)(null);
18618
- const [sceneConfig, setSceneConfig] = (0, import_react47.useState)(() => {
19096
+ const initialSceneConfigRef = (0, import_react48.useRef)(null);
19097
+ const [sceneConfig, setSceneConfig] = (0, import_react48.useState)(() => {
18619
19098
  const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
18620
19099
  initialSceneConfigRef.current = initial;
18621
19100
  return initial;
18622
19101
  });
18623
- const updateSceneConfig = (0, import_react47.useCallback)((update) => {
19102
+ const updateSceneConfig = (0, import_react48.useCallback)((update) => {
18624
19103
  setSceneConfig((current) => {
18625
19104
  const patch = typeof update === "function" ? update(current) : update;
18626
19105
  return patchSceneConfig(current, patch);
18627
19106
  });
18628
19107
  }, []);
18629
- const resetSceneConfig = (0, import_react47.useCallback)(() => {
19108
+ const resetSceneConfig = (0, import_react48.useCallback)(() => {
18630
19109
  const initial = initialSceneConfigRef.current;
18631
19110
  if (initial) setSceneConfig(initial);
18632
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
+ );
18633
19217
  return {
18634
19218
  sceneConfig,
18635
19219
  setSceneConfig,
18636
19220
  updateSceneConfig,
18637
- resetSceneConfig
19221
+ resetSceneConfig,
19222
+ lights
18638
19223
  };
18639
19224
  }
18640
19225
 
18641
19226
  // src/hooks/useViewerHover.ts
18642
- var import_react48 = require("react");
19227
+ var import_react49 = require("react");
18643
19228
  function useViewerHover() {
18644
- const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react48.useState)(null);
18645
- const handleHoveredObject = (0, import_react48.useCallback)(
19229
+ const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react49.useState)(null);
19230
+ const handleHoveredObject = (0, import_react49.useCallback)(
18646
19231
  (binding) => {
18647
19232
  setHoveredObjectBinding(binding);
18648
19233
  },
@@ -18655,23 +19240,23 @@ function useViewerHover() {
18655
19240
  }
18656
19241
 
18657
19242
  // src/hooks/useViewerModel.ts
18658
- var import_react49 = require("react");
18659
- var import_three23 = require("three");
19243
+ var import_react50 = require("react");
19244
+ var import_three24 = require("three");
18660
19245
  function toTuple2(vector) {
18661
19246
  return [vector.x, vector.y, vector.z];
18662
19247
  }
18663
19248
  function useViewerModel() {
18664
- const [isLoading, setIsLoading] = (0, import_react49.useState)(true);
18665
- const [isReady, setIsReady] = (0, import_react49.useState)(false);
18666
- const [error, setError] = (0, import_react49.useState)(null);
18667
- const [bounds, setBounds] = (0, import_react49.useState)(null);
18668
- const [objectCount, setObjectCount] = (0, import_react49.useState)(0);
18669
- const handleModelLoaded = (0, import_react49.useCallback)((scene) => {
18670
- 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);
18671
19256
  const min = box.min.clone();
18672
19257
  const max = box.max.clone();
18673
- const center = new import_three23.Vector3();
18674
- const size = new import_three23.Vector3();
19258
+ const center = new import_three24.Vector3();
19259
+ const size = new import_three24.Vector3();
18675
19260
  let nextObjectCount = 0;
18676
19261
  scene.traverse((child) => {
18677
19262
  if ("isMesh" in child && child.isMesh) {
@@ -18691,14 +19276,14 @@ function useViewerModel() {
18691
19276
  setIsLoading(false);
18692
19277
  setIsReady(true);
18693
19278
  }, []);
18694
- const handleLoadError = (0, import_react49.useCallback)((nextError) => {
19279
+ const handleLoadError = (0, import_react50.useCallback)((nextError) => {
18695
19280
  setError(nextError);
18696
19281
  setBounds(null);
18697
19282
  setObjectCount(0);
18698
19283
  setIsLoading(false);
18699
19284
  setIsReady(false);
18700
19285
  }, []);
18701
- const resetModelState = (0, import_react49.useCallback)(() => {
19286
+ const resetModelState = (0, import_react50.useCallback)(() => {
18702
19287
  setIsLoading(true);
18703
19288
  setIsReady(false);
18704
19289
  setError(null);
@@ -18718,62 +19303,774 @@ function useViewerModel() {
18718
19303
  }
18719
19304
 
18720
19305
  // src/hooks/useViewerActions.ts
18721
- var import_react50 = require("react");
18722
- function useViewerActions(objectBindings, options = {}) {
18723
- const { onAction, onObjectBindingsChange } = options;
18724
- 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)(
18725
19333
  (objectId) => {
18726
19334
  const bindingKey = resolveBindingKey(objectBindings, objectId);
18727
- if (!bindingKey) {
18728
- return [];
18729
- }
18730
- return objectBindings[bindingKey]?.actions ?? [];
19335
+ return bindingKey ? objectBindings[bindingKey]?.actions ?? [] : [];
18731
19336
  },
18732
19337
  [objectBindings]
18733
19338
  );
18734
- const runAction = (0, import_react50.useCallback)(
18735
- (objectId, actionId) => {
18736
- const bindingKey = resolveBindingKey(objectBindings, objectId);
18737
- if (!bindingKey) {
18738
- 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
+ }
18739
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;
18740
19406
  const binding = objectBindings[bindingKey];
18741
- if (!binding) {
18742
- return null;
18743
- }
18744
- const action = (binding.actions ?? []).find(
18745
- (entry) => entry.id === actionId
18746
- );
18747
- if (!action) {
18748
- return null;
18749
- }
19407
+ const action = binding?.actions?.find((entry) => entry.id === actionId);
19408
+ if (!binding || !action) return null;
18750
19409
  const event = {
18751
19410
  objectId: bindingKey,
18752
19411
  action,
18753
19412
  binding
18754
19413
  };
18755
- if (action.id === "toggle-visibility" && onObjectBindingsChange) {
18756
- onObjectBindingsChange({
18757
- ...objectBindings,
18758
- [bindingKey]: {
18759
- ...binding,
18760
- visible: binding.visible === false
18761
- }
18762
- });
18763
- }
18764
- onAction?.(event);
19414
+ await handleAction(event);
18765
19415
  return event;
18766
19416
  },
18767
- [objectBindings, onAction, onObjectBindingsChange]
19417
+ [handleAction, objectBindings]
18768
19418
  );
18769
19419
  return {
18770
19420
  getActionsForObject,
19421
+ handleAction,
18771
19422
  runAction
18772
19423
  };
18773
19424
  }
18774
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
+
18775
20072
  // src/hooks/useShareableViewerState.ts
18776
- var import_react51 = require("react");
20073
+ var import_react54 = require("react");
18777
20074
  function serializeState(cameraState, objectBindings, selectedObjectBinding) {
18778
20075
  const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
18779
20076
  return {
@@ -18793,8 +20090,8 @@ function useShareableViewerState({
18793
20090
  debounceMs = 400,
18794
20091
  autoSync = true
18795
20092
  }) {
18796
- const lastWrittenRawRef = (0, import_react51.useRef)(null);
18797
- const buildHash = (0, import_react51.useCallback)(() => {
20093
+ const lastWrittenRawRef = (0, import_react54.useRef)(null);
20094
+ const buildHash = (0, import_react54.useCallback)(() => {
18798
20095
  const state = serializeState(
18799
20096
  cameraState,
18800
20097
  objectBindings,
@@ -18802,12 +20099,12 @@ function useShareableViewerState({
18802
20099
  );
18803
20100
  return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
18804
20101
  }, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
18805
- const getShareUrl = (0, import_react51.useCallback)(() => {
20102
+ const getShareUrl = (0, import_react54.useCallback)(() => {
18806
20103
  const url = new URL(window.location.href);
18807
20104
  url.hash = buildHash();
18808
20105
  return url.toString();
18809
20106
  }, [buildHash]);
18810
- const syncUrl = (0, import_react51.useCallback)(() => {
20107
+ const syncUrl = (0, import_react54.useCallback)(() => {
18811
20108
  const url = new URL(window.location.href);
18812
20109
  url.hash = buildHash();
18813
20110
  window.history.replaceState(null, "", url.toString());
@@ -18815,7 +20112,7 @@ function useShareableViewerState({
18815
20112
  paramKey
18816
20113
  );
18817
20114
  }, [buildHash, paramKey]);
18818
- const latestStateRef = (0, import_react51.useRef)({
20115
+ const latestStateRef = (0, import_react54.useRef)({
18819
20116
  cameraState,
18820
20117
  objectBindings,
18821
20118
  selectedObjectBinding
@@ -18825,9 +20122,9 @@ function useShareableViewerState({
18825
20122
  objectBindings,
18826
20123
  selectedObjectBinding
18827
20124
  };
18828
- const [restoreAttempted, setRestoreAttempted] = (0, import_react51.useState)(false);
18829
- const lastSyncedHashRef = (0, import_react51.useRef)(null);
18830
- (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)(() => {
18831
20128
  if (!autoSync || !restoreAttempted) return;
18832
20129
  const tick = () => {
18833
20130
  const current = latestStateRef.current;
@@ -18849,9 +20146,9 @@ function useShareableViewerState({
18849
20146
  const id = window.setInterval(tick, debounceMs);
18850
20147
  return () => window.clearInterval(id);
18851
20148
  }, [autoSync, restoreAttempted, debounceMs, paramKey]);
18852
- const lastRestoredRawRef = (0, import_react51.useRef)(null);
18853
- const restoringRawRef = (0, import_react51.useRef)(null);
18854
- 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 () => {
18855
20152
  setRestoreAttempted(true);
18856
20153
  const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
18857
20154
  const raw = new URLSearchParams(rawHash).get(paramKey);
@@ -18904,7 +20201,7 @@ function useShareableViewerState({
18904
20201
  setCameraState,
18905
20202
  setSelectedObjectBinding
18906
20203
  ]);
18907
- (0, import_react51.useEffect)(() => {
20204
+ (0, import_react54.useEffect)(() => {
18908
20205
  const handleHashChange = () => {
18909
20206
  void restoreFromUrl();
18910
20207
  };
@@ -18918,13 +20215,17 @@ function useShareableViewerState({
18918
20215
  // Annotate the CommonJS export names for ESM import in node:
18919
20216
  0 && (module.exports = {
18920
20217
  BindingBuilder,
20218
+ DEFAULT_SCENE_CONFIG,
18921
20219
  MATERIAL_BLENDING_MODES,
18922
20220
  MATERIAL_SIDES,
18923
20221
  ModelViewer,
18924
20222
  SimpleModelViewer,
20223
+ defineObjectBindings,
18925
20224
  material,
18926
20225
  patchBindings,
18927
20226
  patchSceneConfig,
20227
+ selectBindingKeys,
20228
+ selectBindings,
18928
20229
  useObjectBinding,
18929
20230
  useObjectBindingIds,
18930
20231
  useObjectBindings,
@@ -18933,6 +20234,8 @@ function useShareableViewerState({
18933
20234
  useViewerActions,
18934
20235
  useViewerAnimations,
18935
20236
  useViewerCamera,
20237
+ useViewerConnection,
20238
+ useViewerEffects,
18936
20239
  useViewerHover,
18937
20240
  useViewerModel,
18938
20241
  useViewerSelection