@2112-lab/central-plant 0.1.94 → 0.1.96

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.
@@ -25184,8 +25184,8 @@ var EnvironmentManager = /*#__PURE__*/function () {
25184
25184
  case 0:
25185
25185
  component = this.sceneViewer;
25186
25186
  console.debug('Starting addTexturedGround...');
25187
- groundSize = 30;
25188
- groundGeometry = new THREE__namespace.PlaneGeometry(groundSize, groundSize);
25187
+ groundSize = 90;
25188
+ groundGeometry = new THREE__namespace.CircleGeometry(groundSize / 2, 16);
25189
25189
  groundMaterial = new THREE__namespace.MeshStandardMaterial({
25190
25190
  color: 0x777777,
25191
25191
  metalness: 0.2,
@@ -25373,7 +25373,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
25373
25373
  key: "addHorizonFog",
25374
25374
  value: function addHorizonFog() {
25375
25375
  var component = this.sceneViewer;
25376
- var groundSize = 30;
25376
+ var groundSize = 60;
25377
25377
  var fogSize = groundSize * 10;
25378
25378
  var fogMaterial = new THREE__namespace.ShaderMaterial({
25379
25379
  transparent: true,
@@ -25392,7 +25392,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
25392
25392
  var fogGeometry = new THREE__namespace.PlaneGeometry(fogSize, fogSize);
25393
25393
  var fogPlane = new THREE__namespace.Mesh(fogGeometry, fogMaterial);
25394
25394
  fogPlane.rotation.x = 0; // No rotation needed for Z-up coordinate system
25395
- fogPlane.position.z = -3.0; // Position fog plane below ground level
25395
+ fogPlane.position.z = -8.0; // Position fog plane below ground level
25396
25396
  fogPlane.name = "fogPlane";
25397
25397
  component.scene.add(fogPlane);
25398
25398
  return fogPlane;
@@ -25415,12 +25415,10 @@ var EnvironmentManager = /*#__PURE__*/function () {
25415
25415
  _context4.n = 2;
25416
25416
  return this.addTexturedGround();
25417
25417
  case 2:
25418
- _context4.n = 3;
25419
- return this.addBrickWalls();
25420
- case 3:
25418
+ // await this.addBrickWalls()
25421
25419
  this.addHorizonFog();
25422
25420
  console.log('Environment initialization completed');
25423
- case 4:
25421
+ case 3:
25424
25422
  return _context4.a(2);
25425
25423
  }
25426
25424
  }, _callee4, this);
@@ -28581,6 +28579,15 @@ var BehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
28581
28579
  value: function _applyAction(object, propertyPath, value) {
28582
28580
  if (!object || typeof propertyPath !== 'string') return;
28583
28581
  var parts = propertyPath.split('.');
28582
+
28583
+ // Clone shared material onto this mesh before mutating any material property.
28584
+ // Three.js reuses the same Material instance across all meshes loaded from the
28585
+ // same GLB, so without cloning a color/emissive change would bleed to every
28586
+ // other component instance that shares the model.
28587
+ if (parts[0] === 'material' && object.isMesh && object.material && !object.userData._behaviorMaterialCloned) {
28588
+ object.material = object.material.clone();
28589
+ object.userData._behaviorMaterialCloned = true;
28590
+ }
28584
28591
  var target = object;
28585
28592
  for (var i = 0; i < parts.length - 1; i++) {
28586
28593
  if (target == null) {
@@ -28897,9 +28904,13 @@ var ModelManager = /*#__PURE__*/function () {
28897
28904
  var _this = this;
28898
28905
  var connectorChildren = [];
28899
28906
  targetMesh.children.forEach(function (child, index) {
28907
+ var _child$userData;
28900
28908
  var isConnectorGeometry = child.geometry && (child.geometry.uuid === 'CONNECTOR-GEO' || child.geometry.type === 'SphereGeometry' && child.geometry.parameters);
28901
28909
  var isConnectorByName = child.name && child.name.toLowerCase().includes('connector');
28902
- if (isConnectorGeometry && isConnectorByName) {
28910
+ // Also recognise connectors declared via userData (e.g. inline connectors from SAMPLE_1.json
28911
+ // whose geometry may be a fallback BufferGeometry rather than a SphereGeometry).
28912
+ var isConnectorByUserData = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'connector';
28913
+ if (isConnectorGeometry && isConnectorByName || isConnectorByUserData) {
28903
28914
  // Ensure userData exists and has worldBoundingBox
28904
28915
  if (!child.userData) child.userData = {};
28905
28916
  if (!child.userData.worldBoundingBox) {
@@ -29183,8 +29194,8 @@ var ModelManager = /*#__PURE__*/function () {
29183
29194
  requiredModels = new Set();
29184
29195
  console.log("\uD83D\uDD0D Checking ".concat(data.scene.children.length, " scene objects for required models..."));
29185
29196
  data.scene.children.forEach(function (child) {
29186
- var _child$userData;
29187
- var libraryId = (_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.libraryId;
29197
+ var _child$userData2;
29198
+ var libraryId = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.libraryId;
29188
29199
  if (libraryId) {
29189
29200
  if (componentDictionary[libraryId]) {
29190
29201
  var modelKey = componentDictionary[libraryId].modelKey;
@@ -30239,6 +30250,24 @@ var SceneOperationsManager = /*#__PURE__*/function () {
30239
30250
  }
30240
30251
  }
30241
30252
  });
30253
+
30254
+ // Ensure CONNECTOR_GATEWAY_SPHERE exists even when all connectors are nested inside
30255
+ // component children (e.g. SAMPLE_1.json inline connector definitions). Without
30256
+ // this geometry the connector meshes fall back to empty BufferGeometry, which then
30257
+ // causes _preserveConnectorChildren to drop them during GLB model replacement.
30258
+ if (!geometries['CONNECTOR_GATEWAY_SPHERE']) {
30259
+ var hasNestedConnectors = data.scene.children.some(function (child) {
30260
+ var _child$userData3;
30261
+ return ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'component' && Array.isArray(child.children) && child.children.some(function (nestedChild) {
30262
+ var _nestedChild$userData;
30263
+ return ((_nestedChild$userData = nestedChild.userData) === null || _nestedChild$userData === void 0 ? void 0 : _nestedChild$userData.objectType) === 'connector';
30264
+ });
30265
+ });
30266
+ if (hasNestedConnectors) {
30267
+ geometries['CONNECTOR_GATEWAY_SPHERE'] = new THREE__namespace.SphereGeometry(0.1, 16, 16);
30268
+ console.log('🔮 Created shared sphere geometry for nested inline connectors (radius: 0.1)');
30269
+ }
30270
+ }
30242
30271
  return geometries;
30243
30272
  }
30244
30273
 
@@ -30624,12 +30653,12 @@ var SceneOperationsManager = /*#__PURE__*/function () {
30624
30653
  _step;
30625
30654
  try {
30626
30655
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
30627
- var _object$userData, _child$userData3;
30656
+ var _object$userData, _child$userData4;
30628
30657
  var child = _step.value;
30629
30658
  // Enhanced matching logic with hardcoded UUID priority
30630
30659
 
30631
30660
  // Strategy 1: Direct hardcoded UUID match (HIGHEST PRIORITY)
30632
- if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.originalUuid)) {
30661
+ if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.originalUuid)) {
30633
30662
  return child;
30634
30663
  }
30635
30664
 
@@ -30888,10 +30917,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
30888
30917
  var componentsProcessed = 0;
30889
30918
  var connectorsInjected = 0;
30890
30919
  data.scene.children.forEach(function (child) {
30891
- var _child$userData4, _child$userData5, _child$userData6;
30892
- var childType = ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType);
30920
+ var _child$userData5, _child$userData6, _child$userData7;
30921
+ var childType = ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) || ((_child$userData6 = child.userData) === null || _child$userData6 === void 0 ? void 0 : _child$userData6.objectType);
30893
30922
  // Only process components with libraryId
30894
- if (childType === 'component' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.libraryId) {
30923
+ if (childType === 'component' && (_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId) {
30895
30924
  var libraryId = child.userData.libraryId;
30896
30925
  var dictEntry = componentDictionary[libraryId];
30897
30926
 
@@ -31048,17 +31077,17 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31048
31077
  geometries = this.createSceneGeometries(data, componentDictionary); // Create basic objects and track GLB replacements
31049
31078
  libraryObjectsToReplace = [];
31050
31079
  data.scene.children.forEach(function (child, index) {
31051
- var _child$userData7, _child$userData8;
31080
+ var _child$userData8, _child$userData9;
31052
31081
  var createdObject = _this4.createSceneObject(child, geometries, materials, componentDictionary);
31053
31082
  _this4.sceneViewer.scene.add(createdObject);
31054
31083
 
31055
31084
  // Track objects that need GLB model replacement
31056
- if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId && componentDictionary[(_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.libraryId]) {
31057
- var _child$userData9;
31085
+ if ((_child$userData8 = child.userData) !== null && _child$userData8 !== void 0 && _child$userData8.libraryId && componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]) {
31086
+ var _child$userData0;
31058
31087
  libraryObjectsToReplace.push({
31059
31088
  basicObject: createdObject,
31060
31089
  jsonData: child,
31061
- componentData: componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]
31090
+ componentData: componentDictionary[(_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId]
31062
31091
  });
31063
31092
  }
31064
31093
  });
@@ -31179,6 +31208,19 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31179
31208
  return b.id;
31180
31209
  }));
31181
31210
 
31211
+ // Build a set of (component::attachment::state) tuples covered by explicit
31212
+ // behaviors. If an expanded default behavior would produce the SAME input
31213
+ // tuple, the explicit behavior is treated as the intentional override and
31214
+ // the default expansion is skipped. This prevents a component's built-in
31215
+ // switch→LED wiring from doubling-up when the user has deliberately authored
31216
+ // cross-component behaviors that re-wire the same switch.
31217
+ var explicitInputTuples = new Set(explicitBehaviors.filter(function (b) {
31218
+ var _b$input, _b$input2, _b$input3;
31219
+ return ((_b$input = b.input) === null || _b$input === void 0 ? void 0 : _b$input.component) && ((_b$input2 = b.input) === null || _b$input2 === void 0 ? void 0 : _b$input2.attachment) && ((_b$input3 = b.input) === null || _b$input3 === void 0 ? void 0 : _b$input3.state);
31220
+ }).map(function (b) {
31221
+ return "".concat(b.input.component, "::").concat(b.input.attachment, "::").concat(b.input.state);
31222
+ }));
31223
+
31182
31224
  // Build a set of instanceUuids already covered by behaviorRef entries in
31183
31225
  // data.behaviors (written by the exporter for smart component defaults).
31184
31226
  // Step B skips these instances to avoid loading the same behaviors twice —
@@ -31197,8 +31239,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31197
31239
  var instanceBehaviors = [];
31198
31240
  if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
31199
31241
  data.scene.children.forEach(function (child) {
31200
- var _child$userData0, _compData$defaultBeha;
31201
- var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
31242
+ var _child$userData1, _compData$defaultBeha;
31243
+ var libraryId = (_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.libraryId;
31202
31244
  if (!libraryId) return;
31203
31245
  var instanceUuid = child.uuid;
31204
31246
  // Skip instances whose defaults were already resolved by Step A
@@ -31207,10 +31249,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31207
31249
  var compData = componentDictionary[libraryId];
31208
31250
  if (!(compData !== null && compData !== void 0 && (_compData$defaultBeha = compData.defaultBehaviors) !== null && _compData$defaultBeha !== void 0 && _compData$defaultBeha.length)) return;
31209
31251
  compData.defaultBehaviors.forEach(function (template) {
31252
+ var _expanded$input, _expanded$input2, _expanded$input3;
31210
31253
  var expanded = _this5._expandDefaultBehavior(template, instanceUuid, componentDictionary);
31211
31254
  if (!expanded) return;
31212
31255
  // Skip if an explicit scene behavior already covers this id
31213
31256
  if (explicitIds.has(expanded.id)) return;
31257
+ // Skip if an explicit scene behavior already covers the same
31258
+ // (component, attachment, state) input tuple. This prevents a
31259
+ // component's built-in default wiring (e.g. switch → own LED) from
31260
+ // double-firing when the user has authored a cross-component override
31261
+ // that rewires the same switch to a different target.
31262
+ if ((_expanded$input = expanded.input) !== null && _expanded$input !== void 0 && _expanded$input.component && (_expanded$input2 = expanded.input) !== null && _expanded$input2 !== void 0 && _expanded$input2.attachment && (_expanded$input3 = expanded.input) !== null && _expanded$input3 !== void 0 && _expanded$input3.state) {
31263
+ var tuple = "".concat(expanded.input.component, "::").concat(expanded.input.attachment, "::").concat(expanded.input.state);
31264
+ if (explicitInputTuples.has(tuple)) return;
31265
+ }
31214
31266
  // All component defaultBehaviors are re-derivable from the component
31215
31267
  // asset at export time (via compact behaviorRef), so mark them all.
31216
31268
  expanded._isDefaultBehavior = true;
@@ -31589,8 +31641,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31589
31641
  // Process children (connectors, etc.) if they exist
31590
31642
  if (componentModel.children && componentModel.children.length > 0) {
31591
31643
  componentModel.children.forEach(function (child) {
31592
- var _child$userData1, _child$userData10;
31593
- var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
31644
+ var _child$userData10, _child$userData11;
31645
+ var childType = ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType) || ((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType);
31594
31646
  if (childType === 'connector') {
31595
31647
  var _child$geometry;
31596
31648
  var childBoundingBox = new THREE__namespace.Box3().setFromObject(child);
@@ -31675,8 +31727,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
31675
31727
  if (segment.children && segment.children.length > 0) {
31676
31728
  var childrenToRemove = _toConsumableArray(segment.children);
31677
31729
  childrenToRemove.forEach(function (child) {
31678
- var _child$userData11;
31679
- if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
31730
+ var _child$userData12;
31731
+ if ((_child$userData12 = child.userData) !== null && _child$userData12 !== void 0 && _child$userData12.isPipeElbow) {
31680
31732
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
31681
31733
  segment.remove(child);
31682
31734
  if (child.geometry) child.geometry.dispose();
@@ -37181,7 +37233,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
37181
37233
  * Initialize the CentralPlant manager
37182
37234
  *
37183
37235
  * @constructor
37184
- * @version 0.1.94
37236
+ * @version 0.1.96
37185
37237
  * @updated 2025-10-22
37186
37238
  *
37187
37239
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -19,7 +19,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
19
19
  * Initialize the CentralPlant manager
20
20
  *
21
21
  * @constructor
22
- * @version 0.1.94
22
+ * @version 0.1.96
23
23
  * @updated 2025-10-22
24
24
  *
25
25
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -344,6 +344,15 @@ var BehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
344
344
  value: function _applyAction(object, propertyPath, value) {
345
345
  if (!object || typeof propertyPath !== 'string') return;
346
346
  var parts = propertyPath.split('.');
347
+
348
+ // Clone shared material onto this mesh before mutating any material property.
349
+ // Three.js reuses the same Material instance across all meshes loaded from the
350
+ // same GLB, so without cloning a color/emissive change would bleed to every
351
+ // other component instance that shares the model.
352
+ if (parts[0] === 'material' && object.isMesh && object.material && !object.userData._behaviorMaterialCloned) {
353
+ object.material = object.material.clone();
354
+ object.userData._behaviorMaterialCloned = true;
355
+ }
347
356
  var target = object;
348
357
  for (var i = 0; i < parts.length - 1; i++) {
349
358
  if (target == null) {
@@ -195,8 +195,8 @@ var EnvironmentManager = /*#__PURE__*/function () {
195
195
  case 0:
196
196
  component = this.sceneViewer;
197
197
  console.debug('Starting addTexturedGround...');
198
- groundSize = 30;
199
- groundGeometry = new THREE__namespace.PlaneGeometry(groundSize, groundSize);
198
+ groundSize = 90;
199
+ groundGeometry = new THREE__namespace.CircleGeometry(groundSize / 2, 16);
200
200
  groundMaterial = new THREE__namespace.MeshStandardMaterial({
201
201
  color: 0x777777,
202
202
  metalness: 0.2,
@@ -384,7 +384,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
384
384
  key: "addHorizonFog",
385
385
  value: function addHorizonFog() {
386
386
  var component = this.sceneViewer;
387
- var groundSize = 30;
387
+ var groundSize = 60;
388
388
  var fogSize = groundSize * 10;
389
389
  var fogMaterial = new THREE__namespace.ShaderMaterial({
390
390
  transparent: true,
@@ -403,7 +403,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
403
403
  var fogGeometry = new THREE__namespace.PlaneGeometry(fogSize, fogSize);
404
404
  var fogPlane = new THREE__namespace.Mesh(fogGeometry, fogMaterial);
405
405
  fogPlane.rotation.x = 0; // No rotation needed for Z-up coordinate system
406
- fogPlane.position.z = -3.0; // Position fog plane below ground level
406
+ fogPlane.position.z = -8.0; // Position fog plane below ground level
407
407
  fogPlane.name = "fogPlane";
408
408
  component.scene.add(fogPlane);
409
409
  return fogPlane;
@@ -426,12 +426,10 @@ var EnvironmentManager = /*#__PURE__*/function () {
426
426
  _context4.n = 2;
427
427
  return this.addTexturedGround();
428
428
  case 2:
429
- _context4.n = 3;
430
- return this.addBrickWalls();
431
- case 3:
429
+ // await this.addBrickWalls()
432
430
  this.addHorizonFog();
433
431
  console.log('Environment initialization completed');
434
- case 4:
432
+ case 3:
435
433
  return _context4.a(2);
436
434
  }
437
435
  }, _callee4, this);
@@ -146,9 +146,13 @@ var ModelManager = /*#__PURE__*/function () {
146
146
  var _this = this;
147
147
  var connectorChildren = [];
148
148
  targetMesh.children.forEach(function (child, index) {
149
+ var _child$userData;
149
150
  var isConnectorGeometry = child.geometry && (child.geometry.uuid === 'CONNECTOR-GEO' || child.geometry.type === 'SphereGeometry' && child.geometry.parameters);
150
151
  var isConnectorByName = child.name && child.name.toLowerCase().includes('connector');
151
- if (isConnectorGeometry && isConnectorByName) {
152
+ // Also recognise connectors declared via userData (e.g. inline connectors from SAMPLE_1.json
153
+ // whose geometry may be a fallback BufferGeometry rather than a SphereGeometry).
154
+ var isConnectorByUserData = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'connector';
155
+ if (isConnectorGeometry && isConnectorByName || isConnectorByUserData) {
152
156
  // Ensure userData exists and has worldBoundingBox
153
157
  if (!child.userData) child.userData = {};
154
158
  if (!child.userData.worldBoundingBox) {
@@ -432,8 +436,8 @@ var ModelManager = /*#__PURE__*/function () {
432
436
  requiredModels = new Set();
433
437
  console.log("\uD83D\uDD0D Checking ".concat(data.scene.children.length, " scene objects for required models..."));
434
438
  data.scene.children.forEach(function (child) {
435
- var _child$userData;
436
- var libraryId = (_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.libraryId;
439
+ var _child$userData2;
440
+ var libraryId = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.libraryId;
437
441
  if (libraryId) {
438
442
  if (componentDictionary[libraryId]) {
439
443
  var modelKey = componentDictionary[libraryId].modelKey;
@@ -261,6 +261,24 @@ var SceneOperationsManager = /*#__PURE__*/function () {
261
261
  }
262
262
  }
263
263
  });
264
+
265
+ // Ensure CONNECTOR_GATEWAY_SPHERE exists even when all connectors are nested inside
266
+ // component children (e.g. SAMPLE_1.json inline connector definitions). Without
267
+ // this geometry the connector meshes fall back to empty BufferGeometry, which then
268
+ // causes _preserveConnectorChildren to drop them during GLB model replacement.
269
+ if (!geometries['CONNECTOR_GATEWAY_SPHERE']) {
270
+ var hasNestedConnectors = data.scene.children.some(function (child) {
271
+ var _child$userData3;
272
+ return ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'component' && Array.isArray(child.children) && child.children.some(function (nestedChild) {
273
+ var _nestedChild$userData;
274
+ return ((_nestedChild$userData = nestedChild.userData) === null || _nestedChild$userData === void 0 ? void 0 : _nestedChild$userData.objectType) === 'connector';
275
+ });
276
+ });
277
+ if (hasNestedConnectors) {
278
+ geometries['CONNECTOR_GATEWAY_SPHERE'] = new THREE__namespace.SphereGeometry(0.1, 16, 16);
279
+ console.log('🔮 Created shared sphere geometry for nested inline connectors (radius: 0.1)');
280
+ }
281
+ }
264
282
  return geometries;
265
283
  }
266
284
 
@@ -646,12 +664,12 @@ var SceneOperationsManager = /*#__PURE__*/function () {
646
664
  _step;
647
665
  try {
648
666
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
649
- var _object$userData, _child$userData3;
667
+ var _object$userData, _child$userData4;
650
668
  var child = _step.value;
651
669
  // Enhanced matching logic with hardcoded UUID priority
652
670
 
653
671
  // Strategy 1: Direct hardcoded UUID match (HIGHEST PRIORITY)
654
- if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.originalUuid)) {
672
+ if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.originalUuid)) {
655
673
  return child;
656
674
  }
657
675
 
@@ -910,10 +928,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
910
928
  var componentsProcessed = 0;
911
929
  var connectorsInjected = 0;
912
930
  data.scene.children.forEach(function (child) {
913
- var _child$userData4, _child$userData5, _child$userData6;
914
- var childType = ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType);
931
+ var _child$userData5, _child$userData6, _child$userData7;
932
+ var childType = ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) || ((_child$userData6 = child.userData) === null || _child$userData6 === void 0 ? void 0 : _child$userData6.objectType);
915
933
  // Only process components with libraryId
916
- if (childType === 'component' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.libraryId) {
934
+ if (childType === 'component' && (_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId) {
917
935
  var libraryId = child.userData.libraryId;
918
936
  var dictEntry = componentDictionary[libraryId];
919
937
 
@@ -1070,17 +1088,17 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1070
1088
  geometries = this.createSceneGeometries(data, componentDictionary); // Create basic objects and track GLB replacements
1071
1089
  libraryObjectsToReplace = [];
1072
1090
  data.scene.children.forEach(function (child, index) {
1073
- var _child$userData7, _child$userData8;
1091
+ var _child$userData8, _child$userData9;
1074
1092
  var createdObject = _this4.createSceneObject(child, geometries, materials, componentDictionary);
1075
1093
  _this4.sceneViewer.scene.add(createdObject);
1076
1094
 
1077
1095
  // Track objects that need GLB model replacement
1078
- if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId && componentDictionary[(_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.libraryId]) {
1079
- var _child$userData9;
1096
+ if ((_child$userData8 = child.userData) !== null && _child$userData8 !== void 0 && _child$userData8.libraryId && componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]) {
1097
+ var _child$userData0;
1080
1098
  libraryObjectsToReplace.push({
1081
1099
  basicObject: createdObject,
1082
1100
  jsonData: child,
1083
- componentData: componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]
1101
+ componentData: componentDictionary[(_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId]
1084
1102
  });
1085
1103
  }
1086
1104
  });
@@ -1201,6 +1219,19 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1201
1219
  return b.id;
1202
1220
  }));
1203
1221
 
1222
+ // Build a set of (component::attachment::state) tuples covered by explicit
1223
+ // behaviors. If an expanded default behavior would produce the SAME input
1224
+ // tuple, the explicit behavior is treated as the intentional override and
1225
+ // the default expansion is skipped. This prevents a component's built-in
1226
+ // switch→LED wiring from doubling-up when the user has deliberately authored
1227
+ // cross-component behaviors that re-wire the same switch.
1228
+ var explicitInputTuples = new Set(explicitBehaviors.filter(function (b) {
1229
+ var _b$input, _b$input2, _b$input3;
1230
+ return ((_b$input = b.input) === null || _b$input === void 0 ? void 0 : _b$input.component) && ((_b$input2 = b.input) === null || _b$input2 === void 0 ? void 0 : _b$input2.attachment) && ((_b$input3 = b.input) === null || _b$input3 === void 0 ? void 0 : _b$input3.state);
1231
+ }).map(function (b) {
1232
+ return "".concat(b.input.component, "::").concat(b.input.attachment, "::").concat(b.input.state);
1233
+ }));
1234
+
1204
1235
  // Build a set of instanceUuids already covered by behaviorRef entries in
1205
1236
  // data.behaviors (written by the exporter for smart component defaults).
1206
1237
  // Step B skips these instances to avoid loading the same behaviors twice —
@@ -1219,8 +1250,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1219
1250
  var instanceBehaviors = [];
1220
1251
  if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
1221
1252
  data.scene.children.forEach(function (child) {
1222
- var _child$userData0, _compData$defaultBeha;
1223
- var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
1253
+ var _child$userData1, _compData$defaultBeha;
1254
+ var libraryId = (_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.libraryId;
1224
1255
  if (!libraryId) return;
1225
1256
  var instanceUuid = child.uuid;
1226
1257
  // Skip instances whose defaults were already resolved by Step A
@@ -1229,10 +1260,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1229
1260
  var compData = componentDictionary[libraryId];
1230
1261
  if (!(compData !== null && compData !== void 0 && (_compData$defaultBeha = compData.defaultBehaviors) !== null && _compData$defaultBeha !== void 0 && _compData$defaultBeha.length)) return;
1231
1262
  compData.defaultBehaviors.forEach(function (template) {
1263
+ var _expanded$input, _expanded$input2, _expanded$input3;
1232
1264
  var expanded = _this5._expandDefaultBehavior(template, instanceUuid, componentDictionary);
1233
1265
  if (!expanded) return;
1234
1266
  // Skip if an explicit scene behavior already covers this id
1235
1267
  if (explicitIds.has(expanded.id)) return;
1268
+ // Skip if an explicit scene behavior already covers the same
1269
+ // (component, attachment, state) input tuple. This prevents a
1270
+ // component's built-in default wiring (e.g. switch → own LED) from
1271
+ // double-firing when the user has authored a cross-component override
1272
+ // that rewires the same switch to a different target.
1273
+ if ((_expanded$input = expanded.input) !== null && _expanded$input !== void 0 && _expanded$input.component && (_expanded$input2 = expanded.input) !== null && _expanded$input2 !== void 0 && _expanded$input2.attachment && (_expanded$input3 = expanded.input) !== null && _expanded$input3 !== void 0 && _expanded$input3.state) {
1274
+ var tuple = "".concat(expanded.input.component, "::").concat(expanded.input.attachment, "::").concat(expanded.input.state);
1275
+ if (explicitInputTuples.has(tuple)) return;
1276
+ }
1236
1277
  // All component defaultBehaviors are re-derivable from the component
1237
1278
  // asset at export time (via compact behaviorRef), so mark them all.
1238
1279
  expanded._isDefaultBehavior = true;
@@ -1611,8 +1652,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1611
1652
  // Process children (connectors, etc.) if they exist
1612
1653
  if (componentModel.children && componentModel.children.length > 0) {
1613
1654
  componentModel.children.forEach(function (child) {
1614
- var _child$userData1, _child$userData10;
1615
- var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
1655
+ var _child$userData10, _child$userData11;
1656
+ var childType = ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType) || ((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType);
1616
1657
  if (childType === 'connector') {
1617
1658
  var _child$geometry;
1618
1659
  var childBoundingBox = new THREE__namespace.Box3().setFromObject(child);
@@ -1697,8 +1738,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1697
1738
  if (segment.children && segment.children.length > 0) {
1698
1739
  var childrenToRemove = _rollupPluginBabelHelpers.toConsumableArray(segment.children);
1699
1740
  childrenToRemove.forEach(function (child) {
1700
- var _child$userData11;
1701
- if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
1741
+ var _child$userData12;
1742
+ if ((_child$userData12 = child.userData) !== null && _child$userData12 !== void 0 && _child$userData12.isPipeElbow) {
1702
1743
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
1703
1744
  segment.remove(child);
1704
1745
  if (child.geometry) child.geometry.dispose();
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.94
18
+ * @version 0.1.96
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -340,6 +340,15 @@ var BehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
340
340
  value: function _applyAction(object, propertyPath, value) {
341
341
  if (!object || typeof propertyPath !== 'string') return;
342
342
  var parts = propertyPath.split('.');
343
+
344
+ // Clone shared material onto this mesh before mutating any material property.
345
+ // Three.js reuses the same Material instance across all meshes loaded from the
346
+ // same GLB, so without cloning a color/emissive change would bleed to every
347
+ // other component instance that shares the model.
348
+ if (parts[0] === 'material' && object.isMesh && object.material && !object.userData._behaviorMaterialCloned) {
349
+ object.material = object.material.clone();
350
+ object.userData._behaviorMaterialCloned = true;
351
+ }
343
352
  var target = object;
344
353
  for (var i = 0; i < parts.length - 1; i++) {
345
354
  if (target == null) {
@@ -171,8 +171,8 @@ var EnvironmentManager = /*#__PURE__*/function () {
171
171
  case 0:
172
172
  component = this.sceneViewer;
173
173
  console.debug('Starting addTexturedGround...');
174
- groundSize = 30;
175
- groundGeometry = new THREE.PlaneGeometry(groundSize, groundSize);
174
+ groundSize = 90;
175
+ groundGeometry = new THREE.CircleGeometry(groundSize / 2, 16);
176
176
  groundMaterial = new THREE.MeshStandardMaterial({
177
177
  color: 0x777777,
178
178
  metalness: 0.2,
@@ -360,7 +360,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
360
360
  key: "addHorizonFog",
361
361
  value: function addHorizonFog() {
362
362
  var component = this.sceneViewer;
363
- var groundSize = 30;
363
+ var groundSize = 60;
364
364
  var fogSize = groundSize * 10;
365
365
  var fogMaterial = new THREE.ShaderMaterial({
366
366
  transparent: true,
@@ -379,7 +379,7 @@ var EnvironmentManager = /*#__PURE__*/function () {
379
379
  var fogGeometry = new THREE.PlaneGeometry(fogSize, fogSize);
380
380
  var fogPlane = new THREE.Mesh(fogGeometry, fogMaterial);
381
381
  fogPlane.rotation.x = 0; // No rotation needed for Z-up coordinate system
382
- fogPlane.position.z = -3.0; // Position fog plane below ground level
382
+ fogPlane.position.z = -8.0; // Position fog plane below ground level
383
383
  fogPlane.name = "fogPlane";
384
384
  component.scene.add(fogPlane);
385
385
  return fogPlane;
@@ -402,12 +402,10 @@ var EnvironmentManager = /*#__PURE__*/function () {
402
402
  _context4.n = 2;
403
403
  return this.addTexturedGround();
404
404
  case 2:
405
- _context4.n = 3;
406
- return this.addBrickWalls();
407
- case 3:
405
+ // await this.addBrickWalls()
408
406
  this.addHorizonFog();
409
407
  console.log('Environment initialization completed');
410
- case 4:
408
+ case 3:
411
409
  return _context4.a(2);
412
410
  }
413
411
  }, _callee4, this);
@@ -122,9 +122,13 @@ var ModelManager = /*#__PURE__*/function () {
122
122
  var _this = this;
123
123
  var connectorChildren = [];
124
124
  targetMesh.children.forEach(function (child, index) {
125
+ var _child$userData;
125
126
  var isConnectorGeometry = child.geometry && (child.geometry.uuid === 'CONNECTOR-GEO' || child.geometry.type === 'SphereGeometry' && child.geometry.parameters);
126
127
  var isConnectorByName = child.name && child.name.toLowerCase().includes('connector');
127
- if (isConnectorGeometry && isConnectorByName) {
128
+ // Also recognise connectors declared via userData (e.g. inline connectors from SAMPLE_1.json
129
+ // whose geometry may be a fallback BufferGeometry rather than a SphereGeometry).
130
+ var isConnectorByUserData = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'connector';
131
+ if (isConnectorGeometry && isConnectorByName || isConnectorByUserData) {
128
132
  // Ensure userData exists and has worldBoundingBox
129
133
  if (!child.userData) child.userData = {};
130
134
  if (!child.userData.worldBoundingBox) {
@@ -408,8 +412,8 @@ var ModelManager = /*#__PURE__*/function () {
408
412
  requiredModels = new Set();
409
413
  console.log("\uD83D\uDD0D Checking ".concat(data.scene.children.length, " scene objects for required models..."));
410
414
  data.scene.children.forEach(function (child) {
411
- var _child$userData;
412
- var libraryId = (_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.libraryId;
415
+ var _child$userData2;
416
+ var libraryId = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.libraryId;
413
417
  if (libraryId) {
414
418
  if (componentDictionary[libraryId]) {
415
419
  var modelKey = componentDictionary[libraryId].modelKey;
@@ -237,6 +237,24 @@ var SceneOperationsManager = /*#__PURE__*/function () {
237
237
  }
238
238
  }
239
239
  });
240
+
241
+ // Ensure CONNECTOR_GATEWAY_SPHERE exists even when all connectors are nested inside
242
+ // component children (e.g. SAMPLE_1.json inline connector definitions). Without
243
+ // this geometry the connector meshes fall back to empty BufferGeometry, which then
244
+ // causes _preserveConnectorChildren to drop them during GLB model replacement.
245
+ if (!geometries['CONNECTOR_GATEWAY_SPHERE']) {
246
+ var hasNestedConnectors = data.scene.children.some(function (child) {
247
+ var _child$userData3;
248
+ return ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'component' && Array.isArray(child.children) && child.children.some(function (nestedChild) {
249
+ var _nestedChild$userData;
250
+ return ((_nestedChild$userData = nestedChild.userData) === null || _nestedChild$userData === void 0 ? void 0 : _nestedChild$userData.objectType) === 'connector';
251
+ });
252
+ });
253
+ if (hasNestedConnectors) {
254
+ geometries['CONNECTOR_GATEWAY_SPHERE'] = new THREE.SphereGeometry(0.1, 16, 16);
255
+ console.log('🔮 Created shared sphere geometry for nested inline connectors (radius: 0.1)');
256
+ }
257
+ }
240
258
  return geometries;
241
259
  }
242
260
 
@@ -622,12 +640,12 @@ var SceneOperationsManager = /*#__PURE__*/function () {
622
640
  _step;
623
641
  try {
624
642
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
625
- var _object$userData, _child$userData3;
643
+ var _object$userData, _child$userData4;
626
644
  var child = _step.value;
627
645
  // Enhanced matching logic with hardcoded UUID priority
628
646
 
629
647
  // Strategy 1: Direct hardcoded UUID match (HIGHEST PRIORITY)
630
- if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.originalUuid)) {
648
+ if (child.uuid === object.uuid || child.uuid === ((_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.originalUuid) || object.uuid === ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.originalUuid)) {
631
649
  return child;
632
650
  }
633
651
 
@@ -886,10 +904,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
886
904
  var componentsProcessed = 0;
887
905
  var connectorsInjected = 0;
888
906
  data.scene.children.forEach(function (child) {
889
- var _child$userData4, _child$userData5, _child$userData6;
890
- var childType = ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType);
907
+ var _child$userData5, _child$userData6, _child$userData7;
908
+ var childType = ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) || ((_child$userData6 = child.userData) === null || _child$userData6 === void 0 ? void 0 : _child$userData6.objectType);
891
909
  // Only process components with libraryId
892
- if (childType === 'component' && (_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.libraryId) {
910
+ if (childType === 'component' && (_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId) {
893
911
  var libraryId = child.userData.libraryId;
894
912
  var dictEntry = componentDictionary[libraryId];
895
913
 
@@ -1046,17 +1064,17 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1046
1064
  geometries = this.createSceneGeometries(data, componentDictionary); // Create basic objects and track GLB replacements
1047
1065
  libraryObjectsToReplace = [];
1048
1066
  data.scene.children.forEach(function (child, index) {
1049
- var _child$userData7, _child$userData8;
1067
+ var _child$userData8, _child$userData9;
1050
1068
  var createdObject = _this4.createSceneObject(child, geometries, materials, componentDictionary);
1051
1069
  _this4.sceneViewer.scene.add(createdObject);
1052
1070
 
1053
1071
  // Track objects that need GLB model replacement
1054
- if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.libraryId && componentDictionary[(_child$userData8 = child.userData) === null || _child$userData8 === void 0 ? void 0 : _child$userData8.libraryId]) {
1055
- var _child$userData9;
1072
+ if ((_child$userData8 = child.userData) !== null && _child$userData8 !== void 0 && _child$userData8.libraryId && componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]) {
1073
+ var _child$userData0;
1056
1074
  libraryObjectsToReplace.push({
1057
1075
  basicObject: createdObject,
1058
1076
  jsonData: child,
1059
- componentData: componentDictionary[(_child$userData9 = child.userData) === null || _child$userData9 === void 0 ? void 0 : _child$userData9.libraryId]
1077
+ componentData: componentDictionary[(_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId]
1060
1078
  });
1061
1079
  }
1062
1080
  });
@@ -1177,6 +1195,19 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1177
1195
  return b.id;
1178
1196
  }));
1179
1197
 
1198
+ // Build a set of (component::attachment::state) tuples covered by explicit
1199
+ // behaviors. If an expanded default behavior would produce the SAME input
1200
+ // tuple, the explicit behavior is treated as the intentional override and
1201
+ // the default expansion is skipped. This prevents a component's built-in
1202
+ // switch→LED wiring from doubling-up when the user has deliberately authored
1203
+ // cross-component behaviors that re-wire the same switch.
1204
+ var explicitInputTuples = new Set(explicitBehaviors.filter(function (b) {
1205
+ var _b$input, _b$input2, _b$input3;
1206
+ return ((_b$input = b.input) === null || _b$input === void 0 ? void 0 : _b$input.component) && ((_b$input2 = b.input) === null || _b$input2 === void 0 ? void 0 : _b$input2.attachment) && ((_b$input3 = b.input) === null || _b$input3 === void 0 ? void 0 : _b$input3.state);
1207
+ }).map(function (b) {
1208
+ return "".concat(b.input.component, "::").concat(b.input.attachment, "::").concat(b.input.state);
1209
+ }));
1210
+
1180
1211
  // Build a set of instanceUuids already covered by behaviorRef entries in
1181
1212
  // data.behaviors (written by the exporter for smart component defaults).
1182
1213
  // Step B skips these instances to avoid loading the same behaviors twice —
@@ -1195,8 +1226,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1195
1226
  var instanceBehaviors = [];
1196
1227
  if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
1197
1228
  data.scene.children.forEach(function (child) {
1198
- var _child$userData0, _compData$defaultBeha;
1199
- var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
1229
+ var _child$userData1, _compData$defaultBeha;
1230
+ var libraryId = (_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.libraryId;
1200
1231
  if (!libraryId) return;
1201
1232
  var instanceUuid = child.uuid;
1202
1233
  // Skip instances whose defaults were already resolved by Step A
@@ -1205,10 +1236,20 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1205
1236
  var compData = componentDictionary[libraryId];
1206
1237
  if (!(compData !== null && compData !== void 0 && (_compData$defaultBeha = compData.defaultBehaviors) !== null && _compData$defaultBeha !== void 0 && _compData$defaultBeha.length)) return;
1207
1238
  compData.defaultBehaviors.forEach(function (template) {
1239
+ var _expanded$input, _expanded$input2, _expanded$input3;
1208
1240
  var expanded = _this5._expandDefaultBehavior(template, instanceUuid, componentDictionary);
1209
1241
  if (!expanded) return;
1210
1242
  // Skip if an explicit scene behavior already covers this id
1211
1243
  if (explicitIds.has(expanded.id)) return;
1244
+ // Skip if an explicit scene behavior already covers the same
1245
+ // (component, attachment, state) input tuple. This prevents a
1246
+ // component's built-in default wiring (e.g. switch → own LED) from
1247
+ // double-firing when the user has authored a cross-component override
1248
+ // that rewires the same switch to a different target.
1249
+ if ((_expanded$input = expanded.input) !== null && _expanded$input !== void 0 && _expanded$input.component && (_expanded$input2 = expanded.input) !== null && _expanded$input2 !== void 0 && _expanded$input2.attachment && (_expanded$input3 = expanded.input) !== null && _expanded$input3 !== void 0 && _expanded$input3.state) {
1250
+ var tuple = "".concat(expanded.input.component, "::").concat(expanded.input.attachment, "::").concat(expanded.input.state);
1251
+ if (explicitInputTuples.has(tuple)) return;
1252
+ }
1212
1253
  // All component defaultBehaviors are re-derivable from the component
1213
1254
  // asset at export time (via compact behaviorRef), so mark them all.
1214
1255
  expanded._isDefaultBehavior = true;
@@ -1587,8 +1628,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1587
1628
  // Process children (connectors, etc.) if they exist
1588
1629
  if (componentModel.children && componentModel.children.length > 0) {
1589
1630
  componentModel.children.forEach(function (child) {
1590
- var _child$userData1, _child$userData10;
1591
- var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
1631
+ var _child$userData10, _child$userData11;
1632
+ var childType = ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType) || ((_child$userData11 = child.userData) === null || _child$userData11 === void 0 ? void 0 : _child$userData11.objectType);
1592
1633
  if (childType === 'connector') {
1593
1634
  var _child$geometry;
1594
1635
  var childBoundingBox = new THREE.Box3().setFromObject(child);
@@ -1673,8 +1714,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
1673
1714
  if (segment.children && segment.children.length > 0) {
1674
1715
  var childrenToRemove = _toConsumableArray(segment.children);
1675
1716
  childrenToRemove.forEach(function (child) {
1676
- var _child$userData11;
1677
- if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
1717
+ var _child$userData12;
1718
+ if ((_child$userData12 = child.userData) !== null && _child$userData12 !== void 0 && _child$userData12.isPipeElbow) {
1678
1719
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
1679
1720
  segment.remove(child);
1680
1721
  if (child.geometry) child.geometry.dispose();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/src/index.js",