@2112-lab/central-plant 0.3.24 → 0.3.26

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.
@@ -31513,13 +31513,14 @@ var ModelManager = /*#__PURE__*/function () {
31513
31513
  break;
31514
31514
  }
31515
31515
  _loop = /*#__PURE__*/_regenerator().m(function _loop() {
31516
- var _modelPreloader$compo;
31516
+ var _modelPreloader$compo, _deviceData$animation;
31517
31517
  var _Object$entries$_i, attachmentId, attachment, deviceData, deviceRoot;
31518
31518
  return _regenerator().w(function (_context) {
31519
31519
  while (1) switch (_context.n) {
31520
31520
  case 0:
31521
31521
  _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
31522
31522
  deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
31523
+ console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 animationConfig:"), (_deviceData$animation = deviceData === null || deviceData === void 0 ? void 0 : deviceData.animationConfig) !== null && _deviceData$animation !== void 0 ? _deviceData$animation : 'NONE');
31523
31524
  if (deviceData !== null && deviceData !== void 0 && deviceData.animationConfig) {
31524
31525
  _context.n = 1;
31525
31526
  break;
@@ -37187,14 +37188,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37187
37188
  object.traverse(function (child) {
37188
37189
  var _child$userData;
37189
37190
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
37191
+ var _this2$sceneViewer$ma, _this2$sceneViewer;
37190
37192
  var attachmentId = child.userData.attachmentId || '';
37193
+
37194
+ // Use only data points from the animate window (animationConfig).
37195
+ // The static ioConfig.states[] snapshot on userData is intentionally ignored.
37196
+ var dataPoints = (_this2$sceneViewer$ma = (_this2$sceneViewer = _this2.sceneViewer) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.managers) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.ioAnimationManager) === null || _this2$sceneViewer === void 0 ? void 0 : _this2$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this2$sceneViewer$ma !== void 0 ? _this2$sceneViewer$ma : [];
37197
+
37198
+ // When data points come from animationConfig they already carry direction:'input'.
37199
+ // Pass null so _buildDataPointRow uses the per-dp direction instead of the
37200
+ // device-level ioDirection (which may be 'output' and would hide controls).
37201
+ var deviceDirection = dataPoints.length > 0 ? null : child.userData.ioDirection || 'output';
37191
37202
  devices.push({
37192
37203
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
37193
37204
  deviceId: child.userData.deviceId || '',
37194
37205
  attachmentId: attachmentId,
37195
37206
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
37196
- dataPoints: child.userData.dataPoints || [],
37197
- direction: child.userData.ioDirection || 'output'
37207
+ dataPoints: dataPoints,
37208
+ direction: deviceDirection
37198
37209
  });
37199
37210
  }
37200
37211
  });
@@ -37668,7 +37679,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37668
37679
  }
37669
37680
  if (entries.length) {
37670
37681
  this._entries.set(key, entries);
37671
- console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ")"));
37682
+ console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
37683
+ return e.anim.stateVariable;
37684
+ }).join(', ')));
37685
+ } else {
37686
+ console.warn("[IoAnimationManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 animationConfig had ").concat(anims.length, " entries but none matched a mesh"));
37672
37687
  }
37673
37688
  }
37674
37689
 
@@ -37702,6 +37717,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37702
37717
  }
37703
37718
  }
37704
37719
 
