@2112-lab/central-plant 0.3.19 → 0.3.22

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.
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
35
35
  * Initialize the CentralPlant manager
36
36
  *
37
37
  * @constructor
38
- * @version 0.3.19
38
+ * @version 0.3.22
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -27,6 +27,7 @@ var componentDragManager = require('../managers/controls/componentDragManager.js
27
27
  var sceneTooltipsManager = require('../managers/scene/sceneTooltipsManager.js');
28
28
  var componentTooltipManager = require('../managers/scene/componentTooltipManager.js');
29
29
  var viewport2DManager = require('../managers/scene/viewport2DManager.js');
30
+ var IoAnimationManager = require('../managers/behaviors/IoAnimationManager.js');
30
31
  var nameUtils = require('../utils/nameUtils.js');
31
32
  var ioDeviceUtils = require('../utils/ioDeviceUtils.js');
32
33
  var modelPreloader = require('../rendering/modelPreloader.js');
@@ -172,6 +173,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
172
173
  this.centralPlant.managers.cameraControlsManager = new cameraControlsManager.CameraControlsManager(this.centralPlant.sceneViewer);
173
174
  this.centralPlant.managers.componentDragManager = new componentDragManager.ComponentDragManager(this.centralPlant.sceneViewer);
174
175
  this.centralPlant.managers.viewport2DManager = new viewport2DManager.Viewport2DManager(this.centralPlant.sceneViewer);
176
+ this.centralPlant.managers.ioAnimationManager = new IoAnimationManager.IoAnimationManager(this.centralPlant.sceneViewer);
175
177
 
176
178
  // All managers are now stored in the managers collection and will be attached via attachToComponent()
177
179
  }
@@ -102,7 +102,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
102
102
  this.centralPlant.attachToComponent();
103
103
 
104
104
  // Sync our managers tracking object after attachment
