@2112-lab/central-plant 0.3.33 → 0.3.35

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.
@@ -4165,6 +4165,7 @@ var TransformControlsManager = /*#__PURE__*/function () {
4165
4165
  // Detect pointerdown on an IO device mesh and convert a drag gesture into
4166
4166
  // state changes. Up/right = positive direction, down/left = negative.
4167
4167
  this.eventHandlers.pointerdown = function (event) {
4168
+ var _ioDeviceObject$userD, _this4$sceneViewer;
4168
4169
  if (_this4.transformState.isTransforming) return;
4169
4170
  if (!_this4.callbacks.onIODeviceDrag) return;
4170
4171
  _this4._calculateMousePosition(event, mouse);
@@ -4177,14 +4178,14 @@ var TransformControlsManager = /*#__PURE__*/function () {
4177
4178
  try {
4178
4179
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
4179
4180
  var hit = _step.value;
4180
- var obj = hit.object;
4181
- while (obj) {
4182
- var _obj$userData;
4183
- if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'io-device') {
4184
- ioDeviceObject = obj;
4181
+ var _obj = hit.object;
4182
+ while (_obj) {
4183
+ var _obj$userData2;
4184
+ if (((_obj$userData2 = _obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'io-device') {
4185
+ ioDeviceObject = _obj;
4185
4186
  break;
4186
4187
  }
4187
- obj = obj.parent;
4188
+ _obj = _obj.parent;
4188
4189
  }
4189
4190
  if (ioDeviceObject) {
4190
4191
  hitMesh = hit.object;
@@ -4198,6 +4199,27 @@ var TransformControlsManager = /*#__PURE__*/function () {
4198
4199
  }
4199
4200
  if (!ioDeviceObject) return;
4200
4201
 
4202
+ // Only allow drag if the clicked mesh is animated (not the parent io-device group)
4203
+ // Resolve parentUuid and attachmentId
4204
+ var parentUuid = null;
4205
+ var obj = ioDeviceObject.parent;
4206
+ while (obj) {
4207
+ var _obj$userData;
4208
+ if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
4209
+ parentUuid = obj.userData.originalUuid || obj.uuid;
4210
+ break;
4211
+ }
4212
+ obj = obj.parent;
4213
+ }
4214
+ var attachmentId = (_ioDeviceObject$userD = ioDeviceObject.userData) === null || _ioDeviceObject$userD === void 0 ? void 0 : _ioDeviceObject$userD.attachmentId;
4215
+ if (parentUuid && attachmentId && (_this4$sceneViewer = _this4.sceneViewer) !== null && _this4$sceneViewer !== void 0 && (_this4$sceneViewer = _this4$sceneViewer.managers) !== null && _this4$sceneViewer !== void 0 && _this4$sceneViewer.ioBehaviorManager) {
4216
+ var animatedMeshes = _this4.sceneViewer.managers.ioBehaviorManager.getAnimatedMeshes(parentUuid, attachmentId);
4217
+ // If there are animated meshes registered, only allow drag if hitMesh is one of them
4218
+ if (animatedMeshes.length > 0 && !animatedMeshes.includes(hitMesh)) {
4219
+ return; // Clicked on non-draggable part of the io-device
4220
+ }
4221
+ }
4222
+
4201
4223
  // Begin session
4202
4224
  _this4._ioDragMesh = ioDeviceObject;
4203
4225
  _this4._ioDragStartX = event.clientX;
@@ -4260,8 +4282,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
4260
4282
  var hit = _step2.value;
4261
4283
  var obj = hit.object;
4262
4284
  while (obj) {
4263
- var _obj$userData2;
4264
- if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'io-device') {
4285
+ var _obj$userData3;
4286
+ if (((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'io-device') {
4265
4287
  _this4.callbacks.onIODeviceClick(obj);
4266
4288
  return;
4267
4289
  }
@@ -5921,8 +5943,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
5921
5943
  key: "_updateSegmentReference",
5922
5944
  value: function _updateSegmentReference(oldSegment, newSegment, index) {
5923
5945
  var selectedIndex = this.selectedObjects.findIndex(function (obj) {
5924
- var _obj$userData3;
5925
- return obj.uuid === oldSegment.uuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === oldSegment.uuid;
5946
+ var _obj$userData4;
5947
+ return obj.uuid === oldSegment.uuid || ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.originalUuid) === oldSegment.uuid;
5926
5948
  });
5927
5949
  if (selectedIndex !== -1 && newSegment) {
5928
5950
  // Clear bounding box cache
@@ -31350,7 +31372,7 @@ var ModelManager = /*#__PURE__*/function () {
31350
31372
 
31351
31373
  // Attach IO devices for smart components (import flow)
31352
31374
  if (!(componentData.isSmart && componentData.attachedDevices)) {
31353
- _context2.n = 8;
31375
+ _context2.n = 9;
31354
31376
  break;
31355
31377
  }
31356
31378
  _context2.n = 4;
@@ -31359,7 +31381,7 @@ var ModelManager = /*#__PURE__*/function () {
31359
31381
  // Register behavior configs for each attached device
31360
31382
  ioBehavMgr = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.ioBehaviorManager;
31361
31383
  if (!ioBehavMgr) {
31362
- _context2.n = 8;
31384
+ _context2.n = 9;
31363
31385
  break;
31364
31386
  }
31365
31387
  _loop = /*#__PURE__*/_regenerator().m(function _loop() {
@@ -31408,6 +31430,12 @@ var ModelManager = /*#__PURE__*/function () {
31408
31430
  _context2.n = 5;
31409
31431
  break;
31410
31432
  case 8:
31433
+ // Register component-level behaviors (intra-component io-device linking)
31434
+ if (componentData.behaviors && componentData.behaviors.length > 0) {
31435
+ console.log("[ModelManager] Registering ".concat(componentData.behaviors.length, " component-level behavior(s) for ").concat(originalProps.uuid));
31436
+ ioBehavMgr.registerComponentBehaviors(originalProps.uuid, componentData.behaviors);
31437
+ }
31438
+ case 9:
31411
31439
  // Replace mesh in scene
31412
31440
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
31413
31441
 
@@ -31426,13 +31454,13 @@ var ModelManager = /*#__PURE__*/function () {
31426
31454
  }
31427
31455
  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"));
31428
31456
  return _context2.a(2, libraryModel);
31429
- case 9:
31430
- _context2.p = 9;
31457
+ case 10:
31458
+ _context2.p = 10;
31431
31459
  _t = _context2.v;
31432
31460
  console.error("\u274C Error loading ".concat((_jsonEntry$userData4 = jsonEntry.userData) === null || _jsonEntry$userData4 === void 0 ? void 0 : _jsonEntry$userData4.libraryId, " GLB model:"), _t);
31433
31461
  return _context2.a(2, targetMesh);
31434
31462
  }
31435
- }, _callee, this, [[1, 9]]);
31463
+ }, _callee, this, [[1, 10]]);
31436
31464
  }));
31437
31465
  function loadLibraryModel(_x, _x2, _x3) {
31438
31466
  return _loadLibraryModel.apply(this, arguments);
@@ -33373,7 +33401,7 @@ var SceneOperationsManager = /*#__PURE__*/function () {
33373
33401
  key: "_prepareSceneForLoading",
33374
33402
  value: (function () {
33375
33403
  var _prepareSceneForLoading2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(data, isImported) {
33376
- var component, _component$managers;
33404
+ var component, _component$managers, _component$managers2, ioBehavMgr;
33377
33405
  return _regenerator().w(function (_context5) {
33378
33406
  while (1) switch (_context5.n) {
33379
33407
  case 0:
@@ -33390,6 +33418,16 @@ var SceneOperationsManager = /*#__PURE__*/function () {
33390
33418
  if ((_component$managers = component.managers) !== null && _component$managers !== void 0 && (_component$managers = _component$managers.pathfinding) !== null && _component$managers !== void 0 && _component$managers.snapshotManager) {
33391
33419
  component.managers.pathfinding.snapshotManager.reset();
33392
33420
  }
33421
+
33422
+ // Register behaviors with IoBehaviorManager
33423
+ ioBehavMgr = (_component$managers2 = component.managers) === null || _component$managers2 === void 0 ? void 0 : _component$managers2.ioBehaviorManager;
33424
+ if (ioBehavMgr && data.behaviors) {
33425
+ console.log("[Behavior] Registering ".concat(data.behaviors.length, " behavior(s) from scene data"));
33426
+ ioBehavMgr.setCrossComponentBehaviors(data.behaviors);
33427
+ } else if (ioBehavMgr) {
33428
+ console.log('[Behavior] No behaviors in scene data, clearing cross-component behaviors');
33429
+ ioBehavMgr.setCrossComponentBehaviors([]);
33430
+ }
33393
33431
  case 2:
33394
33432
  // Mark all imported objects as declared in the scene data
33395
33433
  // This ensures manual segment connectors and other objects are recognized as declared
@@ -37377,6 +37415,24 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37377
37415
  * }>
37378
37416
  */
37379
37417
  _this._entries = new Map();
37418
+ _this._crossComponentBehaviors = [];
37419
+
37420
+ /**
37421
+ * Map: `${componentUuid}` → Array<{ id, input, outputs }>
37422
+ * Component-level behaviors for intra-component io-device linking
37423
+ */
37424
+ _this._componentBehaviors = new Map();
37425
+
37426
+ /**
37427
+ * Injected by the host application to read and write I/O device state.
37428
+ * Set via configure(). Shape:
37429
+ * {
37430
+ * getState(attachmentId, dataPointId) -> value | null,
37431
+ * setState(attachmentId, dataPointId, value) -> void
37432
+ * }
37433
+ * @type {{ getState: Function, setState: Function } | null}
37434
+ */
37435
+ _this._stateAdapter = null;
37380
37436
  return _this;
37381
37437
  }
37382
37438
 
@@ -37474,29 +37530,485 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37474
37530
  }, {
37475
37531
  key: "triggerState",
37476
37532
  value: function triggerState(attachmentId, dataPointId, value, parentUuid) {
37477
- var _iterator2 = _createForOfIteratorHelper(this._entries.values()),
37478
- _step2;
37479
- try {
37480
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37481
- var entries = _step2.value;
37482
- var _iterator3 = _createForOfIteratorHelper(entries),
37483
- _step3;
37533
+ var _this$_crossComponent;
37534
+ console.log("[Behavior] triggerState called:", {
37535
+ attachmentId: attachmentId,
37536
+ dataPointId: dataPointId,
37537
+ value: value,
37538
+ parentUuid: parentUuid
37539
+ });
37540
+ if (parentUuid) {
37541
+ var key = this._key(parentUuid, attachmentId);
37542
+ var entries = this._entries.get(key);
37543
+ if (entries) {
37544
+ var _iterator2 = _createForOfIteratorHelper(entries),
37545
+ _step2;
37484
37546
  try {
37485
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37486
- var entry = _step3.value;
37547
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37548
+ var entry = _step2.value;
37487
37549
  if (entry.anim.stateVariable !== dataPointId) continue;
37488
37550
  this._applyAnimation(entry, value);
37489
37551
  }
37490
37552
  } catch (err) {
37491
- _iterator3.e(err);
37553
+ _iterator2.e(err);
37492
37554
  } finally {
37493
- _iterator3.f();
37555
+ _iterator2.f();
37556
+ }
37557
+ }
37558
+ } else {
37559
+ // Fallback when parentUuid is not provided: match by attachmentId suffix or exact key match
37560
+ var suffix = "::".concat(attachmentId);
37561
+ var _iterator3 = _createForOfIteratorHelper(this._entries.entries()),
37562
+ _step3;
37563
+ try {
37564
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37565
+ var _step3$value = _slicedToArray(_step3.value, 2),
37566
+ _key2 = _step3$value[0],
37567
+ _entries = _step3$value[1];
37568
+ if (_key2 === attachmentId || _key2.endsWith(suffix)) {
37569
+ var _iterator4 = _createForOfIteratorHelper(_entries),
37570
+ _step4;
37571
+ try {
37572
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37573
+ var _entry = _step4.value;
37574
+ if (_entry.anim.stateVariable !== dataPointId) continue;
37575
+ this._applyAnimation(_entry, value);
37576
+ }
37577
+ } catch (err) {
37578
+ _iterator4.e(err);
37579
+ } finally {
37580
+ _iterator4.f();
37581
+ }
37582
+ }
37583
+ }
37584
+ } catch (err) {
37585
+ _iterator3.e(err);
37586
+ } finally {
37587
+ _iterator3.f();
37588
+ }
37589
+ }
37590
+
37591
+ // Evaluate component-level behaviors (intra-component io-device linking)
37592
+ if (parentUuid && this._componentBehaviors.has(parentUuid)) {
37593
+ var componentBehaviors = this._componentBehaviors.get(parentUuid);
37594
+ console.log("[Behavior] Checking component-level behaviors for ".concat(parentUuid, " (count: ").concat(componentBehaviors.length, ")"));
37595
+ this.triggerCrossComponentBehaviors(componentBehaviors, parentUuid, attachmentId, dataPointId, value);
37596
+ }
37597
+
37598
+ // Evaluate cross-component behaviors if any are registered
37599
+ console.log("[Behavior] Checking cross-component behaviors (count: ".concat(((_this$_crossComponent = this._crossComponentBehaviors) === null || _this$_crossComponent === void 0 ? void 0 : _this$_crossComponent.length) || 0, ")"));
37600
+ if (this._crossComponentBehaviors && this._crossComponentBehaviors.length > 0) {
37601
+ this.triggerCrossComponentBehaviors(this._crossComponentBehaviors, parentUuid, attachmentId, dataPointId, value);
37602
+ }
37603
+ }
37604
+
37605
+ /**
37606
+ * Register the root-level cross-component behaviors from the scene.
37607
+ * Normalizes shorthand syntax to full format.
37608
+ *
37609
+ * @param {Array} behaviors
37610
+ */
37611
+ }, {
37612
+ key: "setCrossComponentBehaviors",
37613
+ value: function setCrossComponentBehaviors(behaviors) {
37614
+ var _this2 = this;
37615
+ this._crossComponentBehaviors = (behaviors || []).map(function (b) {
37616
+ return _this2._normalizeBehavior(b);
37617
+ });
37618
+ console.log("[Behavior] Loaded ".concat(this._crossComponentBehaviors.length, " cross-component behavior(s)"), this._crossComponentBehaviors);
37619
+ }
37620
+
37621
+ /**
37622
+ * Register component-level behaviors (intra-component io-device linking).
37623
+ * These behaviors are defined in the smart component's JSON and link devices within the same component instance.
37624
+ *
37625
+ * @param {string} componentUuid - The UUID of the component
37626
+ * @param {Array} behaviors - Array of behaviors in shorthand format
37627
+ */
37628
+ }, {
37629
+ key: "registerComponentBehaviors",
37630
+ value: function registerComponentBehaviors(componentUuid, behaviors) {
37631
+ var _this3 = this;
37632
+ if (!behaviors || behaviors.length === 0) return;
37633
+ var normalized = behaviors.map(function (b) {
37634
+ return _this3._normalizeBehavior(b);
37635
+ });
37636
+ this._componentBehaviors.set(componentUuid, normalized);
37637
+ console.log("[Behavior] Registered ".concat(normalized.length, " component-level behavior(s) for component ").concat(componentUuid), normalized);
37638
+ }
37639
+
37640
+ /**
37641
+ * Normalize behavior from shorthand to full format.
37642
+ * Supports:
37643
+ * - input: "attachment.state" → { attachment, state }
37644
+ * - outputs: ["attachment.state", ...] → converted to individual behaviors
37645
+ *
37646
+ * @param {Object} behavior - Raw behavior from scene JSON
37647
+ * @returns {Object} Normalized behavior
37648
+ */
37649
+ }, {
37650
+ key: "_normalizeBehavior",
37651
+ value: function _normalizeBehavior(behavior) {
37652
+ var normalized = _objectSpread2({}, behavior);
37653
+
37654
+ // Parse shorthand input: "attachment.state"
37655
+ if (typeof behavior.input === 'string') {
37656
+ var _behavior$input$split = behavior.input.split('.'),
37657
+ _behavior$input$split2 = _slicedToArray(_behavior$input$split, 2),
37658
+ attachment = _behavior$input$split2[0],
37659
+ state = _behavior$input$split2[1];
37660
+ normalized.input = {
37661
+ attachment: attachment,
37662
+ state: state
37663
+ };
37664
+ }
37665
+
37666
+ // Parse shorthand output/outputs
37667
+ if (behavior.outputs) {
37668
+ // Multiple outputs - expand to array
37669
+ normalized._outputs = behavior.outputs.map(function (out) {
37670
+ if (typeof out === 'string') {
37671
+ var _out$split = out.split('.'),
37672
+ _out$split2 = _slicedToArray(_out$split, 2),
37673
+ _attachment = _out$split2[0],
37674
+ _state = _out$split2[1];
37675
+ return {
37676
+ attachment: _attachment,
37677
+ state: _state
37678
+ };
37679
+ }
37680
+ return out;
37681
+ });
37682
+ delete normalized.outputs;
37683
+ } else if (typeof behavior.output === 'string') {
37684
+ // Single output string
37685
+ var _behavior$output$spli = behavior.output.split('.'),
37686
+ _behavior$output$spli2 = _slicedToArray(_behavior$output$spli, 2),
37687
+ _attachment2 = _behavior$output$spli2[0],
37688
+ _state2 = _behavior$output$spli2[1];
37689
+ normalized.output = {
37690
+ attachment: _attachment2,
37691
+ state: _state2
37692
+ };
37693
+ }
37694
+ return normalized;
37695
+ }
37696
+
37697
+ /**
37698
+ * Find the parent component UUID for a given attachment ID.
37699
+ * Searches the scene tree for the io-device with this attachment ID,
37700
+ * then returns its parentComponentId.
37701
+ *
37702
+ * @param {string} attachmentId
37703
+ * @returns {string|null} Component UUID or null if not found
37704
+ */
37705
+ }, {
37706
+ key: "_findComponentByAttachment",
37707
+ value: function _findComponentByAttachment(attachmentId) {
37708
+ var _this$sceneViewer;
37709
+ if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.scene)) return null;
37710
+ var scene = this.sceneViewer.scene;
37711
+ var found = null;
37712
+ scene.traverse(function (obj) {
37713
+ var _obj$userData, _obj$userData2;
37714
+ if (found) return;
37715
+ // Find the io-device object with this attachmentId
37716
+ if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'io-device' && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) {
37717
+ found = obj.userData.parentComponentId;
37718
+ }
37719
+ });
37720
+ if (!found) {
37721
+ console.warn("[Behavior] Could not find parent component for attachment \"".concat(attachmentId, "\""));
37722
+ }
37723
+ return found;
37724
+ }
37725
+
37726
+ /**
37727
+ * Inject a state adapter so cross-component behaviors can write I/O device state.
37728
+ * Call this once after the host application's state store is ready.
37729
+ *
37730
+ * @param {{ getState: Function, setState: Function }} stateAdapter
37731
+ * - getState(attachmentId, dataPointId) -> current value (any) | null
37732
+ * - setState(attachmentId, dataPointId, value) -> void
37733
+ *
37734
+ * @example
37735
+ * // Sandbox (Vuex)
37736
+ * ioBehaviorManager.configure({
37737
+ * getState: (attId, dpId) =>
37738
+ * store.getters['assetManagerStore/ioDeviceState'](attId)(dpId) ?? null,
37739
+ * setState: (attId, dpId, value) =>
37740
+ * store.dispatch('assetManagerStore/setIoDeviceState',
37741
+ * { attachmentId: attId, dataPointId: dpId, value })
37742
+ * })
37743
+ */
37744
+ }, {
37745
+ key: "configure",
37746
+ value: function configure(stateAdapter) {
37747
+ this._stateAdapter = stateAdapter || null;
37748
+ console.log('[Behavior] State adapter configured:', !!this._stateAdapter);
37749
+ }
37750
+
37751
+ /**
37752
+ * Evaluate and apply cross-component behaviors loaded from the scene JSON.
37753
+ *
37754
+ * @param {Array} behaviors - Root-level behaviors array from the scene JSON
37755
+ * @param {string} triggerParentUuid - Parent component UUID of the triggering device
37756
+ * @param {string} triggerAttachmentId - Attachment ID of the triggering device
37757
+ * @param {string} triggerStateId - The state variable ID that changed
37758
+ * @param {*} value - The new state value
37759
+ */
37760
+ }, {
37761
+ key: "triggerCrossComponentBehaviors",
37762
+ value: function triggerCrossComponentBehaviors(behaviors, triggerParentUuid, triggerAttachmentId, triggerStateId, value) {
37763
+ if (!behaviors || !Array.isArray(behaviors)) {
37764
+ console.log('[Behavior] No behaviors to evaluate');
37765
+ return;
37766
+ }
37767
+ console.log("[Behavior] Evaluating ".concat(behaviors.length, " behavior(s) for trigger: ").concat(triggerParentUuid, ".").concat(triggerAttachmentId, ".").concat(triggerStateId, " = ").concat(value));
37768
+ var _iterator5 = _createForOfIteratorHelper(behaviors),
37769
+ _step5;
37770
+ try {
37771
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
37772
+ var behavior = _step5.value;
37773
+ var input = behavior.input,
37774
+ output = behavior.output,
37775
+ _outputs = behavior._outputs,
37776
+ conditions = behavior.conditions;
37777
+ if (!input || !output && !_outputs) {
37778
+ console.warn('[Behavior] Skipping behavior - missing input or output(s):', behavior);
37779
+ continue;
37780
+ }
37781
+
37782
+ // Auto-lookup component if not specified
37783
+ var inputComponent = input.component || this._findComponentByAttachment(input.attachment);
37784
+ console.log("[Behavior] Checking behavior \"".concat(behavior.id, "\":"), {
37785
+ inputMatch: inputComponent === triggerParentUuid,
37786
+ attachmentMatch: input.attachment === triggerAttachmentId,
37787
+ stateMatch: input.state === triggerStateId,
37788
+ expected: {
37789
+ component: inputComponent,
37790
+ attachment: input.attachment,
37791
+ state: input.state
37792
+ },
37793
+ actual: {
37794
+ component: triggerParentUuid,
37795
+ attachment: triggerAttachmentId,
37796
+ state: triggerStateId
37797
+ }
37798
+ });
37799
+
37800
+ // Verify that the input matches the triggering source
37801
+ if (inputComponent === triggerParentUuid && input.attachment === triggerAttachmentId && input.state === triggerStateId) {
37802
+ console.log("[Behavior] \u2713 Behavior \"".concat(behavior.id, "\" matched!"));
37803
+
37804
+ // Collect all outputs (single or multiple)
37805
+ var outputs = _outputs || (output ? [output] : []);
37806
+
37807
+ // Process each output
37808
+ var _iterator6 = _createForOfIteratorHelper(outputs),
37809
+ _step6;
37810
+ try {
37811
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
37812
+ var out = _step6.value;
37813
+ // NEW: State-to-state pass-through pattern
37814
+ if (out.state) {
37815
+ // Auto-lookup output component if not specified
37816
+ var outputComponent = out.component || this._findComponentByAttachment(out.attachment);
37817
+
37818
+ // Direct state mapping without conditions
37819
+ if (this._stateAdapter) {
37820
+ console.log("[Behavior] Dispatching state-to-state: ".concat(out.attachment, ".").concat(out.state, " = ").concat(value));
37821
+ this._stateAdapter.setState(out.attachment, out.state, value);
37822
+ console.log("[Behavior] \u2713 State-to-state triggered: ".concat(input.attachment, ".").concat(input.state, " (").concat(value, ") \u2192 ").concat(out.attachment, ".").concat(out.state));
37823
+
37824
+ // Trigger animations on the output component
37825
+ // triggerState(attachmentId, dataPointId, value, parentUuid)
37826
+ if (outputComponent) {
37827
+ console.log("[Behavior] Triggering animations on output component: ".concat(outputComponent, ".").concat(out.attachment, ".").concat(out.state));
37828
+ this.triggerState(out.attachment, out.state, value, outputComponent);
37829
+ } else {
37830
+ console.warn("[Behavior] Could not find component for attachment \"".concat(out.attachment, "\""));
37831
+ }
37832
+ } else {
37833
+ console.warn('[Behavior] ✗ State adapter not configured for state-to-state behavior');
37834
+ }
37835
+ }
37836
+ // LEGACY: Mesh-based pattern with conditions
37837
+ else if (conditions && out.child) {
37838
+ console.log('[Behavior] Using legacy mesh-based pattern with conditions');
37839
+ // Evaluate conditions
37840
+ var _iterator7 = _createForOfIteratorHelper(conditions),
37841
+ _step7;
37842
+ try {
37843
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
37844
+ var condition = _step7.value;
37845
+ if (this._evaluateCondition(condition.when, value)) {
37846
+ // Apply actions to the target output component/attachment/child mesh
37847
+ this._applyCrossComponentActions(out, condition.actions);
37848
+ }
37849
+ }
37850
+ } catch (err) {
37851
+ _iterator7.e(err);
37852
+ } finally {
37853
+ _iterator7.f();
37854
+ }
37855
+ } else {
37856
+ console.warn('[Behavior] Output has neither state nor (child + conditions):', out);
37857
+ }
37858
+ } // end outputs loop
37859
+ } catch (err) {
37860
+ _iterator6.e(err);
37861
+ } finally {
37862
+ _iterator6.f();
37863
+ }
37494
37864
  }
37495
37865
  }
37496
37866
  } catch (err) {
37497
- _iterator2.e(err);
37867
+ _iterator5.e(err);
37498
37868
  } finally {
37499
- _iterator2.f();
37869
+ _iterator5.f();
37870
+ }
37871
+ }
37872
+
37873
+ /**
37874
+ * Safely evaluate a condition expression.
37875
+ */
37876
+ }, {
37877
+ key: "_evaluateCondition",
37878
+ value: function _evaluateCondition(whenExpr, value) {
37879
+ try {
37880
+ var fn = new Function('state', "return (".concat(whenExpr, ");"));
37881
+ return fn({
37882
+ value: value
37883
+ });
37884
+ } catch (err) {
37885
+ console.warn("[IoBehaviorManager] Failed to evaluate condition: \"".concat(whenExpr, "\""), err);
37886
+ return false;
37887
+ }
37888
+ }
37889
+
37890
+ /**
37891
+ * Resolve target output mesh and apply actions.
37892
+ */
37893
+ }, {
37894
+ key: "_applyCrossComponentActions",
37895
+ value: function _applyCrossComponentActions(output, actions) {
37896
+ var _this$sceneViewer2;
37897
+ if (!actions || !Array.isArray(actions)) return;
37898
+
37899
+ // 1. Resolve output component
37900
+ var scene = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.scene;
37901
+ if (!scene) return;
37902
+ var componentModel = scene.getObjectByProperty('uuid', output.component) || scene.getObjectByProperty('name', output.component);
37903
+ if (!componentModel) {
37904
+ console.warn("[IoBehaviorManager] Output component \"".concat(output.component, "\" not found in scene"));
37905
+ return;
37906
+ }
37907
+
37908
+ // 2. Resolve attachment model under the component
37909
+ var deviceRoot = null;
37910
+ componentModel.traverse(function (obj) {
37911
+ var _obj$userData3;
37912
+ if (!deviceRoot && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.attachmentId) === output.attachment) {
37913
+ deviceRoot = obj;
37914
+ }
37915
+ });
37916
+ if (!deviceRoot) {
37917
+ console.warn("[IoBehaviorManager] Output attachment \"".concat(output.attachment, "\" not found on component \"").concat(output.component, "\""));
37918
+ return;
37919
+ }
37920
+
37921
+ // 3. Resolve child mesh if specified
37922
+ var targetObj = deviceRoot;
37923
+ if (output.child) {
37924
+ var foundChild = null;
37925
+ deviceRoot.traverse(function (obj) {
37926
+ if (!foundChild && obj.name === output.child) {
37927
+ foundChild = obj;
37928
+ }
37929
+ });
37930
+ if (foundChild) {
37931
+ targetObj = foundChild;
37932
+ } else {
37933
+ console.warn("[IoBehaviorManager] Child \"".concat(output.child, "\" not found under attachment \"").concat(output.attachment, "\" on component \"").concat(output.component, "\""));
37934
+ return;
37935
+ }
37936
+ }
37937
+
37938
+ // 4. Apply actions to targetObj
37939
+ var _iterator8 = _createForOfIteratorHelper(actions),
37940
+ _step8;
37941
+ try {
37942
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
37943
+ var action = _step8.value;
37944
+ this._applyCrossComponentAction(targetObj, action);
37945
+ }
37946
+ } catch (err) {
37947
+ _iterator8.e(err);
37948
+ } finally {
37949
+ _iterator8.f();
37950
+ }
37951
+ }
37952
+
37953
+ /**
37954
+ * Apply a single action to the target mesh.
37955
+ */
37956
+ }, {
37957
+ key: "_applyCrossComponentAction",
37958
+ value: function _applyCrossComponentAction(targetObj, action) {
37959
+ var set = action.set,
37960
+ value = action.value;
37961
+ if (!set) return;
37962
+ if (set.startsWith('material.')) {
37963
+ var prop = set.slice(9);
37964
+ targetObj.traverse(function (obj) {
37965
+ if (!obj.isMesh || !obj.material) return;
37966
+ if (!obj.userData._materialCloned) {
37967
+ obj.material = obj.material.clone();
37968
+ obj.userData._materialCloned = true;
37969
+ }
37970
+ try {
37971
+ if (prop === 'color') {
37972
+ obj.material.color.set(value);
37973
+ } else {
37974
+ if (prop in obj.material) {
37975
+ if (obj.material[prop] && typeof obj.material[prop].set === 'function') {
37976
+ obj.material[prop].set(value);
37977
+ } else {
37978
+ obj.material[prop] = value;
37979
+ }
37980
+ }
37981
+ }
37982
+ } catch (err) {
37983
+ console.warn("[IoBehaviorManager] Failed to set material property \"".concat(prop, "\""), err);
37984
+ }
37985
+ });
37986
+ } else if (set === 'visible') {
37987
+ targetObj.visible = !!value;
37988
+ } else {
37989
+ // General fallback path parsing (e.g. position.z)
37990
+ var parts = set.split('.');
37991
+ var current = targetObj;
37992
+ for (var i = 0; i < parts.length - 1; i++) {
37993
+ if (current && current[parts[i]]) {
37994
+ current = current[parts[i]];
37995
+ } else {
37996
+ current = null;
37997
+ break;
37998
+ }
37999
+ }
38000
+ if (current) {
38001
+ var lastPart = parts[parts.length - 1];
38002
+ try {
38003
+ if (current[lastPart] && typeof current[lastPart].set === 'function') {
38004
+ current[lastPart].set(value);
38005
+ } else {
38006
+ current[lastPart] = value;
38007
+ }
38008
+ } catch (err) {
38009
+ console.warn("[IoBehaviorManager] Failed to set property \"".concat(set, "\""), err);
38010
+ }
38011
+ }
37500
38012
  }
37501
38013
  }
37502
38014
 
@@ -37526,7 +38038,7 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37526
38038
  }, {
37527
38039
  key: "getAnimationDataPoints",
37528
38040
  value: function getAnimationDataPoints(parentUuid, attachmentId) {
37529
- var _this2 = this;
38041
+ var _this4 = this;
37530
38042
  var hitMesh = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
37531
38043
  var key = this._key(parentUuid, attachmentId);
37532
38044
  var entries = this._entries.get(key);
@@ -37538,35 +38050,35 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37538
38050
  var filtered = entries;
37539
38051
  if (hitMesh) {
37540
38052
  var matching = entries.filter(function (e) {
37541
- return _this2._isMeshOrDescendant(hitMesh, e.mesh);
38053
+ return _this4._isMeshOrDescendant(hitMesh, e.mesh);
37542
38054
  });
37543
38055
  if (matching.length > 0) filtered = matching;
37544
38056
  }
37545
38057
 
37546
38058
  // Collapse multiple mesh entries that share the same stateVariable
37547
38059
  var seen = new Map(); // stateVariable → anim
37548
- var _iterator4 = _createForOfIteratorHelper(filtered),
37549
- _step4;
38060
+ var _iterator9 = _createForOfIteratorHelper(filtered),
38061
+ _step9;
37550
38062
  try {
37551
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37552
- var anim = _step4.value.anim;
38063
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
38064
+ var anim = _step9.value.anim;
37553
38065
  if (!seen.has(anim.stateVariable)) {
37554
38066
  seen.set(anim.stateVariable, anim);
37555
38067
  }
37556
38068
  }
37557
38069
  } catch (err) {
37558
- _iterator4.e(err);
38070
+ _iterator9.e(err);
37559
38071
  } finally {
37560
- _iterator4.f();
38072
+ _iterator9.f();
37561
38073
  }
37562
38074
  var dps = [];
37563
- var _iterator5 = _createForOfIteratorHelper(seen),
37564
- _step5;
38075
+ var _iterator0 = _createForOfIteratorHelper(seen),
38076
+ _step0;
37565
38077
  try {
37566
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
37567
- var _step5$value = _slicedToArray(_step5.value, 2),
37568
- stateVar = _step5$value[0],
37569
- _anim = _step5$value[1];
38078
+ for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
38079
+ var _step0$value = _slicedToArray(_step0.value, 2),
38080
+ stateVar = _step0$value[0],
38081
+ _anim = _step0$value[1];
37570
38082
  // Normalise stateType from AnimateDevicesDialog variants
37571
38083
  var stateType = void 0;
37572
38084
  var raw = (_anim.stateType || '').toLowerCase();
@@ -37617,9 +38129,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37617
38129
  });
37618
38130
  }
37619
38131
  } catch (err) {
37620
- _iterator5.e(err);
38132
+ _iterator0.e(err);
37621
38133
  } finally {
37622
- _iterator5.f();
38134
+ _iterator0.f();
37623
38135
  }
37624
38136
  return dps;
37625
38137
  }
@@ -37654,19 +38166,19 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37654
38166
  key: "unloadForComponent",
37655
38167
  value: function unloadForComponent(parentUuid) {
37656
38168
  var prefix = "".concat(parentUuid, "::");
37657
- var _iterator6 = _createForOfIteratorHelper(this._entries.keys()),
37658
- _step6;
38169
+ var _iterator1 = _createForOfIteratorHelper(this._entries.keys()),
38170
+ _step1;
37659
38171
  try {
37660
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
37661
- var key = _step6.value;
38172
+ for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
38173
+ var key = _step1.value;
37662
38174
  if (key.startsWith(prefix)) {
37663
38175
  this._entries.delete(key);
37664
38176
  }
37665
38177
  }
37666
38178
  } catch (err) {
37667
- _iterator6.e(err);
38179
+ _iterator1.e(err);
37668
38180
  } finally {
37669
- _iterator6.f();
38181
+ _iterator1.f();
37670
38182
  }
37671
38183
  }
