@2112-lab/central-plant 0.1.90 → 0.1.91

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.
@@ -28570,91 +28570,150 @@ var BehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
28570
28570
  * Attach IO device models to a smart component from cached models.
28571
28571
  * Each device referenced in componentData.attachedDevices is looked up
28572
28572
  * in the model preloader cache, cloned, positioned, and added as a child.
28573
+ * If a device model is not in cache, it will be preloaded first.
28573
28574
  *
28574
28575
  * @param {THREE.Object3D} componentModel - The parent component model
28575
28576
  * @param {Object} componentData - Component dictionary entry (has attachedDevices)
28576
28577
  * @param {Object} modelPreloader - ModelPreloader instance with cache and componentDictionary
28577
28578
  * @param {string} parentComponentId - The parent component's UUID
28578
- * @returns {void}
28579
+ * @returns {Promise<void>}
28579
28580
  */
28580
- function attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
28581
- var attachedDevices = componentData.attachedDevices;
28582
- if (!attachedDevices || Object.keys(attachedDevices).length === 0) {
28583
- return;
28584
- }
28585
- console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
28586
- for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
28587
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
28588
- attachmentId = _Object$entries$_i[0],
28589
- attachment = _Object$entries$_i[1];
28590
- try {
28591
- var _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen;
28592
- var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
28593
- if (!deviceData || !deviceData.modelKey) {
28594
- console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
28595
- continue;
28596
- }
28597
- var cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
28598
- if (!cachedDevice) {
28599
- console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
28600
- continue;
28601
- }
28602
-
28603
- // Clone so each component instance owns its own io-device subtree and materials.
28604
- // Without this, all placed copies of the same smart component share the cached
28605
- // object, causing material mutations (from behaviors) to bleed across instances.
28606
- var deviceModel = cachedDevice.clone();
28607
- deviceModel.traverse(function (child) {
28608
- if (child.isMesh && child.material) {
28609
- child.material = Array.isArray(child.material) ? child.material.map(function (m) {
28610
- return m.clone();
28611
- }) : child.material.clone();
28612
- }
28613
- });
28581
+ function attachIODevicesToComponent(_x, _x2, _x3, _x4) {
28582
+ return _attachIODevicesToComponent.apply(this, arguments);
28583
+ }
28584
+ function _attachIODevicesToComponent() {
28585
+ _attachIODevicesToComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(componentModel, componentData, modelPreloader, parentComponentId) {
28586
+ var attachedDevices, _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t, _t2;
28587
+ return _regenerator().w(function (_context) {
28588
+ while (1) switch (_context.n) {
28589
+ case 0:
28590
+ attachedDevices = componentData.attachedDevices;
28591
+ if (!(!attachedDevices || Object.keys(attachedDevices).length === 0)) {
28592
+ _context.n = 1;
28593
+ break;
28594
+ }
28595
+ return _context.a(2);
28596
+ case 1:
28597
+ console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
28598
+ _i = 0, _Object$entries = Object.entries(attachedDevices);
28599
+ case 2:
28600
+ if (!(_i < _Object$entries.length)) {
28601
+ _context.n = 14;
28602
+ break;
28603
+ }
28604
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
28605
+ _context.p = 3;
28606
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
28607
+ if (!(!deviceData || !deviceData.modelKey)) {
28608
+ _context.n = 4;
28609
+ break;
28610
+ }
28611
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
28612
+ return _context.a(3, 13);
28613
+ case 4:
28614
+ // Try to get from cache first
28615
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId); // If not in cache, try to preload it
28616
+ if (cachedDevice) {
28617
+ _context.n = 10;
28618
+ break;
28619
+ }
28620
+ console.log("\uD83D\uDD04 IO device model not in cache, preloading: ".concat(deviceData.modelKey));
28621
+ _context.p = 5;
28622
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
28623
+ _context.n = 7;
28624
+ break;
28625
+ }
28626
+ _context.n = 6;
28627
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
28628
+ case 6:
28629
+ _context.n = 8;
28630
+ break;
28631
+ case 7:
28632
+ _context.n = 8;
28633
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
28634
+ case 8:
28635
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
28636
+ _context.n = 10;
28637
+ break;
28638
+ case 9:
28639
+ _context.p = 9;
28640
+ _t = _context.v;
28641
+ console.warn("\u26A0\uFE0F Failed to preload IO device model ".concat(deviceData.modelKey, ":"), _t);
28642
+ case 10:
28643
+ if (cachedDevice) {
28644
+ _context.n = 11;
28645
+ break;
28646
+ }
28647
+ console.warn("\u26A0\uFE0F IO device model could not be loaded: ".concat(deviceData.modelKey, ", skipping"));
28648
+ return _context.a(3, 13);
28649
+ case 11:
28650
+ // Clone so each component instance owns its own io-device subtree and materials.
28651
+ // Without this, all placed copies of the same smart component share the cached
28652
+ // object, causing material mutations (from behaviors) to bleed across instances.
28653
+ deviceModel = cachedDevice.clone();
28654
+ deviceModel.traverse(function (child) {
28655
+ if (child.isMesh && child.material) {
28656
+ child.material = Array.isArray(child.material) ? child.material.map(function (m) {
28657
+ return m.clone();
28658
+ }) : child.material.clone();
28659
+ }
28660
+ });
28614
28661
 
28615
- // Name the device model
28616
- deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
28617
-
28618
- // Set user data for identification — include ioConfig data points so the
28619
- // component tooltip can render state displays without an extra lookup.
28620
- deviceModel.userData = {
28621
- objectType: 'io-device',
28622
- deviceId: attachment.deviceId,
28623
- attachmentId: attachmentId,
28624
- attachmentLabel: attachment.attachmentLabel,
28625
- parentComponentId: parentComponentId,
28626
- deviceName: deviceData.name || '',
28627
- // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
28628
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
28629
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
28630
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
28631
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
28632
- // Signal wiring sourced from this attachment (for state propagation reference)
28633
- signalOutputs: attachment.signalOutputs || []
28634
- };
28662
+ // Name the device model
28663
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
28635
28664
 
28636
- // Position at the attachment point
28637
- if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
28638
- var pos = attachment.attachmentPoint.position;
28639
- deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
28640
- }
28665
+ // Set user data for identification — include ioConfig data points so the
28666
+ // component tooltip can render state displays without an extra lookup.
28667
+ deviceModel.userData = {
28668
+ objectType: 'io-device',
28669
+ deviceId: attachment.deviceId,
28670
+ attachmentId: attachmentId,
28671
+ attachmentLabel: attachment.attachmentLabel,
28672
+ parentComponentId: parentComponentId,
28673
+ deviceName: deviceData.name || '',
28674
+ // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
28675
+ // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
28676
+ dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
28677
+ // Device-level I/O direction: 'input' means the user can write state via the tooltip
28678
+ ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
28679
+ // Signal wiring sourced from this attachment (for state propagation reference)
28680
+ signalOutputs: attachment.signalOutputs || []
28681
+ };
28641
28682
 
28642
- // IO device models are authored at the same real-world unit scale
28643
- // as the host component, so keep them at their natural (1:1) size.
28644
- // Note: attachmentPoint.scale is the connector marker sphere size,
28645
- // NOT a desired device model scale.
28646
- deviceModel.scale.setScalar(1);
28683
+ // Position at the attachment point
28684
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
28685
+ pos = attachment.attachmentPoint.position;
28686
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
28687
+ }
28647
28688
 
28648
- // Add as child of the component
28649
- componentModel.add(deviceModel);
28650
- console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
28651
- position: deviceModel.position,
28652
- scale: deviceModel.scale
28653
- });
28654
- } catch (err) {
28655
- console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
28656
- }
28657
- }
28689
+ // IO device models are authored at the same real-world unit scale
28690
+ // as the host component, so keep them at their natural (1:1) size.
28691
+ // Note: attachmentPoint.scale is the connector marker sphere size,
28692
+ // NOT a desired device model scale.
28693
+ deviceModel.scale.setScalar(1);
28694
+
28695
+ // Add as child of the component
28696
+ componentModel.add(deviceModel);
28697
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
28698
+ position: deviceModel.position,
28699
+ scale: deviceModel.scale
28700
+ });
28701
+ _context.n = 13;
28702
+ break;
28703
+ case 12:
28704
+ _context.p = 12;
28705
+ _t2 = _context.v;
28706
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), _t2);
28707
+ case 13:
28708
+ _i++;
28709
+ _context.n = 2;
28710
+ break;
28711
+ case 14:
28712
+ return _context.a(2);
28713
+ }
28714
+ }, _callee, null, [[5, 9], [3, 12]]);
28715
+ }));
28716
+ return _attachIODevicesToComponent.apply(this, arguments);
28658
28717
  }
