@2112-lab/central-plant 0.1.76 → 0.1.77

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.
@@ -30938,7 +30938,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
30938
30938
  console.log("\uD83D\uDD0D ModelPreloader available:", !!modelPreloader);
30939
30939
  console.log("\uD83D\uDD0D ComponentDictionary available:", !!(modelPreloader !== null && modelPreloader !== void 0 && modelPreloader.componentDictionary));
30940
30940
  if (!(modelPreloader && modelPreloader.componentDictionary)) {
30941
- _context2.n = 13;
30941
+ _context2.n = 14;
30942
30942
  break;
30943
30943
  }
30944
30944
  console.log("\uD83D\uDCDA Available dictionary keys:", Object.keys(modelPreloader.componentDictionary));
@@ -30954,7 +30954,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
30954
30954
  });
30955
30955
  }
30956
30956
  if (!(componentData && componentData.modelKey)) {
30957
- _context2.n = 11;
30957
+ _context2.n = 12;
30958
30958
  break;
30959
30959
  }
30960
30960
  // Try to get cached model first
@@ -30994,7 +30994,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
30994
30994
  console.warn("\u26A0\uFE0F Failed to preload model ".concat(componentData.modelKey, ":"), _t2);
30995
30995
  case 8:
30996
30996
  if (!cachedModel) {
30997
- _context2.n = 9;
30997
+ _context2.n = 10;
30998
30998
  break;
30999
30999
  }
31000
31000
  this.dragData.previewObject = cachedModel.clone();
@@ -31005,6 +31005,14 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31005
31005
  // Store original colors BEFORE making transparent
31006
31006
  this._storeOriginalColors(this.dragData.previewObject);
31007
31007
 
31008
+ // For smart components, load and attach IO device models to the preview
31009
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
31010
+ _context2.n = 9;
31011
+ break;
31012
+ }
31013
+ _context2.n = 9;
31014
+ return this._attachIODeviceModelsToPreview(this.dragData.previewObject, componentData, modelPreloader);
31015
+ case 9:
31008
31016
  // Make the preview semi-transparent
31009
31017
  this._setPreviewTransparency(this.dragData.previewObject, 0.5);
31010
31018
  this.dragData.previewObject.userData = {
@@ -31018,19 +31026,19 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31018
31026
  this.sceneViewer.scene.add(this.dragData.previewObject);
31019
31027
  console.log("\u2705 Created ".concat(componentData.isS3Component ? 'S3' : 'static', " GLB preview object for: ").concat(componentId));
31020
31028
  return _context2.a(2);
31021
- case 9:
31022
- console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
31023
31029
  case 10:
31024
- _context2.n = 12;
31025
- break;
31030
+ console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
31026
31031
  case 11:
31027
- console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
31028
- case 12:
31029
- _context2.n = 14;
31032
+ _context2.n = 13;
31030
31033
  break;
31034
+ case 12:
31035
+ console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
31031
31036
  case 13:
31032
- console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
31037
+ _context2.n = 15;
31038
+ break;
31033
31039
  case 14:
31040
+ console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
31041
+ case 15:
31034
31042
  // Fallback: Create a simple preview mesh if model not available
31035
31043
  geometry = new THREE__namespace.BoxGeometry(1, 1, 1);
31036
31044
  material = new THREE__namespace.MeshPhysicalMaterial({
@@ -31053,7 +31061,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31053
31061
  this.dragData.previewObject.position.set(1000, 1000, 1000);
31054
31062
  this.sceneViewer.scene.add(this.dragData.previewObject);
31055
31063
  console.log("\u26A0\uFE0F Created fallback wireframe preview for: ".concat(componentId));
31056
- case 15:
31064
+ case 16:
31057
31065
  return _context2.a(2);
31058
31066
  }
31059
31067
  }, _callee2, this, [[5, 7], [1, 3]]);
@@ -31063,6 +31071,111 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31063
31071
  }
31064
31072
  return _createPreviewObject;
31065
31073
  }()