37672
38184
  }, {
@@ -37750,11 +38262,11 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37750
38262
  var mapping = this._resolveMapping(anim, value);
37751
38263
  if (!mapping) return;
37752
38264
  var types = anim.transformTypes || [];
37753
- var _iterator7 = _createForOfIteratorHelper(types),
37754
- _step7;
38265
+ var _iterator10 = _createForOfIteratorHelper(types),
38266
+ _step10;
37755
38267
  try {
37756
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
37757
- var type = _step7.value;
38268
+ for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
38269
+ var type = _step10.value;
37758
38270
  if (type === 'translation') {
37759
38271
  this._applyTranslation(mesh, origPos, mapping.transform);
37760
38272
  } else if (type === 'rotation') {
@@ -37764,9 +38276,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37764
38276
  }
37765
38277
  }
37766
38278
  } catch (err) {
37767
- _iterator7.e(err);
38279
+ _iterator10.e(err);
37768
38280
  } finally {
37769
- _iterator7.f();
38281
+ _iterator10.f();
37770
38282
  }
37771
38283
  }
37772
38284
 
@@ -39362,6 +39874,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39362
39874
  for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
39363
39875
  if (_loop()) continue;
39364
39876
  }
39877
+
39878
+ // Register component-level behaviors (intra-component io-device linking)
39879
+ if (componentData.behaviors && componentData.behaviors.length > 0) {
39880
+ console.log("[DragDrop] Registering ".concat(componentData.behaviors.length, " component-level behavior(s) for ").concat(componentId));
39881
+ ioBehavMgr.registerComponentBehaviors(componentId, componentData.behaviors);
39882
+ }
39365
39883
  }
