@2112-lab/central-plant 0.3.25 → 0.3.27

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.
@@ -168,6 +168,169 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
168
168
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
169
169
  }
170
170
 
171
+ // ── IO device drag-to-state ─────────────────────────────────────────────
172
+
173
+ /**
174
+ * Begin tracking a drag gesture on an IO device mesh.
175
+ * Records the initial state of each animation data point so that
176
+ * `updateIODeviceDrag` can compute relative offsets from it.
177
+ *
178
+ * @param {THREE.Object3D} ioDeviceObject
179
+ */
180
+ }, {
181
+ key: "startIODeviceDrag",
182
+ value: function startIODeviceDrag(ioDeviceObject) {
183
+ var _this$sceneViewer3,
184
+ _this2 = this;
185
+ if (!ioDeviceObject || !this._stateAdapter) return;
186
+ var ud = ioDeviceObject.userData;
187
+ var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
188
+ if (!attachmentId) return;
189
+ var parentUuid = null;
190
+ var obj = ioDeviceObject.parent;
191
+ while (obj) {
192
+ var _obj$userData2;
193
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component') {
194
+ parentUuid = obj.uuid;
195
+ break;
196
+ }
197
+ obj = obj.parent;
198
+ }
199
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
200
+ var ioAnimMgr = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.ioAnimationManager;
201
+ var dataPoints = ((ioAnimMgr === null || ioAnimMgr === void 0 ? void 0 : ioAnimMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
202
+ // deduplicate by id
203
+ .filter(function (dp, i, arr) {
204
+ return arr.findIndex(function (d) {
205
+ return d.id === dp.id;
206
+ }) === i;
207
+ });
208
+ var dpSessions = [];
209
+ var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(dataPoints),
210
+ _step;
211
+ try {
212
+ var _loop = function _loop() {
213
+ var _this2$_stateAdapter$;
214
+ var dp = _step.value;
215
+ var stateType = (dp.stateType || '').toLowerCase();
216
+ if (stateType !== 'binary' && stateType !== 'boolean' && stateType !== 'enum') return 1; // continue
217
+ var curVal = (_this2$_stateAdapter$ = _this2._stateAdapter.getState(scopedAttachmentId, dp.id)) !== null && _this2$_stateAdapter$ !== void 0 ? _this2$_stateAdapter$ : dp.defaultValue;
218
+ if (stateType === 'binary' || stateType === 'boolean') {
219
+ dpSessions.push({
220
+ dp: dp,
221
+ scopedAttachmentId: scopedAttachmentId,
222
+ attachmentId: attachmentId,
223
+ parentUuid: parentUuid,
224
+ stateType: 'binary',
225
+ lastApplied: curVal
226
+ });
227
+ } else {
228
+ var _dp$stateConfig;
229
+ var opts = ((_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.options) || [];
230
+ var curIdx = opts.findIndex(function (o) {
231
+ return String(o) === String(curVal);
232
+ });
233
+ dpSessions.push({
234
+ dp: dp,
235
+ scopedAttachmentId: scopedAttachmentId,
236
+ attachmentId: attachmentId,
237
+ parentUuid: parentUuid,
238
+ stateType: 'enum',
239
+ opts: opts,
240
+ startIdx: curIdx >= 0 ? curIdx : 0,
241
+ lastAppliedIdx: curIdx >= 0 ? curIdx : 0
242
+ });
243
+ }
244
+ };
245
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
246
+ if (_loop()) continue;
247
+ }
248
+ } catch (err) {
249
+ _iterator.e(err);
250
+ } finally {
251
+ _iterator.f();
252
+ }
253
+ this._ioDragSession = dpSessions.length ? {
254
+ dpSessions: dpSessions
255
+ } : null;
256
+ }
257
+
258
+ /**
259
+ * Update animated mesh state while a drag is in progress.
260
+ * Called continuously during pointermove.
261
+ *
262
+ * Sign convention: up/right = positive `signedDelta`.
263
+ * - Binary: > +20 px → true/on state, < −20 px → false/off state.
264
+ * - Enum: each ±30 px step advances/retreats one option in the list.
265
+ *
266
+ * @param {number} signedDelta - Cumulative signed pixel displacement since drag start
267
+ */
268
+ }, {
269
+ key: "updateIODeviceDrag",
270
+ value: function updateIODeviceDrag(signedDelta) {
271
+ var session = this._ioDragSession;
272
+ if (!session) return;
273
+ var BINARY_THRESHOLD = 20;
274
+ var ENUM_STEP_PX = 30;
275
+ var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(session.dpSessions),
276
+ _step2;
277
+ try {
278
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
279
+ var dps = _step2.value;
280
+ if (dps.stateType === 'binary') {
281
+ var newVal = void 0;
282
+ if (signedDelta > BINARY_THRESHOLD) {
283
+ newVal = true;
284
+ } else if (signedDelta < -BINARY_THRESHOLD) {
285
+ newVal = false;
286
+ } else {
287
+ continue; // dead zone
288
+ }
289
+ if (newVal === dps.lastApplied) continue;
290
+ dps.lastApplied = newVal;
291
+ this._applyDpState(dps, newVal);
292
+ } else if (dps.stateType === 'enum') {
293
+ var steps = Math.round(signedDelta / ENUM_STEP_PX);
294
+ var newIdx = Math.max(0, Math.min(dps.opts.length - 1, dps.startIdx + steps));
295
+ if (newIdx === dps.lastAppliedIdx) continue;
296
+ dps.lastAppliedIdx = newIdx;
297
+ this._applyDpState(dps, dps.opts[newIdx]);
298
+ }
299
+ }
300
+ } catch (err) {
301
+ _iterator2.e(err);
302
+ } finally {
303
+ _iterator2.f();
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Clean up drag session state on pointerup.
309
+ */
310
+ }, {
311
+ key: "endIODeviceDrag",
312
+ value: function endIODeviceDrag() {
313
+ this._ioDragSession = null;
314
+ }
315
+
316
+ /**
317
+ * Apply a new value to a data point, updating Vuex state and firing behavior/animation triggers.
318
+ * @private
319
+ */
320
+ }, {
321
+ key: "_applyDpState",
322
+ value: function _applyDpState(_ref2, newVal) {
323
+ var _this$_stateAdapter, _this$sceneViewer4, _this$sceneViewer5;
324
+ var scopedAttachmentId = _ref2.scopedAttachmentId,
325
+ attachmentId = _ref2.attachmentId,
326
+ parentUuid = _ref2.parentUuid,
327
+ dp = _ref2.dp;
328
+ var dpId = dp.id;
329
+ (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
330
+ (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.behaviorManager) === null || _this$sceneViewer4 === void 0 || _this$sceneViewer4.triggerState(attachmentId, dpId, newVal, parentUuid);
331
+ (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioAnimationManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, dpId, newVal, parentUuid);
332
+ }
333
+
171
334
  /**
172
335
  * Should be called when an object is selected or deselected.
173
336
  * @param {THREE.Object3D|null} object
@@ -281,26 +444,30 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
281
444
  }, {
282
445
  key: "_getIODevices",
283
446
  value: function _getIODevices(object) {
284
- var _this2 = this;
447
+ var _this3 = this;
285
448
  var devices = [];
286
449
  var parentUuid = object.uuid; // The component's own UUID
287
450
  object.traverse(function (child) {
288
451
  var _child$userData;
289
452
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
290
- var _this2$sceneViewer;
453
+ var _this3$sceneViewer$ma, _this3$sceneViewer;
291
454
  var attachmentId = child.userData.attachmentId || '';
292
455
 
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 || [];
456
+ // Use only data points from the animate window (animationConfig).
457
+ // The static ioConfig.states[] snapshot on userData is intentionally ignored.
458
+ var dataPoints = (_this3$sceneViewer$ma = (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.ioAnimationManager) === null || _this3$sceneViewer === void 0 ? void 0 : _this3$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this3$sceneViewer$ma !== void 0 ? _this3$sceneViewer$ma : [];
459
+
460
+ // When data points come from animationConfig they already carry direction:'input'.
461
+ // Pass null so _buildDataPointRow uses the per-dp direction instead of the
462
+ // device-level ioDirection (which may be 'output' and would hide controls).
463
+ var deviceDirection = dataPoints.length > 0 ? null : child.userData.ioDirection || 'output';
297
464
  devices.push({
298
465
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
299
466
  deviceId: child.userData.deviceId || '',
300
467
  attachmentId: attachmentId,
301
- scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
468
+ scopedAttachmentId: _this3._getScopedAttachmentKey(attachmentId, parentUuid),
302
469
  dataPoints: dataPoints,
303
- direction: child.userData.ioDirection || 'output'
470
+ direction: deviceDirection
304
471
  });
305
472
  }
306
473
  });
@@ -314,7 +481,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
314
481
  }, {
315
482
  key: "_buildTooltip",
316
483
  value: function _buildTooltip(object) {
317
- var _this3 = this;
484
+ var _this4 = this;
318
485
  // Remove any existing tooltip first
319
486
  this.hide();
320
487
  // Re-assign selected object since hide() clears it
@@ -385,7 +552,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
385
552
  // Use scopedAttachmentId to ensure state is isolated per component instance
386
553
  if (device.scopedAttachmentId && device.dataPoints.length > 0) {
387
554
  device.dataPoints.forEach(function (dp) {
388
- var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
555
+ var row = _this4._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
389
556
  list.appendChild(row);
390
557
  });
391
558
  }
@@ -396,11 +563,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
396
563
  // Hover expand/collapse
397
564
  trigger.addEventListener('mouseenter', function () {
398
565
  ioSection.classList.add('expanded');
399
- _this3._ioExpanded = true;
566
+ _this4._ioExpanded = true;
400
567
  });
401
568
  ioSection.addEventListener('mouseleave', function () {
402
569
  ioSection.classList.remove('expanded');
403
- _this3._ioExpanded = false;
570
+ _this4._ioExpanded = false;
404
571
  });
405
572
  card.appendChild(ioSection);
406
573
  } else {
@@ -444,11 +611,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
444
611
  }, {
445
612
  key: "_positionTooltip",
446
613
  value: function _positionTooltip() {
447
- var _this$sceneViewer3, _this$sceneViewer4;
614
+ var _this$sceneViewer6, _this$sceneViewer7;
448
615
  if (!this.tooltipEl || !this.selectedObject) return;
449
616
  var container = this._getContainer();
450
- var camera = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.camera;
451
- var renderer = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.renderer;
617
+ var camera = (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.camera;
618
+ var renderer = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.renderer;
452
619
  if (!container || !camera || !renderer) return;
453
620
 
454
621
  // Compute bounding box to position above the component
@@ -485,8 +652,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
485
652
  }, {
486
653
  key: "_getContainer",
487
654
  value: function _getContainer() {
488
- var _this$sceneViewer5;
489
- 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;
655
+ var _this$sceneViewer8;
656
+ return ((_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.renderer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.domElement) === null || _this$sceneViewer8 === void 0 ? void 0 : _this$sceneViewer8.parentElement) || null;
490
657
  }
491
658
 
492
659
  // -----------------------------------------------------------------------
@@ -508,10 +675,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
508
675
  }, {
509
676
  key: "_buildDataPointRow",
510
677
  value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
511
- var _ref2,
678
+ var _ref3,
512
679
  _this$_stateAdapter$g,
513
- _this$_stateAdapter,
514
- _this4 = this;
680
+ _this$_stateAdapter2,
681
+ _this5 = this;
515
682
  var row = document.createElement('div');
516
683
  row.className = 'cp-tooltip__dp-row';
517
684
  var nameEl = document.createElement('span');
@@ -523,18 +690,18 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
523
690
  // Device-level direction takes precedence; fall back to per-dp direction
524
691
  var resolvedDirection = deviceDirection || dp.direction || 'output';
525
692
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
526
- 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;
693
+ var currentVal = (_ref3 = (_this$_stateAdapter$g = (_this$_stateAdapter2 = this._stateAdapter) === null || _this$_stateAdapter2 === void 0 ? void 0 : _this$_stateAdapter2.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : null;
527
694
  if (isInput) {
528
695
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
529
- var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer, _this4$sceneViewer2;
530
- (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
696
+ var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer, _this5$sceneViewer2;
697
+ (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
531
698
  // Also fire BehaviorManager so any wired behaviors react immediately.
532
699
  // Pass the parent component UUID so behaviors scoped to a specific instance
533
700
  // don't bleed across clones that share the same attachmentId.
534
701
  // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
535
- var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
536
- (_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);
537
- (_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);
702
+ var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
703
+ (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.behaviorManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
704
+ (_this5$sceneViewer2 = _this5.sceneViewer) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.managers) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.ioAnimationManager) === null || _this5$sceneViewer2 === void 0 || _this5$sceneViewer2.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
538
705
  });
539
706
  row.appendChild(ctrl);
540
707
  this._stateElements.set(key, {
@@ -543,9 +710,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
543
710
  isInput: true
544
711
  });
545
712
  } else {
546
- var _dp$stateConfig;
713
+ var _dp$stateConfig2;
547
714
  // unit suffix (optional, shown between name and badge)
548
- var unit = (_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.unit;
715
+ var unit = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.unit;
549
716
  if (unit) {
550
717
  var unitEl = document.createElement('span');
551
718
  unitEl.className = 'cp-tooltip__dp-unit';
@@ -690,7 +857,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
690
857
  }, {
691
858
  key: "_refreshStateDisplays",
692
859
  value: function _refreshStateDisplays() {
693
- var _this5 = this;
860
+ var _this6 = this;
694
861
  if (!this._stateAdapter || !this._stateElements.size) return;
695
862
  this._stateElements.forEach(function (entry, key) {
696
863
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
@@ -699,8 +866,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
699
866
  if (sepIdx === -1) return;
700
867
  var scopedAttachmentId = key.slice(0, sepIdx);
701
868
  var dataPointId = key.slice(sepIdx + 2);
702
- var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
703
- _this5._applyBadgeValue(entry.el, val, entry.dp);
869
+ var val = _this6._stateAdapter.getState(scopedAttachmentId, dataPointId);
870
+ _this6._applyBadgeValue(entry.el, val, entry.dp);
704
871
  });
705
872
  }
706
873
  }]);
@@ -77,7 +77,7 @@ var ModelManager = /*#__PURE__*/function () {
77
77
  key: "loadLibraryModel",
78
78
  value: function () {
79
79
  var _loadLibraryModel = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee(targetMesh, jsonEntry, componentData) {
80
- var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, _jsonEntry$userData4, _t;
80
+ var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
81
81
  return _rollupPluginBabelHelpers.regenerator().w(function (_context2) {
82
82
  while (1) switch (_context2.n) {
83
83
  case 0:
@@ -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;
@@ -173,6 +174,20 @@ var ModelManager = /*#__PURE__*/function () {
173
174
  case 8:
174
175
  // Replace mesh in scene
175
176
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
177
+
178
+ // Pre-warm the filtered bounding-box cache for smart components so the
179
+ // first selection is instant. Deferred to idle time so it does not
180
+ // block the current frame.
181
+ if (componentData.isSmart) {
182
+ warmFn = function warmFn() {
183
+ return boundingBoxUtils.computeFilteredBoundingBoxCached(libraryModel, ['io-device', 'connector']);
184
+ };
185
+ if (typeof requestIdleCallback !== 'undefined') {
186
+ requestIdleCallback(warmFn);
187
+ } else {
188
+ setTimeout(warmFn, 0);
189
+ }
190
+ }
176
191
  console.log("\uD83C\uDF89 ".concat((_jsonEntry$userData3 = jsonEntry.userData) === null || _jsonEntry$userData3 === void 0 ? void 0 : _jsonEntry$userData3.libraryId, " GLB model successfully rendered in scene"));
177
192
  return _context2.a(2, libraryModel);
178
193
  case 9:
@@ -233,6 +233,34 @@ function computeIODeviceBoundingBoxes(componentObject) {
233
233
  * const helpers = createSelectionBoxHelpers(pumpModel, 0x00ff00)
234
234
  * helpers.forEach(h => scene.add(h))
235
235
  */
236
+ /**
237
+ * Returns a filtered bounding box for `object`, using a cache stored on
238
+ * `object.userData._filteredBBoxCache`. The cache key is the serialised
239
+ * world-matrix elements string; if the object has moved the cache is
240
+ * automatically invalidated and recomputed.
241
+ *
242
+ * This avoids the expensive full-traverse on every selection event for
243
+ * large smart components with many child meshes.
244
+ *
245
+ * @param {THREE.Object3D} object
246
+ * @param {string[]} excludeTypes
247
+ * @returns {THREE.Box3}
248
+ */
249
+ function computeFilteredBoundingBoxCached(object, excludeTypes) {
250
+ object.updateMatrixWorld(true);
251
+ var matrixKey = object.matrixWorld.elements.join(',');
252
+ var cache = object.userData._filteredBBoxCache;
253
+ if (cache && cache.matrixKey === matrixKey) {
254
+ return new THREE__namespace.Box3(_rollupPluginBabelHelpers.construct(THREE__namespace.Vector3, _rollupPluginBabelHelpers.toConsumableArray(cache.min)), _rollupPluginBabelHelpers.construct(THREE__namespace.Vector3, _rollupPluginBabelHelpers.toConsumableArray(cache.max)));
255
+ }
256
+ var box = computeFilteredBoundingBox(object, excludeTypes);
257
+ object.userData._filteredBBoxCache = {
258
+ matrixKey: matrixKey,
259
+ min: box.min.toArray(),
260
+ max: box.max.toArray()
261
+ };
262
+ return box;
263
+ }
236
264
  function createSelectionBoxHelpers(object) {
237
265
  var _object$children;
238
266
  var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0x00ff00;
@@ -246,7 +274,7 @@ function createSelectionBoxHelpers(object) {
246
274
  });
247
275
  if (hasIODevices) {
248
276
  // 1. Create filtered helper for the component body
249
- var filteredBox = computeFilteredBoundingBox(object, excludeTypes);
277
+ var filteredBox = computeFilteredBoundingBoxCached(object, excludeTypes);
250
278
  var componentHelper = _createBoxHelperFromBox3(filteredBox, color);
251
279
  componentHelper.isHelper = true;
252
280
  componentHelper.userData = {
@@ -256,33 +284,6 @@ function createSelectionBoxHelpers(object) {
256
284
  excludeTypes: excludeTypes
257
285
  };
258
286
  helpers.push(componentHelper);
259
-
260
- // 2. Create individual helpers for each io-device
261
- var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(object.children),
262
- _step2;
263
- try {
264
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
265
- var _child$userData3;
266
- var child = _step2.value;
267
- if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) !== 'io-device') continue;
268
- var deviceBox = new THREE__namespace.Box3().setFromObject(child);
269
- if (deviceBox.isEmpty()) continue;
270
- var deviceHelper = _createBoxHelperFromBox3(deviceBox, color);
271
- deviceHelper.isHelper = true;
272
- deviceHelper.userData = {
273
- isBoundingBox: true,
274
- sourceObjectUuid: child.uuid,
275
- isFiltered: false,
276
- isIODevice: true,
277
- parentComponentUuid: object.uuid
278
- };
279
- helpers.push(deviceHelper);
280
- }
281
- } catch (err) {
282
- _iterator2.e(err);
283
- } finally {
284
- _iterator2.f();
285
- }
286
287
  } else {
287
288
  // Standard BoxHelper for non-smart objects
288
289
  var boxHelper = new THREE__namespace.BoxHelper(object, color);
@@ -306,11 +307,11 @@ function createSelectionBoxHelpers(object) {
306
307
  * @param {THREE.Scene} scene - The scene (for finding objects by uuid)
307
308
  */
308
309
  function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
309
- var _iterator3 = _rollupPluginBabelHelpers.createForOfIteratorHelper(helpers),
310
- _step3;
310
+ var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(helpers),
311
+ _step2;
311
312
  try {
312
313
  var _loop = function _loop() {
313
- var helper = _step3.value;
314
+ var helper = _step2.value;
314
315
  var _helper$userData = helper.userData,
315
316
  sourceObjectUuid = _helper$userData.sourceObjectUuid,
316
317
  isFiltered = _helper$userData.isFiltered,
@@ -333,29 +334,26 @@ function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
333
334
  if (!sourceObject) return 1; // continue
334
335
  sourceObject.updateMatrixWorld(true);
335
336
  if (isFiltered && excludeTypes) {
336
- // Recompute filtered bbox
337
- var box = computeFilteredBoundingBox(sourceObject, excludeTypes);
337
+ // Recompute filtered bbox (uses cache when the object hasn't moved)
338
+ var box = computeFilteredBoundingBoxCached(sourceObject, excludeTypes);
338
339
  _updateBoxHelperPositions(helper, box);
339
- } else if (isIODevice) {
340
- // Recompute io-device bbox
341
- var _box = new THREE__namespace.Box3().setFromObject(sourceObject);
342
- _updateBoxHelperPositions(helper, _box);
343
340
  } else if (helper.update) {
344
341
  // Standard BoxHelper — use built-in update
345
342
  helper.update();
346
343
  }
347
344
  };
348
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
345
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
349
346
  if (_loop()) continue;
350
347
  }
351
348
  } catch (err) {
352
- _iterator3.e(err);
349
+ _iterator2.e(err);
353
350
  } finally {
354
- _iterator3.f();
351
+ _iterator2.f();
355
352
  }
356
353
  }
357
354
 
358
355
  exports.computeFilteredBoundingBox = computeFilteredBoundingBox;
356
+ exports.computeFilteredBoundingBoxCached = computeFilteredBoundingBoxCached;
359
357
  exports.computeIODeviceBoundingBoxes = computeIODeviceBoundingBoxes;
360
358
  exports.createSelectionBoxHelpers = createSelectionBoxHelpers;
361
359
  exports.updateSelectionBoxHelpers = updateSelectionBoxHelpers;
@@ -31,7 +31,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
31
31
  * Initialize the CentralPlant manager
32
32
  *
33
33
  * @constructor
34
- * @version 0.3.25
34
+ * @version 0.3.27
35
35
  * @updated 2025-10-22
36
36
  *
37
37
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.