105
- managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
105
+ managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
106
106
  managerKeys.forEach(function (key) {
107
107
  if (_this2[key]) {
108
108
  _this2.managers[key] = _this2[key];
@@ -14,6 +14,7 @@ var sceneTooltipsManager = require('./managers/scene/sceneTooltipsManager.js');
14
14
  var componentTooltipManager = require('./managers/scene/componentTooltipManager.js');
15
15
  var sceneHierarchyManager = require('./managers/scene/sceneHierarchyManager.js');
16
16
  var BehaviorManager = require('./managers/behaviors/BehaviorManager.js');
17
+ var IoAnimationManager = require('./managers/behaviors/IoAnimationManager.js');
17
18
  var componentManager = require('./managers/components/componentManager.js');
18
19
  var animationManager = require('./managers/scene/animationManager.js');
19
20
  var pathfindingManager = require('./managers/pathfinding/pathfindingManager.js');
@@ -68,6 +69,7 @@ exports.SceneTooltipsManager = sceneTooltipsManager.SceneTooltipsManager;
68
69
  exports.ComponentTooltipManager = componentTooltipManager.ComponentTooltipManager;
69
70
  exports.SceneHierarchyManager = sceneHierarchyManager.SceneHierarchyManager;
70
71
  exports.BehaviorManager = BehaviorManager.BehaviorManager;
72
+ exports.IoAnimationManager = IoAnimationManager.IoAnimationManager;
71
73
  exports.ComponentManager = componentManager.ComponentManager;
72
74
  exports.AnimationManager = animationManager.AnimationManager;
73
75
  exports.PathfindingManager = pathfindingManager.PathfindingManager;
@@ -0,0 +1,422 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
6
+ var THREE = require('three');
7
+ var baseDisposable = require('../../core/baseDisposable.js');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n["default"] = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
28
+
29
+ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
30
+ function IoAnimationManager(sceneViewer) {
31
+ var _this;
32
+ _rollupPluginBabelHelpers.classCallCheck(this, IoAnimationManager);
33
+ _this = _rollupPluginBabelHelpers.callSuper(this, IoAnimationManager);
34
+ _this.sceneViewer = sceneViewer;
35
+
36
+ /**
37
+ * Map: `${parentUuid}::${attachmentId}` → Array<{
38
+ * anim: Object,
39
+ * mesh: THREE.Object3D,
40
+ * origPos: THREE.Vector3,
41
+ * origRot: THREE.Euler
42
+ * }>
43
+ */
44
+ _this._entries = new Map();
45
+ return _this;
46
+ }
47
+
48
+ // ─────────────────────────────────────────────────────────────────────────
49
+ // PUBLIC API
50
+ // ─────────────────────────────────────────────────────────────────────────
51
+
52
+ /**
53
+ * Register animation entries for one attached I/O device.
54
+ * Should be called after the device GLB has been merged into the scene
55
+ * so that mesh references are live.
56
+ *
57
+ * @param {string} attachmentId - The attachment key (e.g. 'attch-switch-01')
58
+ * @param {Object|Array} animationConfig - Serialized config; either the full object
59
+ * { animations: [...] } or a plain array of animation entries.
60
+ * @param {THREE.Object3D} deviceModelRoot - The device's root Object3D as added to the scene
61
+ * @param {string} parentUuid - UUID of the host component Object3D
62
+ */
63
+ _rollupPluginBabelHelpers.inherits(IoAnimationManager, _BaseDisposable);
64
+ return _rollupPluginBabelHelpers.createClass(IoAnimationManager, [{
65
+ key: "loadAnimations",
66
+ value: function loadAnimations(attachmentId, animationConfig, deviceModelRoot, parentUuid) {
67
+ var _animationConfig$anim;
68
+ if (!animationConfig || !deviceModelRoot) return;
69
+ var anims = Array.isArray(animationConfig) ? animationConfig : (_animationConfig$anim = animationConfig.animations) !== null && _animationConfig$anim !== void 0 ? _animationConfig$anim : [];
70
+ if (!anims.length) return;
71
+ var key = this._key(parentUuid, attachmentId);
72
+ var entries = [];
73
+ var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(anims),
74
+ _step;
75
+ try {
76
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
77
+ var anim = _step.value;
78
+ var mesh = this._resolveMesh(anim, deviceModelRoot);
79
+ if (!mesh) {
80
+ console.warn("[IoAnimationManager] Could not find mesh for animation \"".concat(anim.name || anim.stateVariable, "\" (uuid: ").concat(anim.meshUuid, ", name: \"").concat(anim.meshName, "\")"));
81
+ continue;
82
+ }
83
+ entries.push({
84
+ anim: anim,
85
+ mesh: mesh,
86
+ origPos: mesh.position.clone(),
87
+ origRot: mesh.rotation.clone()
88
+ });
89
+ }
90
+ } catch (err) {
91
+ _iterator.e(err);
92
+ } finally {
93
+ _iterator.f();
94
+ }
95
+ if (entries.length) {
96
+ this._entries.set(key, entries);
97
+ console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ")"));
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Apply animations triggered by an IO device state change.
103
+ * Should be called in parallel with BehaviorManager.triggerState().
104
+ *
105
+ * @param {string} attachmentId - Raw attachment key (not scoped)
106
+ * @param {string} dataPointId - The data point / state variable id that changed
107
+ * @param {*} value - New state value
108
+ * @param {string} parentUuid - UUID of the host component
109
+ */
110
+ }, {
111
+ key: "triggerState",
112
+ value: function triggerState(attachmentId, dataPointId, value, parentUuid) {
113
+ var key = this._key(parentUuid, attachmentId);
114
+ var entries = this._entries.get(key);
115
+ if (!(entries !== null && entries !== void 0 && entries.length)) return;
116
+ var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(entries),
117
+ _step2;
118
+ try {
119
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
120
+ var entry = _step2.value;
121
+ if (entry.anim.stateVariable !== dataPointId) continue;
122
+ this._applyAnimation(entry, value);
123
+ }
124
+ } catch (err) {
125
+ _iterator2.e(err);
126
+ } finally {
127
+ _iterator2.f();
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Remove all animation entries associated with a given host component.
133
+ * Call when a component is removed from the scene.
134
+ *
135
+ * @param {string} parentUuid
136
+ */
137
+ }, {
138
+ key: "unloadForComponent",
139
+ value: function unloadForComponent(parentUuid) {
140
+ var prefix = "".concat(parentUuid, "::");
141
+ var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(this._entries.keys()),
142
+ _step3;
143
+ try {
144
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
145
+ var key = _step3.value;
146
+ if (key.startsWith(prefix)) {
147
+ this._entries.delete(key);
148
+ }
149
+ }
150
+ } catch (err) {
151
+ _iterator3.e(err);
152
+ } finally {
153
+ _iterator3.f();
154
+ }
155
+ }
156
+ }, {
157
+ key: "dispose",
158
+ value: function dispose() {
159
+ this._entries.clear();
160
+ _rollupPluginBabelHelpers.superPropGet(IoAnimationManager, "dispose", this, 3)([]);
161
+ }
162
+
163
+ // ─────────────────────────────────────────────────────────────────────────
164
+ // PRIVATE HELPERS
165
+ // ─────────────────────────────────────────────────────────────────────────
166
+ }, {
167
+ key: "_key",
168
+ value: function _key(parentUuid, attachmentId) {
169
+ return "".concat(parentUuid, "::").concat(attachmentId);
170
+ }
171
+
172
+ /**
173
+ * Find the mesh inside `root` using UUID first, then name as fallback.
174
+ * GLTFLoader assigns fresh UUIDs on every load, so name is the reliable key.
175
+ * @param {Object} anim
176
+ * @param {THREE.Object3D} root
177
+ * @returns {THREE.Object3D|null}
178
+ */
179
+ }, {
180
+ key: "_resolveMesh",
181
+ value: function _resolveMesh(anim, root) {
182
+ var found = null;
183
+ root.traverse(function (obj) {
184
+ if (found) return;
185
+
186
+ // Prefer UUID match (works when the device was cloned from a cached instance
187
+ // whose UUIDs were preserved)
188
+ if (anim.meshUuid && obj.uuid === anim.meshUuid) {
189
+ found = obj;
190
+ return;
191
+ }
192
+
193
+ // Reliable fallback: mesh name
194
+ if (anim.meshName && obj.name === anim.meshName) {
195
+ found = obj;
196
+ }
197
+ });
198
+ return found;
199
+ }
200
+
201
+ /**
202
+ * Resolve which mapping row to use and apply all declared transform types.
203
+ * @param {{ anim, mesh, origPos, origRot }} entry
204
+ * @param {*} value - Current state value
205
+ */
206
+ }, {
207
+ key: "_applyAnimation",
208
+ value: function _applyAnimation(entry, value) {
209
+ var anim = entry.anim,
210
+ mesh = entry.mesh,
211
+ origPos = entry.origPos,
212
+ origRot = entry.origRot;
213
+ var mapping = this._resolveMapping(anim, value);
214
+ if (!mapping) return;
215
+ var types = anim.transformTypes || [];
216
+ var _iterator4 = _rollupPluginBabelHelpers.createForOfIteratorHelper(types),
217
+ _step4;
218
+ try {
219
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
220
+ var type = _step4.value;
221
+ if (type === 'translation') {
222
+ this._applyTranslation(mesh, origPos, mapping.transform);
223
+ } else if (type === 'rotation') {
224
+ this._applyRotation(mesh, origPos, origRot, anim, mapping.rotationTransform);
225
+ } else if (type === 'color') {
226
+ this._applyColor(mesh, mapping.colorTransform);
227
+ }
228
+ }
229
+ } catch (err) {
230
+ _iterator4.e(err);
231
+ } finally {
232
+ _iterator4.f();
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Find the mapping row that matches `value` for the given animation entry.
238
+ *
239
+ * - binary / enum: find where mapping.stateValue == value (loose equality so
240
+ * JSON-deserialised "true" matches boolean true)
241
+ * - continuous: linear interpolation between ordered anchor points
242
+ *
243
+ * @returns {Object|null} The matched/interpolated mapping row, or null.
244
+ */
245
+ }, {
246
+ key: "_resolveMapping",
247
+ value: function _resolveMapping(anim, value) {
248
+ var stateType = anim.stateType,
249
+ mappings = anim.mappings;
250
+ if (!(mappings !== null && mappings !== void 0 && mappings.length)) return null;
251
+ if (stateType === 'binary' || stateType === 'enum') {
252
+ var _mappings$find;
253
+ // eslint-disable-next-line eqeqeq
254
+ return (_mappings$find = mappings.find(function (m) {
255
+ return m.stateValue == value;
256
+ })) !== null && _mappings$find !== void 0 ? _mappings$find : null;
257
+ }
258
+ if (stateType === 'continuous') {
259
+ var num = Number(value);
260
+ if (isNaN(num)) return null;
261
+
262
+ // Sort anchors by numeric stateValue
263
+ var sorted = _rollupPluginBabelHelpers.toConsumableArray(mappings).filter(function (m) {
264
+ return m.stateValue != null && !isNaN(Number(m.stateValue));
265
+ }).sort(function (a, b) {
266
+ return Number(a.stateValue) - Number(b.stateValue);
267
+ });
268
+ if (!sorted.length) return null;
269
+ if (sorted.length === 1) return sorted[0];
270
+
271
+ // Clamp to range
272
+ if (num <= Number(sorted[0].stateValue)) return sorted[0];
273
+ if (num >= Number(sorted[sorted.length - 1].stateValue)) return sorted[sorted.length - 1];
274
+
275
+ // Find surrounding anchors and interpolate
276
+ for (var i = 0; i < sorted.length - 1; i++) {
277
+ var lo = sorted[i];
278
+ var hi = sorted[i + 1];
279
+ var loVal = Number(lo.stateValue);
280
+ var hiVal = Number(hi.stateValue);
281
+ if (num >= loVal && num <= hiVal) {
282
+ var t = (num - loVal) / (hiVal - loVal);
283
+ return this._lerpMapping(lo, hi, t);
284
+ }
285
+ }
286
+ }
287
+ return null;
288
+ }
289
+
290
+ /**
291
+ * Linear-interpolate between two mapping rows.
292
+ * @param {Object} lo - lower anchor mapping
293
+ * @param {Object} hi - upper anchor mapping
294
+ * @param {number} t - 0..1
295
+ */
296
+ }, {
297
+ key: "_lerpMapping",
298
+ value: function _lerpMapping(lo, hi, t) {
299
+ var _lo$transform$x, _lo$transform, _hi$transform$x, _hi$transform, _lo$transform$y, _lo$transform2, _hi$transform$y, _hi$transform2, _lo$transform$z, _lo$transform3, _hi$transform$z, _hi$transform3;
300
+ var lerp = function lerp(a, b) {
301
+ return a + (b - a) * t;
302
+ };
303
+ var transform = {
304
+ x: lerp((_lo$transform$x = (_lo$transform = lo.transform) === null || _lo$transform === void 0 ? void 0 : _lo$transform.x) !== null && _lo$transform$x !== void 0 ? _lo$transform$x : 0, (_hi$transform$x = (_hi$transform = hi.transform) === null || _hi$transform === void 0 ? void 0 : _hi$transform.x) !== null && _hi$transform$x !== void 0 ? _hi$transform$x : 0),
305
+ y: lerp((_lo$transform$y = (_lo$transform2 = lo.transform) === null || _lo$transform2 === void 0 ? void 0 : _lo$transform2.y) !== null && _lo$transform$y !== void 0 ? _lo$transform$y : 0, (_hi$transform$y = (_hi$transform2 = hi.transform) === null || _hi$transform2 === void 0 ? void 0 : _hi$transform2.y) !== null && _hi$transform$y !== void 0 ? _hi$transform$y : 0),
306
+ z: lerp((_lo$transform$z = (_lo$transform3 = lo.transform) === null || _lo$transform3 === void 0 ? void 0 : _lo$transform3.z) !== null && _lo$transform$z !== void 0 ? _lo$transform$z : 0, (_hi$transform$z = (_hi$transform3 = hi.transform) === null || _hi$transform3 === void 0 ? void 0 : _hi$transform3.z) !== null && _hi$transform$z !== void 0 ? _hi$transform$z : 0)
307
+ };
308
+ var rotationTransform = lerp(typeof lo.rotationTransform === 'number' ? lo.rotationTransform : 0, typeof hi.rotationTransform === 'number' ? hi.rotationTransform : 0);
309
+
310
+ // Color interpolation: convert hex → RGB components → lerp → back to hex
311
+ var colorTransform = this._lerpHex(lo.colorTransform, hi.colorTransform, t);
312
+ return {
313
+ stateValue: null,
314
+ transform: transform,
315
+ rotationTransform: rotationTransform,
316
+ colorTransform: colorTransform
317
+ };
318
+ }
319
+
320
+ /**
321
+ * Interpolate between two hex colour strings.
322
+ */
323
+ }, {
324
+ key: "_lerpHex",
325
+ value: function _lerpHex(hexA, hexB, t) {
326
+ try {
327
+ var ca = new THREE__namespace.Color(hexA);
328
+ var cb = new THREE__namespace.Color(hexB);
329
+ ca.lerp(cb, t);
330
+ return "#".concat(ca.getHexString());
331
+ } catch (_unused) {
332
+ return hexA !== null && hexA !== void 0 ? hexA : '#ffffff';
333
+ }
334
+ }
335
+
336
+ // ─────────────────────────────────────────────────────────────────────────
337
+ // TRANSFORM APPLIERS
338
+ // ─────────────────────────────────────────────────────────────────────────
339
+
340
+ /**
341
+ * Apply a position delta relative to the mesh's original position.
342
+ * @param {THREE.Object3D} mesh
343
+ * @param {THREE.Vector3} origPos
344
+ * @param {{ x, y, z }} transform - Deltas
345
+ */
346
+ }, {
347
+ key: "_applyTranslation",
348
+ value: function _applyTranslation(mesh, origPos, transform) {
349
+ var _transform$x, _transform$y, _transform$z;
350
+ if (!transform) return;
351
+ mesh.position.set(origPos.x + ((_transform$x = transform.x) !== null && _transform$x !== void 0 ? _transform$x : 0), origPos.y + ((_transform$y = transform.y) !== null && _transform$y !== void 0 ? _transform$y : 0), origPos.z + ((_transform$z = transform.z) !== null && _transform$z !== void 0 ? _transform$z : 0));
352
+ }
353
+
354
+ /**
355
+ * Apply a rotation around an arbitrary pivot point in device-local space,
356
+ * optionally also displacing the mesh position to simulate orbital motion.
357
+ *
358
+ * Math (all in device-local space):
359
+ * pivot = rotAxisOffset
360
+ * delta = origPos - pivot
361
+ * newDelta = rotate(delta, angle, axis)
362
+ * newPos = pivot + newDelta
363
+ * newRot[axis] = origRot[axis] + angle
364
+ *
365
+ * @param {THREE.Object3D} mesh
366
+ * @param {THREE.Vector3} origPos
367
+ * @param {THREE.Euler} origRot
368
+ * @param {Object} anim
369
+ * @param {number} angleDeg - Degrees
370
+ */
371
+ }, {
372
+ key: "_applyRotation",
373
+ value: function _applyRotation(mesh, origPos, origRot, anim, angleDeg) {
374
+ var _anim$rotAxis, _anim$rotAxisOffset, _off$x, _off$y, _off$z;
375
+ var angle = THREE__namespace.MathUtils.degToRad(typeof angleDeg === 'number' ? angleDeg : 0);
376
+ var axis = ((_anim$rotAxis = anim.rotAxis) !== null && _anim$rotAxis !== void 0 ? _anim$rotAxis : 'x').toLowerCase();
377
+ var off = (_anim$rotAxisOffset = anim.rotAxisOffset) !== null && _anim$rotAxisOffset !== void 0 ? _anim$rotAxisOffset : {
378
+ x: 0,
379
+ y: 0,
380
+ z: 0
381
+ };
382
+
383
+ // Unit vector for the chosen axis
384
+ var axisVec = new THREE__namespace.Vector3(axis === 'x' ? 1 : 0, axis === 'y' ? 1 : 0, axis === 'z' ? 1 : 0);
385
+ var pivot = new THREE__namespace.Vector3((_off$x = off.x) !== null && _off$x !== void 0 ? _off$x : 0, (_off$y = off.y) !== null && _off$y !== void 0 ? _off$y : 0, (_off$z = off.z) !== null && _off$z !== void 0 ? _off$z : 0);
386
+ var delta = origPos.clone().sub(pivot);
387
+ delta.applyAxisAngle(axisVec, angle);
388
+ mesh.position.copy(pivot).add(delta);
389
+ mesh.rotation[axis] = origRot[axis] + angle;
390
+ }
391
+
392
+ /**
393
+ * Apply a colour to all Mesh descendants of `mesh`.
394
+ * Creates a cloned material per mesh on first call to avoid shared-material
395
+ * cross-contamination between different device instances.
396
+ *
397
+ * @param {THREE.Object3D} mesh
398
+ * @param {string} colorHex - e.g. '#ff0000'
399
+ */
400
+ }, {
401
+ key: "_applyColor",
402
+ value: function _applyColor(mesh, colorHex) {
403
+ if (!colorHex) return;
404
+ mesh.traverse(function (obj) {
405
+ if (!obj.isMesh || !obj.material) return;
406
+
407
+ // Ensure this mesh has its own material instance
408
+ if (!obj.userData._materialCloned) {
409
+ obj.material = obj.material.clone();
410
+ obj.userData._materialCloned = true;
411
+ }
412
+ try {
413
+ obj.material.color.set(colorHex);
414
+ } catch (e) {
415
+ // Material may not have a color property (e.g. MeshDepthMaterial)
416
+ }
417
+ });
418
+ }
419
+ }]);
420
+ }(baseDisposable.BaseDisposable);
421
+
422
+ exports.IoAnimationManager = IoAnimationManager;
@@ -129,7 +129,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
129
129
  }, {
130
130
  key: "toggleIODeviceBinaryState",
131
131
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
132
- var _ref, _this$sceneViewer;
132
+ var _ref, _this$sceneViewer, _this$sceneViewer2;
133
133
  if (!ioDeviceObject || !this._stateAdapter) return;
134
134
  var ud = ioDeviceObject.userData;
135
135
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
@@ -164,6 +164,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
164
164
  var newVal = !Boolean(currentVal);
165
165
  this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
166
166
  (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.behaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
167
+ (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.ioAnimationManager) === null || _this$sceneViewer2 === void 0 || _this$sceneViewer2.triggerState(attachmentId, dpId, newVal, parentUuid);
167
168
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
168
169
  }
169
170
 
@@ -437,11 +438,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
437
438
  }, {
438
439
  key: "_positionTooltip",
439
440
  value: function _positionTooltip() {
440
- var _this$sceneViewer2, _this$sceneViewer3;
441
+ var _this$sceneViewer3, _this$sceneViewer4;
441
442
  if (!this.tooltipEl || !this.selectedObject) return;
442
443
  var container = this._getContainer();
443
- var camera = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.camera;
444
- var renderer = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.renderer;
444
+ var camera = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.camera;
445
+ var renderer = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.renderer;
445
446
  if (!container || !camera || !renderer) return;
446
447
 
447
448
  // Compute bounding box to position above the component
@@ -478,8 +479,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
478
479
  }, {
479
480
  key: "_getContainer",
480
481
  value: function _getContainer() {
481
- var _this$sceneViewer4;
482
- return ((_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.renderer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.domElement) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.parentElement) || null;
482
+ var _this$sceneViewer5;
483
+ return ((_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.renderer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.domElement) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.parentElement) || null;
483
484
  }
484
485
 
485
486
  // -----------------------------------------------------------------------
@@ -519,7 +520,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
519
520
  var currentVal = (_ref2 = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : null;
520
521
  if (isInput) {
521
522
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
522
- var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer;
523
+ var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer, _this4$sceneViewer2;
523
524
  (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
524
525
  // Also fire BehaviorManager so any wired behaviors react immediately.
525
526
  // Pass the parent component UUID so behaviors scoped to a specific instance
@@ -527,6 +528,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
527
528
  // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
528
529
  var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
529
530
  (_this4$sceneViewer = _this4.sceneViewer) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.managers) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.behaviorManager) === null || _this4$sceneViewer === void 0 || _this4$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
531
+ (_this4$sceneViewer2 = _this4.sceneViewer) === null || _this4$sceneViewer2 === void 0 || (_this4$sceneViewer2 = _this4$sceneViewer2.managers) === null || _this4$sceneViewer2 === void 0 || (_this4$sceneViewer2 = _this4$sceneViewer2.ioAnimationManager) === null || _this4$sceneViewer2 === void 0 || _this4$sceneViewer2.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
530
532
  });
531
533
  row.appendChild(ctrl);
532
534
  this._stateElements.set(key, {