@2112-lab/central-plant 0.3.23 → 0.3.25

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.
@@ -37187,13 +37187,19 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37187
37187
  object.traverse(function (child) {
37188
37188
  var _child$userData;
37189
37189
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
37190
+ var _this2$sceneViewer;
37190
37191
  var attachmentId = child.userData.attachmentId || '';
37192
+
37193
+ // Prefer data point definitions from the animate window (richer, user-authored).
37194
+ // Fall back to the static ioConfig.states[] snapshot on the mesh userData.
37195
+ var animDps = (_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);
37196
+ var dataPoints = (animDps === null || animDps === void 0 ? void 0 : animDps.length) > 0 ? animDps : child.userData.dataPoints || [];
37191
37197
  devices.push({
37192
37198
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
37193
37199
  deviceId: child.userData.deviceId || '',
37194
37200
  attachmentId: attachmentId,
37195
37201
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
37196
- dataPoints: child.userData.dataPoints || [],
37202
+ dataPoints: dataPoints,
37197
37203
  direction: child.userData.ioDirection || 'output'
37198
37204
  });
37199
37205
  }
@@ -37702,6 +37708,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37702
37708
  }
37703
37709
  }
37704
37710
 
37711
+ /**
37712
+ * Return tooltip-compatible data point definitions derived from the loaded
37713
+ * animation entries for a given attachment. Used by componentTooltipManager
37714
+ * to replace the static ioConfig.states[] snapshot with the richer animation
37715
+ * state definitions created in the Animate window.
37716
+ *
37717
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
37718
+ * that share the same stateVariable are collapsed into one.
37719
+ *
37720
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
37721
+ * {
37722
+ * id: string, // stateVariable name
37723
+ * name: string, // human-readable label (anim.name or stateVariable)
37724
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
37725
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
37726
+ * defaultValue: any, // sensible default for the stateType
37727
+ * direction: 'input' // all animation-driven states are interactive
37728
+ * }
37729
+ *
37730
+ * @param {string} parentUuid
37731
+ * @param {string} attachmentId
37732
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
37733
+ */
37734
+ }, {
37735
+ key: "getAnimationDataPoints",
37736
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
37737
+ var key = this._key(parentUuid, attachmentId);
37738
+ var entries = this._entries.get(key);
37739
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
37740
+
37741
+ // Collapse multiple mesh entries that share the same stateVariable
37742
+ var seen = new Map(); // stateVariable → anim
37743
+ var _iterator3 = _createForOfIteratorHelper(entries),
37744
+ _step3;
37745
+ try {
37746
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37747
+ var anim = _step3.value.anim;
37748
+ if (!seen.has(anim.stateVariable)) {
37749
+ seen.set(anim.stateVariable, anim);
37750
+ }
37751
+ }
37752
+ } catch (err) {
37753
+ _iterator3.e(err);
37754
+ } finally {
37755
+ _iterator3.f();
37756
+ }
37757
+ var dps = [];
37758
+ var _iterator4 = _createForOfIteratorHelper(seen),
37759
+ _step4;
37760
+ try {
37761
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37762
+ var _step4$value = _slicedToArray(_step4.value, 2),
37763
+ stateVar = _step4$value[0],
37764
+ _anim = _step4$value[1];
37765
+ // Normalise stateType from AnimateDevicesDialog variants
37766
+ var stateType = void 0;
37767
+ var raw = (_anim.stateType || '').toLowerCase();
37768
+ if (raw === 'binary' || raw === 'boolean') {
37769
+ stateType = 'binary';
37770
+ } else if (raw === 'enum') {
37771
+ stateType = 'enum';
37772
+ } else {
37773
+ // 'continuous', 'range', 'number', or anything else → numeric slider
37774
+ stateType = 'number';
37775
+ }
37776
+
37777
+ // Derive stateConfig from mappings
37778
+ var stateConfig = {};
37779
+ var mappingValues = (_anim.mappings || []).map(function (m) {
37780
+ return m.stateValue;
37781
+ });
37782
+ if (stateType === 'enum') {
37783
+ stateConfig.options = _toConsumableArray(new Set(mappingValues.map(String)));
37784
+ } else if (stateType === 'number') {
37785
+ var nums = mappingValues.map(Number).filter(function (n) {
37786
+ return !isNaN(n);
37787
+ });
37788
+ if (nums.length) {
37789
+ stateConfig.min = Math.min.apply(Math, _toConsumableArray(nums));
37790
+ stateConfig.max = Math.max.apply(Math, _toConsumableArray(nums));
37791
+ }
37792
+ }
37793
+
37794
+ // Sensible default values
37795
+ var defaultValue = void 0;
37796
+ if (stateType === 'binary') {
37797
+ defaultValue = 0;
37798
+ } else if (stateType === 'enum') {
37799
+ var _stateConfig$options$, _stateConfig$options;
37800
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
37801
+ } else {
37802
+ var _stateConfig$min;
37803
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
37804
+ }
37805
+ dps.push({
37806
+ id: stateVar,
37807
+ name: _anim.name || stateVar,
37808
+ stateType: stateType,
37809
+ stateConfig: stateConfig,
37810
+ defaultValue: defaultValue,
37811
+ direction: 'input'
37812
+ });
37813
+ }
37814
+ } catch (err) {
37815
+ _iterator4.e(err);
37816
+ } finally {
37817
+ _iterator4.f();
37818
+ }
37819
+ return dps;
37820
+ }
37821
+
37705
37822
  /**
37706
37823
  * Remove all animation entries associated with a given host component.
37707
37824
  * Call when a component is removed from the scene.
@@ -37712,19 +37829,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37712
37829
  key: "unloadForComponent",
37713
37830
  value: function unloadForComponent(parentUuid) {
37714
37831
  var prefix = "".concat(parentUuid, "::");
37715
- var _iterator3 = _createForOfIteratorHelper(this._entries.keys()),
37716
- _step3;
37832
+ var _iterator5 = _createForOfIteratorHelper(this._entries.keys()),
37833
+ _step5;
37717
37834
  try {
37718
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37719
- var key = _step3.value;
37835
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
37836
+ var key = _step5.value;
37720
37837
  if (key.startsWith(prefix)) {
37721
37838
  this._entries.delete(key);
37722
37839
  }
37723
37840
  }
37724
37841
  } catch (err) {
37725
- _iterator3.e(err);
37842
+ _iterator5.e(err);
37726
37843
  } finally {
37727
- _iterator3.f();
37844
+ _iterator5.f();
37728
37845
  }
37729
37846
  }
37730
37847
  }, {
@@ -37787,11 +37904,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37787
37904
  var mapping = this._resolveMapping(anim, value);
37788
37905
  if (!mapping) return;
37789
37906
  var types = anim.transformTypes || [];
37790
- var _iterator4 = _createForOfIteratorHelper(types),
37791
- _step4;
37907
+ var _iterator6 = _createForOfIteratorHelper(types),
37908
+ _step6;
37792
37909
  try {
37793
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37794
- var type = _step4.value;
37910
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
37911
+ var type = _step6.value;
37795
37912
  if (type === 'translation') {
37796
37913
  this._applyTranslation(mesh, origPos, mapping.transform);
37797
37914
  } else if (type === 'rotation') {
@@ -37801,9 +37918,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37801
37918
  }
37802
37919
  }
37803
37920
  } catch (err) {
37804
- _iterator4.e(err);
37921
+ _iterator6.e(err);
37805
37922
  } finally {
37806
- _iterator4.f();
37923
+ _iterator6.f();
37807
37924
  }
37808
37925
  }
37809
37926
 
@@ -37819,9 +37936,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
37819
37936
  }, {
37820
37937
  key: "_resolveMapping",
37821
37938
  value: function _resolveMapping(anim, value) {
37822
- var stateType = anim.stateType,
37823
- mappings = anim.mappings;
37939
+ var mappings = anim.mappings;
37824
37940
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
37941
+
37942
+ // Normalise stateType variants saved by AnimateDevicesDialog
37943
+ // ('boolean' → 'binary', 'range' → 'continuous')
37944
+ var raw = (anim.stateType || '').toLowerCase();
37945
+ var stateType;
37946
+ if (raw === 'binary' || raw === 'boolean') {
37947
+ stateType = 'binary';
37948
+ } else if (raw === 'continuous' || raw === 'range') {
37949
+ stateType = 'continuous';
37950
+ } else {
37951
+ stateType = raw;
37952
+ }
37825
37953
  if (stateType === 'binary' || stateType === 'enum') {
37826
37954
  var _mappings$find;
37827
37955
  // eslint-disable-next-line eqeqeq
@@ -39385,7 +39513,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
39385
39513
  * Initialize the CentralPlant manager
39386
39514
  *
39387
39515
  * @constructor
39388
- * @version 0.3.23
39516
+ * @version 0.3.25
39389
39517
  * @updated 2025-10-22
39390
39518
  *
39391
39519
  * @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.23
38
+ * @version 0.3.25
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -128,6 +128,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
128
128
  }
129
129
  }
130
130
 
131
+ /**
132
+ * Return tooltip-compatible data point definitions derived from the loaded
133
+ * animation entries for a given attachment. Used by componentTooltipManager
134
+ * to replace the static ioConfig.states[] snapshot with the richer animation
135
+ * state definitions created in the Animate window.
136
+ *
137
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
138
+ * that share the same stateVariable are collapsed into one.
139
+ *
140
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
141
+ * {
142
+ * id: string, // stateVariable name
143
+ * name: string, // human-readable label (anim.name or stateVariable)
144
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
145
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
146
+ * defaultValue: any, // sensible default for the stateType
147
+ * direction: 'input' // all animation-driven states are interactive
148
+ * }
149
+ *
150
+ * @param {string} parentUuid
151
+ * @param {string} attachmentId
152
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
153
+ */
154
+ }, {
155
+ key: "getAnimationDataPoints",
156
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
157
+ var key = this._key(parentUuid, attachmentId);
158
+ var entries = this._entries.get(key);
159
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
160
+
161
+ // Collapse multiple mesh entries that share the same stateVariable
162
+ var seen = new Map(); // stateVariable → anim
163
+ var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(entries),
164
+ _step3;
165
+ try {
166
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
167
+ var anim = _step3.value.anim;
168
+ if (!seen.has(anim.stateVariable)) {
169
+ seen.set(anim.stateVariable, anim);
170
+ }
171
+ }
172
+ } catch (err) {
173
+ _iterator3.e(err);
174
+ } finally {
175
+ _iterator3.f();
176
+ }
177
+ var dps = [];
178
+ var _iterator4 = _rollupPluginBabelHelpers.createForOfIteratorHelper(seen),
179
+ _step4;
180
+ try {
181
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
182
+ var _step4$value = _rollupPluginBabelHelpers.slicedToArray(_step4.value, 2),
183
+ stateVar = _step4$value[0],
184
+ _anim = _step4$value[1];
185
+ // Normalise stateType from AnimateDevicesDialog variants
186
+ var stateType = void 0;
187
+ var raw = (_anim.stateType || '').toLowerCase();
188
+ if (raw === 'binary' || raw === 'boolean') {
189
+ stateType = 'binary';
190
+ } else if (raw === 'enum') {
191
+ stateType = 'enum';
192
+ } else {
193
+ // 'continuous', 'range', 'number', or anything else → numeric slider
194
+ stateType = 'number';
195
+ }
196
+
197
+ // Derive stateConfig from mappings
198
+ var stateConfig = {};
199
+ var mappingValues = (_anim.mappings || []).map(function (m) {
200
+ return m.stateValue;
201
+ });
202
+ if (stateType === 'enum') {
203
+ stateConfig.options = _rollupPluginBabelHelpers.toConsumableArray(new Set(mappingValues.map(String)));
204
+ } else if (stateType === 'number') {
205
+ var nums = mappingValues.map(Number).filter(function (n) {
206
+ return !isNaN(n);
207
+ });
208
+ if (nums.length) {
209
+ stateConfig.min = Math.min.apply(Math, _rollupPluginBabelHelpers.toConsumableArray(nums));
210
+ stateConfig.max = Math.max.apply(Math, _rollupPluginBabelHelpers.toConsumableArray(nums));
211
+ }
212
+ }
213
+
214
+ // Sensible default values
215
+ var defaultValue = void 0;
216
+ if (stateType === 'binary') {
217
+ defaultValue = 0;
218
+ } else if (stateType === 'enum') {
219
+ var _stateConfig$options$, _stateConfig$options;
220
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
221
+ } else {
222
+ var _stateConfig$min;
223
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
224
+ }
225
+ dps.push({
226
+ id: stateVar,
227
+ name: _anim.name || stateVar,
228
+ stateType: stateType,
229
+ stateConfig: stateConfig,
230
+ defaultValue: defaultValue,
231
+ direction: 'input'
232
+ });
233
+ }
234
+ } catch (err) {
235
+ _iterator4.e(err);
236
+ } finally {
237
+ _iterator4.f();
238
+ }
239
+ return dps;
240
+ }
241
+
131
242
  /**
132
243
  * Remove all animation entries associated with a given host component.
133
244
  * Call when a component is removed from the scene.
@@ -138,19 +249,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
138
249
  key: "unloadForComponent",
139
250
  value: function unloadForComponent(parentUuid) {
140
251
  var prefix = "".concat(parentUuid, "::");
141
- var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._entries.keys()),
142
- _step3;
252
+ var _iterator5 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._entries.keys()),
253
+ _step5;
143
254
  try {
144
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
145
- var key = _step3.value;
255
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
256
+ var key = _step5.value;
146
257
  if (key.startsWith(prefix)) {
147
258
  this._entries.delete(key);
148
259
  }
149
260
  }
150
261
  } catch (err) {
151
- _iterator3.e(err);
262
+ _iterator5.e(err);
152
263
  } finally {
153
- _iterator3.f();
264
+ _iterator5.f();
154
265
  }
155
266
  }
156
267
  }, {
@@ -213,11 +324,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
213
324
  var mapping = this._resolveMapping(anim, value);
214
325
  if (!mapping) return;
215
326
  var types = anim.transformTypes || [];
216
- var _iterator4 = _rollupPluginBabelHelpers.createForOfIteratorHelper(types),
217
- _step4;
327
+ var _iterator6 = _rollupPluginBabelHelpers.createForOfIteratorHelper(types),
328
+ _step6;
218
329
  try {
219
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
220
- var type = _step4.value;
330
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
331
+ var type = _step6.value;
221
332
  if (type === 'translation') {
222
333
  this._applyTranslation(mesh, origPos, mapping.transform);
223
334
  } else if (type === 'rotation') {
@@ -227,9 +338,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
227
338
  }
228
339
  }
229
340
  } catch (err) {
230
- _iterator4.e(err);
341
+ _iterator6.e(err);
231
342
  } finally {
232
- _iterator4.f();
343
+ _iterator6.f();
233
344
  }
234
345
  }
235
346
 
@@ -245,9 +356,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
245
356
  }, {
246
357
  key: "_resolveMapping",
247
358
  value: function _resolveMapping(anim, value) {
248
- var stateType = anim.stateType,
249
- mappings = anim.mappings;
359
+ var mappings = anim.mappings;
250
360
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
361
+
362
+ // Normalise stateType variants saved by AnimateDevicesDialog
363
+ // ('boolean' → 'binary', 'range' → 'continuous')
364
+ var raw = (anim.stateType || '').toLowerCase();
365
+ var stateType;
366
+ if (raw === 'binary' || raw === 'boolean') {
367
+ stateType = 'binary';
368
+ } else if (raw === 'continuous' || raw === 'range') {
369
+ stateType = 'continuous';
370
+ } else {
371
+ stateType = raw;
372
+ }
251
373
  if (stateType === 'binary' || stateType === 'enum') {
252
374
  var _mappings$find;
253
375
  // eslint-disable-next-line eqeqeq
@@ -287,13 +287,19 @@ 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;
290
291
  var attachmentId = child.userData.attachmentId || '';
292
+
293
+ // Prefer data point definitions from the animate window (richer, user-authored).
294
+ // Fall back to the static ioConfig.states[] snapshot on the mesh userData.
295
+ var animDps = (_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);
296
+ var dataPoints = (animDps === null || animDps === void 0 ? void 0 : animDps.length) > 0 ? animDps : child.userData.dataPoints || [];
291
297
  devices.push({
292
298
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
293
299
  deviceId: child.userData.deviceId || '',
294
300
  attachmentId: attachmentId,
295
301
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
296
- dataPoints: child.userData.dataPoints || [],
302
+ dataPoints: dataPoints,
297
303
  direction: child.userData.ioDirection || 'output'
298
304
  });
299
305
  }
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
31
31
  * Initialize the CentralPlant manager
32
32
  *
33
33
  * @constructor
34
- * @version 0.3.23
34
+ * @version 0.3.25
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 { 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
 
@@ -104,6 +104,117 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
104
104
  }
105
105
  }
106
106
 
107
+ /**
108
+ * Return tooltip-compatible data point definitions derived from the loaded
109
+ * animation entries for a given attachment. Used by componentTooltipManager
110
+ * to replace the static ioConfig.states[] snapshot with the richer animation
111
+ * state definitions created in the Animate window.
112
+ *
113
+ * One dp object is emitted per unique stateVariable; multiple mesh entries
114
+ * that share the same stateVariable are collapsed into one.
115
+ *
116
+ * Returned dp shape (matches what _buildDataPointRow / _buildInputControl expect):
117
+ * {
118
+ * id: string, // stateVariable name
119
+ * name: string, // human-readable label (anim.name or stateVariable)
120
+ * stateType: string, // normalised to 'binary' | 'enum' | 'number'
121
+ * stateConfig: Object, // { onLabel?, offLabel?, options?, min?, max?, unit? }
122
+ * defaultValue: any, // sensible default for the stateType
123
+ * direction: 'input' // all animation-driven states are interactive
124
+ * }
125
+ *
126
+ * @param {string} parentUuid
127
+ * @param {string} attachmentId
128
+ * @returns {Object[]} Array of dp objects, or empty array if none loaded.
129
+ */
130
+ }, {
131
+ key: "getAnimationDataPoints",
132
+ value: function getAnimationDataPoints(parentUuid, attachmentId) {
133
+ var key = this._key(parentUuid, attachmentId);
134
+ var entries = this._entries.get(key);
135
+ if (!(entries !== null && entries !== void 0 && entries.length)) return [];
136
+
137
+ // Collapse multiple mesh entries that share the same stateVariable
138
+ var seen = new Map(); // stateVariable → anim
139
+ var _iterator3 = _createForOfIteratorHelper(entries),
140
+ _step3;
141
+ try {
142
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
143
+ var anim = _step3.value.anim;
144
+ if (!seen.has(anim.stateVariable)) {
145
+ seen.set(anim.stateVariable, anim);
146
+ }
147
+ }
148
+ } catch (err) {
149
+ _iterator3.e(err);
150
+ } finally {
151
+ _iterator3.f();
152
+ }
153
+ var dps = [];
154
+ var _iterator4 = _createForOfIteratorHelper(seen),
155
+ _step4;
156
+ try {
157
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
158
+ var _step4$value = _slicedToArray(_step4.value, 2),
159
+ stateVar = _step4$value[0],
160
+ _anim = _step4$value[1];
161
+ // Normalise stateType from AnimateDevicesDialog variants
162
+ var stateType = void 0;
163
+ var raw = (_anim.stateType || '').toLowerCase();
164
+ if (raw === 'binary' || raw === 'boolean') {
165
+ stateType = 'binary';
166
+ } else if (raw === 'enum') {
167
+ stateType = 'enum';
168
+ } else {
169
+ // 'continuous', 'range', 'number', or anything else → numeric slider
170
+ stateType = 'number';
171
+ }
172
+
173
+ // Derive stateConfig from mappings
174
+ var stateConfig = {};
175
+ var mappingValues = (_anim.mappings || []).map(function (m) {
176
+ return m.stateValue;
177
+ });
178
+ if (stateType === 'enum') {
179
+ stateConfig.options = _toConsumableArray(new Set(mappingValues.map(String)));
180
+ } else if (stateType === 'number') {
181
+ var nums = mappingValues.map(Number).filter(function (n) {
182
+ return !isNaN(n);
183
+ });
184
+ if (nums.length) {
185
+ stateConfig.min = Math.min.apply(Math, _toConsumableArray(nums));
186
+ stateConfig.max = Math.max.apply(Math, _toConsumableArray(nums));
187
+ }
188
+ }
189
+
190
+ // Sensible default values
191
+ var defaultValue = void 0;
192
+ if (stateType === 'binary') {
193
+ defaultValue = 0;
194
+ } else if (stateType === 'enum') {
195
+ var _stateConfig$options$, _stateConfig$options;
196
+ defaultValue = (_stateConfig$options$ = (_stateConfig$options = stateConfig.options) === null || _stateConfig$options === void 0 ? void 0 : _stateConfig$options[0]) !== null && _stateConfig$options$ !== void 0 ? _stateConfig$options$ : '';
197
+ } else {
198
+ var _stateConfig$min;
199
+ defaultValue = (_stateConfig$min = stateConfig.min) !== null && _stateConfig$min !== void 0 ? _stateConfig$min : 0;
200
+ }
201
+ dps.push({
202
+ id: stateVar,
203
+ name: _anim.name || stateVar,
204
+ stateType: stateType,
205
+ stateConfig: stateConfig,
206
+ defaultValue: defaultValue,
207
+ direction: 'input'
208
+ });
209
+ }
210
+ } catch (err) {
211
+ _iterator4.e(err);
212
+ } finally {
213
+ _iterator4.f();
214
+ }
215
+ return dps;
216
+ }
217
+
107
218
  /**
108
219
  * Remove all animation entries associated with a given host component.
109
220
  * Call when a component is removed from the scene.
@@ -114,19 +225,19 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
114
225
  key: "unloadForComponent",
115
226
  value: function unloadForComponent(parentUuid) {
116
227
  var prefix = "".concat(parentUuid, "::");
117
- var _iterator3 = _createForOfIteratorHelper(this._entries.keys()),
118
- _step3;
228
+ var _iterator5 = _createForOfIteratorHelper(this._entries.keys()),
229
+ _step5;
119
230
  try {
120
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
121
- var key = _step3.value;
231
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
232
+ var key = _step5.value;
122
233
  if (key.startsWith(prefix)) {
123
234
  this._entries.delete(key);
124
235
  }
125
236
  }
126
237
  } catch (err) {
127
- _iterator3.e(err);
238
+ _iterator5.e(err);
128
239
  } finally {
129
- _iterator3.f();
240
+ _iterator5.f();
130
241
  }
131
242
  }
132
243
  }, {
@@ -189,11 +300,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
189
300
  var mapping = this._resolveMapping(anim, value);
190
301
  if (!mapping) return;
191
302
  var types = anim.transformTypes || [];
192
- var _iterator4 = _createForOfIteratorHelper(types),
193
- _step4;
303
+ var _iterator6 = _createForOfIteratorHelper(types),
304
+ _step6;
194
305
  try {
195
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
196
- var type = _step4.value;
306
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
307
+ var type = _step6.value;
197
308
  if (type === 'translation') {
198
309
  this._applyTranslation(mesh, origPos, mapping.transform);
199
310
  } else if (type === 'rotation') {
@@ -203,9 +314,9 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
203
314
  }
204
315
  }
205
316
  } catch (err) {
206
- _iterator4.e(err);
317
+ _iterator6.e(err);
207
318
  } finally {
208
- _iterator4.f();
319
+ _iterator6.f();
209
320
  }
210
321
  }
211
322
 
@@ -221,9 +332,20 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
221
332
  }, {
222
333
  key: "_resolveMapping",
223
334
  value: function _resolveMapping(anim, value) {
224
- var stateType = anim.stateType,
225
- mappings = anim.mappings;
335
+ var mappings = anim.mappings;
226
336
  if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
337
+
338
+ // Normalise stateType variants saved by AnimateDevicesDialog
339
+ // ('boolean' → 'binary', 'range' → 'continuous')
340
+ var raw = (anim.stateType || '').toLowerCase();
341
+ var stateType;
342
+ if (raw === 'binary' || raw === 'boolean') {
343
+ stateType = 'binary';
344
+ } else if (raw === 'continuous' || raw === 'range') {
345
+ stateType = 'continuous';
346
+ } else {
347
+ stateType = raw;
348
+ }
227
349
  if (stateType === 'binary' || stateType === 'enum') {
228
350
  var _mappings$find;
229
351
  // eslint-disable-next-line eqeqeq
@@ -263,13 +263,19 @@ 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;
266
267
  var attachmentId = child.userData.attachmentId || '';
268
+
269
+ // Prefer data point definitions from the animate window (richer, user-authored).
270
+ // Fall back to the static ioConfig.states[] snapshot on the mesh userData.
271
+ var animDps = (_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);
272
+ var dataPoints = (animDps === null || animDps === void 0 ? void 0 : animDps.length) > 0 ? animDps : child.userData.dataPoints || [];
267
273
  devices.push({
268
274
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
269
275
  deviceId: child.userData.deviceId || '',
270
276
  attachmentId: attachmentId,
271
277
  scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
272
- dataPoints: child.userData.dataPoints || [],
278
+ dataPoints: dataPoints,
273
279
  direction: child.userData.ioDirection || 'output'
274
280
  });
275
281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
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",