31074
+ /**
31075
+ * Load and attach IO device models to a smart component preview
31076
+ * @param {THREE.Object3D} parentObject - The parent preview object
31077
+ * @param {Object} componentData - Component dictionary entry (must have attachedDevices)
31078
+ * @param {Object} modelPreloader - ModelPreloader instance
31079
+ * @private
31080
+ */
31081
+ )
31082
+ }, {
31083
+ key: "_attachIODeviceModelsToPreview",
31084
+ value: (function () {
31085
+ var _attachIODeviceModelsToPreview2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
31086
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t3;
31087
+ return _regenerator().w(function (_context3) {
31088
+ while (1) switch (_context3.n) {
31089
+ case 0:
31090
+ if (componentData.attachedDevices) {
31091
+ _context3.n = 1;
31092
+ break;
31093
+ }
31094
+ return _context3.a(2);
31095
+ case 1:
31096
+ _i = 0, _Object$entries = Object.entries(componentData.attachedDevices);
31097
+ case 2:
31098
+ if (!(_i < _Object$entries.length)) {
31099
+ _context3.n = 12;
31100
+ break;
31101
+ }
31102
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
31103
+ _context3.p = 3;
31104
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
31105
+ if (!(!deviceData || !deviceData.modelKey)) {
31106
+ _context3.n = 4;
31107
+ break;
31108
+ }
31109
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary for preview"));
31110
+ return _context3.a(3, 11);
31111
+ case 4:
31112
+ // Ensure device model is loaded
31113
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
31114
+ if (cachedDevice) {
31115
+ _context3.n = 8;
31116
+ break;
31117
+ }
31118
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
31119
+ _context3.n = 6;
31120
+ break;
31121
+ }
31122
+ _context3.n = 5;
31123
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
31124
+ case 5:
31125
+ _context3.n = 7;
31126
+ break;
31127
+ case 6:
31128
+ _context3.n = 7;
31129
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
31130
+ case 7:
31131
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
31132
+ case 8:
31133
+ if (cachedDevice) {
31134
+ _context3.n = 9;
31135
+ break;
31136
+ }
31137
+ console.warn("\u26A0\uFE0F Could not load IO device model: ".concat(deviceData.modelKey));
31138
+ return _context3.a(3, 11);
31139
+ case 9:
31140
+ deviceModel = cachedDevice.clone();
31141
+ this._cloneMaterials(deviceModel);
31142
+ this._storeOriginalColors(deviceModel);
31143
+ deviceModel.userData = {
31144
+ objectType: 'io-device',
31145
+ deviceId: attachment.deviceId,
31146
+ attachmentId: attachmentId,
31147
+ attachmentLabel: attachment.attachmentLabel
31148
+ };
31149
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
31150
+ pos = attachment.attachmentPoint.position;
31151
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
31152
+ }
31153
+
31154
+ // IO device models use their natural (1:1) scale — the stored
31155
+ // attachmentPoint.scale value is for the connector marker sphere.
31156
+ deviceModel.scale.setScalar(1);
31157
+ parentObject.add(deviceModel);
31158
+ console.log("\u2705 Attached IO device preview: ".concat(attachment.attachmentLabel || attachment.deviceId));
31159
+ _context3.n = 11;
31160
+ break;
31161
+ case 10:
31162
+ _context3.p = 10;
31163
+ _t3 = _context3.v;
31164
+ console.warn("\u26A0\uFE0F Could not attach IO device model ".concat(attachment.deviceId, " to preview:"), _t3);
31165
+ case 11:
31166
+ _i++;
31167
+ _context3.n = 2;
31168
+ break;
31169
+ case 12:
31170
+ return _context3.a(2);
31171
+ }
31172
+ }, _callee3, this, [[3, 10]]);
31173
+ }));
31174
+ function _attachIODeviceModelsToPreview(_x5, _x6, _x7) {
31175
+ return _attachIODeviceModelsToPreview2.apply(this, arguments);
31176
+ }
31177
+ return _attachIODeviceModelsToPreview;
31178
+ }()
31066
31179
  /**
31067
31180
  * Clone all materials in an object hierarchy to avoid shared material issues
31068
31181
  * @param {THREE.Object3D} object - The object to clone materials for
@@ -31210,8 +31323,8 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31210
31323
  });
31211
31324
 
31212
31325
  // Check for overlaps
31213
- for (var _i = 0, _sceneMeshes = sceneMeshes; _i < _sceneMeshes.length; _i++) {
31214
- var mesh = _sceneMeshes[_i];
31326
+ for (var _i2 = 0, _sceneMeshes = sceneMeshes; _i2 < _sceneMeshes.length; _i2++) {
31327
+ var mesh = _sceneMeshes[_i2];
31215
31328
  var meshBBox = new THREE__namespace.Box3().setFromObject(mesh);
31216
31329
  if (previewBBox.intersectsBox(meshBBox)) {
31217
31330
  console.log('⚠️ ComponentDragManager: Overlap detected with:', mesh.userData.objectType || mesh.name || mesh.uuid);
@@ -31525,50 +31638,50 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
31525
31638
  var _this3 = this;
31526
31639
  if (!element || !componentId) return;
31527
31640
  var handleMouseDown = /*#__PURE__*/function () {
31528
- var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(event) {
31529
- return _regenerator().w(function (_context3) {
31530
- while (1) switch (_context3.n) {
31641
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(event) {
31642
+ return _regenerator().w(function (_context4) {
31643
+ while (1) switch (_context4.n) {
31531
31644
  case 0:
31532
31645
  if (!(event.button !== 0)) {
31533
- _context3.n = 1;
31646
+ _context4.n = 1;
31534
31647
  break;
31535
31648
  }
31536
- return _context3.a(2);
31649
+ return _context4.a(2);
31537
31650
  case 1:
31538
31651
  // Only left mouse button
31539
31652
  event.preventDefault();
31540
- _context3.n = 2;
31653
+ _context4.n = 2;
31541
31654
  return _this3.startComponentDrag(componentId, element, event);
31542
31655
  case 2:
31543
- return _context3.a(2);
31656
+ return _context4.a(2);
31544
31657
  }
31545
- }, _callee3);
31658
+ }, _callee4);
31546
31659
  }));
31547
- return function handleMouseDown(_x5) {
31660
+ return function handleMouseDown(_x8) {
31548
31661
  return _ref.apply(this, arguments);
31549
31662
  };
31550
31663
  }();
31551
31664
  var handleTouchStart = /*#__PURE__*/function () {
31552
- var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(event) {
31553
- return _regenerator().w(function (_context4) {
31554
- while (1) switch (_context4.n) {
31665
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(event) {
31666
+ return _regenerator().w(function (_context5) {
31667
+ while (1) switch (_context5.n) {
31555
31668
  case 0:
31556
31669
  if (!(event.touches.length !== 1)) {
31557
- _context4.n = 1;
31670
+ _context5.n = 1;
31558
31671
  break;
31559
31672
  }
31560
- return _context4.a(2);
31673
+ return _context5.a(2);
31561
31674
  case 1:
31562
31675
  // Only single touch
31563
31676
  event.preventDefault();
31564
- _context4.n = 2;
31677
+ _context5.n = 2;
31565
31678
  return _this3.startComponentDrag(componentId, element, event);
31566
31679
  case 2:
31567
- return _context4.a(2);
31680
+ return _context5.a(2);
31568
31681
  }
31569
- }, _callee4);
31682
+ }, _callee5);
31570
31683
  }));
31571
- return function handleTouchStart(_x6) {
31684
+ return function handleTouchStart(_x9) {
31572
31685
  return _ref2.apply(this, arguments);
31573
31686
  };
31574
31687
  }();
@@ -34710,6 +34823,11 @@ var CentralPlantInternals = /*#__PURE__*/function () {
34710
34823
  });
34711
34824
  }
