@2112-lab/central-plant 0.3.26 → 0.3.28

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.
Files changed (28) hide show
  1. package/dist/bundle/index.js +922 -974
  2. package/dist/cjs/src/core/centralPlant.js +8 -115
  3. package/dist/cjs/src/core/centralPlantInternals.js +23 -17
  4. package/dist/cjs/src/core/sceneViewer.js +55 -2
  5. package/dist/cjs/src/index.js +0 -2
  6. package/dist/cjs/src/managers/behaviors/IoAnimationManager.js +24 -1
  7. package/dist/cjs/src/managers/behaviors/IoOutlineManager.js +258 -0
  8. package/dist/cjs/src/managers/controls/transformControlsManager.js +319 -43
  9. package/dist/cjs/src/managers/scene/animationManager.js +9 -2
  10. package/dist/cjs/src/managers/scene/componentTooltipManager.js +190 -34
  11. package/dist/cjs/src/managers/scene/modelManager.js +15 -1
  12. package/dist/cjs/src/managers/scene/sceneExportManager.js +3 -29
  13. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +12 -289
  14. package/dist/cjs/src/utils/boundingBoxUtils.js +38 -40
  15. package/dist/esm/src/core/centralPlant.js +8 -115
  16. package/dist/esm/src/core/centralPlantInternals.js +23 -17
  17. package/dist/esm/src/core/sceneViewer.js +55 -2
  18. package/dist/esm/src/index.js +0 -1
  19. package/dist/esm/src/managers/behaviors/IoAnimationManager.js +24 -1
  20. package/dist/esm/src/managers/behaviors/IoOutlineManager.js +234 -0
  21. package/dist/esm/src/managers/controls/transformControlsManager.js +319 -43
  22. package/dist/esm/src/managers/scene/animationManager.js +9 -2
  23. package/dist/esm/src/managers/scene/componentTooltipManager.js +191 -35
  24. package/dist/esm/src/managers/scene/modelManager.js +16 -2
  25. package/dist/esm/src/managers/scene/sceneExportManager.js +4 -30
  26. package/dist/esm/src/managers/scene/sceneOperationsManager.js +12 -289
  27. package/dist/esm/src/utils/boundingBoxUtils.js +39 -42
  28. package/package.json +1 -1
@@ -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, _this$sceneViewer2;
132
+ var _ref, _this$sceneViewer;
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;
@@ -163,11 +163,172 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
163
163
  var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
164
164
  var newVal = !Boolean(currentVal);
165
165
  this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
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);
166
+ (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.ioAnimationManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
168
167
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
169
168
  }
170
169
 