28659
28718
 
28660
28719
  var ModelManager = /*#__PURE__*/function () {
@@ -28741,21 +28800,24 @@ var ModelManager = /*#__PURE__*/function () {
28741
28800
  });
28742
28801
 
28743
28802
  // Attach IO devices for smart components (import flow)
28744
- if (componentData.isSmart && componentData.attachedDevices) {
28745
- attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
28803
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
28804
+ _context.n = 4;
28805
+ break;
28746
28806
  }
28747
-
28807
+ _context.n = 4;
28808
+ return attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
28809
+ case 4:
28748
28810
  // Replace mesh in scene
28749
28811
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
28750
28812
  console.log("\uD83C\uDF89 ".concat((_jsonEntry$userData3 = jsonEntry.userData) === null || _jsonEntry$userData3 === void 0 ? void 0 : _jsonEntry$userData3.libraryId, " GLB model successfully rendered in scene"));
28751
28813
  return _context.a(2, libraryModel);
28752
- case 4:
28753
- _context.p = 4;
28814
+ case 5:
28815
+ _context.p = 5;
28754
28816
  _t = _context.v;
28755
28817
  console.error("\u274C Error loading ".concat((_jsonEntry$userData4 = jsonEntry.userData) === null || _jsonEntry$userData4 === void 0 ? void 0 : _jsonEntry$userData4.libraryId, " GLB model:"), _t);