34712
34825
 
34826
+ // Add attached IO device models for smart components
34827
+ if (componentData.isSmart && componentData.attachedDevices) {
34828
+ this._attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
34829
+ }
34830
+
34713
34831
  // Notify the component manager about the new component
34714
34832
  if (componentManager.registerComponent) {
34715
34833
  componentManager.registerComponent(componentModel);
@@ -34760,6 +34878,74 @@ var CentralPlantInternals = /*#__PURE__*/function () {
34760
34878
  }
34761
34879
  }
34762
34880
 
34881
+ /**
34882
+ * Attach IO device models to a smart component from cached models.
34883
+ * Each device referenced in componentData.attachedDevices is looked up
34884
+ * in the model preloader cache, cloned, positioned, and added as a child.
34885
+ * @param {THREE.Object3D} componentModel - The parent component model
34886
+ * @param {Object} componentData - Component dictionary entry (has attachedDevices)
34887
+ * @param {Object} modelPreloader - ModelPreloader instance
34888
+ * @param {string} parentComponentId - The parent component's UUID
34889
+ * @private
34890
+ */
34891
+ }, {
34892
+ key: "_attachIODevicesToComponent",
34893
+ value: function _attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
34894
+ var attachedDevices = componentData.attachedDevices;
34895
+ console.log("\uD83D\uDD0C addComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
34896
+ for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
34897
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
34898
+ attachmentId = _Object$entries$_i[0],
34899
+ attachment = _Object$entries$_i[1];
34900
+ try {
34901
+ var _modelPreloader$compo, _attachment$attachmen;
34902
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
34903
+ if (!deviceData || !deviceData.modelKey) {
34904
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
34905
+ continue;
34906
+ }
34907
+ var deviceModel = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
34908
+ if (!deviceModel) {
34909
+ console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
34910
+ continue;
34911
+ }
34912
+
34913
+ // Name the device model
34914
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
34915
+
34916
+ // Set user data for identification
34917
+ deviceModel.userData = {
34918
+ objectType: 'io-device',
34919
+ deviceId: attachment.deviceId,
34920
+ attachmentId: attachmentId,
34921
+ attachmentLabel: attachment.attachmentLabel,
34922
+ parentComponentId: parentComponentId
34923
+ };
34924
+
34925
+ // Position at the attachment point
34926
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
34927
+ var pos = attachment.attachmentPoint.position;
34928
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
34929
+ }
34930
+
34931
+ // IO device models are authored at the same real-world unit scale
34932
+ // as the host component, so keep them at their natural (1:1) size.
34933
+ // Note: attachmentPoint.scale is the connector marker sphere size,
34934
+ // NOT a desired device model scale.
34935
+ deviceModel.scale.setScalar(1);
34936
+
34937
+ // Add as child of the component
34938
+ componentModel.add(deviceModel);
34939
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
34940
+ position: deviceModel.position,
34941
+ scale: deviceModel.scale
34942
+ });
34943
+ } catch (err) {
34944
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
34945
+ }
34946
+ }
34947
+ }
34948
+
34763
34949
  /**
34764
34950
  * Delete a component from the scene by componentId (internal implementation)
34765
34951
  * @param {string} componentId - The UUID of the component to delete
@@ -34865,7 +35051,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
34865
35051
  * Initialize the CentralPlant manager
34866
35052
  *
34867
35053
  * @constructor
34868
- * @version 0.1.76
35054
+ * @version 0.1.77
34869
35055
  * @updated 2025-10-22
34870
35056
  *
34871
35057
  * @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.76
22
+ * @version 0.1.77
23
23
  * @updated 2025-10-22
24
24
  *
25
25
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -1055,6 +1055,11 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1055
1055
  });
1056
1056
  }
1057
1057
 
1058
+ // Add attached IO device models for smart components
1059
+ if (componentData.isSmart && componentData.attachedDevices) {
1060
+ this._attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
1061
+ }
1062
+
1058
1063
  // Notify the component manager about the new component
1059
1064
  if (componentManager.registerComponent) {
1060
1065
  componentManager.registerComponent(componentModel);
@@ -1105,6 +1110,74 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1105
1110
  }
1106
1111
  }
1107
1112
 
1113
+ /**
1114
+ * Attach IO device models to a smart component from cached models.
1115
+ * Each device referenced in componentData.attachedDevices is looked up
1116
+ * in the model preloader cache, cloned, positioned, and added as a child.
1117
+ * @param {THREE.Object3D} componentModel - The parent component model
1118
+ * @param {Object} componentData - Component dictionary entry (has attachedDevices)
1119
+ * @param {Object} modelPreloader - ModelPreloader instance
1120
+ * @param {string} parentComponentId - The parent component's UUID
1121
+ * @private
1122
+ */
1123
+ }, {
1124
+ key: "_attachIODevicesToComponent",
1125
+ value: function _attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
1126
+ var attachedDevices = componentData.attachedDevices;
1127
+ console.log("\uD83D\uDD0C addComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
1128
+ for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
1129
+ var _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2),
1130
+ attachmentId = _Object$entries$_i[0],
1131
+ attachment = _Object$entries$_i[1];
1132
+ try {
1133
+ var _modelPreloader$compo, _attachment$attachmen;
1134
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
1135
+ if (!deviceData || !deviceData.modelKey) {
1136
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
1137
+ continue;
1138
+ }
1139
+ var deviceModel = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
1140
+ if (!deviceModel) {
1141
+ console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
1142
+ continue;
1143
+ }
1144
+
1145
+ // Name the device model
1146
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
1147
+
1148
+ // Set user data for identification
1149
+ deviceModel.userData = {
1150
+ objectType: 'io-device',
1151
+ deviceId: attachment.deviceId,
1152
+ attachmentId: attachmentId,
1153
+ attachmentLabel: attachment.attachmentLabel,
1154
+ parentComponentId: parentComponentId
1155
+ };
1156
+
1157
+ // Position at the attachment point
1158
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
1159
+ var pos = attachment.attachmentPoint.position;
1160
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
1161
+ }
1162
+
1163
+ // IO device models are authored at the same real-world unit scale
1164
+ // as the host component, so keep them at their natural (1:1) size.
1165
+ // Note: attachmentPoint.scale is the connector marker sphere size,
1166
+ // NOT a desired device model scale.
1167
+ deviceModel.scale.setScalar(1);
1168
+
1169
+ // Add as child of the component
1170
+ componentModel.add(deviceModel);
1171
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
1172
+ position: deviceModel.position,
1173
+ scale: deviceModel.scale
1174
+ });
1175
+ } catch (err) {
1176
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
1177
+ }
1178
+ }
1179
+ }
1180
+
1108
1181
  /**
1109
1182
  * Delete a component from the scene by componentId (internal implementation)
1110
1183
  * @param {string} componentId - The UUID of the component to delete
@@ -191,7 +191,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
191
191
  console.log("\uD83D\uDD0D ModelPreloader available:", !!modelPreloader);
192
192
  console.log("\uD83D\uDD0D ComponentDictionary available:", !!(modelPreloader !== null && modelPreloader !== void 0 && modelPreloader.componentDictionary));
193
193
  if (!(modelPreloader && modelPreloader.componentDictionary)) {
194
- _context2.n = 13;
194
+ _context2.n = 14;
195
195
  break;
196
196
  }
197
197
  console.log("\uD83D\uDCDA Available dictionary keys:", Object.keys(modelPreloader.componentDictionary));
@@ -207,7 +207,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
207
207
  });
208
208
  }
209
209
  if (!(componentData && componentData.modelKey)) {
210
- _context2.n = 11;
210
+ _context2.n = 12;
211
211
  break;
212
212
  }
213
213
  // Try to get cached model first
@@ -247,7 +247,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
247
247
  console.warn("\u26A0\uFE0F Failed to preload model ".concat(componentData.modelKey, ":"), _t2);
248
248
  case 8:
249
249
  if (!cachedModel) {
250
- _context2.n = 9;
250
+ _context2.n = 10;
251
251
  break;
252
252
  }
253
253
  this.dragData.previewObject = cachedModel.clone();
@@ -258,6 +258,14 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
258
258
  // Store original colors BEFORE making transparent
259
259
  this._storeOriginalColors(this.dragData.previewObject);
260
260
 
261
+ // For smart components, load and attach IO device models to the preview
262
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
263
+ _context2.n = 9;
264
+ break;
265
+ }
266
+ _context2.n = 9;
267
+ return this._attachIODeviceModelsToPreview(this.dragData.previewObject, componentData, modelPreloader);
268
+ case 9:
261
269
  // Make the preview semi-transparent
262
270
  this._setPreviewTransparency(this.dragData.previewObject, 0.5);
263
271
  this.dragData.previewObject.userData = {
@@ -271,19 +279,19 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
271
279
  this.sceneViewer.scene.add(this.dragData.previewObject);
272
280
  console.log("\u2705 Created ".concat(componentData.isS3Component ? 'S3' : 'static', " GLB preview object for: ").concat(componentId));
273
281
  return _context2.a(2);
274
- case 9:
275
- console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
276
282
  case 10:
277
- _context2.n = 12;
278
- break;
283
+ console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
279
284
  case 11:
280
- console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
281
- case 12:
282
- _context2.n = 14;
285
+ _context2.n = 13;
283
286
  break;
287
+ case 12:
288
+ console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
284
289
  case 13:
285
- console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
290
+ _context2.n = 15;
291
+ break;
286
292
  case 14:
293
+ console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
294
+ case 15:
287
295
  // Fallback: Create a simple preview mesh if model not available
288
296
  geometry = new THREE__namespace.BoxGeometry(1, 1, 1);
289
297
  material = new THREE__namespace.MeshPhysicalMaterial({
@@ -306,7 +314,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
306
314
  this.dragData.previewObject.position.set(1000, 1000, 1000);
307
315
  this.sceneViewer.scene.add(this.dragData.previewObject);
308
316
  console.log("\u26A0\uFE0F Created fallback wireframe preview for: ".concat(componentId));
309
- case 15:
317
+ case 16:
310
318
  return _context2.a(2);
311
319
  }
312
320
  }, _callee2, this, [[5, 7], [1, 3]]);
@@ -316,6 +324,111 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
316
324
  }
317
325
  return _createPreviewObject;
318
326
  }()
327
+ /**
328
+ * Load and attach IO device models to a smart component preview
329
+ * @param {THREE.Object3D} parentObject - The parent preview object
330
+ * @param {Object} componentData - Component dictionary entry (must have attachedDevices)
331
+ * @param {Object} modelPreloader - ModelPreloader instance
332
+ * @private
333
+ */
334
+ )
335
+ }, {
336
+ key: "_attachIODeviceModelsToPreview",
337
+ value: (function () {
338
+ var _attachIODeviceModelsToPreview2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
339
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t3;
340
+ return _rollupPluginBabelHelpers.regenerator().w(function (_context3) {
341
+ while (1) switch (_context3.n) {
342
+ case 0:
343
+ if (componentData.attachedDevices) {
344
+ _context3.n = 1;
345
+ break;
346
+ }
347
+ return _context3.a(2);
348
+ case 1:
349
+ _i = 0, _Object$entries = Object.entries(componentData.attachedDevices);
350
+ case 2:
351
+ if (!(_i < _Object$entries.length)) {
352
+ _context3.n = 12;
353
+ break;
354
+ }
355
+ _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
356
+ _context3.p = 3;
357
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
358
+ if (!(!deviceData || !deviceData.modelKey)) {
359
+ _context3.n = 4;
360
+ break;
361
+ }
362
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary for preview"));
363
+ return _context3.a(3, 11);
364
+ case 4:
365
+ // Ensure device model is loaded
366
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
367
+ if (cachedDevice) {
368
+ _context3.n = 8;
369
+ break;
370
+ }
371
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
372
+ _context3.n = 6;
373
+ break;
374
+ }
375
+ _context3.n = 5;
376
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
377
+ case 5:
378
+ _context3.n = 7;
379
+ break;
380
+ case 6:
381
+ _context3.n = 7;
382
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
383
+ case 7:
384
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
385
+ case 8:
386
+ if (cachedDevice) {
387
+ _context3.n = 9;
388
+ break;
389
+ }
390
+ console.warn("\u26A0\uFE0F Could not load IO device model: ".concat(deviceData.modelKey));
391
+ return _context3.a(3, 11);
392
+ case 9:
393
+ deviceModel = cachedDevice.clone();
394
+ this._cloneMaterials(deviceModel);
395
+ this._storeOriginalColors(deviceModel);
396
+ deviceModel.userData = {
397
+ objectType: 'io-device',
398
+ deviceId: attachment.deviceId,
399
+ attachmentId: attachmentId,
400
+ attachmentLabel: attachment.attachmentLabel
401
+ };
402
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
403
+ pos = attachment.attachmentPoint.position;
404
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
405
+ }
406
+
407
+ // IO device models use their natural (1:1) scale — the stored
408
+ // attachmentPoint.scale value is for the connector marker sphere.
409
+ deviceModel.scale.setScalar(1);
410
+ parentObject.add(deviceModel);
411
+ console.log("\u2705 Attached IO device preview: ".concat(attachment.attachmentLabel || attachment.deviceId));
412
+ _context3.n = 11;
413
+ break;
414
+ case 10:
415
+ _context3.p = 10;
416
+ _t3 = _context3.v;
417
+ console.warn("\u26A0\uFE0F Could not attach IO device model ".concat(attachment.deviceId, " to preview:"), _t3);
418
+ case 11:
419
+ _i++;
420
+ _context3.n = 2;
421
+ break;
422
+ case 12:
423
+ return _context3.a(2);
424
+ }
425
+ }, _callee3, this, [[3, 10]]);
426
+ }));
427
+ function _attachIODeviceModelsToPreview(_x5, _x6, _x7) {
428
+ return _attachIODeviceModelsToPreview2.apply(this, arguments);
429
+ }
430
+ return _attachIODeviceModelsToPreview;
431
+ }()
319
432
  /**
320
433
  * Clone all materials in an object hierarchy to avoid shared material issues
321
434
  * @param {THREE.Object3D} object - The object to clone materials for
@@ -463,8 +576,8 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
463
576
  });
464
577
 
465
578
  // Check for overlaps
466
- for (var _i = 0, _sceneMeshes = sceneMeshes; _i < _sceneMeshes.length; _i++) {
467
- var mesh = _sceneMeshes[_i];
579
+ for (var _i2 = 0, _sceneMeshes = sceneMeshes; _i2 < _sceneMeshes.length; _i2++) {
580
+ var mesh = _sceneMeshes[_i2];
468
581
  var meshBBox = new THREE__namespace.Box3().setFromObject(mesh);
469
582
  if (previewBBox.intersectsBox(meshBBox)) {
470
583
  console.log('⚠️ ComponentDragManager: Overlap detected with:', mesh.userData.objectType || mesh.name || mesh.uuid);
@@ -778,50 +891,50 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
778
891
  var _this3 = this;
779
892
  if (!element || !componentId) return;
780
893
  var handleMouseDown = /*#__PURE__*/function () {
781
- var _ref = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee3(event) {
782
- return _rollupPluginBabelHelpers.regenerator().w(function (_context3) {
783
- while (1) switch (_context3.n) {
894
+ var _ref = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee4(event) {
895
+ return _rollupPluginBabelHelpers.regenerator().w(function (_context4) {
896
+ while (1) switch (_context4.n) {
784
897
  case 0:
785
898
  if (!(event.button !== 0)) {
786
- _context3.n = 1;
899
+ _context4.n = 1;
787
900
  break;
788
901
  }
789
- return _context3.a(2);
902
+ return _context4.a(2);
790
903
  case 1:
791
904
  // Only left mouse button
792
905
  event.preventDefault();
793
- _context3.n = 2;
906
+ _context4.n = 2;
794
907
  return _this3.startComponentDrag(componentId, element, event);
795
908
  case 2:
796
- return _context3.a(2);
909
+ return _context4.a(2);
797
910
  }
798
- }, _callee3);
911
+ }, _callee4);
799
912
  }));
800
- return function handleMouseDown(_x5) {
913
+ return function handleMouseDown(_x8) {
801
914
  return _ref.apply(this, arguments);
802
915
  };
803
916
  }();
804
917
  var handleTouchStart = /*#__PURE__*/function () {
805
- var _ref2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee4(event) {
806
- return _rollupPluginBabelHelpers.regenerator().w(function (_context4) {
807
- while (1) switch (_context4.n) {
918
+ var _ref2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee5(event) {
919
+ return _rollupPluginBabelHelpers.regenerator().w(function (_context5) {
920
+ while (1) switch (_context5.n) {
808
921
  case 0:
809
922
  if (!(event.touches.length !== 1)) {
810
- _context4.n = 1;
923
+ _context5.n = 1;
811
924
  break;
812
925
  }
813
- return _context4.a(2);
926
+ return _context5.a(2);
814
927
  case 1:
815
928
  // Only single touch
816
929
  event.preventDefault();
817
- _context4.n = 2;
930
+ _context5.n = 2;
818
931
  return _this3.startComponentDrag(componentId, element, event);
819
932
  case 2:
820
- return _context4.a(2);
933
+ return _context5.a(2);
821
934
  }
822
- }, _callee4);
935
+ }, _callee5);
823
936
  }));
824
- return function handleTouchStart(_x6) {
937
+ return function handleTouchStart(_x9) {
825
938
  return _ref2.apply(this, arguments);
826
939
  };
827
940
  }();
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.76
18
+ * @version 0.1.77
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -1,4 +1,4 @@
1
- import { createClass as _createClass, objectSpread2 as _objectSpread2, createForOfIteratorHelper as _createForOfIteratorHelper, typeof as _typeof, classCallCheck as _classCallCheck, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { createClass as _createClass, objectSpread2 as _objectSpread2, createForOfIteratorHelper as _createForOfIteratorHelper, typeof as _typeof, slicedToArray as _slicedToArray, classCallCheck as _classCallCheck, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import * as THREE from 'three';
3
3
  import { CentralPlantValidator } from './centralPlantValidator.js';
4
4
  import { createTransformControls } from '../managers/controls/transformControlsManager.js';
@@ -1031,6 +1031,11 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1031
1031
  });
1032
1032
  }
1033
1033
 
1034
+ // Add attached IO device models for smart components
1035
+ if (componentData.isSmart && componentData.attachedDevices) {
1036
+ this._attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
1037
+ }
1038
+
1034
1039
  // Notify the component manager about the new component
1035
1040
  if (componentManager.registerComponent) {
1036
1041
  componentManager.registerComponent(componentModel);
@@ -1081,6 +1086,74 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1081
1086
  }
1082
1087
  }