39366
39884
  }
39367
39885
 
@@ -39663,7 +40181,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
39663
40181
  * Initialize the CentralPlant manager
39664
40182
  *
39665
40183
  * @constructor
39666
- * @version 0.3.33
40184
+ * @version 0.3.35
39667
40185
  * @updated 2025-10-22
39668
40186
  *
39669
40187
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -39837,7 +40355,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
39837
40355
  key: "setImportedSceneData",
39838
40356
  value: (function () {
39839
40357
  var _setImportedSceneData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(sceneData) {
39840
- var _this$importedSceneDa, _this$importedSceneDa2;
40358
+ var _this$importedSceneDa, _this$importedSceneDa2, _this$importedSceneDa3, _this$sceneViewer;
40359
+ var ioBehavMgr;
39841
40360
  return _regenerator().w(function (_context2) {
39842
40361
  while (1) switch (_context2.n) {
39843
40362
  case 0:
@@ -39848,8 +40367,19 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
39848
40367
  console.log('📥 Imported scene data stored in CentralPlant:', {
39849
40368
  connections: ((_this$importedSceneDa = this.importedSceneData.connections) === null || _this$importedSceneDa === void 0 ? void 0 : _this$importedSceneDa.length) || 0,
39850
40369
  sceneObjects: ((_this$importedSceneDa2 = this.importedSceneData.scene) === null || _this$importedSceneDa2 === void 0 || (_this$importedSceneDa2 = _this$importedSceneDa2.children) === null || _this$importedSceneDa2 === void 0 ? void 0 : _this$importedSceneDa2.length) || 0,
40370
+ behaviors: ((_this$importedSceneDa3 = this.importedSceneData.behaviors) === null || _this$importedSceneDa3 === void 0 ? void 0 : _this$importedSceneDa3.length) || 0,
39851
40371
  timestamp: new Date().toISOString()
39852
40372
  });
40373
+ console.log('[Behavior] Scene behaviors detail:', this.importedSceneData.behaviors);
40374
+
40375
+ // Register behaviors with IoBehaviorManager
40376
+ ioBehavMgr = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.ioBehaviorManager;
40377
+ if (ioBehavMgr) {
40378
+ console.log('[Behavior] Calling setCrossComponentBehaviors with:', this.importedSceneData.behaviors || []);
40379
+ ioBehavMgr.setCrossComponentBehaviors(this.importedSceneData.behaviors || []);
40380
+ } else {
40381
+ console.warn('[Behavior] ioBehaviorManager not available!');
40382
+ }
39853
40383
 
39854
40384
  // Reset component counter based on imported components to avoid ID conflicts
39855
40385
  this.internals.resetComponentCounter();
@@ -40030,8 +40560,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40030
40560
  }, {
40031
40561
  key: "selectObject",
40032
40562
  value: function selectObject(objectOrId) {
40033
- var _this$sceneViewer, _object;
40034
- if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.transformManager)) {
40563
+ var _this$sceneViewer2, _object;
40564
+ if (!((_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.transformManager)) {
40035
40565
  console.warn('⚠️ Transform manager not initialized');
40036
40566
  return false;
40037
40567
  }
@@ -40077,8 +40607,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40077
40607
  }, {
40078
40608
  key: "toggleObject",
40079
40609
  value: function toggleObject(objectOrId) {
40080
- var _this$sceneViewer2, _object2;
40081
- if (!((_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.transformManager)) {
40610
+ var _this$sceneViewer3, _object2;
40611
+ if (!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformManager)) {
40082
40612
  console.warn('⚠️ Transform manager not initialized');
40083
40613
  return false;
40084
40614
  }
@@ -40114,8 +40644,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40114
40644
  }, {
40115
40645
  key: "deselectObject",
40116
40646
  value: function deselectObject() {
40117
- var _this$sceneViewer3;
40118
- if (!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformManager)) {
40647
+ var _this$sceneViewer4;
40648
+ if (!((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.transformManager)) {
40119
40649
  console.warn('⚠️ Transform manager not initialized');
40120
40650
  return false;
40121
40651
  }
@@ -40729,9 +41259,9 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40729
41259
  }, {
40730
41260
  key: "setIoDeviceState",
40731
41261
  value: function setIoDeviceState(attachmentId, stateId, value, parentUuid) {
40732
- var _this$sceneViewer4, _this$sceneViewer5, _this$sceneViewer6;
41262
+ var _this$sceneViewer5, _this$sceneViewer6, _this$sceneViewer7;
40733
41263
  // 1. Persist via state adapter if one has been configured
40734
- var stateAdapter = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.componentTooltipManager) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4._stateAdapter;
41264
+ var stateAdapter = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.componentTooltipManager) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5._stateAdapter;
40735
41265
  if (stateAdapter !== null && stateAdapter !== void 0 && stateAdapter.setState) {
40736
41266
  var scopedKey = parentUuid ? "".concat(parentUuid, "::").concat(attachmentId) : attachmentId;
40737
41267
  try {
@@ -40742,10 +41272,19 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40742
41272
  }
40743
41273
 
40744
41274
  // 2. Apply io-behavior changes
40745
- (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioBehaviorManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, stateId, value, parentUuid);
41275
+ var ioBehavMgr = (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.managers) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.ioBehaviorManager;
41276
+ if (ioBehavMgr) {
41277
+ var _this$importedSceneDa4;
41278
+ ioBehavMgr.triggerState(attachmentId, stateId, value, parentUuid);
41279
+
41280
+ // Evaluate cross-component behaviors if they exist in the imported scene
41281
+ if ((_this$importedSceneDa4 = this.importedSceneData) !== null && _this$importedSceneDa4 !== void 0 && _this$importedSceneDa4.behaviors) {
41282
+ ioBehavMgr.triggerCrossComponentBehaviors(this.importedSceneData.behaviors, parentUuid, attachmentId, stateId, value);
41283
+ }
41284
+ }
40746
41285
 
40747
41286
  // 3. Emit event for host apps that don't use the state adapter (e.g. cp3d-viewer)
40748
- (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || _this$sceneViewer6.emit('io-device-state-changed', {
41287
+ (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 || _this$sceneViewer7.emit('io-device-state-changed', {
40749
41288
  attachmentId: attachmentId,
40750
41289
  stateId: stateId,
40751
41290
  value: value,
@@ -40767,8 +41306,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40767
41306
  }, {
40768
41307
  key: "getSceneAttachments",
40769
41308
  value: function getSceneAttachments() {
40770
- var _this$sceneViewer7;
40771
- var scene = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.scene;
41309
+ var _this$sceneViewer8;
41310
+ var scene = (_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 ? void 0 : _this$sceneViewer8.scene;
40772
41311
  if (!scene) return [];
40773
41312
  var results = [];
40774
41313
  scene.traverse(function (obj) {
@@ -42395,7 +42934,12 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42395
42934
  }, {
42396
42935
  key: "clearScene",
42397
42936
  value: function clearScene() {
42937
+ var _this$sceneViewer9;
42398
42938
  this.importedSceneData = null;
42939
+ var ioBehavMgr = (_this$sceneViewer9 = this.sceneViewer) === null || _this$sceneViewer9 === void 0 || (_this$sceneViewer9 = _this$sceneViewer9.managers) === null || _this$sceneViewer9 === void 0 ? void 0 : _this$sceneViewer9.ioBehaviorManager;
42940
+ if (ioBehavMgr) {
42941
+ ioBehavMgr.setCrossComponentBehaviors([]);
42942
+ }
42399
42943
  if (this.sceneViewer && this.sceneViewer.scene) {
42400
42944
  this.sceneViewer.scene.clear();
42401
42945
  }
@@ -42905,28 +43449,10 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
42905
43449
  },
42906
43450
  onIODeviceDrag: function onIODeviceDrag(ioDeviceObject, signedDelta, isStart, hitMesh) {
42907
43451
  if (isStart) {
42908
- var _ioDeviceObject$userD, _this4$managers$ioBeh, _this4$managers, _this4$managers2;
42909
- // Resolve parentUuid by walking up to the host component.
42910
- // Use userData.originalUuid (the custom componentId) because that
42911
- // is what IoBehaviorManager uses as the map key NOT obj.uuid.
42912
- var parentUuid = null;
42913
- var obj = ioDeviceObject.parent;
42914
- while (obj) {
42915
- var _obj$userData;
42916
- if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
42917
- parentUuid = obj.userData.originalUuid || obj.uuid;
42918
- break;
42919
- }
42920
- obj = obj.parent;
42921
- }
42922
- var attachmentId = (_ioDeviceObject$userD = ioDeviceObject.userData) === null || _ioDeviceObject$userD === void 0 ? void 0 : _ioDeviceObject$userD.attachmentId;
42923
- // When animated meshes are available, outline ONLY them so their
42924
- // silhouette is isolated and the outline ring is visible around
42925
- // them specifically (not swallowed by the larger device body).
42926
- // Fall back to the whole device group when none are registered.
42927
- var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioBeh = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioBehaviorManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioBeh !== void 0 ? _this4$managers$ioBeh : [] : [];
42928
- var targets = animatedMeshes.length > 0 ? animatedMeshes : [ioDeviceObject];
42929
- (_this4$managers2 = _this4.managers) === null || _this4$managers2 === void 0 || (_this4$managers2 = _this4$managers2.ioOutlineManager) === null || _this4$managers2 === void 0 || _this4$managers2.setTargets(targets);
43452
+ var _this4$managers;
43453
+ // Outline only the specific mesh that was clicked
43454
+ var targets = hitMesh ? [hitMesh] : [ioDeviceObject];
43455
+ (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioOutlineManager) === null || _this4$managers === void 0 || _this4$managers.setTargets(targets);
42930
43456
  }
42931
43457
  if (!_this4.componentTooltipManager) return;
42932
43458
  if (isStart) {
@@ -42936,8 +43462,8 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
42936
43462
  }
42937
43463
  },
42938
43464
  onIODeviceDragEnd: function onIODeviceDragEnd(ioDeviceObject) {
42939
- var _this4$managers3;
42940
- (_this4$managers3 = _this4.managers) === null || _this4$managers3 === void 0 || (_this4$managers3 = _this4$managers3.ioOutlineManager) === null || _this4$managers3 === void 0 || _this4$managers3.setTargets([]);
43465
+ var _this4$managers2;
43466
+ (_this4$managers2 = _this4.managers) === null || _this4$managers2 === void 0 || (_this4$managers2 = _this4$managers2.ioOutlineManager) === null || _this4$managers2 === void 0 || _this4$managers2.setTargets([]);
42941
43467
  if (_this4.componentTooltipManager) {
42942
43468
  _this4.componentTooltipManager.endIODeviceDrag();
42943
43469
  }