28756
28818
  return _context.a(2, targetMesh);
28757
28819
  }
28758
- }, _callee, this, [[1, 4]]);
28820
+ }, _callee, this, [[1, 5]]);
28759
28821
  }));
28760
28822
  function loadLibraryModel(_x, _x2, _x3) {
28761
28823
  return _loadLibraryModel.apply(this, arguments);
@@ -36814,7 +36876,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
36814
36876
  * Initialize the CentralPlant manager
36815
36877
  *
36816
36878
  * @constructor
36817
- * @version 0.1.90
36879
+ * @version 0.1.91
36818
36880
  * @updated 2025-10-22
36819
36881
  *
36820
36882
  * @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.90
22
+ * @version 0.1.91
23
23
  * @updated 2025-10-22
24
24
  *
25
25
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -111,21 +111,24 @@ var ModelManager = /*#__PURE__*/function () {
111
111
  });
112
112
 
113
113
  // Attach IO devices for smart components (import flow)
114
- if (componentData.isSmart && componentData.attachedDevices) {
115
- ioDeviceUtils.attachIODevicesToComponent(libraryModel, componentData, modelPreloader["default"], originalProps.uuid);
114
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
115
+ _context.n = 4;
116
+ break;
116
117
  }
117
-
118
+ _context.n = 4;
119
+ return ioDeviceUtils.attachIODevicesToComponent(libraryModel, componentData, modelPreloader["default"], originalProps.uuid);
120
+ case 4:
118
121
  // Replace mesh in scene
119
122
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
120
123
  console.log("\uD83C\uDF89 ".concat((_jsonEntry$userData3 = jsonEntry.userData) === null || _jsonEntry$userData3 === void 0 ? void 0 : _jsonEntry$userData3.libraryId, " GLB model successfully rendered in scene"));
121
124
  return _context.a(2, libraryModel);
122
- case 4:
123
- _context.p = 4;
125
+ case 5:
126
+ _context.p = 5;
124
127
  _t = _context.v;