1083
1088
 
1089
+ /**
1090
+ * Attach IO device models to a smart component from cached models.
1091
+ * Each device referenced in componentData.attachedDevices is looked up
1092
+ * in the model preloader cache, cloned, positioned, and added as a child.
1093
+ * @param {THREE.Object3D} componentModel - The parent component model
1094
+ * @param {Object} componentData - Component dictionary entry (has attachedDevices)
1095
+ * @param {Object} modelPreloader - ModelPreloader instance
1096
+ * @param {string} parentComponentId - The parent component's UUID
1097
+ * @private
1098
+ */
1099
+ }, {
1100
+ key: "_attachIODevicesToComponent",
1101
+ value: function _attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
1102
+ var attachedDevices = componentData.attachedDevices;
1103
+ console.log("\uD83D\uDD0C addComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
1104
+ for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
1105
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
1106
+ attachmentId = _Object$entries$_i[0],
1107
+ attachment = _Object$entries$_i[1];
1108
+ try {
1109
+ var _modelPreloader$compo, _attachment$attachmen;
1110
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
1111
+ if (!deviceData || !deviceData.modelKey) {
1112
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
1113
+ continue;
1114
+ }
1115
+ var deviceModel = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
1116
+ if (!deviceModel) {
1117
+ console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
1118
+ continue;
1119
+ }
1120
+
1121
+ // Name the device model
1122
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
1123
+
1124
+ // Set user data for identification
1125
+ deviceModel.userData = {
1126
+ objectType: 'io-device',
1127
+ deviceId: attachment.deviceId,
1128
+ attachmentId: attachmentId,
1129
+ attachmentLabel: attachment.attachmentLabel,
1130
+ parentComponentId: parentComponentId
1131
+ };
1132
+
1133
+ // Position at the attachment point
1134
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
1135
+ var pos = attachment.attachmentPoint.position;
1136
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
1137
+ }
1138
+
1139
+ // IO device models are authored at the same real-world unit scale
1140
+ // as the host component, so keep them at their natural (1:1) size.
1141
+ // Note: attachmentPoint.scale is the connector marker sphere size,
1142
+ // NOT a desired device model scale.
1143
+ deviceModel.scale.setScalar(1);
1144
+
1145
+ // Add as child of the component
1146
+ componentModel.add(deviceModel);
1147
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
1148
+ position: deviceModel.position,
1149
+ scale: deviceModel.scale
1150
+ });
1151
+ } catch (err) {
1152
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
1153
+ }
1154
+ }
1155
+ }
1156
+
1084
1157
  /**
1085
1158
  * Delete a component from the scene by componentId (internal implementation)
1086
1159
  * @param {string} componentId - The UUID of the component to delete
@@ -1,4 +1,4 @@
1
- import { inherits as _inherits, createClass as _createClass, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, slicedToArray as _slicedToArray } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import { BaseDisposable } from '../../core/baseDisposable.js';
3
3
  import * as THREE from 'three';
4
4
 
@@ -167,7 +167,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
167
167
  console.log("\uD83D\uDD0D ModelPreloader available:", !!modelPreloader);
168
168
  console.log("\uD83D\uDD0D ComponentDictionary available:", !!(modelPreloader !== null && modelPreloader !== void 0 && modelPreloader.componentDictionary));
169
169
  if (!(modelPreloader && modelPreloader.componentDictionary)) {
170
- _context2.n = 13;
170
+ _context2.n = 14;
171
171
  break;
172
172
  }
173
173
  console.log("\uD83D\uDCDA Available dictionary keys:", Object.keys(modelPreloader.componentDictionary));
@@ -183,7 +183,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
183
183
  });
184
184
  }
185
185
  if (!(componentData && componentData.modelKey)) {
186
- _context2.n = 11;
186
+ _context2.n = 12;
187
187
  break;
188
188
  }
189
189
  // Try to get cached model first
@@ -223,7 +223,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
223
223
  console.warn("\u26A0\uFE0F Failed to preload model ".concat(componentData.modelKey, ":"), _t2);
224
224
  case 8:
225
225
  if (!cachedModel) {
226
- _context2.n = 9;
226
+ _context2.n = 10;
227
227
  break;
228
228
  }
229
229
  this.dragData.previewObject = cachedModel.clone();
@@ -234,6 +234,14 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
234
234
  // Store original colors BEFORE making transparent
235
235
  this._storeOriginalColors(this.dragData.previewObject);
236
236
 
237
+ // For smart components, load and attach IO device models to the preview
238
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
239
+ _context2.n = 9;
240
+ break;
241
+ }
242
+ _context2.n = 9;
243
+ return this._attachIODeviceModelsToPreview(this.dragData.previewObject, componentData, modelPreloader);
244
+ case 9:
237
245
  // Make the preview semi-transparent
238
246
  this._setPreviewTransparency(this.dragData.previewObject, 0.5);
239
247
  this.dragData.previewObject.userData = {
@@ -247,19 +255,19 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
247
255
  this.sceneViewer.scene.add(this.dragData.previewObject);
248
256
  console.log("\u2705 Created ".concat(componentData.isS3Component ? 'S3' : 'static', " GLB preview object for: ").concat(componentId));
249
257
  return _context2.a(2);
250
- case 9:
251
- console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
252
258
  case 10:
253
- _context2.n = 12;
254
- break;
259
+ console.warn("\u26A0\uFE0F Failed to load model for ".concat(componentId, ", will use fallback"));
255
260
  case 11:
256
- console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
257
- case 12:
258
- _context2.n = 14;
261
+ _context2.n = 13;
259
262
  break;
263
+ case 12:
264
+ console.warn("\u26A0\uFE0F No modelKey found for component ".concat(componentId));
260
265
  case 13:
261
- console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
266
+ _context2.n = 15;
267
+ break;
262
268
  case 14:
269
+ console.warn("\u26A0\uFE0F ModelPreloader or component dictionary not available");
270
+ case 15:
263
271
  // Fallback: Create a simple preview mesh if model not available
264
272
  geometry = new THREE.BoxGeometry(1, 1, 1);
265
273
  material = new THREE.MeshPhysicalMaterial({
@@ -282,7 +290,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
282
290
  this.dragData.previewObject.position.set(1000, 1000, 1000);
283
291
  this.sceneViewer.scene.add(this.dragData.previewObject);
284
292
  console.log("\u26A0\uFE0F Created fallback wireframe preview for: ".concat(componentId));
285
- case 15:
293
+ case 16:
286
294
  return _context2.a(2);
287
295
  }
288
296
  }, _callee2, this, [[5, 7], [1, 3]]);
@@ -292,6 +300,111 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
292
300
  }
293
301
  return _createPreviewObject;
294
302
  }()
303
+ /**
304
+ * Load and attach IO device models to a smart component preview
305
+ * @param {THREE.Object3D} parentObject - The parent preview object
306
+ * @param {Object} componentData - Component dictionary entry (must have attachedDevices)
307
+ * @param {Object} modelPreloader - ModelPreloader instance
308
+ * @private
309
+ */
310
+ )
311
+ }, {
312
+ key: "_attachIODeviceModelsToPreview",
313
+ value: (function () {
314
+ var _attachIODeviceModelsToPreview2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
315
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t3;
316
+ return _regenerator().w(function (_context3) {
317
+ while (1) switch (_context3.n) {
318
+ case 0:
319
+ if (componentData.attachedDevices) {
320
+ _context3.n = 1;
321
+ break;
322
+ }
323
+ return _context3.a(2);
324
+ case 1:
325
+ _i = 0, _Object$entries = Object.entries(componentData.attachedDevices);
326
+ case 2:
327
+ if (!(_i < _Object$entries.length)) {
328
+ _context3.n = 12;
329
+ break;
330
+ }
331
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
332
+ _context3.p = 3;
333
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
334
+ if (!(!deviceData || !deviceData.modelKey)) {
335
+ _context3.n = 4;
336
+ break;
337
+ }
338
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary for preview"));
339
+ return _context3.a(3, 11);
340
+ case 4:
341
+ // Ensure device model is loaded
342
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
343
+ if (cachedDevice) {
344
+ _context3.n = 8;
345
+ break;
346
+ }
347
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
348
+ _context3.n = 6;
349
+ break;
350
+ }
351
+ _context3.n = 5;
352
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
353
+ case 5:
354
+ _context3.n = 7;
355
+ break;
356
+ case 6:
357
+ _context3.n = 7;
358
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
359
+ case 7:
360
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
361
+ case 8:
362
+ if (cachedDevice) {
363
+ _context3.n = 9;
364
+ break;
365
+ }
366
+ console.warn("\u26A0\uFE0F Could not load IO device model: ".concat(deviceData.modelKey));
367
+ return _context3.a(3, 11);
368
+ case 9:
369
+ deviceModel = cachedDevice.clone();
370
+ this._cloneMaterials(deviceModel);
371
+ this._storeOriginalColors(deviceModel);
372
+ deviceModel.userData = {
373
+ objectType: 'io-device',
374
+ deviceId: attachment.deviceId,
375
+ attachmentId: attachmentId,
376
+ attachmentLabel: attachment.attachmentLabel
377
+ };
378
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
379
+ pos = attachment.attachmentPoint.position;
380
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
381
+ }
382
+
383
+ // IO device models use their natural (1:1) scale — the stored
384
+ // attachmentPoint.scale value is for the connector marker sphere.
385
+ deviceModel.scale.setScalar(1);
386
+ parentObject.add(deviceModel);
387
+ console.log("\u2705 Attached IO device preview: ".concat(attachment.attachmentLabel || attachment.deviceId));
388
+ _context3.n = 11;
389
+ break;
390
+ case 10:
391
+ _context3.p = 10;
392
+ _t3 = _context3.v;
393
+ console.warn("\u26A0\uFE0F Could not attach IO device model ".concat(attachment.deviceId, " to preview:"), _t3);
394
+ case 11:
395
+ _i++;
396
+ _context3.n = 2;
397
+ break;
398
+ case 12:
399
+ return _context3.a(2);
400
+ }
401
+ }, _callee3, this, [[3, 10]]);
402
+ }));
403
+ function _attachIODeviceModelsToPreview(_x5, _x6, _x7) {
404
+ return _attachIODeviceModelsToPreview2.apply(this, arguments);
405
+ }
406
+ return _attachIODeviceModelsToPreview;
407
+ }()
295
408
  /**
296
409
  * Clone all materials in an object hierarchy to avoid shared material issues
297
410
  * @param {THREE.Object3D} object - The object to clone materials for
@@ -439,8 +552,8 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
439
552
  });
440
553
 
441
554
  // Check for overlaps
442
- for (var _i = 0, _sceneMeshes = sceneMeshes; _i < _sceneMeshes.length; _i++) {
443
- var mesh = _sceneMeshes[_i];
555
+ for (var _i2 = 0, _sceneMeshes = sceneMeshes; _i2 < _sceneMeshes.length; _i2++) {
556
+ var mesh = _sceneMeshes[_i2];
444
557
  var meshBBox = new THREE.Box3().setFromObject(mesh);
445
558
  if (previewBBox.intersectsBox(meshBBox)) {
446
559
  console.log('⚠️ ComponentDragManager: Overlap detected with:', mesh.userData.objectType || mesh.name || mesh.uuid);
@@ -754,50 +867,50 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
754
867
  var _this3 = this;
755
868
  if (!element || !componentId) return;
756
869
  var handleMouseDown = /*#__PURE__*/function () {
757
- var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(event) {
758
- return _regenerator().w(function (_context3) {
759
- while (1) switch (_context3.n) {
870
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(event) {
871
+ return _regenerator().w(function (_context4) {
872
+ while (1) switch (_context4.n) {
760
873
  case 0:
761
874
  if (!(event.button !== 0)) {
762
- _context3.n = 1;
875
+ _context4.n = 1;
763
876
  break;
764
877
  }
765
- return _context3.a(2);
878
+ return _context4.a(2);
766
879
  case 1:
767
880
  // Only left mouse button
768
881
  event.preventDefault();
769
- _context3.n = 2;
882
+ _context4.n = 2;
770
883
  return _this3.startComponentDrag(componentId, element, event);
771
884
  case 2:
772
- return _context3.a(2);
885
+ return _context4.a(2);
773
886
  }
774
- }, _callee3);
887
+ }, _callee4);
775
888
  }));
776
- return function handleMouseDown(_x5) {
889
+ return function handleMouseDown(_x8) {
777
890
  return _ref.apply(this, arguments);
778
891
  };
779
892
  }();
780
893
  var handleTouchStart = /*#__PURE__*/function () {
781
- var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(event) {
782
- return _regenerator().w(function (_context4) {
783
- while (1) switch (_context4.n) {
894
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(event) {
895
+ return _regenerator().w(function (_context5) {
896
+ while (1) switch (_context5.n) {
784
897
  case 0:
785
898
  if (!(event.touches.length !== 1)) {
786
- _context4.n = 1;
899
+ _context5.n = 1;
787
900
  break;
788
901
  }
789
- return _context4.a(2);
902
+ return _context5.a(2);
790
903
  case 1:
791
904
  // Only single touch
792
905
  event.preventDefault();
793
- _context4.n = 2;
906
+ _context5.n = 2;
794
907
  return _this3.startComponentDrag(componentId, element, event);
795
908
  case 2:
796
- return _context4.a(2);
909
+ return _context5.a(2);
797
910
  }
798
- }, _callee4);
911
+ }, _callee5);
799
912
  }));
800
- return function handleTouchStart(_x6) {
913
+ return function handleTouchStart(_x9) {
801
914
  return _ref2.apply(this, arguments);
802
915
  };
803
916
  }();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.1.76",
3
+ "version": "0.1.77",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/index.js",