@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
@@ -1,4 +1,4 @@
1
- import { inherits as _inherits, createClass as _createClass, classCallCheck as _classCallCheck, callSuper as _callSuper } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { inherits as _inherits, createClass as _createClass, createForOfIteratorHelper as _createForOfIteratorHelper, 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
 
@@ -105,7 +105,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
105
105
  }, {
106
106
  key: "toggleIODeviceBinaryState",
107
107
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
108
- var _ref, _this$sceneViewer, _this$sceneViewer2;
108
+ var _ref, _this$sceneViewer;
109
109
  if (!ioDeviceObject || !this._stateAdapter) return;
110
110
  var ud = ioDeviceObject.userData;
111
111
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
@@ -139,11 +139,172 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
139
139
  var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
140
140
  var newVal = !Boolean(currentVal);
141
141
  this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
142
- (_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);
143
- (_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);
142
+ (_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);
144
143
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
145
144
  }
146
145
 
146
+ // ── IO device drag-to-state ─────────────────────────────────────────────
147
+
148
+ /**
149
+ * Begin tracking a drag gesture on an IO device mesh.
150
+ * Records the initial state of each animation data point so that
151
+ * `updateIODeviceDrag` can compute relative offsets from it.
152
+ *
153
+ * @param {THREE.Object3D} ioDeviceObject
154
+ */
155
+ }, {
156
+ key: "startIODeviceDrag",
157
+ value: function startIODeviceDrag(ioDeviceObject) {
158
+ var _this$sceneViewer2,
159
+ _this2 = this;
160
+ if (!ioDeviceObject || !this._stateAdapter) return;
161
+ var ud = ioDeviceObject.userData;
162
+ var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
163
+ if (!attachmentId) return;
164
+ var parentUuid = null;
165
+ var obj = ioDeviceObject.parent;
166
+ while (obj) {
167
+ var _obj$userData2;
168
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component') {
169
+ parentUuid = obj.uuid;
170
+ break;
171
+ }
172
+ obj = obj.parent;
173
+ }
174
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
175
+ 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;
176
+ var dataPoints = ((ioAnimMgr === null || ioAnimMgr === void 0 ? void 0 : ioAnimMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
177
+ // deduplicate by id
178
+ .filter(function (dp, i, arr) {
179
+ return arr.findIndex(function (d) {
180
+ return d.id === dp.id;
181
+ }) === i;
182
+ });
183
+ var dpSessions = [];
184
+ var _iterator = _createForOfIteratorHelper(dataPoints),
185
+ _step;
186
+ try {
187
+ var _loop = function _loop() {
188
+ var _this2$_stateAdapter$;
189
+ var dp = _step.value;
190
+ var stateType = (dp.stateType || '').toLowerCase();
191
+ if (stateType !== 'binary' && stateType !== 'boolean' && stateType !== 'enum') return 1; // continue
192
+ var curVal = (_this2$_stateAdapter$ = _this2._stateAdapter.getState(scopedAttachmentId, dp.id)) !== null && _this2$_stateAdapter$ !== void 0 ? _this2$_stateAdapter$ : dp.defaultValue;
193
+ if (stateType === 'binary' || stateType === 'boolean') {
194
+ dpSessions.push({
195
+ dp: dp,
196
+ scopedAttachmentId: scopedAttachmentId,
197
+ attachmentId: attachmentId,
198
+ parentUuid: parentUuid,
199
+ stateType: 'binary',
200
+ lastApplied: curVal
201
+ });
202
+ } else {
203
+ var _dp$stateConfig;
204
+ var opts = ((_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.options) || [];
205
+ var curIdx = opts.findIndex(function (o) {
206
+ return String(o) === String(curVal);
207
+ });
208
+ dpSessions.push({
209
+ dp: dp,
210
+ scopedAttachmentId: scopedAttachmentId,
211
+ attachmentId: attachmentId,
212
+ parentUuid: parentUuid,
213
+ stateType: 'enum',
214
+ opts: opts,
215
+ startIdx: curIdx >= 0 ? curIdx : 0,
216
+ lastAppliedIdx: curIdx >= 0 ? curIdx : 0
217
+ });
218
+ }
219
+ };
220
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
221
+ if (_loop()) continue;
222
+ }
223
+ } catch (err) {
224
+ _iterator.e(err);
225
+ } finally {
226
+ _iterator.f();
227
+ }
228
+ this._ioDragSession = dpSessions.length ? {
229
+ dpSessions: dpSessions
230
+ } : null;
231
+ }
232
+
233
+ /**
234
+ * Update animated mesh state while a drag is in progress.
235
+ * Called continuously during pointermove.
236
+ *
237
+ * Sign convention: up/right = positive `signedDelta`.
238
+ * - Binary: > +20 px → true/on state, < −20 px → false/off state.
239
+ * - Enum: each ±30 px step advances/retreats one option in the list.
240
+ *
241
+ * @param {number} signedDelta - Cumulative signed pixel displacement since drag start
242
+ */
243
+ }, {
244
+ key: "updateIODeviceDrag",
245
+ value: function updateIODeviceDrag(signedDelta) {
246
+ var session = this._ioDragSession;
247
+ if (!session) return;
248
+ var BINARY_THRESHOLD = 20;
249
+ var ENUM_STEP_PX = 30;
250
+ var _iterator2 = _createForOfIteratorHelper(session.dpSessions),
251
+ _step2;
252
+ try {
253
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
254
+ var dps = _step2.value;
255
+ if (dps.stateType === 'binary') {
256
+ var newVal = void 0;
257
+ if (signedDelta > BINARY_THRESHOLD) {
258
+ newVal = true;
259
+ } else if (signedDelta < -BINARY_THRESHOLD) {
260
+ newVal = false;
261
+ } else {
262
+ continue; // dead zone
263
+ }
264
+ if (newVal === dps.lastApplied) continue;
265
+ dps.lastApplied = newVal;
266
+ this._applyDpState(dps, newVal);
267
+ } else if (dps.stateType === 'enum') {
268
+ var steps = Math.round(signedDelta / ENUM_STEP_PX);
269
+ var newIdx = Math.max(0, Math.min(dps.opts.length - 1, dps.startIdx + steps));
270
+ if (newIdx === dps.lastAppliedIdx) continue;
271
+ dps.lastAppliedIdx = newIdx;
272
+ this._applyDpState(dps, dps.opts[newIdx]);
273
+ }
274
+ }
275
+ } catch (err) {
276
+ _iterator2.e(err);
277
+ } finally {
278
+ _iterator2.f();
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Clean up drag session state on pointerup.
284
+ */
285
+ }, {
286
+ key: "endIODeviceDrag",
287
+ value: function endIODeviceDrag() {
288
+ this._ioDragSession = null;
289
+ }
290
+
291
+ /**
292
+ * Apply a new value to a data point, updating Vuex state and firing behavior/animation triggers.
293
+ * @private
294
+ */
295
+ }, {
296
+ key: "_applyDpState",
297
+ value: function _applyDpState(_ref2, newVal) {
298
+ var _this$_stateAdapter, _this$sceneViewer3;
299
+ var scopedAttachmentId = _ref2.scopedAttachmentId,
300
+ attachmentId = _ref2.attachmentId,
301
+ parentUuid = _ref2.parentUuid,
302
+ dp = _ref2.dp;
303
+ var dpId = dp.id;
304
+ (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
305
+ (_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);
306
+ }
307
+
147
308
  /**
148
309
  * Should be called when an object is selected or deselected.
149
310
  * @param {THREE.Object3D|null} object
@@ -257,18 +418,18 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
257
418
  }, {
258
419
  key: "_getIODevices",
259
420
  value: function _getIODevices(object) {
260
- var _this2 = this;
421
+ var _this3 = this;
261
422
  var devices = [];
262
423
  var parentUuid = object.uuid; // The component's own UUID
263
424
  object.traverse(function (child) {
264
425
  var _child$userData;
265
426
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
266
- var _this2$sceneViewer$ma, _this2$sceneViewer;
427
+ var _this3$sceneViewer$ma, _this3$sceneViewer;
267
428
  var attachmentId = child.userData.attachmentId || '';
268
429
 
269
430
  // Use only data points from the animate window (animationConfig).
270
431
  // The static ioConfig.states[] snapshot on userData is intentionally ignored.
271
- var dataPoints = (_this2$sceneViewer$ma = (_this2$sceneViewer = _this2.sceneViewer) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.managers) === null || _this2$sceneViewer === void 0 || (_this2$sceneViewer = _this2$sceneViewer.ioAnimationManager) === null || _this2$sceneViewer === void 0 ? void 0 : _this2$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this2$sceneViewer$ma !== void 0 ? _this2$sceneViewer$ma : [];
432
+ 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 : [];
272
433
 
273
434
  // When data points come from animationConfig they already carry direction:'input'.
274
435
  // Pass null so _buildDataPointRow uses the per-dp direction instead of the
@@ -278,7 +439,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
278
439
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
279
440
  deviceId: child.userData.deviceId || '',
280
441
  attachmentId: attachmentId,
281
- scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
442
+ scopedAttachmentId: _this3._getScopedAttachmentKey(attachmentId, parentUuid),
282
443
  dataPoints: dataPoints,
283
444
  direction: deviceDirection
284
445
  });
@@ -294,7 +455,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
294
455
  }, {
295
456
  key: "_buildTooltip",
296
457
  value: function _buildTooltip(object) {
297
- var _this3 = this;
458
+ var _this4 = this;
298
459
  // Remove any existing tooltip first
299
460
  this.hide();
300
461
  // Re-assign selected object since hide() clears it
@@ -365,7 +526,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
365
526
  // Use scopedAttachmentId to ensure state is isolated per component instance
366
527
  if (device.scopedAttachmentId && device.dataPoints.length > 0) {
367
528
  device.dataPoints.forEach(function (dp) {
368
- var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
529
+ var row = _this4._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
369
530
  list.appendChild(row);
370
531
  });
371
532
  }
@@ -376,11 +537,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
376
537
  // Hover expand/collapse
377
538
  trigger.addEventListener('mouseenter', function () {
378
539
  ioSection.classList.add('expanded');
379
- _this3._ioExpanded = true;
540
+ _this4._ioExpanded = true;
380
541
  });
381
542
  ioSection.addEventListener('mouseleave', function () {
382
543
  ioSection.classList.remove('expanded');
383
- _this3._ioExpanded = false;
544
+ _this4._ioExpanded = false;
384
545
  });
385
546
  card.appendChild(ioSection);
386
547
  } else {
@@ -424,11 +585,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
424
585
  }, {
425
586
  key: "_positionTooltip",
426
587
  value: function _positionTooltip() {
427
- var _this$sceneViewer3, _this$sceneViewer4;
588
+ var _this$sceneViewer4, _this$sceneViewer5;
428
589
  if (!this.tooltipEl || !this.selectedObject) return;
429
590
  var container = this._getContainer();
430
- var camera = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.camera;
431
- var renderer = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.renderer;
591
+ var camera = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.camera;
592
+ var renderer = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.renderer;
432
593
  if (!container || !camera || !renderer) return;
433
594
 
434
595
  // Compute bounding box to position above the component
@@ -465,8 +626,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
465
626
  }, {
466
627
  key: "_getContainer",
467
628
  value: function _getContainer() {
468
- var _this$sceneViewer5;
469
- 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;
629
+ var _this$sceneViewer6;
630
+ 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;
470
631
  }
471
632
 
472
633
  // -----------------------------------------------------------------------
@@ -488,10 +649,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
488
649
  }, {
489
650
  key: "_buildDataPointRow",
490
651
  value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
491
- var _ref2,
652
+ var _ref3,
492
653
  _this$_stateAdapter$g,
493
- _this$_stateAdapter,
494
- _this4 = this;
654
+ _this$_stateAdapter2,
655
+ _this5 = this;
495
656
  var row = document.createElement('div');
496
657
  row.className = 'cp-tooltip__dp-row';
497
658
  var nameEl = document.createElement('span');
@@ -503,18 +664,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
503
664
  // Device-level direction takes precedence; fall back to per-dp direction
504
665
  var resolvedDirection = deviceDirection || dp.direction || 'output';
505
666
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
506
- 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;
667
+ 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;
507
668
  if (isInput) {
508
669
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
509
- var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer, _this4$sceneViewer2;
510
- (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
511
- // Also fire BehaviorManager so any wired behaviors react immediately.
512
- // Pass the parent component UUID so behaviors scoped to a specific instance
513
- // don't bleed across clones that share the same attachmentId.
514
- // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
515
- var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
516
- (_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);
517
- (_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);
670
+ var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer;
671
+ (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
672
+ var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
673
+ (_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);
518
674
  });
519
675
  row.appendChild(ctrl);
520
676
  this._stateElements.set(key, {
@@ -523,9 +679,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
523
679
  isInput: true
524
680
  });
525
681
  } else {
526
- var _dp$stateConfig;
682
+ var _dp$stateConfig2;
527
683
  // unit suffix (optional, shown between name and badge)
528
- var unit = (_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.unit;
684
+ var unit = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.unit;
529
685
  if (unit) {
530
686
  var unitEl = document.createElement('span');
531
687
  unitEl.className = 'cp-tooltip__dp-unit';
@@ -670,7 +826,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
670
826
  }, {
671
827
  key: "_refreshStateDisplays",
672
828
  value: function _refreshStateDisplays() {
673
- var _this5 = this;
829
+ var _this6 = this;
674
830
  if (!this._stateAdapter || !this._stateElements.size) return;
675
831
  this._stateElements.forEach(function (entry, key) {
676
832
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
@@ -679,8 +835,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
679
835
  if (sepIdx === -1) return;
680
836
  var scopedAttachmentId = key.slice(0, sepIdx);
681
837
  var dataPointId = key.slice(sepIdx + 2);
682
- var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
683
- _this5._applyBadgeValue(entry.el, val, entry.dp);
838
+ var val = _this6._stateAdapter.getState(scopedAttachmentId, dataPointId);
839
+ _this6._applyBadgeValue(entry.el, val, entry.dp);
684
840
  });
685
841
  }
686
842
  }]);
@@ -2,7 +2,7 @@ import { createClass as _createClass, objectSpread2 as _objectSpread2, toConsuma
2
2
  import * as THREE from 'three';
3
3
  import { attachIODevicesToComponent } from '../../utils/ioDeviceUtils.js';
4
4
  import modelPreloader from '../../rendering/modelPreloader.js';
5
- import { computeFilteredBoundingBox } from '../../utils/boundingBoxUtils.js';
5
+ import { computeFilteredBoundingBox, computeFilteredBoundingBoxCached } from '../../utils/boundingBoxUtils.js';
6
6
  import { cacheBasePosition } from './viewport2DManager.js';
7
7
 
8
8
  var ModelManager = /*#__PURE__*/function () {
@@ -53,7 +53,7 @@ var ModelManager = /*#__PURE__*/function () {
53
53
  key: "loadLibraryModel",
54
54
  value: function () {
55
55
  var _loadLibraryModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(targetMesh, jsonEntry, componentData) {
56
- var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, _jsonEntry$userData4, _t;
56
+ var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
57
57
  return _regenerator().w(function (_context2) {
58
58
  while (1) switch (_context2.n) {
59
59
  case 0:
@@ -150,6 +150,20 @@ var ModelManager = /*#__PURE__*/function () {
150
150
  case 8:
151
151
  // Replace mesh in scene
152
152
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
153
+
154
+ // Pre-warm the filtered bounding-box cache for smart components so the
155
+ // first selection is instant. Deferred to idle time so it does not
156
+ // block the current frame.
157
+ if (componentData.isSmart) {
158
+ warmFn = function warmFn() {
159
+ return computeFilteredBoundingBoxCached(libraryModel, ['io-device', 'connector']);
160
+ };
161
+ if (typeof requestIdleCallback !== 'undefined') {
162
+ requestIdleCallback(warmFn);
163
+ } else {
164
+ setTimeout(warmFn, 0);
165
+ }
166
+ }
153
167
  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"));
154
168
  return _context2.a(2, libraryModel);
155
169
  case 9:
@@ -1,4 +1,4 @@
1
- import { createClass as _createClass, objectSpread2 as _objectSpread2, classCallCheck as _classCallCheck, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
1
+ import { createClass as _createClass, classCallCheck as _classCallCheck, toConsumableArray as _toConsumableArray, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
2
  import 'three';
3
3
  import { GLTFExporter } from '../../../node_modules/three/examples/jsm/exporters/GLTFExporter.js';
4
4
  import { getHardcodedUuid } from '../../utils/nameUtils.js';
@@ -259,40 +259,14 @@ var SceneExportManager = /*#__PURE__*/function () {
259
259
  }
260
260
  });
261
261
 
262
- // Helper function to extract behaviors from current scene data
263
- var extractBehaviors = function extractBehaviors() {
264
- var _this$sceneViewer, _this$sceneViewer2;
265
- // Only export behaviors that are NOT re-derivable from a component's
266
- // defaultBehaviors[]. All component/device default behaviors are
267
- // reconstructed at load time by Step B of _processBehaviors() using the
268
- // component dictionary, so writing compact behaviorRef entries for them
269
- // would be redundant. The scene JSON behaviors[] array is reserved for
270
- // any future scene-level overrides that cannot be derived from the asset.
271
- if ((_this$sceneViewer = _this.sceneViewer) !== null && _this$sceneViewer !== void 0 && (_this$sceneViewer = _this$sceneViewer.managers) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.behaviorManager) {
272
- return _this.sceneViewer.managers.behaviorManager.getBehaviors().filter(function (b) {
273
- return !b._isDefaultBehavior;
274
- });
275
- }
276
- // Fallback when BehaviorManager is not available: exclude any entry that
277
- // was already a behaviorRef (it was derivable from the component asset)
278
- // and exclude the legacy _isDefaultBehavior marker if present.
279
- 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) {
280
- return !b.behaviorRef && !b._isDefaultBehavior;
281
- });
282
- };
283
-
284
262
  // Build the complete export data structure (matching central-plant-input.json format)
285
- var behaviors = extractBehaviors();
286
- var exportData = _objectSpread2(_objectSpread2({
263
+ var exportData = {
287
264
  version: '2.3',
288
- connections: extractConnections()
289
- }, behaviors.length > 0 ? {
290
- behaviors: behaviors
291
- } : {}), {}, {
265
+ connections: extractConnections(),
292
266
  scene: {
293
267
  children: sceneChildren
294
268
  }
295
- });
269
+ };
296
270
  console.log('✅ Scene export completed:', exportData);
297
271
  console.log("\uD83D\uDCCA Exported ".concat(sceneChildren.length, " components and ").concat(exportData.connections.length, " connections"));
298
272
  return exportData;