37720
+ /**
37721
+ * Return tooltip-compatible data point definitions derived from the loaded
37722
+ * animation entries for a given attachment. Used by componentTooltipManager
37723
+ * to replace the static ioConfig.states[] snapshot with the richer animation
37724
+ * state definitions created in the Animate window.
37725
+ *
37726
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
37727
+ * that share the same stateVariable are collapsed into one.
37728
+ *
37729
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
37730
+ * {
37731
+ * id: string, // stateVariable name
37732
+ * name: string, // human-readable label (anim.name or stateVariable)
37733
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
37734
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
37735
+ * defaultValue: any, // sensible default for the stateType
37736
+ * direction: 'input' // all animation-driven states are interactive
37737
+ * }
37738
+ *
37739
+ * @param {string} parentUuid
37740
+ * @param {string} attachmentId
37741
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
37742
+ */
37743
+ }, {
37744
+ key: "getAnimationDataPoints",
37745
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
37746
+ var key = this._key(parentUuid, attachmentId);
37747
+ var entries = this._entries.get(key);
37748
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
37749
+
37750
+ // Collapse multiple mesh entries that share the same stateVariable
37751
+ var seen = new Map(); // stateVariable → anim
37752
+ var _iterator3 = _createForOfIteratorHelper(entries),
37753
+ _step3;
37754
+ try {
37755
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37756
+ var anim = _step3.value.anim;
37757
+ if (!seen.has(anim.stateVariable)) {
37758
+ seen.set(anim.stateVariable, anim);
37759
+ }
37760
+ }
37761
+ } catch (err) {
37762
+ _iterator3.e(err);
37763
+ } finally {
37764
+ _iterator3.f();
37765
+ }
37766
+ var dps = [];
37767
+ var _iterator4 = _createForOfIteratorHelper(seen),
37768
+ _step4;
37769
+ try {
37770
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37771
+ var _step4$value = _slicedToArray(_step4.value, 2),
37772
+ stateVar = _step4$value[0],
37773
+ _anim = _step4$value[1];
37774
+ // Normalise stateType from AnimateDevicesDialog variants
37775
+ var stateType = void 0;
37776
+ var raw = (_anim.stateType || '').toLowerCase();
37777
+ if (raw === 'binary' || raw === 'boolean') {
37778
+ stateType = 'binary';
37779
+ } else if (raw === 'enum') {
37780
+ stateType = 'enum';
37781
+ } else {
37782
+ // 'continuous', 'range', 'number', or anything else → numeric slider
37783
+ stateType = 'number';
37784
+ }
37785
+
37786
+ // Derive stateConfig from mappings
37787
+ var stateConfig = {};
37788
+ var mappingValues = (_anim.mappings || []).map(function (m) {
37789
+ return m.stateValue;
37790
+ });
37791
+ if (stateType === 'enum') {
37792
+ stateConfig.options = _toConsumableArray(new Set(mappingValues.map(String)));
37793
+ } else if (stateType === 'number') {
37794
+ var nums = mappingValues.map(Number).filter(function (n) {
37795
+ return !isNaN(n);
37796
+ });
37797
+ if (nums.length) {
37798
+ stateConfig.min = Math.min.apply(Math, _toConsumableArray(nums));
37799
+ stateConfig.max = Math.max.apply(Math, _toConsumableArray(nums));
37800
+ }
37801
+ }
37802
+
37803
+ // Sensible default values
37804
+ var defaultValue = void 0;
37805
+ if (stateType === 'binary') {
37806
+ defaultValue = 0;
37807
+ } else if (stateType === 'enum') {
37808
+ var _stateConfig$options$, _stateConfig$options;
37809
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
37810
+ } else {
37811
+ var _stateConfig$min;
37812
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
37813
+ }
37814
+ dps.push({
37815
+ id: stateVar,
37816
+ name: _anim.name || stateVar,
37817
+ stateType: stateType,
37818
+ stateConfig: stateConfig,
37819
+ defaultValue: defaultValue,
37820
+ direction: 'input'
37821
+ });
37822
+ }
37823
+ } catch (err) {
37824
+ _iterator4.e(err);
37825
+ } finally {
37826
+ _iterator4.f();
37827
+ }
37828
+ return dps;
37829
+ }
37830
+
37705
37831
  /**
37706
37832
  * Remove all animation entries associated with a given host component.
37707
37833
  * Call when a component is removed from the scene.
@@ -37712,19 +37838,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37712
37838
  key: "unloadForComponent",
37713
37839
  value: function unloadForComponent(parentUuid) {
37714
37840
  var prefix = "".concat(parentUuid, "::");
37715
- var _iterator3 = _createForOfIteratorHelper(this._entries.keys()),
37716
- _step3;
37841
+ var _iterator5 = _createForOfIteratorHelper(this._entries.keys()),
37842
+ _step5;
37717
37843
  try {
37718
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37719
- var key = _step3.value;
37844
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
37845
+ var key = _step5.value;
37720
37846
  if (key.startsWith(prefix)) {
37721
37847
  this._entries.delete(key);
37722
37848
  }
37723
37849
  }
37724
37850
  } catch (err) {
37725
- _iterator3.e(err);
37851
+ _iterator5.e(err);
37726
37852
  } finally {
37727
- _iterator3.f();
37853
+ _iterator5.f();
37728
37854
  }
37729
37855
  }
37730
37856
  }, {
@@ -37787,11 +37913,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37787
37913
  var mapping = this._resolveMapping(anim, value);
37788
37914
  if (!mapping) return;
37789
37915
  var types = anim.transformTypes || [];
37790
- var _iterator4 = _createForOfIteratorHelper(types),
37791
- _step4;
37916
+ var _iterator6 = _createForOfIteratorHelper(types),
37917
+ _step6;
37792
37918
  try {
37793
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37794
- var type = _step4.value;
37919
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
37920
+ var type = _step6.value;
37795
37921
  if (type === 'translation') {
37796
37922
  this._applyTranslation(mesh, origPos, mapping.transform);
37797
37923
  } else if (type === 'rotation') {
@@ -37801,9 +37927,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37801
37927
  }
37802
37928
  }
37803
37929
  } catch (err) {
37804
- _iterator4.e(err);
37930
+ _iterator6.e(err);
37805
37931
  } finally {
37806
- _iterator4.f();
37932
+ _iterator6.f();
37807
37933
  }
37808
37934
  }
37809
37935
 
@@ -37819,9 +37945,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37819
37945
  }, {
37820
37946
  key: "_resolveMapping",
37821
37947
  value: function _resolveMapping(anim, value) {
37822
- var stateType = anim.stateType,
37823
- mappings = anim.mappings;
37948
+ var mappings = anim.mappings;
37824
37949
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
37950
+
37951
+ // Normalise stateType variants saved by AnimateDevicesDialog
37952
+ // ('boolean' → 'binary', 'range' → 'continuous')
37953
+ var raw = (anim.stateType || '').toLowerCase();
37954
+ var stateType;
37955
+ if (raw === 'binary' || raw === 'boolean') {
37956
+ stateType = 'binary';
37957
+ } else if (raw === 'continuous' || raw === 'range') {
37958
+ stateType = 'continuous';
37959
+ } else {
37960
+ stateType = raw;
37961
+ }
37825
37962
  if (stateType === 'binary' || stateType === 'enum') {
37826
37963
  var _mappings$find;
37827
37964
  // eslint-disable-next-line eqeqeq
@@ -39089,15 +39226,40 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39089
39226
 
39090
39227
  // Add attached IO device models for smart components
39091
39228
  if (componentData.isSmart && componentData.attachedDevices) {
39229
+ var _this$centralPlant$sc8;
39092
39230
  attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
39231
+
39232
+ // Register animation configs so IoAnimationManager can respond to state changes
39233
+ var ioAnimMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioAnimationManager;
39234
+ if (ioAnimMgr) {
39235
+ var _loop = function _loop() {
39236
+ var _modelPreloader$compo;
39237
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
39238
+ attachmentId = _Object$entries$_i[0],
39239
+ attachment = _Object$entries$_i[1];
39240
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
39241
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.animationConfig)) return 1; // continue
39242
+ var deviceRoot = null;
39243
+ componentModel.traverse(function (obj) {
39244
+ var _obj$userData2;
39245
+ if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
39246
+ });
39247
+ if (deviceRoot) {
39248
+ ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, componentId);
39249
+ }
39250
+ };
39251
+ for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
39252
+ if (_loop()) continue;
39253
+ }
39254
+ }
39093
39255
  }
39094
39256
 
39095
39257
  // Register default behaviors for smart components so the BehaviorManager
39096
39258
  // responds to tooltip-driven state changes immediately after drop.
39097
39259
  // (The scene-load path uses _processBehaviors instead, which runs on loadSceneData.)
39098
39260
  if ((_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length) {
39099
- var _this$centralPlant$sc8, _som$registerBehavior;
39100
- var som = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.sceneOperationsManager;
39261
+ var _this$centralPlant$sc9, _som$registerBehavior;
39262
+ var som = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.sceneOperationsManager;
39101
39263
  som === null || som === void 0 || (_som$registerBehavior = som.registerBehaviorsForComponentInstance) === null || _som$registerBehavior === void 0 || _som$registerBehavior.call(som, componentData, componentId);
39102
39264
  }
39103
39265
 
@@ -39159,18 +39321,18 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39159
39321
  }, {
39160
39322
  key: "deleteComponent",
39161
39323
  value: function deleteComponent(componentId) {
39162
- var _this$centralPlant$sc9;
39324
+ var _this$centralPlant$sc0;
39163
39325
  // Check if component manager is available
39164
- var componentManager = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.componentManager;
39326
+ var componentManager = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.componentManager;
39165
39327
  if (!componentManager) {
39166
39328
  console.error('❌ deleteComponent(): Component manager not available');
39167
39329
  return false;
39168
39330
  }
39169
39331
  try {
39170
- var _this$centralPlant$sc0, _this$centralPlant$sc1, _sceneData$scene2, _sceneData$scene3;
39332
+ var _this$centralPlant$sc1, _this$centralPlant$sc10, _sceneData$scene2, _sceneData$scene3;
39171
39333
  console.log("\uD83D\uDDD1\uFE0F deleteComponent(): Deleting component ".concat(componentId));
39172
- var threeScene = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.scene;
39173
- var sceneData = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.currentSceneData;
39334
+ var threeScene = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.scene;
39335
+ var sceneData = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.currentSceneData;
39174
39336
 
39175
39337
  // Step 1: Resolve the actual Three.js UUID from componentId.
39176
39338
  // The UI emits object.name (e.g. "Pump (PUMP-1)") as the selection ID, but
@@ -39214,8 +39376,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39214
39376
  // the component (e.g., dynamically added but not yet synced to sceneData)
39215
39377
  if (connectorIds.size === 0 && threeScene) {
39216
39378
  threeScene.traverse(function (obj) {
39217
- var _obj$userData2, _obj$userData3;
39218
- if ((obj.uuid === resolvedUuid || ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.originalUuid) === resolvedUuid) && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'component') {
39379
+ var _obj$userData3, _obj$userData4;
39380
+ if ((obj.uuid === resolvedUuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === resolvedUuid) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'component') {
39219
39381
  obj.children.forEach(function (child) {
39220
39382
  var _child$userData3;
39221
39383
  if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'connector') {
@@ -39265,12 +39427,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39265
39427
  if (connectorIds.size > 0 && threeScene) {
39266
39428
  var objectsToRemove = [];
39267
39429
  threeScene.traverse(function (obj) {
39268
- var _obj$uuid, _obj$userData4, _obj$uuid2, _obj$userData5;
39269
- var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.isDeclared) !== true;
39270
- var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
39430
+ var _obj$uuid, _obj$userData5, _obj$uuid2, _obj$userData6;
39431
+ var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
39432
+ var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.isDeclared) !== true;
39271
39433
  if (isComputedSegment || isComputedGateway) {
39272
- var _obj$userData6, _obj$userData7;
39273
- if (connectorIds.has((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.pathFrom) || connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathTo)) {
39434
+ var _obj$userData7, _obj$userData8;
39435
+ if (connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathFrom) || connectorIds.has((_obj$userData8 = obj.userData) === null || _obj$userData8 === void 0 ? void 0 : _obj$userData8.pathTo)) {
39274
39436
  objectsToRemove.push(obj);
39275
39437
  }
39276
39438
  }
@@ -39385,7 +39547,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
39385
39547
  * Initialize the CentralPlant manager
39386
39548
  *
39387
39549
  * @constructor
39388
- * @version 0.3.24
39550
+ * @version 0.3.26
39389
39551
  * @updated 2025-10-22
39390
39552
  *
39391
39553
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
35
35
  * Initialize the CentralPlant manager
36
36
  *
37
37
  * @constructor
38
- * @version 0.3.24
38
+ * @version 0.3.26
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -1148,15 +1148,40 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1148
1148
 
1149
1149
  // Add attached IO device models for smart components
1150
1150
  if (componentData.isSmart && componentData.attachedDevices) {
1151
+ var _this$centralPlant$sc8;
1151
1152
  ioDeviceUtils.attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
1153
+
1154
+ // Register animation configs so IoAnimationManager can respond to state changes
1155
+ var ioAnimMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioAnimationManager;
1156
+ if (ioAnimMgr) {
1157
+ var _loop = function _loop() {
1158
+ var _modelPreloader$compo;
1159
+ var _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2),
1160
+ attachmentId = _Object$entries$_i[0],
1161
+ attachment = _Object$entries$_i[1];
1162
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
1163
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.animationConfig)) return 1; // continue
1164
+ var deviceRoot = null;
1165
+ componentModel.traverse(function (obj) {
1166
+ var _obj$userData2;
1167
+ if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
1168
+ });
1169
+ if (deviceRoot) {
1170
+ ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, componentId);
1171
+ }
1172
+ };
1173
+ for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
1174
+ if (_loop()) continue;
1175
+ }
1176
+ }
1152
1177
  }
1153
1178
 
1154
1179
  // Register default behaviors for smart components so the BehaviorManager
1155
1180
  // responds to tooltip-driven state changes immediately after drop.
1156
1181
  // (The scene-load path uses _processBehaviors instead, which runs on loadSceneData.)
1157
1182
  if ((_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length) {
1158
- var _this$centralPlant$sc8, _som$registerBehavior;
1159
- var som = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.sceneOperationsManager;
1183
+ var _this$centralPlant$sc9, _som$registerBehavior;
1184
+ var som = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.sceneOperationsManager;
1160
1185
  som === null || som === void 0 || (_som$registerBehavior = som.registerBehaviorsForComponentInstance) === null || _som$registerBehavior === void 0 || _som$registerBehavior.call(som, componentData, componentId);
1161
1186
  }
1162
1187
 
@@ -1218,18 +1243,18 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1218
1243
  }, {
1219
1244
  key: "deleteComponent",
1220
1245
  value: function deleteComponent(componentId) {
1221
- var _this$centralPlant$sc9;
1246
+ var _this$centralPlant$sc0;
1222
1247
  // Check if component manager is available
1223
- var componentManager = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.componentManager;
1248
+ var componentManager = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.componentManager;
1224
1249
  if (!componentManager) {
1225
1250
  console.error('❌ deleteComponent(): Component manager not available');
1226
1251
  return false;
1227
1252
  }
1228
1253
  try {
1229
- var _this$centralPlant$sc0, _this$centralPlant$sc1, _sceneData$scene2, _sceneData$scene3;
1254
+ var _this$centralPlant$sc1, _this$centralPlant$sc10, _sceneData$scene2, _sceneData$scene3;
1230
1255
  console.log("\uD83D\uDDD1\uFE0F deleteComponent(): Deleting component ".concat(componentId));
1231
- var threeScene = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.scene;
1232
- var sceneData = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.currentSceneData;
1256
+ var threeScene = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.scene;
1257
+ var sceneData = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.currentSceneData;
1233
1258
 
1234
1259
  // Step 1: Resolve the actual Three.js UUID from componentId.
1235
1260
  // The UI emits object.name (e.g. "Pump (PUMP-1)") as the selection ID, but
@@ -1273,8 +1298,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1273
1298
  // the component (e.g., dynamically added but not yet synced to sceneData)
1274
1299
  if (connectorIds.size === 0 && threeScene) {
1275
1300
  threeScene.traverse(function (obj) {
1276
- var _obj$userData2, _obj$userData3;
1277
- if ((obj.uuid === resolvedUuid || ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.originalUuid) === resolvedUuid) && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'component') {
1301
+ var _obj$userData3, _obj$userData4;
1302
+ if ((obj.uuid === resolvedUuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === resolvedUuid) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'component') {
1278
1303
  obj.children.forEach(function (child) {
1279
1304
  var _child$userData3;
1280
1305
  if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'connector') {
@@ -1324,12 +1349,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1324
1349
  if (connectorIds.size > 0 && threeScene) {
1325
1350
  var objectsToRemove = [];
1326
1351
  threeScene.traverse(function (obj) {
1327
- var _obj$uuid, _obj$userData4, _obj$uuid2, _obj$userData5;
1328
- var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.isDeclared) !== true;
1329
- var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
1352
+ var _obj$uuid, _obj$userData5, _obj$uuid2, _obj$userData6;
1353
+ var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
1354
+ var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.isDeclared) !== true;
1330
1355
  if (isComputedSegment || isComputedGateway) {
1331
- var _obj$userData6, _obj$userData7;
1332
- if (connectorIds.has((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.pathFrom) || connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathTo)) {
1356
+ var _obj$userData7, _obj$userData8;
1357
+ if (connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathFrom) || connectorIds.has((_obj$userData8 = obj.userData) === null || _obj$userData8 === void 0 ? void 0 : _obj$userData8.pathTo)) {
1333
1358
  objectsToRemove.push(obj);
1334
1359
  }
1335
1360
  }
@@ -94,7 +94,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
94
94
  }
95
95
  if (entries.length) {
96
96
  this._entries.set(key, entries);
97
- console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ")"));
97
+ console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
98
+ return e.anim.stateVariable;
99
+ }).join(', ')));
100
+ } else {
101
+ console.warn("[IoAnimationManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 animationConfig had ").concat(anims.length, " entries but none matched a mesh"));
98
102
  }
99
103
  }
100
104
 
@@ -128,6 +132,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
128
132
  }
129
133
  }
130
134
 
135
+ /**
136
+ * Return tooltip-compatible data point definitions derived from the loaded
137
+ * animation entries for a given attachment. Used by componentTooltipManager
138
+ * to replace the static ioConfig.states[] snapshot with the richer animation
139
+ * state definitions created in the Animate window.
140
+ *
141
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
142
+ * that share the same stateVariable are collapsed into one.
143
+ *
144
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
145
+ * {
146
+ * id: string, // stateVariable name
147
+ * name: string, // human-readable label (anim.name or stateVariable)
148
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
149
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
150
+ * defaultValue: any, // sensible default for the stateType
151
+ * direction: 'input' // all animation-driven states are interactive
152
+ * }
153
+ *
154
+ * @param {string} parentUuid
155
+ * @param {string} attachmentId
156
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
157
+ */
158
+ }, {
159
+ key: "getAnimationDataPoints",
160
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
161
+ var key = this._key(parentUuid, attachmentId);
162
+ var entries = this._entries.get(key);
163
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
164
+
165
+ // Collapse multiple mesh entries that share the same stateVariable
166
+ var seen = new Map(); // stateVariable → anim
167
+ var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(entries),
168
+ _step3;
169
+ try {
170
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
171
+ var anim = _step3.value.anim;
172
+ if (!seen.has(anim.stateVariable)) {
173
+ seen.set(anim.stateVariable, anim);
174
+ }
175
+ }
176
+ } catch (err) {
177
+ _iterator3.e(err);
178
+ } finally {
179
+ _iterator3.f();
180
+ }
181
+ var dps = [];
182
+ var _iterator4 = _rollupPluginBabelHelpers.createForOfIteratorHelper(seen),
183
+ _step4;
184
+ try {
185
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
186
+ var _step4$value = _rollupPluginBabelHelpers.slicedToArray(_step4.value, 2),
187
+ stateVar = _step4$value[0],
188
+ _anim = _step4$value[1];
189
+ // Normalise stateType from AnimateDevicesDialog variants
190
+ var stateType = void 0;
191
+ var raw = (_anim.stateType || '').toLowerCase();
192
+ if (raw === 'binary' || raw === 'boolean') {
193
+ stateType = 'binary';
194
+ } else if (raw === 'enum') {
195
+ stateType = 'enum';
196
+ } else {
197
+ // 'continuous', 'range', 'number', or anything else → numeric slider
198
+ stateType = 'number';
199
+ }
200
+
201
+ // Derive stateConfig from mappings
202
+ var stateConfig = {};
203
+ var mappingValues = (_anim.mappings || []).map(function (m) {
204
+ return m.stateValue;
205
+ });
206
+ if (stateType === 'enum') {
207
+ stateConfig.options = _rollupPluginBabelHelpers.toConsumableArray(new Set(mappingValues.map(String)));
208
+ } else if (stateType === 'number') {
209
+ var nums = mappingValues.map(Number).filter(function (n) {
210
+ return !isNaN(n);
211
+ });
212
+ if (nums.length) {
213
+ stateConfig.min = Math.min.apply(Math, _rollupPluginBabelHelpers.toConsumableArray(nums));
214
+ stateConfig.max = Math.max.apply(Math, _rollupPluginBabelHelpers.toConsumableArray(nums));
215
+ }
216
+ }
217
+
218
+ // Sensible default values
219
+ var defaultValue = void 0;
220
+ if (stateType === 'binary') {
221
+ defaultValue = 0;
222
+ } else if (stateType === 'enum') {
223
+ var _stateConfig$options$, _stateConfig$options;
224
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
225
+ } else {
226
+ var _stateConfig$min;
227
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
228
+ }
229
+ dps.push({
230
+ id: stateVar,
231
+ name: _anim.name || stateVar,
232
+ stateType: stateType,
233
+ stateConfig: stateConfig,
234
+ defaultValue: defaultValue,
235
+ direction: 'input'
236
+ });
237
+ }
238
+ } catch (err) {
239
+ _iterator4.e(err);
240
+ } finally {
241
+ _iterator4.f();
242
+ }
243
+ return dps;
244
+ }
245
+
131
246
  /**
132
247
  * Remove all animation entries associated with a given host component.
133
248
  * Call when a component is removed from the scene.
@@ -138,19 +253,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
138
253
  key: "unloadForComponent",
139
254
  value: function unloadForComponent(parentUuid) {
140
255
  var prefix = "".concat(parentUuid, "::");
141
- var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._entries.keys()),
142
- _step3;
256
+ var _iterator5 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._entries.keys()),
257
+ _step5;
143
258
  try {
144
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
145
- var key = _step3.value;
259
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
260
+ var key = _step5.value;
146
261
  if (key.startsWith(prefix)) {
147
262
  this._entries.delete(key);
148
263
  }
149
264
  }
150
265
  } catch (err) {
151
- _iterator3.e(err);
266
+ _iterator5.e(err);
152
267
  } finally {
153
- _iterator3.f();
268
+ _iterator5.f();
154
269
  }
155
270
  }
156
271
  }, {
@@ -213,11 +328,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
213
328
  var mapping = this._resolveMapping(anim, value);
214
329
  if (!mapping) return;
215
330
  var types = anim.transformTypes || [];
216
- var _iterator4 = _rollupPluginBabelHelpers.createForOfIteratorHelper(types),
217
- _step4;
331
+ var _iterator6 = _rollupPluginBabelHelpers.createForOfIteratorHelper(types),
332
+ _step6;
218
333
  try {
219
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
220
- var type = _step4.value;
334
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
335
+ var type = _step6.value;
221
336
  if (type === 'translation') {
222
337
  this._applyTranslation(mesh, origPos, mapping.transform);
223
338
  } else if (type === 'rotation') {
@@ -227,9 +342,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
227
342
  }
228
343
  }
229
344
  } catch (err) {
230
- _iterator4.e(err);
345
+ _iterator6.e(err);
231
346
  } finally {
232
- _iterator4.f();
347
+ _iterator6.f();
233
348
  }
234
349
  }
235
350
 
@@ -245,9 +360,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
245
360
  }, {
246
361
  key: "_resolveMapping",
247
362
  value: function _resolveMapping(anim, value) {
248
- var stateType = anim.stateType,
249
- mappings = anim.mappings;
363
+ var mappings = anim.mappings;
250
364
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
365
+
366
+ // Normalise stateType variants saved by AnimateDevicesDialog
367
+ // ('boolean' → 'binary', 'range' → 'continuous')
368
+ var raw = (anim.stateType || '').toLowerCase();
369
+ var stateType;
370
+ if (raw === 'binary' || raw === 'boolean') {
371
+ stateType = 'binary';
372
+ } else if (raw === 'continuous' || raw === 'range') {
373
+ stateType = 'continuous';
374
+ } else {
375
+ stateType = raw;
376
+ }
251
377
  if (stateType === 'binary' || stateType === 'enum') {
252
378
  var _mappings$find;
253
379
  // eslint-disable-next-line eqeqeq
@@ -287,14 +287,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
287
287
  object.traverse(function (child) {
288
288
  var _child$userData;
289
289
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
290
+ var _this2$sceneViewer$ma, _this2$sceneViewer;
290
291
  var attachmentId = child.userData.attachmentId || '';
292
+
293
+ // Use only data points from the animate window (animationConfig).
294
+ // The static ioConfig.states[] snapshot on userData is intentionally ignored.
295
+ var dataPoints = (_this2$sceneViewer$ma = (_this2$sceneViewer = _this2.sceneViewer) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.managers) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.ioAnimationManager) === null || _this2$sceneViewer === void 0 ? void 0 : _this2$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this2$sceneViewer$ma !== void 0 ? _this2$sceneViewer$ma : [];
296
+
297
+ // When data points come from animationConfig they already carry direction:'input'.
298
+ // Pass null so _buildDataPointRow uses the per-dp direction instead of the
299
+ // device-level ioDirection (which may be 'output' and would hide controls).
300
+ var deviceDirection = dataPoints.length > 0 ? null : child.userData.ioDirection || 'output';
291
301
  devices.push({
292
302
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
293
303
  deviceId: child.userData.deviceId || '',
294
304
  attachmentId: attachmentId,
295
305
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
296
- dataPoints: child.userData.dataPoints || [],
297
- direction: child.userData.ioDirection || 'output'
306
+ dataPoints: dataPoints,
307
+ direction: deviceDirection
298
308
  });
299
309
  }
300
310
  });
@@ -127,13 +127,14 @@ var ModelManager = /*#__PURE__*/function () {
127
127
  break;
128
128
  }