170
+ // ── IO device drag-to-state ─────────────────────────────────────────────
171
+
172
+ /**
173
+ * Begin tracking a drag gesture on an IO device mesh.
174
+ * Records the initial state of each animation data point so that
175
+ * `updateIODeviceDrag` can compute relative offsets from it.
176
+ *
177
+ * @param {THREE.Object3D} ioDeviceObject
178
+ */
179
+ }, {
180
+ key: "startIODeviceDrag",
181
+ value: function startIODeviceDrag(ioDeviceObject) {
182
+ var _this$sceneViewer2,
183
+ _this2 = this;
184
+ if (!ioDeviceObject || !this._stateAdapter) return;
185
+ var ud = ioDeviceObject.userData;
186
+ var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
187
+ if (!attachmentId) return;
188
+ var parentUuid = null;
189
+ var obj = ioDeviceObject.parent;
190
+ while (obj) {
191
+ var _obj$userData2;
192
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component') {
193
+ parentUuid = obj.uuid;
194
+ break;
195
+ }
196
+ obj = obj.parent;
197
+ }
198
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
199
+ var ioAnimMgr = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.ioAnimationManager;
200
+ var dataPoints = ((ioAnimMgr === null || ioAnimMgr === void 0 ? void 0 : ioAnimMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
201
+ // deduplicate by id
202
+ .filter(function (dp, i, arr) {
203
+ return arr.findIndex(function (d) {
204
+ return d.id === dp.id;
205
+ }) === i;
206
+ });
207
+ var dpSessions = [];
208
+ var _iterator = _rollupPluginBabelHelpers.createForOfIteratorHelper(dataPoints),
209
+ _step;
210
+ try {
211
+ var _loop = function _loop() {
212
+ var _this2$_stateAdapter$;
213
+ var dp = _step.value;
214
+ var stateType = (dp.stateType || '').toLowerCase();
215
+ if (stateType !== 'binary' && stateType !== 'boolean' && stateType !== 'enum') return 1; // continue
216
+ var curVal = (_this2$_stateAdapter$ = _this2._stateAdapter.getState(scopedAttachmentId, dp.id)) !== null && _this2$_stateAdapter$ !== void 0 ? _this2$_stateAdapter$ : dp.defaultValue;
217
+ if (stateType === 'binary' || stateType === 'boolean') {
218
+ dpSessions.push({
219
+ dp: dp,
220
+ scopedAttachmentId: scopedAttachmentId,
221
+ attachmentId: attachmentId,
222
+ parentUuid: parentUuid,
223
+ stateType: 'binary',
224
+ lastApplied: curVal
225
+ });
226
+ } else {
227
+ var _dp$stateConfig;
228
+ var opts = ((_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.options) || [];
229
+ var curIdx = opts.findIndex(function (o) {
230
+ return String(o) === String(curVal);
231
+ });
232
+ dpSessions.push({
233
+ dp: dp,
234
+ scopedAttachmentId: scopedAttachmentId,
235
+ attachmentId: attachmentId,
236
+ parentUuid: parentUuid,
237
+ stateType: 'enum',
238
+ opts: opts,
239
+ startIdx: curIdx >= 0 ? curIdx : 0,
240
+ lastAppliedIdx: curIdx >= 0 ? curIdx : 0
241
+ });
242
+ }
243
+ };
244
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
245
+ if (_loop()) continue;
246
+ }
247
+ } catch (err) {
248
+ _iterator.e(err);
249
+ } finally {
250
+ _iterator.f();
251
+ }
252
+ this._ioDragSession = dpSessions.length ? {
253
+ dpSessions: dpSessions
254
+ } : null;
255
+ }
256
+
257
+ /**
258
+ * Update animated mesh state while a drag is in progress.
259
+ * Called continuously during pointermove.
260
+ *
261
+ * Sign convention: up/right = positive `signedDelta`.
262
+ * - Binary: > +20 px → true/on state, < −20 px → false/off state.
263
+ * - Enum: each ±30 px step advances/retreats one option in the list.
264
+ *
265
+ * @param {number} signedDelta - Cumulative signed pixel displacement since drag start
266
+ */
267
+ }, {
268
+ key: "updateIODeviceDrag",
269
+ value: function updateIODeviceDrag(signedDelta) {
270
+ var session = this._ioDragSession;
271
+ if (!session) return;
272
+ var BINARY_THRESHOLD = 20;
273
+ var ENUM_STEP_PX = 30;
274
+ var _iterator2 = _rollupPluginBabelHelpers.createForOfIteratorHelper(session.dpSessions),
275
+ _step2;
276
+ try {
277
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
278
+ var dps = _step2.value;
279
+ if (dps.stateType === 'binary') {
280
+ var newVal = void 0;
281
+ if (signedDelta > BINARY_THRESHOLD) {
282
+ newVal = true;
283
+ } else if (signedDelta < -BINARY_THRESHOLD) {
284
+ newVal = false;
285
+ } else {
286
+ continue; // dead zone
287
+ }
288
+ if (newVal === dps.lastApplied) continue;
289
+ dps.lastApplied = newVal;
290
+ this._applyDpState(dps, newVal);
291
+ } else if (dps.stateType === 'enum') {
292
+ var steps = Math.round(signedDelta / ENUM_STEP_PX);
293
+ var newIdx = Math.max(0, Math.min(dps.opts.length - 1, dps.startIdx + steps));
294
+ if (newIdx === dps.lastAppliedIdx) continue;
295
+ dps.lastAppliedIdx = newIdx;
296
+ this._applyDpState(dps, dps.opts[newIdx]);
297
+ }
298
+ }
299
+ } catch (err) {
300
+ _iterator2.e(err);
301
+ } finally {
302
+ _iterator2.f();
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Clean up drag session state on pointerup.
308
+ */
309
+ }, {
310
+ key: "endIODeviceDrag",
311
+ value: function endIODeviceDrag() {
312
+ this._ioDragSession = null;
313
+ }
314
+
315
+ /**
316
+ * Apply a new value to a data point, updating Vuex state and firing behavior/animation triggers.
317
+ * @private
318
+ */
319
+ }, {
320
+ key: "_applyDpState",
321
+ value: function _applyDpState(_ref2, newVal) {
322
+ var _this$_stateAdapter, _this$sceneViewer3;
323
+ var scopedAttachmentId = _ref2.scopedAttachmentId,
324
+ attachmentId = _ref2.attachmentId,
325
+ parentUuid = _ref2.parentUuid,
326
+ dp = _ref2.dp;
327
+ var dpId = dp.id;
328
+ (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
329
+ (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.ioAnimationManager) === null || _this$sceneViewer3 === void 0 || _this$sceneViewer3.triggerState(attachmentId, dpId, newVal, parentUuid);
330
+ }
331
+
171
332
  /**
172
333
  * Should be called when an object is selected or deselected.
173
334
  * @param {THREE.Object3D|null} object
@@ -281,18 +442,18 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
281
442
  }, {
282
443
  key: "_getIODevices",
283
444
  value: function _getIODevices(object) {
284
- var _this2 = this;
445
+ var _this3 = this;
285
446
  var devices = [];
286
447
  var parentUuid = object.uuid; // The component's own UUID
287
448
  object.traverse(function (child) {
288
449
  var _child$userData;
289
450
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
290
- var _this2$sceneViewer$ma, _this2$sceneViewer;
451
+ var _this3$sceneViewer$ma, _this3$sceneViewer;
291
452
  var attachmentId = child.userData.attachmentId || '';
292
453
 
293
454
  // Use only data points from the animate window (animationConfig).
294
455
  // 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 : [];
456
+ 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 : [];
296
457
 
297
458
  // When data points come from animationConfig they already carry direction:'input'.
298
459
  // Pass null so _buildDataPointRow uses the per-dp direction instead of the
@@ -302,7 +463,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
302
463
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
303
464
  deviceId: child.userData.deviceId || '',
304
465
  attachmentId: attachmentId,
305
- scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
466
+ scopedAttachmentId: _this3._getScopedAttachmentKey(attachmentId, parentUuid),
306
467
  dataPoints: dataPoints,
307
468
  direction: deviceDirection
308
469
  });
@@ -318,7 +479,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
318
479
  }, {
319
480
  key: "_buildTooltip",
320
481
  value: function _buildTooltip(object) {
321
- var _this3 = this;
482
+ var _this4 = this;
322
483
  // Remove any existing tooltip first
323
484
  this.hide();
324
485
  // Re-assign selected object since hide() clears it
@@ -389,7 +550,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
389
550
  // Use scopedAttachmentId to ensure state is isolated per component instance
390
551
  if (device.scopedAttachmentId && device.dataPoints.length > 0) {
391
552
  device.dataPoints.forEach(function (dp) {
392
- var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
553
+ var row = _this4._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
393
554
  list.appendChild(row);
394
555
  });
395
556
  }
@@ -400,11 +561,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
400
561
  // Hover expand/collapse
401
562
  trigger.addEventListener('mouseenter', function () {
402
563
  ioSection.classList.add('expanded');
403
- _this3._ioExpanded = true;
564
+ _this4._ioExpanded = true;
404
565
  });
405
566
  ioSection.addEventListener('mouseleave', function () {
406
567
  ioSection.classList.remove('expanded');
407
- _this3._ioExpanded = false;
568
+ _this4._ioExpanded = false;
408
569
  });
409
570
  card.appendChild(ioSection);
410
571
  } else {
@@ -448,11 +609,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
448
609
  }, {
449
610
  key: "_positionTooltip",
450
611
  value: function _positionTooltip() {
451
- var _this$sceneViewer3, _this$sceneViewer4;
612
+ var _this$sceneViewer4, _this$sceneViewer5;
452
613
  if (!this.tooltipEl || !this.selectedObject) return;
453
614
  var container = this._getContainer();
454
- var camera = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.camera;
455
- var renderer = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.renderer;
615
+ var camera = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.camera;
616
+ var renderer = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.renderer;
456
617
  if (!container || !camera || !renderer) return;
457
618
 
458
619
  // Compute bounding box to position above the component
@@ -489,8 +650,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
489
650
  }, {
490
651
  key: "_getContainer",
491
652
  value: function _getContainer() {
492
- var _this$sceneViewer5;
493
- 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;
653
+ var _this$sceneViewer6;
654
+ return ((_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.renderer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.domElement) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.parentElement) || null;
494
655
  }
495
656
 
496
657
  // -----------------------------------------------------------------------
@@ -512,10 +673,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
512
673
  }, {
513
674
  key: "_buildDataPointRow",
514
675
  value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
515
- var _ref2,
676
+ var _ref3,
516
677
  _this$_stateAdapter$g,
517
- _this$_stateAdapter,
518
- _this4 = this;
678
+ _this$_stateAdapter2,
679
+ _this5 = this;
519
680
  var row = document.createElement('div');
520
681
  row.className = 'cp-tooltip__dp-row';
521
682
  var nameEl = document.createElement('span');
@@ -527,18 +688,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
527
688
  // Device-level direction takes precedence; fall back to per-dp direction
528
689
  var resolvedDirection = deviceDirection || dp.direction || 'output';
529
690
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
530
- 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;
691
+ 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;
531
692
  if (isInput) {
532
693
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
533
- var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer, _this4$sceneViewer2;
534
- (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
535
- // Also fire BehaviorManager so any wired behaviors react immediately.
536
- // Pass the parent component UUID so behaviors scoped to a specific instance
537
- // don't bleed across clones that share the same attachmentId.
538
- // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
539
- var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
540
- (_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);
541
- (_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);
694
+ var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer;
695
+ (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
696
+ var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
697
+ (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.ioAnimationManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
542
698
  });
543
699
  row.appendChild(ctrl);
544
700
  this._stateElements.set(key, {
@@ -547,9 +703,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
547
703
  isInput: true
548
704
  });
549
705
  } else {
550
- var _dp$stateConfig;
706
+ var _dp$stateConfig2;
551
707
  // unit suffix (optional, shown between name and badge)
552
- var unit = (_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.unit;
708
+ var unit = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.unit;
553
709
  if (unit) {
554
710
  var unitEl = document.createElement('span');
555
711
  unitEl.className = 'cp-tooltip__dp-unit';
@@ -694,7 +850,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
694
850
  }, {
695
851
  key: "_refreshStateDisplays",
696
852
  value: function _refreshStateDisplays() {
697
- var _this5 = this;
853
+ var _this6 = this;
698
854
  if (!this._stateAdapter || !this._stateElements.size) return;
699
855
  this._stateElements.forEach(function (entry, key) {
700
856
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
@@ -703,8 +859,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
703
859
  if (sepIdx === -1) return;
704
860
  var scopedAttachmentId = key.slice(0, sepIdx);
705
861
  var dataPointId = key.slice(sepIdx + 2);
706
- var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
707
- _this5._applyBadgeValue(entry.el, val, entry.dp);
862
+ var val = _this6._stateAdapter.getState(scopedAttachmentId, dataPointId);
863
+ _this6._applyBadgeValue(entry.el, val, entry.dp);
708
864
  });
709
865
  }
710
866
  }]);
@@ -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:
@@ -174,6 +174,20 @@ var ModelManager = /*#__PURE__*/function () {
174
174
  case 8:
175
175
  // Replace mesh in scene
176
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
+ }
177
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"));
178
192
  return _context2.a(2, libraryModel);
179
193
  case 9:
@@ -281,40 +281,14 @@ var SceneExportManager = /*#__PURE__*/function () {
281
281
  }
282
282
  });
283
283
 
284
- // Helper function to extract behaviors from current scene data
285
- var extractBehaviors = function extractBehaviors() {
286
- var _this$sceneViewer, _this$sceneViewer2;
287
- // Only export behaviors that are NOT re-derivable from a component's
288
- // defaultBehaviors[]. All component/device default behaviors are
289
- // reconstructed at load time by Step B of _processBehaviors() using the
290
- // component dictionary, so writing compact behaviorRef entries for them
291
- // would be redundant. The scene JSON behaviors[] array is reserved for
292
- // any future scene-level overrides that cannot be derived from the asset.
293
- if ((_this$sceneViewer = _this.sceneViewer) !== null && _this$sceneViewer !== void 0 && (_this$sceneViewer = _this$sceneViewer.managers) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.behaviorManager) {
294
- return _this.sceneViewer.managers.behaviorManager.getBehaviors().filter(function (b) {
295
- return !b._isDefaultBehavior;
296
- });
297
- }
298
- // Fallback when BehaviorManager is not available: exclude any entry that
299
- // was already a behaviorRef (it was derivable from the component asset)
300
- // and exclude the legacy _isDefaultBehavior marker if present.
301
- return (((_this$sceneViewer2 = _this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.currentSceneData) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.behaviors) || []).filter(function (b) {
302
- return !b.behaviorRef && !b._isDefaultBehavior;
303
- });
304
- };
305
-
306
284
  // Build the complete export data structure (matching central-plant-input.json format)
307
- var behaviors = extractBehaviors();
308
- var exportData = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({
285
+ var exportData = {
309
286
  version: '2.3',
310
- connections: extractConnections()
311
- }, behaviors.length > 0 ? {
312
- behaviors: behaviors
313
- } : {}), {}, {
287
+ connections: extractConnections(),
314
288
  scene: {
315
289
  children: sceneChildren
316
290
  }
317
- });
291
+ };
318
292
  console.log('✅ Scene export completed:', exportData);
319
293
  console.log("\uD83D\uDCCA Exported ".concat(sceneChildren.length, " components and ").concat(exportData.connections.length, " connections"));
320
294
  return exportData;