125
128
  console.error("\u274C Error loading ".concat((_jsonEntry$userData4 = jsonEntry.userData) === null || _jsonEntry$userData4 === void 0 ? void 0 : _jsonEntry$userData4.libraryId, " GLB model:"), _t);
126
129
  return _context.a(2, targetMesh);
127
130
  }
128
- }, _callee, this, [[1, 4]]);
131
+ }, _callee, this, [[1, 5]]);
129
132
  }));
130
133
  function loadLibraryModel(_x, _x2, _x3) {
131
134
  return _loadLibraryModel.apply(this, arguments);
@@ -14,91 +14,150 @@ var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelper
14
14
  * Attach IO device models to a smart component from cached models.
15
15
  * Each device referenced in componentData.attachedDevices is looked up
16
16
  * in the model preloader cache, cloned, positioned, and added as a child.
17
+ * If a device model is not in cache, it will be preloaded first.
17
18
  *
18
19
  * @param {THREE.Object3D} componentModel - The parent component model
19
20
  * @param {Object} componentData - Component dictionary entry (has attachedDevices)
20
21
  * @param {Object} modelPreloader - ModelPreloader instance with cache and componentDictionary
21
22
  * @param {string} parentComponentId - The parent component's UUID
22
- * @returns {void}
23
+ * @returns {Promise<void>}
23
24
  */
24
- function attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
25
- var attachedDevices = componentData.attachedDevices;
26
- if (!attachedDevices || Object.keys(attachedDevices).length === 0) {
27
- return;
28
- }
29
- console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
30
- for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
31
- var _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2),
32
- attachmentId = _Object$entries$_i[0],
33
- attachment = _Object$entries$_i[1];
34
- try {
35
- var _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen;
36
- var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
37
- if (!deviceData || !deviceData.modelKey) {
38
- console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
39
- continue;
40
- }
41
- var cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
42
- if (!cachedDevice) {
43
- console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
44
- continue;
45
- }
46
-
47
- // Clone so each component instance owns its own io-device subtree and materials.
48
- // Without this, all placed copies of the same smart component share the cached
49
- // object, causing material mutations (from behaviors) to bleed across instances.
50
- var deviceModel = cachedDevice.clone();
51
- deviceModel.traverse(function (child) {
52
- if (child.isMesh && child.material) {
53
- child.material = Array.isArray(child.material) ? child.material.map(function (m) {
54
- return m.clone();
55
- }) : child.material.clone();
56
- }
57
- });
25
+ function attachIODevicesToComponent(_x, _x2, _x3, _x4) {
26
+ return _attachIODevicesToComponent.apply(this, arguments);
27
+ }
28
+ function _attachIODevicesToComponent() {
29
+ _attachIODevicesToComponent = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee(componentModel, componentData, modelPreloader, parentComponentId) {
30
+ var attachedDevices, _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t, _t2;
31
+ return _rollupPluginBabelHelpers.regenerator().w(function (_context) {
32
+ while (1) switch (_context.n) {
33
+ case 0:
34
+ attachedDevices = componentData.attachedDevices;
35
+ if (!(!attachedDevices || Object.keys(attachedDevices).length === 0)) {
36
+ _context.n = 1;
37
+ break;
38
+ }
39
+ return _context.a(2);
40
+ case 1:
41
+ console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
42
+ _i = 0, _Object$entries = Object.entries(attachedDevices);
43
+ case 2:
44
+ if (!(_i < _Object$entries.length)) {
45
+ _context.n = 14;
46
+ break;
47
+ }
48
+ _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
49
+ _context.p = 3;
50
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
51
+ if (!(!deviceData || !deviceData.modelKey)) {
52
+ _context.n = 4;
53
+ break;
54
+ }
55
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
56
+ return _context.a(3, 13);
57
+ case 4:
58
+ // Try to get from cache first
59
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId); // If not in cache, try to preload it
60
+ if (cachedDevice) {
61
+ _context.n = 10;
62
+ break;
63
+ }
64
+ console.log("\uD83D\uDD04 IO device model not in cache, preloading: ".concat(deviceData.modelKey));
65
+ _context.p = 5;
66
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
67
+ _context.n = 7;
68
+ break;
69
+ }
70
+ _context.n = 6;
71
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
72
+ case 6:
73
+ _context.n = 8;
74
+ break;
75
+ case 7:
76
+ _context.n = 8;
77
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
78
+ case 8:
79
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
80
+ _context.n = 10;
81
+ break;
82
+ case 9:
83
+ _context.p = 9;
84
+ _t = _context.v;
85
+ console.warn("\u26A0\uFE0F Failed to preload IO device model ".concat(deviceData.modelKey, ":"), _t);
86
+ case 10:
87
+ if (cachedDevice) {
88
+ _context.n = 11;
89
+ break;
90
+ }
91
+ console.warn("\u26A0\uFE0F IO device model could not be loaded: ".concat(deviceData.modelKey, ", skipping"));
92
+ return _context.a(3, 13);
93
+ case 11:
94
+ // Clone so each component instance owns its own io-device subtree and materials.
95
+ // Without this, all placed copies of the same smart component share the cached
96
+ // object, causing material mutations (from behaviors) to bleed across instances.
97
+ deviceModel = cachedDevice.clone();
98
+ deviceModel.traverse(function (child) {
99
+ if (child.isMesh && child.material) {
100
+ child.material = Array.isArray(child.material) ? child.material.map(function (m) {
101
+ return m.clone();
102
+ }) : child.material.clone();
103
+ }
104
+ });
58
105
 
59
- // Name the device model
60
- deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
106
+ // Name the device model
107
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
61
108
 
62
- // Set user data for identification — include ioConfig data points so the
63
- // component tooltip can render state displays without an extra lookup.
64
- deviceModel.userData = {
65
- objectType: 'io-device',
66
- deviceId: attachment.deviceId,
67
- attachmentId: attachmentId,
68
- attachmentLabel: attachment.attachmentLabel,
69
- parentComponentId: parentComponentId,
70
- deviceName: deviceData.name || '',
71
- // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
72
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
73
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
74
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
75
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
76
- // Signal wiring sourced from this attachment (for state propagation reference)
77
- signalOutputs: attachment.signalOutputs || []
78
- };
109
+ // Set user data for identification — include ioConfig data points so the
110
+ // component tooltip can render state displays without an extra lookup.
111
+ deviceModel.userData = {
112
+ objectType: 'io-device',
113
+ deviceId: attachment.deviceId,
114
+ attachmentId: attachmentId,
115
+ attachmentLabel: attachment.attachmentLabel,
116
+ parentComponentId: parentComponentId,
117
+ deviceName: deviceData.name || '',
118
+ // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
119
+ // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
120
+ dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
121
+ // Device-level I/O direction: 'input' means the user can write state via the tooltip
122
+ ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
123
+ // Signal wiring sourced from this attachment (for state propagation reference)
124
+ signalOutputs: attachment.signalOutputs || []
125
+ };
79
126
 
80
- // Position at the attachment point
81
- if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
82
- var pos = attachment.attachmentPoint.position;
83
- deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
84
- }
127
+ // Position at the attachment point
128
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
129
+ pos = attachment.attachmentPoint.position;
130
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
131
+ }
85
132
 
86
- // IO device models are authored at the same real-world unit scale
87
- // as the host component, so keep them at their natural (1:1) size.
88
- // Note: attachmentPoint.scale is the connector marker sphere size,
89
- // NOT a desired device model scale.
90
- deviceModel.scale.setScalar(1);
133
+ // IO device models are authored at the same real-world unit scale
134
+ // as the host component, so keep them at their natural (1:1) size.
135
+ // Note: attachmentPoint.scale is the connector marker sphere size,
136
+ // NOT a desired device model scale.
137
+ deviceModel.scale.setScalar(1);
91
138
 
92
- // Add as child of the component
93
- componentModel.add(deviceModel);
94
- console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
95
- position: deviceModel.position,
96
- scale: deviceModel.scale
97
- });
98
- } catch (err) {
99
- console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
100
- }
101
- }
139
+ // Add as child of the component
140
+ componentModel.add(deviceModel);
141
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
142
+ position: deviceModel.position,
143
+ scale: deviceModel.scale
144
+ });
145
+ _context.n = 13;
146
+ break;
147
+ case 12:
148
+ _context.p = 12;
149
+ _t2 = _context.v;
150
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), _t2);
151
+ case 13:
152
+ _i++;
153
+ _context.n = 2;
154
+ break;
155
+ case 14:
156
+ return _context.a(2);
157
+ }
158
+ }, _callee, null, [[5, 9], [3, 12]]);
159
+ }));
160
+ return _attachIODevicesToComponent.apply(this, arguments);
102
161
  }