129
129
  _loop = /*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _loop() {
130
- var _modelPreloader$compo;
130
+ var _modelPreloader$compo, _deviceData$animation;
131
131
  var _Object$entries$_i, attachmentId, attachment, deviceData, deviceRoot;
132
132
  return _rollupPluginBabelHelpers.regenerator().w(function (_context) {
133
133
  while (1) switch (_context.n) {
134
134
  case 0:
135
135
  _Object$entries$_i = _rollupPluginBabelHelpers.slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
136
136
  deviceData = (_modelPreloader$compo = modelPreloader["default"].componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
137
+ console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 animationConfig:"), (_deviceData$animation = deviceData === null || deviceData === void 0 ? void 0 : deviceData.animationConfig) !== null && _deviceData$animation !== void 0 ? _deviceData$animation : 'NONE');
137
138
  if (deviceData !== null && deviceData !== void 0 && deviceData.animationConfig) {
138
139
  _context.n = 1;
139
140
  break;
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
31
31
  * Initialize the CentralPlant manager
32
32
  *
33
33
  * @constructor
34
- * @version 0.3.24
34
+ * @version 0.3.26
35
35
  * @updated 2025-10-22
36
36
  *
37
37
  * @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, toConsumableArray as _toConsumableArray, 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, toConsumableArray as _toConsumableArray, 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';
@@ -1124,15 +1124,40 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1124
1124
 
1125
1125
  // Add attached IO device models for smart components
1126
1126
  if (componentData.isSmart && componentData.attachedDevices) {
1127
+ var _this$centralPlant$sc8;
1127
1128
  attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
1129
+
1130
+ // Register animation configs so IoAnimationManager can respond to state changes
1131
+ var ioAnimMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioAnimationManager;
1132
+ if (ioAnimMgr) {
1133
+ var _loop = function _loop() {
1134
+ var _modelPreloader$compo;
1135
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
1136
+ attachmentId = _Object$entries$_i[0],
1137
+ attachment = _Object$entries$_i[1];
1138
+ var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
1139
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.animationConfig)) return 1; // continue
1140
+ var deviceRoot = null;
1141
+ componentModel.traverse(function (obj) {
1142
+ var _obj$userData2;
1143
+ if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
1144
+ });
1145
+ if (deviceRoot) {
1146
+ ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, componentId);
1147
+ }
1148
+ };
1149
+ for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
1150
+ if (_loop()) continue;
1151
+ }
1152
+ }
1128
1153
  }
1129
1154
 
1130
1155
  // Register default behaviors for smart components so the BehaviorManager
1131
1156
  // responds to tooltip-driven state changes immediately after drop.
1132
1157
  // (The scene-load path uses _processBehaviors instead, which runs on loadSceneData.)
1133
1158
  if ((_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length) {
1134
- var _this$centralPlant$sc8, _som$registerBehavior;
1135
- var som = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.sceneOperationsManager;
1159
+ var _this$centralPlant$sc9, _som$registerBehavior;
1160
+ var som = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.sceneOperationsManager;
1136
1161
  som === null || som === void 0 || (_som$registerBehavior = som.registerBehaviorsForComponentInstance) === null || _som$registerBehavior === void 0 || _som$registerBehavior.call(som, componentData, componentId);
1137
1162
  }
1138
1163
 
@@ -1194,18 +1219,18 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1194
1219
  }, {
1195
1220
  key: "deleteComponent",
1196
1221
  value: function deleteComponent(componentId) {
1197
- var _this$centralPlant$sc9;
1222
+ var _this$centralPlant$sc0;
1198
1223
  // Check if component manager is available
1199
- var componentManager = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.componentManager;
1224
+ var componentManager = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.componentManager;
1200
1225
  if (!componentManager) {
1201
1226
  console.error('❌ deleteComponent(): Component manager not available');
1202
1227
  return false;
1203
1228
  }
1204
1229
  try {
1205
- var _this$centralPlant$sc0, _this$centralPlant$sc1, _sceneData$scene2, _sceneData$scene3;
1230
+ var _this$centralPlant$sc1, _this$centralPlant$sc10, _sceneData$scene2, _sceneData$scene3;
1206
1231
  console.log("\uD83D\uDDD1\uFE0F deleteComponent(): Deleting component ".concat(componentId));
1207
- var threeScene = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.scene;
1208
- var sceneData = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.currentSceneData;
1232
+ var threeScene = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.scene;
1233
+ var sceneData = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.currentSceneData;
1209
1234
 
1210
1235
  // Step 1: Resolve the actual Three.js UUID from componentId.
1211
1236
  // The UI emits object.name (e.g. "Pump (PUMP-1)") as the selection ID, but
@@ -1249,8 +1274,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1249
1274
  // the component (e.g., dynamically added but not yet synced to sceneData)
1250
1275
  if (connectorIds.size === 0 && threeScene) {
1251
1276
  threeScene.traverse(function (obj) {
1252
- var _obj$userData2, _obj$userData3;
1253
- if ((obj.uuid === resolvedUuid || ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.originalUuid) === resolvedUuid) && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'component') {
1277
+ var _obj$userData3, _obj$userData4;
1278
+ if ((obj.uuid === resolvedUuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === resolvedUuid) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'component') {
1254
1279
  obj.children.forEach(function (child) {
1255
1280
  var _child$userData3;
1256
1281
  if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'connector') {
@@ -1300,12 +1325,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
1300
1325
  if (connectorIds.size > 0 && threeScene) {
1301
1326
  var objectsToRemove = [];
1302
1327
  threeScene.traverse(function (obj) {
1303
- var _obj$uuid, _obj$userData4, _obj$uuid2, _obj$userData5;
1304
- var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.isDeclared) !== true;
1305
- var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
1328
+ var _obj$uuid, _obj$userData5, _obj$uuid2, _obj$userData6;
1329
+ var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
1330
+ var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.isDeclared) !== true;
1306
1331
  if (isComputedSegment || isComputedGateway) {
1307
- var _obj$userData6, _obj$userData7;
1308
- if (connectorIds.has((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.pathFrom) || connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathTo)) {
1332
+ var _obj$userData7, _obj$userData8;
1333
+ if (connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathFrom) || connectorIds.has((_obj$userData8 = obj.userData) === null || _obj$userData8 === void 0 ? void 0 : _obj$userData8.pathTo)) {
1309
1334
  objectsToRemove.push(obj);
1310
1335
  }
1311
1336
  }
@@ -1,4 +1,4 @@
1
- import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, superPropGet as _superPropGet, toConsumableArray as _toConsumableArray, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, slicedToArray as _slicedToArray, toConsumableArray as _toConsumableArray, superPropGet as _superPropGet, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import * as THREE from 'three';
3
3
  import { BaseDisposable } from '../../core/baseDisposable.js';
4
4
 
@@ -70,7 +70,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
70
70
  }
71
71
  if (entries.length) {
72
72
  this._entries.set(key, entries);
73
- console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ")"));
73
+ console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
74
+ return e.anim.stateVariable;
75
+ }).join(', ')));
76
+ } else {
77
+ console.warn("[IoAnimationManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 animationConfig had ").concat(anims.length, " entries but none matched a mesh"));
74
78
  }
75
79
  }
76
80
 
@@ -104,6 +108,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
104
108
  }
105
109
  }
106
110
 
111
+ /**
112
+ * Return tooltip-compatible data point definitions derived from the loaded
113
+ * animation entries for a given attachment. Used by componentTooltipManager
114
+ * to replace the static ioConfig.states[] snapshot with the richer animation
115
+ * state definitions created in the Animate window.
116
+ *
117
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
118
+ * that share the same stateVariable are collapsed into one.
119
+ *
120
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
121
+ * {
122
+ * id: string, // stateVariable name
123
+ * name: string, // human-readable label (anim.name or stateVariable)
124
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
125
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
126
+ * defaultValue: any, // sensible default for the stateType
127
+ * direction: 'input' // all animation-driven states are interactive
128
+ * }
129
+ *
130
+ * @param {string} parentUuid
131
+ * @param {string} attachmentId
132
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
133
+ */
134
+ }, {
135
+ key: "getAnimationDataPoints",
136
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
137
+ var key = this._key(parentUuid, attachmentId);
138
+ var entries = this._entries.get(key);
139
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
140
+
141
+ // Collapse multiple mesh entries that share the same stateVariable
142
+ var seen = new Map(); // stateVariable → anim
143
+ var _iterator3 = _createForOfIteratorHelper(entries),
144
+ _step3;
145
+ try {
146
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
147
+ var anim = _step3.value.anim;
148
+ if (!seen.has(anim.stateVariable)) {
149
+ seen.set(anim.stateVariable, anim);
150
+ }
151
+ }
152
+ } catch (err) {
153
+ _iterator3.e(err);
154
+ } finally {
155
+ _iterator3.f();
156
+ }
157
+ var dps = [];
158
+ var _iterator4 = _createForOfIteratorHelper(seen),
159
+ _step4;
160
+ try {
161
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
162
+ var _step4$value = _slicedToArray(_step4.value, 2),
163
+ stateVar = _step4$value[0],
164
+ _anim = _step4$value[1];
165
+ // Normalise stateType from AnimateDevicesDialog variants
166
+ var stateType = void 0;
167
+ var raw = (_anim.stateType || '').toLowerCase();
168
+ if (raw === 'binary' || raw === 'boolean') {
169
+ stateType = 'binary';
170
+ } else if (raw === 'enum') {
171
+ stateType = 'enum';
172
+ } else {
173
+ // 'continuous', 'range', 'number', or anything else → numeric slider
174
+ stateType = 'number';
175
+ }
176
+
177
+ // Derive stateConfig from mappings
178
+ var stateConfig = {};
179
+ var mappingValues = (_anim.mappings || []).map(function (m) {
180
+ return m.stateValue;
181
+ });
182
+ if (stateType === 'enum') {
183
+ stateConfig.options = _toConsumableArray(new Set(mappingValues.map(String)));
184
+ } else if (stateType === 'number') {
185
+ var nums = mappingValues.map(Number).filter(function (n) {
186
+ return !isNaN(n);
187
+ });
188
+ if (nums.length) {
189
+ stateConfig.min = Math.min.apply(Math, _toConsumableArray(nums));
190
+ stateConfig.max = Math.max.apply(Math, _toConsumableArray(nums));
191
+ }
192
+ }
193
+
194
+ // Sensible default values
195
+ var defaultValue = void 0;
196
+ if (stateType === 'binary') {
197
+ defaultValue = 0;
198
+ } else if (stateType === 'enum') {
199
+ var _stateConfig$options$, _stateConfig$options;
200
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
201
+ } else {
202
+ var _stateConfig$min;
203
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
204
+ }
205
+ dps.push({
206
+ id: stateVar,
207
+ name: _anim.name || stateVar,
208
+ stateType: stateType,
209
+ stateConfig: stateConfig,
210
+ defaultValue: defaultValue,
211
+ direction: 'input'
212
+ });
213
+ }
214
+ } catch (err) {
215
+ _iterator4.e(err);
216
+ } finally {
217
+ _iterator4.f();
218
+ }
219
+ return dps;
220
+ }
221
+
107
222
  /**
108
223
  * Remove all animation entries associated with a given host component.
109
224
  * Call when a component is removed from the scene.
@@ -114,19 +229,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
114
229
  key: "unloadForComponent",
115
230
  value: function unloadForComponent(parentUuid) {
116
231
  var prefix = "".concat(parentUuid, "::");
117
- var _iterator3 = _createForOfIteratorHelper(this._entries.keys()),
118
- _step3;
232
+ var _iterator5 = _createForOfIteratorHelper(this._entries.keys()),
233
+ _step5;
119
234
  try {
120
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
121
- var key = _step3.value;
235
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
236
+ var key = _step5.value;
122
237
  if (key.startsWith(prefix)) {
123
238
  this._entries.delete(key);
124
239
  }
125
240
  }
126
241
  } catch (err) {
127
- _iterator3.e(err);
242
+ _iterator5.e(err);
128
243
  } finally {
129
- _iterator3.f();
244
+ _iterator5.f();
130
245
  }
131
246
  }
132
247
  }, {
@@ -189,11 +304,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
189
304
  var mapping = this._resolveMapping(anim, value);
190
305
  if (!mapping) return;
191
306
  var types = anim.transformTypes || [];
192
- var _iterator4 = _createForOfIteratorHelper(types),
193
- _step4;
307
+ var _iterator6 = _createForOfIteratorHelper(types),
308
+ _step6;
194
309
  try {
195
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
196
- var type = _step4.value;
310
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
311
+ var type = _step6.value;
197
312
  if (type === 'translation') {
198
313
  this._applyTranslation(mesh, origPos, mapping.transform);
199
314
  } else if (type === 'rotation') {
@@ -203,9 +318,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
203
318
  }
204
319
  }
205
320
  } catch (err) {
206
- _iterator4.e(err);
321
+ _iterator6.e(err);
207
322
  } finally {
208
- _iterator4.f();
323
+ _iterator6.f();
209
324
  }
210
325
  }
211
326
 
@@ -221,9 +336,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
221
336
  }, {
222
337
  key: "_resolveMapping",
223
338
  value: function _resolveMapping(anim, value) {
224
- var stateType = anim.stateType,
225
- mappings = anim.mappings;
339
+ var mappings = anim.mappings;
226
340
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
341
+
342
+ // Normalise stateType variants saved by AnimateDevicesDialog
343
+ // ('boolean' → 'binary', 'range' → 'continuous')
344
+ var raw = (anim.stateType || '').toLowerCase();
345
+ var stateType;
346
+ if (raw === 'binary' || raw === 'boolean') {
347
+ stateType = 'binary';
348
+ } else if (raw === 'continuous' || raw === 'range') {
349
+ stateType = 'continuous';
350
+ } else {
351
+ stateType = raw;
352
+ }
227
353
  if (stateType === 'binary' || stateType === 'enum') {
228
354
  var _mappings$find;
229
355
  // eslint-disable-next-line eqeqeq
@@ -263,14 +263,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
263
263
  object.traverse(function (child) {
264
264
  var _child$userData;
265
265
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
266
+ var _this2$sceneViewer$ma, _this2$sceneViewer;
266
267
  var attachmentId = child.userData.attachmentId || '';
268
+
269
+ // Use only data points from the animate window (animationConfig).
270
+ // The static ioConfig.states[] snapshot on userData is intentionally ignored.
271
+ var dataPoints = (_this2$sceneViewer$ma = (_this2$sceneViewer = _this2.sceneViewer) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.managers) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.ioAnimationManager) === null || _this2$sceneViewer === void 0 ? void 0 : _this2$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this2$sceneViewer$ma !== void 0 ? _this2$sceneViewer$ma : [];
272
+
273
+ // When data points come from animationConfig they already carry direction:'input'.
274
+ // Pass null so _buildDataPointRow uses the per-dp direction instead of the
275
+ // device-level ioDirection (which may be 'output' and would hide controls).
276
+ var deviceDirection = dataPoints.length > 0 ? null : child.userData.ioDirection || 'output';
267
277
  devices.push({
268
278
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
269
279
  deviceId: child.userData.deviceId || '',
270
280
  attachmentId: attachmentId,
271
281
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
272
- dataPoints: child.userData.dataPoints || [],
273
- direction: child.userData.ioDirection || 'output'
282
+ dataPoints: dataPoints,
283
+ direction: deviceDirection
274
284
  });
275
285
  }
276
286
  });
@@ -103,13 +103,14 @@ var ModelManager = /*#__PURE__*/function () {
103
103
  break;
104
104
  }
105
105
  _loop = /*#__PURE__*/_regenerator().m(function _loop() {
106
- var _modelPreloader$compo;
106
+ var _modelPreloader$compo, _deviceData$animation;
107
107
  var _Object$entries$_i, attachmentId, attachment, deviceData, deviceRoot;
108
108
  return _regenerator().w(function (_context) {
109
109
  while (1) switch (_context.n) {
110
110
  case 0:
111
111
  _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
112
112
  deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
113
+ console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 animationConfig:"), (_deviceData$animation = deviceData === null || deviceData === void 0 ? void 0 : deviceData.animationConfig) !== null && _deviceData$animation !== void 0 ? _deviceData$animation : 'NONE');
113
114
  if (deviceData !== null && deviceData !== void 0 && deviceData.animationConfig) {
114
115
  _context.n = 1;
115
116
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.24",
3
+ "version": "0.3.26",
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",