103
162
 
104
163
  exports.attachIODevicesToComponent = attachIODevicesToComponent;
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.90
18
+ * @version 0.1.91
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -87,21 +87,24 @@ var ModelManager = /*#__PURE__*/function () {
87
87
  });
88
88
 
89
89
  // Attach IO devices for smart components (import flow)
90
- if (componentData.isSmart && componentData.attachedDevices) {
91
- attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
90
+ if (!(componentData.isSmart && componentData.attachedDevices)) {
91
+ _context.n = 4;
92
+ break;
92
93
  }
93
-
94
+ _context.n = 4;
95
+ return attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
96
+ case 4:
94
97
  // Replace mesh in scene
95
98
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
96
99
  console.log("\uD83C\uDF89 ".concat((_jsonEntry$userData3 = jsonEntry.userData) === null || _jsonEntry$userData3 === void 0 ? void 0 : _jsonEntry$userData3.libraryId, " GLB model successfully rendered in scene"));
97
100
  return _context.a(2, libraryModel);
98
- case 4:
99
- _context.p = 4;
101
+ case 5:
102
+ _context.p = 5;
100
103
  _t = _context.v;
101
104
  console.error("\u274C Error loading ".concat((_jsonEntry$userData4 = jsonEntry.userData) === null || _jsonEntry$userData4 === void 0 ? void 0 : _jsonEntry$userData4.libraryId, " GLB model:"), _t);
102
105
  return _context.a(2, targetMesh);
103
106
  }
104
- }, _callee, this, [[1, 4]]);
107
+ }, _callee, this, [[1, 5]]);
105
108
  }));
106
109
  function loadLibraryModel(_x, _x2, _x3) {
107
110
  return _loadLibraryModel.apply(this, arguments);
@@ -1,4 +1,4 @@
1
- import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
2
 
3
3
  /**
4
4
  * IO Device Utilities
@@ -10,91 +10,150 @@ import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBab
10
10
  * Attach IO device models to a smart component from cached models.
11
11
  * Each device referenced in componentData.attachedDevices is looked up
12
12
  * in the model preloader cache, cloned, positioned, and added as a child.
13
+ * If a device model is not in cache, it will be preloaded first.
13
14
  *
14
15
  * @param {THREE.Object3D} componentModel - The parent component model
15
16
  * @param {Object} componentData - Component dictionary entry (has attachedDevices)
16
17
  * @param {Object} modelPreloader - ModelPreloader instance with cache and componentDictionary
17
18
  * @param {string} parentComponentId - The parent component's UUID
18
- * @returns {void}
19
+ * @returns {Promise<void>}
19
20
  */
20
- function attachIODevicesToComponent(componentModel, componentData, modelPreloader, parentComponentId) {
21
- var attachedDevices = componentData.attachedDevices;
22
- if (!attachedDevices || Object.keys(attachedDevices).length === 0) {
23
- return;
24
- }
25
- console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
26
- for (var _i = 0, _Object$entries = Object.entries(attachedDevices); _i < _Object$entries.length; _i++) {
27
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
28
- attachmentId = _Object$entries$_i[0],
29
- attachment = _Object$entries$_i[1];
30
- try {
31
- var _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen;
32
- var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
33
- if (!deviceData || !deviceData.modelKey) {
34
- console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
35
- continue;
36
- }
37
- var cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
38
- if (!cachedDevice) {
39
- console.warn("\u26A0\uFE0F IO device model not in cache: ".concat(deviceData.modelKey, ", skipping"));
40
- continue;
41
- }
42
-
43
- // Clone so each component instance owns its own io-device subtree and materials.
44
- // Without this, all placed copies of the same smart component share the cached
45
- // object, causing material mutations (from behaviors) to bleed across instances.
46
- var deviceModel = cachedDevice.clone();
47
- deviceModel.traverse(function (child) {
48
- if (child.isMesh && child.material) {
49
- child.material = Array.isArray(child.material) ? child.material.map(function (m) {
50
- return m.clone();
51
- }) : child.material.clone();
52
- }
53
- });
21
+ function attachIODevicesToComponent(_x, _x2, _x3, _x4) {
22
+ return _attachIODevicesToComponent.apply(this, arguments);
23
+ }
24
+ function _attachIODevicesToComponent() {
25
+ _attachIODevicesToComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(componentModel, componentData, modelPreloader, parentComponentId) {
26
+ var attachedDevices, _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, _t, _t2;
27
+ return _regenerator().w(function (_context) {
28
+ while (1) switch (_context.n) {
29
+ case 0:
30
+ attachedDevices = componentData.attachedDevices;
31
+ if (!(!attachedDevices || Object.keys(attachedDevices).length === 0)) {
32
+ _context.n = 1;
33
+ break;
34
+ }
35
+ return _context.a(2);
36
+ case 1:
37
+ console.log("\uD83D\uDD0C attachIODevicesToComponent(): Attaching ".concat(Object.keys(attachedDevices).length, " IO devices to smart component"));
38
+ _i = 0, _Object$entries = Object.entries(attachedDevices);
39
+ case 2:
40
+ if (!(_i < _Object$entries.length)) {
41
+ _context.n = 14;
42
+ break;
43
+ }
44
+ _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
45
+ _context.p = 3;
46
+ deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
47
+ if (!(!deviceData || !deviceData.modelKey)) {
48
+ _context.n = 4;
49
+ break;
50
+ }
51
+ console.warn("\u26A0\uFE0F IO device ".concat(attachment.deviceId, " not found in dictionary, skipping"));
52
+ return _context.a(3, 13);
53
+ case 4:
54
+ // Try to get from cache first
55
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId); // If not in cache, try to preload it
56
+ if (cachedDevice) {
57
+ _context.n = 10;
58
+ break;
59
+ }
60
+ console.log("\uD83D\uDD04 IO device model not in cache, preloading: ".concat(deviceData.modelKey));
61
+ _context.p = 5;
62
+ if (!((_modelPreloader$loadi = modelPreloader.loadingPromises) !== null && _modelPreloader$loadi !== void 0 && _modelPreloader$loadi.has(deviceData.modelKey))) {
63
+ _context.n = 7;
64
+ break;
65
+ }
66
+ _context.n = 6;
67
+ return modelPreloader.loadingPromises.get(deviceData.modelKey);
68
+ case 6:
69
+ _context.n = 8;
70
+ break;
71
+ case 7:
72
+ _context.n = 8;
73
+ return modelPreloader.preloadSingleModel(deviceData.modelKey);
74
+ case 8:
75
+ cachedDevice = modelPreloader.getCachedModelWithDimensions(deviceData.modelKey, attachment.deviceId);
76
+ _context.n = 10;
77
+ break;
78
+ case 9:
79
+ _context.p = 9;
80
+ _t = _context.v;
81
+ console.warn("\u26A0\uFE0F Failed to preload IO device model ".concat(deviceData.modelKey, ":"), _t);
82
+ case 10:
83
+ if (cachedDevice) {
84
+ _context.n = 11;
85
+ break;
86
+ }
87
+ console.warn("\u26A0\uFE0F IO device model could not be loaded: ".concat(deviceData.modelKey, ", skipping"));
88
+ return _context.a(3, 13);
89
+ case 11:
90
+ // Clone so each component instance owns its own io-device subtree and materials.
91
+ // Without this, all placed copies of the same smart component share the cached
92
+ // object, causing material mutations (from behaviors) to bleed across instances.
93
+ deviceModel = cachedDevice.clone();
94
+ deviceModel.traverse(function (child) {
95
+ if (child.isMesh && child.material) {
96
+ child.material = Array.isArray(child.material) ? child.material.map(function (m) {
97
+ return m.clone();
98
+ }) : child.material.clone();
99
+ }
100
+ });
54
101
 
55
- // Name the device model
56
- deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
102
+ // Name the device model
103
+ deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
57
104
 
58
- // Set user data for identification — include ioConfig data points so the
59
- // component tooltip can render state displays without an extra lookup.
60
- deviceModel.userData = {
61
- objectType: 'io-device',
62
- deviceId: attachment.deviceId,
63
- attachmentId: attachmentId,
64
- attachmentLabel: attachment.attachmentLabel,
65
- parentComponentId: parentComponentId,
66
- deviceName: deviceData.name || '',
67
- // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
68
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
69
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
70
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
71
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
72
- // Signal wiring sourced from this attachment (for state propagation reference)
73
- signalOutputs: attachment.signalOutputs || []
74
- };
105
+ // Set user data for identification — include ioConfig data points so the
106
+ // component tooltip can render state displays without an extra lookup.
107
+ deviceModel.userData = {
108
+ objectType: 'io-device',
109
+ deviceId: attachment.deviceId,
110
+ attachmentId: attachmentId,
111
+ attachmentLabel: attachment.attachmentLabel,
112
+ parentComponentId: parentComponentId,
113
+ deviceName: deviceData.name || '',
114
+ // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
115
+ // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
116
+ dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
117
+ // Device-level I/O direction: 'input' means the user can write state via the tooltip
118
+ ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output',
119
+ // Signal wiring sourced from this attachment (for state propagation reference)
120
+ signalOutputs: attachment.signalOutputs || []
121
+ };
75
122
 
76
- // Position at the attachment point
77
- if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
78
- var pos = attachment.attachmentPoint.position;
79
- deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
80
- }
123
+ // Position at the attachment point
124
+ if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
125
+ pos = attachment.attachmentPoint.position;
126
+ deviceModel.position.set(pos.x || 0, pos.y || 0, pos.z || 0);
127
+ }
81
128
 
82
- // IO device models are authored at the same real-world unit scale
83
- // as the host component, so keep them at their natural (1:1) size.
84
- // Note: attachmentPoint.scale is the connector marker sphere size,
85
- // NOT a desired device model scale.
86
- deviceModel.scale.setScalar(1);
129
+ // IO device models are authored at the same real-world unit scale
130
+ // as the host component, so keep them at their natural (1:1) size.
131
+ // Note: attachmentPoint.scale is the connector marker sphere size,
132
+ // NOT a desired device model scale.
133
+ deviceModel.scale.setScalar(1);
87
134
 
88
- // Add as child of the component
89
- componentModel.add(deviceModel);
90
- console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
91
- position: deviceModel.position,
92
- scale: deviceModel.scale
93
- });
94
- } catch (err) {
95
- console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), err);
96
- }
97
- }
135
+ // Add as child of the component
136
+ componentModel.add(deviceModel);
137
+ console.log("\u2705 Attached IO device: ".concat(attachment.attachmentLabel || attachment.deviceId, " at"), {
138
+ position: deviceModel.position,
139
+ scale: deviceModel.scale
140
+ });
141
+ _context.n = 13;
142
+ break;
143
+ case 12:
144
+ _context.p = 12;
145
+ _t2 = _context.v;
146
+ console.error("\u274C Error attaching IO device ".concat(attachment.deviceId, ":"), _t2);
147
+ case 13:
148
+ _i++;
149
+ _context.n = 2;
150
+ break;
151
+ case 14:
152
+ return _context.a(2);
153
+ }
154
+ }, _callee, null, [[5, 9], [3, 12]]);
155
+ }));
156
+ return _attachIODevicesToComponent.apply(this, arguments);
98
157
  }
99
158
 
100
159
  export { attachIODevicesToComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.1.90",
3
+ "version": "0.1.91",
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",