@2112-lab/central-plant 0.3.37 → 0.3.39

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 (31) hide show
  1. package/dist/bundle/index.js +1080 -563
  2. package/dist/cjs/src/core/centralPlant.js +115 -68
  3. package/dist/cjs/src/core/centralPlantInternals.js +20 -36
  4. package/dist/cjs/src/index.js +19 -0
  5. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +175 -234
  6. package/dist/cjs/src/managers/components/componentDataManager.js +63 -11
  7. package/dist/cjs/src/managers/scene/componentTooltipManager.js +95 -65
  8. package/dist/cjs/src/managers/scene/modelManager.js +93 -145
  9. package/dist/cjs/src/managers/scene/sceneExportManager.js +2 -1
  10. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +8 -3
  11. package/dist/cjs/src/utils/animationTransformUtils.js +66 -0
  12. package/dist/cjs/src/utils/behaviorDispatch.js +62 -0
  13. package/dist/cjs/src/utils/behaviorRegistration.js +76 -0
  14. package/dist/cjs/src/utils/behaviorSceneUtils.js +155 -0
  15. package/dist/cjs/src/utils/behaviorSchema.js +209 -0
  16. package/dist/esm/src/core/centralPlant.js +115 -68
  17. package/dist/esm/src/core/centralPlantInternals.js +21 -37
  18. package/dist/esm/src/index.js +4 -0
  19. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +176 -235
  20. package/dist/esm/src/managers/components/componentDataManager.js +63 -11
  21. package/dist/esm/src/managers/scene/componentTooltipManager.js +95 -65
  22. package/dist/esm/src/managers/scene/modelManager.js +94 -146
  23. package/dist/esm/src/managers/scene/sceneExportManager.js +2 -1
  24. package/dist/esm/src/managers/scene/sceneOperationsManager.js +8 -3
  25. package/dist/esm/src/utils/animationTransformUtils.js +41 -0
  26. package/dist/esm/src/utils/behaviorDispatch.js +56 -0
  27. package/dist/esm/src/utils/behaviorRegistration.js +71 -0
  28. package/dist/esm/src/utils/behaviorSceneUtils.js +147 -0
  29. package/dist/esm/src/utils/behaviorSchema.js +201 -0
  30. package/dist/index.d.ts +169 -1
  31. package/package.json +1 -1
@@ -1164,6 +1164,74 @@ var DisposalUtilities = /*#__PURE__*/function () {
1164
1164
  }]);
1165
1165
  }();
1166
1166
 
1167
+ /**
1168
+ * Register mesh animations and intra-component state links when a smart
1169
+ * component is placed or imported into the scene.
1170
+ *
1171
+ * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager} ioBehavMgr
1172
+ * @param {string} componentUuid - Instance UUID of the parent component
1173
+ * @param {Object} componentData - Smart component dictionary entry
1174
+ * @param {Object} componentModel - Root THREE.Object3D of the placed component
1175
+ * @param {Object} componentDictionary - modelPreloader.componentDictionary
1176
+ */
1177
+ function registerBehaviorsForComponent(ioBehavMgr, componentUuid, componentData, componentModel, componentDictionary) {
1178
+ var _componentData$behavi;
1179
+ if (!ioBehavMgr || !componentData || !componentModel) {
1180
+ return;
1181
+ }
1182
+ if (componentData.isSmart && componentData.attachedDevices) {
1183
+ var _loop = function _loop() {
1184
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
1185
+ attachmentId = _Object$entries$_i[0],
1186
+ attachment = _Object$entries$_i[1];
1187
+ var deviceData = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[attachment.deviceId];
1188
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return 1; // continue
1189
+ var deviceRoot = null;
1190
+ componentModel.traverse(function (obj) {
1191
+ var _obj$userData;
1192
+ if (!deviceRoot && ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.attachmentId) === attachmentId) deviceRoot = obj;
1193
+ });
1194
+ if (deviceRoot) {
1195
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, componentUuid);
1196
+ }
1197
+ };
1198
+ for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
1199
+ if (_loop()) continue;
1200
+ }
1201
+ }
1202
+ if (((_componentData$behavi = componentData.behaviors) === null || _componentData$behavi === void 0 ? void 0 : _componentData$behavi.length) > 0) {
1203
+ ioBehavMgr.registerComponentBehaviors(componentUuid, componentData.behaviors);
1204
+ } else if (componentData.isSmart) {
1205
+ ioBehavMgr.registerComponentBehaviors(componentUuid, []);
1206
+ }
1207
+ }
1208
+
1209
+ /**
1210
+ * Reload mesh behaviorConfig for every placed instance of an I/O device asset.
1211
+ * Call after the animation wizard saves updated behaviorConfig to the dictionary.
1212
+ *
1213
+ * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager} ioBehavMgr
1214
+ * @param {string} deviceAssetId - Dictionary / asset id of the I/O device
1215
+ * @param {Object} componentDictionary
1216
+ * @param {Object} centralPlant
1217
+ */
1218
+ function reloadBehaviorsForDeviceAsset(ioBehavMgr, deviceAssetId, componentDictionary, centralPlant) {
1219
+ var _centralPlant$sceneVi;
1220
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi = centralPlant.sceneViewer) === null || _centralPlant$sceneVi === void 0 ? void 0 : _centralPlant$sceneVi.scene;
1221
+ var deviceData = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[deviceAssetId];
1222
+ if (!ioBehavMgr || !scene || !(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return;
1223
+ scene.traverse(function (obj) {
1224
+ var _obj$userData2, _obj$userData3, _obj$parent;
1225
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) !== 'io-device') return;
1226
+ if (((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.deviceId) !== deviceAssetId) return;
1227
+ var parentUuid = obj.userData.parentComponentId || ((_obj$parent = obj.parent) === null || _obj$parent === void 0 ? void 0 : _obj$parent.uuid);
1228
+ var attachmentId = obj.userData.attachmentId;
1229
+ if (!parentUuid || !attachmentId) return;
1230
+ ioBehavMgr.unloadForAttachment(parentUuid, attachmentId);
1231
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, obj, parentUuid);
1232
+ });
1233
+ }
1234
+
1167
1235
  /**
1168
1236
  * Validation severity levels
1169
1237
  * @enum {string}
@@ -11772,7 +11840,8 @@ var SceneExportManager = /*#__PURE__*/function () {
11772
11840
 
11773
11841
  // Copy only essential userData properties (libraryId, objectType, direction, group)
11774
11842
  Object.keys(threeObject.userData).forEach(function (key) {
11775
- if (!excludedKeys.has(key)) {
11843
+ // Skip runtime/internal keys (e.g. _filteredBBoxCache from boundingBoxUtils)
11844
+ if (!excludedKeys.has(key) && !key.startsWith('_')) {
11776
11845
  jsonObject.userData[key] = threeObject.userData[key];
11777
11846
  }
11778
11847
  });
@@ -20324,6 +20393,14 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20324
20393
  if (!_this3.componentDictionary[key]) {
20325
20394
  newComponents[key] = component;
20326
20395
  } else {
20396
+ var _component$behaviors;
20397
+ var existing = _this3.componentDictionary[key];
20398
+ if (component.behaviorConfig) existing.behaviorConfig = component.behaviorConfig;
20399
+ if (component.meshNameMap) existing.meshNameMap = component.meshNameMap;
20400
+ if ((_component$behaviors = component.behaviors) !== null && _component$behaviors !== void 0 && _component$behaviors.length) existing.behaviors = component.behaviors;
20401
+ if (component.attachedDevices) {
20402
+ existing.attachedDevices = _objectSpread2(_objectSpread2({}, existing.attachedDevices || {}), component.attachedDevices);
20403
+ }
20327
20404
  console.log("\u26A0\uFE0F Skipping duplicate component: ".concat(key, " (").concat(component.name || 'unnamed', ")"));
20328
20405
  }
20329
20406
  });
@@ -20355,17 +20432,61 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20355
20432
  }
20356
20433
  return extendComponentDictionary;
20357
20434
  }()
20435
+ /**
20436
+ * Merge mesh animation configs from host persistence into the live dictionary.
20437
+ * @param {Object<string, { behaviorConfig?: Array, meshNameMap?: Object, behaviors?: Array }>} configsByAssetId
20438
+ * @returns {number} Count of entries updated
20439
+ */
20440
+ )
20441
+ }, {
20442
+ key: "mergeBehaviorConfigsIntoDictionary",
20443
+ value: function mergeBehaviorConfigsIntoDictionary(configsByAssetId) {
20444
+ var _this$sceneViewer2;
20445
+ if (!configsByAssetId || !this.componentDictionary) return 0;
20446
+ var merged = 0;
20447
+ for (var _i2 = 0, _Object$entries2 = Object.entries(configsByAssetId); _i2 < _Object$entries2.length; _i2++) {
20448
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
20449
+ assetId = _Object$entries2$_i[0],
20450
+ attrs = _Object$entries2$_i[1];
20451
+ var entry = this.componentDictionary[assetId];
20452
+ if (!entry || !attrs) continue;
20453
+ var updated = false;
20454
+ if (attrs.behaviorConfig) {
20455
+ entry.behaviorConfig = attrs.behaviorConfig;
20456
+ updated = true;
20457
+ }
20458
+ if (attrs.meshNameMap) {
20459
+ entry.meshNameMap = attrs.meshNameMap;
20460
+ updated = true;
20461
+ }
20462
+ if ('behaviors' in attrs) {
20463
+ var _attrs$behaviors;
20464
+ if ((_attrs$behaviors = attrs.behaviors) !== null && _attrs$behaviors !== void 0 && _attrs$behaviors.length) {
20465
+ entry.behaviors = attrs.behaviors;
20466
+ } else {
20467
+ delete entry.behaviors;
20468
+ }
20469
+ updated = true;
20470
+ }
20471
+ if (updated) merged++;
20472
+ }
20473
+ var modelPreloader = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.centralPlant) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.utilities) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.modelPreloader;
20474
+ if (modelPreloader) {
20475
+ modelPreloader.componentDictionary = this.componentDictionary;
20476
+ }
20477
+ return merged;
20478
+ }
20479
+
20358
20480
  /**
20359
20481
  * Remove S3 components from the component dictionary
20360
20482
  * @returns {Promise<boolean>} True if components were removed successfully
20361
20483
  */
20362
- )
20363
20484
  }, {
20364
20485
  key: "removeS3Components",
20365
20486
  value: (function () {
20366
20487
  var _removeS3Components = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
20367
20488
  var _this4 = this;
20368
- var _this$sceneViewer2, keysToRemove, _i2, _Object$entries2, _Object$entries2$_i, key, component, modelPreloader, _t2;
20489
+ var _this$sceneViewer3, keysToRemove, _i3, _Object$entries3, _Object$entries3$_i, key, component, modelPreloader, _t2;
20369
20490
  return _regenerator().w(function (_context4) {
20370
20491
  while (1) switch (_context4.n) {
20371
20492
  case 0:
@@ -20382,8 +20503,8 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20382
20503
  _context4.p = 2;
20383
20504
  // Find and remove all S3 components
20384
20505
  keysToRemove = [];
20385
- for (_i2 = 0, _Object$entries2 = Object.entries(this.componentDictionary); _i2 < _Object$entries2.length; _i2++) {
20386
- _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2), key = _Object$entries2$_i[0], component = _Object$entries2$_i[1];
20506
+ for (_i3 = 0, _Object$entries3 = Object.entries(this.componentDictionary); _i3 < _Object$entries3.length; _i3++) {
20507
+ _Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2), key = _Object$entries3$_i[0], component = _Object$entries3$_i[1];
20387
20508
  if (component.isS3Component) {
20388
20509
  keysToRemove.push(key);
20389
20510
  }
@@ -20395,7 +20516,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20395
20516
  });
20396
20517
 
20397
20518
  // Update ModelPreloader's dictionary reference
20398
- modelPreloader = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.centralPlant) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.utilities) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.modelPreloader;
20519
+ modelPreloader = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.centralPlant) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.utilities) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.modelPreloader;
20399
20520
  if (modelPreloader) {
20400
20521
  modelPreloader.componentDictionary = this.componentDictionary;
20401
20522
  console.log("\uD83D\uDD04 Updated ModelPreloader's dictionary reference (".concat(Object.keys(this.componentDictionary).length, " total components)"));
@@ -20428,7 +20549,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20428
20549
  key: "removeComponentFromDictionary",
20429
20550
  value: (function () {
20430
20551
  var _removeComponentFromDictionary = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(componentKey) {
20431
- var _this$sceneViewer3, modelPreloader, _t3;
20552
+ var _this$sceneViewer4, modelPreloader, _t3;
20432
20553
  return _regenerator().w(function (_context5) {
20433
20554
  while (1) switch (_context5.n) {
20434
20555
  case 0:
@@ -20461,7 +20582,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20461
20582
  delete this.componentDictionary[componentKey];
20462
20583
 
20463
20584
  // Update ModelPreloader's dictionary reference
20464
- modelPreloader = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.centralPlant) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.utilities) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.modelPreloader;
20585
+ modelPreloader = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.centralPlant) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.utilities) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.modelPreloader;
20465
20586
  if (modelPreloader) {
20466
20587
  modelPreloader.componentDictionary = this.componentDictionary;
20467
20588
  console.log("\uD83D\uDD04 Updated ModelPreloader's dictionary reference (".concat(Object.keys(this.componentDictionary).length, " total components)"));
@@ -20561,10 +20682,10 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
20561
20682
  if (!this.componentDictionary) return 'unknown';
20562
20683
 
20563
20684
  // Look for component in dictionary
20564
- for (var _i3 = 0, _Object$entries3 = Object.entries(this.componentDictionary); _i3 < _Object$entries3.length; _i3++) {
20565
- var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i3], 2),
20566
- key = _Object$entries3$_i[0],
20567
- component = _Object$entries3$_i[1];
20685
+ for (var _i4 = 0, _Object$entries4 = Object.entries(this.componentDictionary); _i4 < _Object$entries4.length; _i4++) {
20686
+ var _Object$entries4$_i = _slicedToArray(_Object$entries4[_i4], 2),
20687
+ key = _Object$entries4$_i[0],
20688
+ component = _Object$entries4$_i[1];
20568
20689
  if (key === 'categories') continue;
20569
20690
  if (name && name.toLowerCase().includes(key.toLowerCase())) {
20570
20691
  return component.category || 'unknown';
@@ -31317,12 +31438,12 @@ var ModelManager = /*#__PURE__*/function () {
31317
31438
  key: "loadLibraryModel",
31318
31439
  value: function () {
31319
31440
  var _loadLibraryModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(targetMesh, jsonEntry, componentData) {
31320
- var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioBehavMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
31321
- return _regenerator().w(function (_context2) {
31322
- while (1) switch (_context2.n) {
31441
+ var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioBehavMgr, warmFn, _jsonEntry$userData4, _t;
31442
+ return _regenerator().w(function (_context) {
31443
+ while (1) switch (_context.n) {
31323
31444
  case 0:
31324
31445
  component = this.sceneViewer;
31325
- _context2.p = 1;
31446
+ _context.p = 1;
31326
31447
  console.log("Loading library GLB model for ".concat((_jsonEntry$userData = jsonEntry.userData) === null || _jsonEntry$userData === void 0 ? void 0 : _jsonEntry$userData.libraryId, "..."));
31327
31448
 
31328
31449
  // Store original mesh properties before async operation
@@ -31335,16 +31456,16 @@ var ModelManager = /*#__PURE__*/function () {
31335
31456
  uuid: targetMesh.uuid
31336
31457
  }; // Preserve connector children (pass parent UUID for proper connector UUID generation)
31337
31458
  connectorChildren = this._preserveConnectorChildren(targetMesh, originalProps.uuid); // Get model from cache or load directly
31338
- _context2.n = 2;
31459
+ _context.n = 2;
31339
31460
  return this._getLibraryModel(componentData.modelKey, (_jsonEntry$userData2 = jsonEntry.userData) === null || _jsonEntry$userData2 === void 0 ? void 0 : _jsonEntry$userData2.libraryId);
31340
31461
  case 2:
31341
- gltfScene = _context2.v;
31462
+ gltfScene = _context.v;
31342
31463
  if (gltfScene) {
31343
- _context2.n = 3;
31464
+ _context.n = 3;
31344
31465
  break;
31345
31466
  }
31346
31467
  console.warn("\u26A0\uFE0F Could not load model ".concat(componentData.modelKey, ", keeping original mesh"));
31347
- return _context2.a(2, targetMesh);
31468
+ return _context.a(2, targetMesh);
31348
31469
  case 3:
31349
31470
  // Configure the loaded model
31350
31471
  libraryModel = this._configureLibraryModel(gltfScene, jsonEntry, componentData, originalProps); // Add preserved connectors
@@ -31354,70 +31475,17 @@ var ModelManager = /*#__PURE__*/function () {
31354
31475
 
31355
31476
  // Attach IO devices for smart components (import flow)
31356
31477
  if (!(componentData.isSmart && componentData.attachedDevices)) {
31357
- _context2.n = 9;
31478
+ _context.n = 5;
31358
31479
  break;
31359
31480
  }
31360
- _context2.n = 4;
31481
+ _context.n = 4;
31361
31482
  return attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
31362
31483
  case 4:
31363
- // Register behavior configs for each attached device
31364
31484
  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;
31365
- if (!ioBehavMgr) {
31366
- _context2.n = 9;
31367
- break;
31485
+ if (ioBehavMgr) {
31486
+ registerBehaviorsForComponent(ioBehavMgr, originalProps.uuid, componentData, libraryModel, modelPreloader.componentDictionary);
31368
31487
  }
31369
- _loop = /*#__PURE__*/_regenerator().m(function _loop() {
31370
- var _modelPreloader$compo, _deviceData$behaviorC;
31371
- var _Object$entries$_i, attachmentId, attachment, deviceData, deviceRoot;
31372
- return _regenerator().w(function (_context) {
31373
- while (1) switch (_context.n) {
31374
- case 0:
31375
- _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
31376
- deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
31377
- console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 behaviorConfig:"), (_deviceData$behaviorC = deviceData === null || deviceData === void 0 ? void 0 : deviceData.behaviorConfig) !== null && _deviceData$behaviorC !== void 0 ? _deviceData$behaviorC : 'NONE');
31378
- if (deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig) {
31379
- _context.n = 1;
31380
- break;
31381
- }
31382
- return _context.a(2, 1);
31383
- case 1:
31384
- deviceRoot = null;
31385
- libraryModel.traverse(function (obj) {
31386
- var _obj$userData;
31387
- if (!deviceRoot && ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.attachmentId) === attachmentId) deviceRoot = obj;
31388
- });
31389
- if (deviceRoot) {
31390
- ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, originalProps.uuid);
31391
- }
31392
- case 2:
31393
- return _context.a(2);
31394
- }
31395
- }, _loop);
31396
- });
31397
- _i = 0, _Object$entries = Object.entries(componentData.attachedDevices);
31398
31488
  case 5:
31399
- if (!(_i < _Object$entries.length)) {
31400
- _context2.n = 8;
31401
- break;
31402
- }
31403
- return _context2.d(_regeneratorValues(_loop()), 6);
31404
- case 6:
31405
- if (!_context2.v) {
31406
- _context2.n = 7;
31407
- break;
31408
- }
31409
- return _context2.a(3, 7);
31410
- case 7:
31411
- _i++;
31412
- _context2.n = 5;
31413
- break;
31414
- case 8:
31415
- // Register component-level behaviors (intra-component io-device linking)
31416
- if (componentData.behaviors && componentData.behaviors.length > 0) {
31417
- console.log("[ModelManager] Registering ".concat(componentData.behaviors.length, " component-level behavior(s) for ").concat(originalProps.uuid));
31418
- ioBehavMgr.registerComponentBehaviors(originalProps.uuid, componentData.behaviors);
31419
- }
31420
- case 9:
31421
31489
  // Replace mesh in scene
31422
31490
  this._replaceMeshInScene(targetMesh, libraryModel, originalProps.parent, component);
31423
31491
 
@@ -31435,14 +31503,14 @@ var ModelManager = /*#__PURE__*/function () {
31435
31503
  }
31436
31504
  }
31437
31505
  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"));
31438
- return _context2.a(2, libraryModel);
31439
- case 10:
31440
- _context2.p = 10;
31441
- _t = _context2.v;
31506
+ return _context.a(2, libraryModel);
31507
+ case 6:
31508
+ _context.p = 6;
31509
+ _t = _context.v;
31442
31510
  console.error("\u274C Error loading ".concat((_jsonEntry$userData4 = jsonEntry.userData) === null || _jsonEntry$userData4 === void 0 ? void 0 : _jsonEntry$userData4.libraryId, " GLB model:"), _t);
31443
- return _context2.a(2, targetMesh);
31511
+ return _context.a(2, targetMesh);
31444
31512
  }
31445
- }, _callee, this, [[1, 10]]);
31513
+ }, _callee, this, [[1, 6]]);
31446
31514
  }));
31447
31515
  function loadLibraryModel(_x, _x2, _x3) {
31448
31516
  return _loadLibraryModel.apply(this, arguments);
@@ -31521,84 +31589,84 @@ var ModelManager = /*#__PURE__*/function () {
31521
31589
  var _getLibraryModel2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(modelKey, libraryId) {
31522
31590
  var _this2 = this;
31523
31591
  var gltfScene, preloaderStatus, modelPath, gltf, _t2, _t3, _t4;
31524
- return _regenerator().w(function (_context3) {
31525
- while (1) switch (_context3.n) {
31592
+ return _regenerator().w(function (_context2) {
31593
+ while (1) switch (_context2.n) {
31526
31594
  case 0:
31527
31595
  // Try cache first
31528
31596
  gltfScene = modelPreloader.getCachedModelWithDimensions(modelKey, libraryId);
31529
31597
  if (!gltfScene) {
31530
- _context3.n = 1;
31598
+ _context2.n = 1;
31531
31599
  break;
31532
31600
  }
31533
31601
  console.log("\uD83C\uDFAF GLB cache HIT: ".concat(modelKey));
31534
- return _context3.a(2, gltfScene);
31602
+ return _context2.a(2, gltfScene);
31535
31603
  case 1:
31536
31604
  // Check if preloading is in progress
31537
31605
  preloaderStatus = modelPreloader.getStatus();
31538
31606
  if (!preloaderStatus.isPreloading) {
31539
- _context3.n = 6;
31607
+ _context2.n = 6;
31540
31608
  break;
31541
31609
  }
31542
31610
  console.log("\u23F3 Waiting for preloader: ".concat(modelKey));
31543
- _context3.p = 2;
31544
- _context3.n = 3;
31611
+ _context2.p = 2;
31612
+ _context2.n = 3;
31545
31613
  return modelPreloader.preloadingPromise;
31546
31614
  case 3:
31547
31615
  gltfScene = modelPreloader.getCachedModelWithDimensions(modelKey, libraryId);
31548
31616
  if (!gltfScene) {
31549
- _context3.n = 4;
31617
+ _context2.n = 4;
31550
31618
  break;
31551
31619
  }
31552
31620
  console.log("\uD83C\uDFAF GLB cache HIT (after preload): ".concat(modelKey));
31553
- return _context3.a(2, gltfScene);
31621
+ return _context2.a(2, gltfScene);
31554
31622
  case 4:
31555
- _context3.n = 6;
31623
+ _context2.n = 6;
31556
31624
  break;
31557
31625
  case 5:
31558
- _context3.p = 5;
31559
- _t2 = _context3.v;
31626
+ _context2.p = 5;
31627
+ _t2 = _context2.v;
31560
31628
  console.warn("\u26A0\uFE0F Preloading failed:", _t2);
31561
31629
  case 6:
31562
- _context3.p = 6;
31630
+ _context2.p = 6;
31563
31631
  if (!modelPreloader.urlResolver) {
31564
- _context3.n = 11;
31632
+ _context2.n = 11;
31565
31633
  break;
31566
31634
  }
31567
- _context3.p = 7;
31568
- _context3.n = 8;
31635
+ _context2.p = 7;
31636
+ _context2.n = 8;
31569
31637
  return modelPreloader.urlResolver(modelKey);
31570
31638
  case 8:
31571
- modelPath = _context3.v;
31639
+ modelPath = _context2.v;
31572
31640
  console.log("\uD83D\uDD17 Resolved URL for ".concat(modelKey));
31573
- _context3.n = 10;
31641
+ _context2.n = 10;
31574
31642
  break;
31575
31643
  case 9:
31576
- _context3.p = 9;
31577
- _t3 = _context3.v;
31644
+ _context2.p = 9;
31645
+ _t3 = _context2.v;
31578
31646
  console.warn("\u26A0\uFE0F URL resolver failed for ".concat(modelKey, ", falling back to local path:"), _t3);
31579
31647
  modelPath = "".concat(modelPreloader.modelsBasePath).concat(modelKey);
31580
31648
  case 10:
31581
- _context3.n = 12;
31649
+ _context2.n = 12;
31582
31650
  break;
31583
31651
  case 11:
31584
31652
  modelPath = "".concat(modelPreloader.modelsBasePath).concat(modelKey);
31585
31653
  case 12:
31586
31654
  console.log("\uD83D\uDCC2 Fallback loading from: ".concat(modelPath));
31587
- _context3.n = 13;
31655
+ _context2.n = 13;
31588
31656
  return new Promise(function (resolve, reject) {
31589
31657
  _this2.sceneViewer.gltfLoader.load(modelPath, resolve, undefined, reject);
31590
31658
  });
31591
31659
  case 13:
31592
- gltf = _context3.v;
31660
+ gltf = _context2.v;
31593
31661
  if (libraryId) {
31594
31662
  modelPreloader.cacheModel(modelKey, gltf.scene.clone(), libraryId);
31595
31663
  }
31596
- return _context3.a(2, gltf.scene);
31664
+ return _context2.a(2, gltf.scene);
31597
31665
  case 14:
31598
- _context3.p = 14;
31599
- _t4 = _context3.v;
31666
+ _context2.p = 14;
31667
+ _t4 = _context2.v;
31600
31668
  console.error("Failed to load model ".concat(modelKey, ":"), _t4);
31601
- return _context3.a(2, null);
31669
+ return _context2.a(2, null);
31602
31670
  }
31603
31671
  }, _callee2, null, [[7, 9], [6, 14], [2, 5]]);
31604
31672
  }));
@@ -31705,8 +31773,8 @@ var ModelManager = /*#__PURE__*/function () {
31705
31773
  value: (function () {
31706
31774
  var _verifyModelPreloaderCache = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
31707
31775
  var preloaderStatus, _t5;
31708
- return _regenerator().w(function (_context4) {
31709
- while (1) switch (_context4.n) {
31776
+ return _regenerator().w(function (_context3) {
31777
+ while (1) switch (_context3.n) {
31710
31778
  case 0:
31711
31779
  console.log('🔍 Verifying model preloader cache state...');
31712
31780
  preloaderStatus = modelPreloader.getStatus();
@@ -31714,23 +31782,23 @@ var ModelManager = /*#__PURE__*/function () {
31714
31782
 
31715
31783
  // If preloading is still in progress, wait for completion
31716
31784
  if (!preloaderStatus.isPreloading) {
31717
- _context4.n = 4;
31785
+ _context3.n = 4;
31718
31786
  break;
31719
31787
  }
31720
31788
  console.log('⏳ Waiting for model preloading to complete...');
31721
- _context4.p = 1;
31722
- _context4.n = 2;
31789
+ _context3.p = 1;
31790
+ _context3.n = 2;
31723
31791
  return modelPreloader.preloadingPromise;
31724
31792
  case 2:
31725
31793
  console.log('✅ Model preloading completed');
31726
- _context4.n = 4;
31794
+ _context3.n = 4;
31727
31795
  break;
31728
31796
  case 3:
31729
- _context4.p = 3;
31730
- _t5 = _context4.v;
31797
+ _context3.p = 3;
31798
+ _t5 = _context3.v;
31731
31799
  console.warn('⚠️ Model preloading failed, some models may load directly:', _t5);
31732
31800
  case 4:
31733
- return _context4.a(2, preloaderStatus);
31801
+ return _context3.a(2, preloaderStatus);
31734
31802
  }
31735
31803
  }, _callee3, null, [[1, 3]]);
31736
31804
  }));
@@ -31748,11 +31816,11 @@ var ModelManager = /*#__PURE__*/function () {
31748
31816
  value: (function () {
31749
31817
  var _preloadMissingModels = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(data, componentDictionary) {
31750
31818
  var _data$scene, _data$scene2, requiredModels, preloaderStatus, cachedModels, missingModels, basePath, modelUrls, _iterator, _step, modelKey, _t6, _t7;
31751
- return _regenerator().w(function (_context5) {
31752
- while (1) switch (_context5.n) {
31819
+ return _regenerator().w(function (_context4) {
31820
+ while (1) switch (_context4.n) {
31753
31821
  case 0:
31754
31822
  if (!(!data || !data.scene || !Array.isArray(data.scene.children))) {
31755
- _context5.n = 1;
31823
+ _context4.n = 1;
31756
31824
  break;
31757
31825
  }
31758
31826
  console.warn('⚠️ Invalid scene data structure in preloadMissingModels:', {
@@ -31761,7 +31829,7 @@ var ModelManager = /*#__PURE__*/function () {
31761
31829
  hasChildren: !!(data !== null && data !== void 0 && (_data$scene = data.scene) !== null && _data$scene !== void 0 && _data$scene.children),
31762
31830
  childrenType: data !== null && data !== void 0 && (_data$scene2 = data.scene) !== null && _data$scene2 !== void 0 && _data$scene2.children ? _typeof(data.scene.children) : 'undefined'
31763
31831
  });
31764
- return _context5.a(2);
31832
+ return _context4.a(2);
31765
31833
  case 1:
31766
31834
  requiredModels = new Set();
31767
31835
  console.log("\uD83D\uDD0D Checking ".concat(data.scene.children.length, " scene objects for required models..."));
@@ -31783,10 +31851,10 @@ var ModelManager = /*#__PURE__*/function () {
31783
31851
  }
31784
31852
  });
31785
31853
  console.log('🔍 Required models for this scene (Set):', Array.from(requiredModels));
31786
- _context5.n = 2;
31854
+ _context4.n = 2;
31787
31855
  return this.verifyModelPreloaderCache();
31788
31856
  case 2:
31789
- preloaderStatus = _context5.v;
31857
+ preloaderStatus = _context4.v;
31790
31858
  cachedModels = preloaderStatus.cachedModels;
31791
31859
  console.log('ModelPreloader cached models:', cachedModels);
31792
31860
 
@@ -31817,7 +31885,7 @@ var ModelManager = /*#__PURE__*/function () {
31817
31885
  });
31818
31886
  }
31819
31887
  if (!(missingModels.length > 0)) {
31820
- _context5.n = 12;
31888
+ _context4.n = 12;
31821
31889
  break;
31822
31890
  }
31823
31891
  console.warn('⚠️ Some required models are not cached:', missingModels);
@@ -31825,41 +31893,41 @@ var ModelManager = /*#__PURE__*/function () {
31825
31893
 
31826
31894
  // Preload missing models (Main in-memory preloader)
31827
31895
  _iterator = _createForOfIteratorHelper(missingModels);
31828
- _context5.p = 3;
31896
+ _context4.p = 3;
31829
31897
  _iterator.s();
31830
31898
  case 4:
31831
31899
  if ((_step = _iterator.n()).done) {
31832
- _context5.n = 9;
31900
+ _context4.n = 9;
31833
31901
  break;
31834
31902
  }
31835
31903
  modelKey = _step.value;
31836
- _context5.p = 5;
31837
- _context5.n = 6;
31904
+ _context4.p = 5;
31905
+ _context4.n = 6;
31838
31906
  return modelPreloader.preloadSingleModel(modelKey);
31839
31907
  case 6:
31840
31908
  console.log("\u2705 Successfully preloaded missing model: ".concat(modelKey));
31841
- _context5.n = 8;
31909
+ _context4.n = 8;
31842
31910
  break;
31843
31911
  case 7:
31844
- _context5.p = 7;
31845
- _t6 = _context5.v;
31912
+ _context4.p = 7;
31913
+ _t6 = _context4.v;
31846
31914
  console.warn("\u274C Failed to preload missing model ".concat(modelKey, ":"), _t6);
31847
31915
  case 8:
31848
- _context5.n = 4;
31916
+ _context4.n = 4;
31849
31917
  break;
31850
31918
  case 9:
31851
- _context5.n = 11;
31919
+ _context4.n = 11;
31852
31920
  break;
31853
31921
  case 10:
31854
- _context5.p = 10;
31855
- _t7 = _context5.v;
31922
+ _context4.p = 10;
31923
+ _t7 = _context4.v;
31856
31924
  _iterator.e(_t7);
31857
31925
  case 11:
31858
- _context5.p = 11;
31926
+ _context4.p = 11;
31859
31927
  _iterator.f();
31860
- return _context5.f(11);
31928
+ return _context4.f(11);
31861
31929
  case 12:
31862
- return _context5.a(2);
31930
+ return _context4.a(2);
31863
31931
  }
31864
31932
  }, _callee4, this, [[5, 7], [3, 10, 11, 12]]);
31865
31933
  }));
@@ -31878,14 +31946,14 @@ var ModelManager = /*#__PURE__*/function () {
31878
31946
  var _replaceWithGLBModels = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(libraryObjectsToReplace) {
31879
31947
  var _this3 = this;
31880
31948
  var startTime, glbLoadingPromises;
31881
- return _regenerator().w(function (_context6) {
31882
- while (1) switch (_context6.n) {
31949
+ return _regenerator().w(function (_context5) {
31950
+ while (1) switch (_context5.n) {
31883
31951
  case 0:
31884
31952
  if (!(libraryObjectsToReplace.length === 0)) {
31885
- _context6.n = 1;
31953
+ _context5.n = 1;
31886
31954
  break;
31887
31955
  }
31888
- return _context6.a(2);
31956
+ return _context5.a(2);
31889
31957
  case 1:
31890
31958
  startTime = performance.now();
31891
31959
  console.log("\uD83D\uDD04 Replacing ".concat(libraryObjectsToReplace.length, " objects with GLB models..."));
@@ -31905,7 +31973,7 @@ var ModelManager = /*#__PURE__*/function () {
31905
31973
  return basicObject;
31906
31974
  });
31907
31975
  });
31908
- _context6.n = 2;
31976
+ _context5.n = 2;
31909
31977
  return Promise.all(glbLoadingPromises);
31910
31978
  case 2:
31911
31979
  console.log("\u23F1\uFE0F All GLB models loaded in ".concat((performance.now() - startTime).toFixed(0), "ms"));
@@ -31948,7 +32016,7 @@ var ModelManager = /*#__PURE__*/function () {
31948
32016
  }));
31949
32017
  }
31950
32018
  case 3:
31951
- return _context6.a(2);
32019
+ return _context5.a(2);
31952
32020
  }
31953
32021
  }, _callee5);
31954
32022
  }));
@@ -31983,26 +32051,26 @@ var ModelManager = /*#__PURE__*/function () {
31983
32051
  value: (function () {
31984
32052
  var _loadComponentDictionary = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
31985
32053
  var response, dict, _t8;
31986
- return _regenerator().w(function (_context7) {
31987
- while (1) switch (_context7.n) {
32054
+ return _regenerator().w(function (_context6) {
32055
+ while (1) switch (_context6.n) {
31988
32056
  case 0:
31989
- _context7.p = 0;
32057
+ _context6.p = 0;
31990
32058
  console.log('🔄 ModelManager loading component dictionary...');
31991
- _context7.n = 1;
32059
+ _context6.n = 1;
31992
32060
  return fetch('/library/component-dictionary.json');
31993
32061
  case 1:
31994
- response = _context7.v;
31995
- _context7.n = 2;
32062
+ response = _context6.v;
32063
+ _context6.n = 2;
31996
32064
  return response.json();
31997
32065
  case 2:
31998
- dict = _context7.v;
32066
+ dict = _context6.v;
31999
32067
  console.log('✅ ModelManager loaded dictionary:', Object.keys(dict).length, 'entries');
32000
- return _context7.a(2, dict);
32068
+ return _context6.a(2, dict);
32001
32069
  case 3:
32002
- _context7.p = 3;
32003
- _t8 = _context7.v;
32070
+ _context6.p = 3;
32071
+ _t8 = _context6.v;
32004
32072
  console.warn('⚠️ Could not load component dictionary:', _t8);
32005
- return _context7.a(2, {});
32073
+ return _context6.a(2, {});
32006
32074
  }
32007
32075
  }, _callee6, null, [[0, 3]]);
32008
32076
  }));
@@ -32705,10 +32773,15 @@ var SceneOperationsManager = /*#__PURE__*/function () {
32705
32773
  key: "clearSceneObjects",
32706
32774
  value: function () {
32707
32775
  var _clearSceneObjects = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
32708
- var result;
32776
+ var _this$sceneViewer;
32777
+ var ioBehavMgr, result;
32709
32778
  return _regenerator().w(function (_context) {
32710
32779
  while (1) switch (_context.n) {
32711
32780
  case 0:
32781
+ 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;
32782
+ if (ioBehavMgr !== null && ioBehavMgr !== void 0 && ioBehavMgr.resetForScene) {
32783
+ ioBehavMgr.resetForScene();
32784
+ }
32712
32785
  _context.n = 1;
32713
32786
  return this.sceneClearingUtility.clearAllObjects();
32714
32787
  case 1:
@@ -33074,9 +33147,9 @@ var SceneOperationsManager = /*#__PURE__*/function () {
33074
33147
  // Use crosscubeTextureSet if available, otherwise fallback material
33075
33148
  var material = materials[obj.material];
33076
33149
  if (!material) {
33077
- var _this$sceneViewer;
33150
+ var _this$sceneViewer2;
33078
33151
  // Check if we have crosscubeTextureSet from scene loading
33079
- var crosscubeTextureSet = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.crosscubeTextureSet;
33152
+ var crosscubeTextureSet = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.crosscubeTextureSet;
33080
33153
  if (crosscubeTextureSet) {
33081
33154
  // Match PathfindingManager.createPipeMaterial() with textures
33082
33155
  var pathColor = '#245e29'; // Default green color from PathfindingManager.getPathColor()
@@ -36539,6 +36612,61 @@ var SceneTooltipsManager = /*#__PURE__*/function (_BaseDisposable) {
36539
36612
  }]);
36540
36613
  }(BaseDisposable);
36541
36614
 
36615
+ /**
36616
+ * Shared I/O device state dispatch helpers used by centralPlant and componentTooltipManager.
36617
+ */
36618
+
36619
+ /**
36620
+ * @param {string} attachmentId
36621
+ * @param {string|null|undefined} parentUuid
36622
+ * @returns {string}
36623
+ */
36624
+ function getScopedAttachmentKey(attachmentId, parentUuid) {
36625
+ if (!parentUuid) return attachmentId;
36626
+ return "".concat(parentUuid, "::").concat(attachmentId);
36627
+ }
36628
+
36629
+ /**
36630
+ * @param {Object} sceneViewer
36631
+ * @returns {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager|null}
36632
+ */
36633
+ function getIoBehaviorManager(sceneViewer) {
36634
+ var _sceneViewer$managers, _sceneViewer$centralP;
36635
+ if (!sceneViewer) return null;
36636
+ return ((_sceneViewer$managers = sceneViewer.managers) === null || _sceneViewer$managers === void 0 ? void 0 : _sceneViewer$managers.ioBehaviorManager) || sceneViewer.ioBehaviorManager || ((_sceneViewer$centralP = sceneViewer.centralPlant) === null || _sceneViewer$centralP === void 0 || (_sceneViewer$centralP = _sceneViewer$centralP.managers) === null || _sceneViewer$centralP === void 0 ? void 0 : _sceneViewer$centralP.ioBehaviorManager) || null;
36637
+ }
36638
+
36639
+ /**
36640
+ * Resolve tooltip/drag data points for an I/O device attachment.
36641
+ * Prefers behaviorConfig-driven animation data points; merges legacy ioConfig snapshot.
36642
+ *
36643
+ * @param {string} parentUuid
36644
+ * @param {string} attachmentId
36645
+ * @param {Object} userData - io-device userData (may include dataPoints snapshot)
36646
+ * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager|null} ioBehaviorManager
36647
+ * @param {THREE.Object3D|null} [hitMesh]
36648
+ * @returns {Object[]}
36649
+ */
36650
+ function resolveDataPoints(parentUuid, attachmentId, userData, ioBehaviorManager) {
36651
+ var hitMesh = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
36652
+ var fromAnimations = (ioBehaviorManager === null || ioBehaviorManager === void 0 ? void 0 : ioBehaviorManager.getAnimationDataPoints(parentUuid, attachmentId, hitMesh)) || [];
36653
+ if (fromAnimations.length) return fromAnimations;
36654
+ var legacy = (userData === null || userData === void 0 ? void 0 : userData.dataPoints) || [];
36655
+ return legacy.map(function (dp) {
36656
+ var _dp$defaultValue;
36657
+ return {
36658
+ id: dp.id || dp.name,
36659
+ name: dp.name || dp.id,
36660
+ stateType: dp.stateType || 'binary',
36661
+ stateConfig: dp.stateConfig || {},
36662
+ defaultValue: (_dp$defaultValue = dp.defaultValue) !== null && _dp$defaultValue !== void 0 ? _dp$defaultValue : null,
36663
+ direction: dp.direction || (userData === null || userData === void 0 ? void 0 : userData.ioDirection) || 'output'
36664
+ };
36665
+ }).filter(function (dp) {
36666
+ return dp.id;
36667
+ });
36668
+ }
36669
+
36542
36670
  // ---------------------------------------------------------------------------
36543
36671
  // Inline styles (injected once into the document head)
36544
36672
  // ---------------------------------------------------------------------------
@@ -36548,6 +36676,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36548
36676
  * @param {Object} sceneViewer - The sceneViewer instance
36549
36677
  */
36550
36678
  function ComponentTooltipManager(sceneViewer) {
36679
+ var _this$sceneViewer, _this$sceneViewer$on;
36551
36680
  var _this;
36552
36681
  _classCallCheck(this, ComponentTooltipManager);
36553
36682
  _this = _callSuper(this, ComponentTooltipManager);
@@ -36572,29 +36701,38 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36572
36701
  * Map of live DOM elements that display current data point values.
36573
36702
  * Key: `${attachmentId}::${dataPointId}`
36574
36703
  * Value: { el: HTMLElement, dp: Object, isInput: boolean }
36575
- * Populated during _buildTooltip; polled each frame in _refreshStateDisplays.
36704
+ * Populated during _buildTooltip; refreshed on io-device-state-changed events.
36576
36705
  * @type {Map<string, {el: HTMLElement, dp: Object, isInput: boolean}>}
36577
36706
  */
36578
36707
  _this._stateElements = new Map();
36708
+ _this._onIoStateChanged = _this._onIoStateChanged.bind(_this);
36579
36709
  _this._injectStyles();
36580
36710
  _this._onKeyDown = _this._onKeyDown.bind(_this);
36581
36711
  document.addEventListener('keydown', _this._onKeyDown);
36712
+ (_this$sceneViewer = _this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer$on = _this$sceneViewer.on) === null || _this$sceneViewer$on === void 0 || _this$sceneViewer$on.call(_this$sceneViewer, 'io-device-state-changed', _this._onIoStateChanged);
36582
36713
  return _this;
36583
36714
  }
36584
-
36585
- // -----------------------------------------------------------------------
36586
- // Lifecycle
36587
- // -----------------------------------------------------------------------
36588
-
36589
- /**
36590
- * Called automatically by BaseDisposable.dispose()
36591
- * @override
36592
- */
36593
36715
  _inherits(ComponentTooltipManager, _BaseDisposable);
36594
36716
  return _createClass(ComponentTooltipManager, [{
36717
+ key: "_onIoStateChanged",
36718
+ value: function _onIoStateChanged() {
36719
+ this._refreshStateDisplays();
36720
+ }
36721
+
36722
+ // -----------------------------------------------------------------------
36723
+ // Lifecycle
36724
+ // -----------------------------------------------------------------------
36725
+
36726
+ /**
36727
+ * Called automatically by BaseDisposable.dispose()
36728
+ * @override
36729
+ */
36730
+ }, {
36595
36731
  key: "_doDispose",
36596
36732
  value: function _doDispose() {
36733
+ var _this$sceneViewer2, _this$sceneViewer2$of;
36597
36734
  document.removeEventListener('keydown', this._onKeyDown);
36735
+ (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2$of = _this$sceneViewer2.off) === null || _this$sceneViewer2$of === void 0 || _this$sceneViewer2$of.call(_this$sceneViewer2, 'io-device-state-changed', this._onIoStateChanged);
36598
36736
  this.hide();
36599
36737
  this._removeStyleTag();
36600
36738
  this._stateElements.clear();
@@ -36642,7 +36780,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36642
36780
  }, {
36643
36781
  key: "toggleIODeviceBinaryState",
36644
36782
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
36645
- var _ref, _this$sceneViewer;
36783
+ var _ref;
36646
36784
  if (!ioDeviceObject || !this._stateAdapter) return;
36647
36785
  var ud = ioDeviceObject.userData;
36648
36786
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
@@ -36663,7 +36801,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36663
36801
 
36664
36802
  // Create a scoped attachment key to prevent state sharing between instances
36665
36803
  // of the same smart component that share the same attachmentId
36666
- var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
36804
+ var scopedAttachmentId = getScopedAttachmentKey(attachmentId, parentUuid);
36667
36805
 
36668
36806
  // Find the first binary state
36669
36807
  var binaryState = dataPoints.find(function (dp) {
@@ -36675,8 +36813,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36675
36813
  // Fall back to defaultValue when state is uninitialized (null/undefined)
36676
36814
  var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
36677
36815
  var newVal = !Boolean(currentVal);
36678
- this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
36679
- (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.ioBehaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
36816
+ this._dispatchIoState(attachmentId, dpId, newVal, parentUuid);
36680
36817
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
36681
36818
  }
36682
36819
 
@@ -36692,8 +36829,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36692
36829
  }, {
36693
36830
  key: "startIODeviceDrag",
36694
36831
  value: function startIODeviceDrag(ioDeviceObject, hitMesh) {
36695
- var _this$sceneViewer2,
36696
- _this2 = this;
36832
+ var _this2 = this;
36697
36833
  if (!ioDeviceObject || !this._stateAdapter) return;
36698
36834
  var ud = ioDeviceObject.userData;
36699
36835
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
@@ -36708,15 +36844,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36708
36844
  }
36709
36845
  obj = obj.parent;
36710
36846
  }
36711
- var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
36712
- var ioBehavMgr = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.ioBehaviorManager;
36713
- var dataPoints = ((ioBehavMgr === null || ioBehavMgr === void 0 ? void 0 : ioBehavMgr.getAnimationDataPoints(parentUuid, attachmentId, hitMesh)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
36714
- // deduplicate by id
36715
- .filter(function (dp, i, arr) {
36716
- return arr.findIndex(function (d) {
36717
- return d.id === dp.id;
36718
- }) === i;
36719
- });
36847
+ var scopedAttachmentId = getScopedAttachmentKey(attachmentId, parentUuid);
36848
+ var dataPoints = resolveDataPoints(parentUuid, attachmentId, ud, getIoBehaviorManager(this.sceneViewer), hitMesh);
36720
36849
  var dpSessions = [];
36721
36850
  var _iterator = _createForOfIteratorHelper(dataPoints),
36722
36851
  _step;
@@ -36725,7 +36854,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36725
36854
  var _this2$_stateAdapter$;
36726
36855
  var dp = _step.value;
36727
36856
  var stateType = (dp.stateType || '').toLowerCase();
36728
- if (stateType !== 'binary' && stateType !== 'boolean' && stateType !== 'enum') return 1; // continue
36729
36857
  var curVal = (_this2$_stateAdapter$ = _this2._stateAdapter.getState(scopedAttachmentId, dp.id)) !== null && _this2$_stateAdapter$ !== void 0 ? _this2$_stateAdapter$ : dp.defaultValue;
36730
36858
  if (stateType === 'binary' || stateType === 'boolean') {
36731
36859
  dpSessions.push({
@@ -36736,7 +36864,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36736
36864
  stateType: 'binary',
36737
36865
  lastApplied: curVal
36738
36866
  });
36739
- } else {
36867
+ } else if (stateType === 'enum') {
36740
36868
  var _dp$stateConfig;
36741
36869
  var opts = ((_dp$stateConfig = dp.stateConfig) === null || _dp$stateConfig === void 0 ? void 0 : _dp$stateConfig.options) || [];
36742
36870
  var curIdx = opts.findIndex(function (o) {
@@ -36752,10 +36880,27 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36752
36880
  startIdx: curIdx >= 0 ? curIdx : 0,
36753
36881
  lastAppliedIdx: curIdx >= 0 ? curIdx : 0
36754
36882
  });
36883
+ } else if (stateType === 'number' || stateType === 'continuous') {
36884
+ var _dp$stateConfig$min, _dp$stateConfig2, _dp$stateConfig$max, _dp$stateConfig3;
36885
+ var min = (_dp$stateConfig$min = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.min) !== null && _dp$stateConfig$min !== void 0 ? _dp$stateConfig$min : 0;
36886
+ var max = (_dp$stateConfig$max = (_dp$stateConfig3 = dp.stateConfig) === null || _dp$stateConfig3 === void 0 ? void 0 : _dp$stateConfig3.max) !== null && _dp$stateConfig$max !== void 0 ? _dp$stateConfig$max : 1;
36887
+ var parsed = Number(curVal);
36888
+ var startVal = Number.isFinite(parsed) ? Math.max(min, Math.min(max, parsed)) : min;
36889
+ dpSessions.push({
36890
+ dp: dp,
36891
+ scopedAttachmentId: scopedAttachmentId,
36892
+ attachmentId: attachmentId,
36893
+ parentUuid: parentUuid,
36894
+ stateType: 'number',
36895
+ min: min,
36896
+ max: max,
36897
+ startVal: startVal,
36898
+ lastApplied: startVal
36899
+ });
36755
36900
  }
36756
36901
  };
36757
36902
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
36758
- if (_loop()) continue;
36903
+ _loop();
36759
36904
  }
36760
36905
  } catch (err) {
36761
36906
  _iterator.e(err);
@@ -36772,8 +36917,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36772
36917
  * Called continuously during pointermove.
36773
36918
  *
36774
36919
  * Sign convention: up/right = positive `signedDelta`.
36775
- * - Binary: > +20 px → true/on state, < −20 px → false/off state.
36776
- * - Enum: each ±30 px step advances/retreats one option in the list.
36920
+ * - Binary: > +20 px → true/on state, < −20 px → false/off state.
36921
+ * - Enum: each ±30 px step advances/retreats one option in the list.
36922
+ * - Continuous: ±200 px spans the configured min→max range.
36777
36923
  *
36778
36924
  * @param {number} signedDelta - Cumulative signed pixel displacement since drag start
36779
36925
  */
@@ -36784,6 +36930,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36784
36930
  if (!session) return;
36785
36931
  var BINARY_THRESHOLD = 20;
36786
36932
  var ENUM_STEP_PX = 30;
36933
+ var RANGE_PX = 200;
36787
36934
  var _iterator2 = _createForOfIteratorHelper(session.dpSessions),
36788
36935
  _step2;
36789
36936
  try {
@@ -36807,6 +36954,13 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36807
36954
  if (newIdx === dps.lastAppliedIdx) continue;
36808
36955
  dps.lastAppliedIdx = newIdx;
36809
36956
  this._applyDpState(dps, dps.opts[newIdx]);
36957
+ } else if (dps.stateType === 'number') {
36958
+ var span = dps.max - dps.min || 1;
36959
+ var delta = signedDelta / RANGE_PX * span;
36960
+ var _newVal = Math.max(dps.min, Math.min(dps.max, dps.startVal + delta));
36961
+ if (Math.abs(_newVal - dps.lastApplied) < span * 0.001) continue;
36962
+ dps.lastApplied = _newVal;
36963
+ this._applyDpState(dps, _newVal);
36810
36964
  }
36811
36965
  }
36812
36966
  } catch (err) {
@@ -36832,14 +36986,35 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36832
36986
  }, {
36833
36987
  key: "_applyDpState",
36834
36988
  value: function _applyDpState(_ref2, newVal) {
36835
- var _this$_stateAdapter, _this$sceneViewer3;
36836
- var scopedAttachmentId = _ref2.scopedAttachmentId,
36837
- attachmentId = _ref2.attachmentId,
36989
+ _ref2.scopedAttachmentId;
36990
+ var attachmentId = _ref2.attachmentId,
36838
36991
  parentUuid = _ref2.parentUuid,
36839
36992
  dp = _ref2.dp;
36840
- var dpId = dp.id;
36841
- (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
36842
- (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.ioBehaviorManager) === null || _this$sceneViewer3 === void 0 || _this$sceneViewer3.triggerState(attachmentId, dpId, newVal, parentUuid);
36993
+ this._dispatchIoState(attachmentId, dp.id, newVal, parentUuid);
36994
+ }
36995
+
36996
+ /**
36997
+ * Unified I/O state dispatch with fallback when centralPlant is unavailable.
36998
+ * @private
36999
+ */
37000
+ }, {
37001
+ key: "_dispatchIoState",
37002
+ value: function _dispatchIoState(attachmentId, stateId, value, parentUuid) {
37003
+ var _this$sceneViewer3, _this$_stateAdapter, _getIoBehaviorManager, _this$sceneViewer4;
37004
+ var cp = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.centralPlant;
37005
+ if (cp && typeof cp._dispatchIoState === 'function') {
37006
+ cp._dispatchIoState(attachmentId, stateId, value, parentUuid);
37007
+ return;
37008
+ }
37009
+ var scopedKey = getScopedAttachmentKey(attachmentId, parentUuid);
37010
+ (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedKey, stateId, value);
37011
+ (_getIoBehaviorManager = getIoBehaviorManager(this.sceneViewer)) === null || _getIoBehaviorManager === void 0 || _getIoBehaviorManager.triggerState(attachmentId, stateId, value, parentUuid);
37012
+ (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || _this$sceneViewer4.emit('io-device-state-changed', {
37013
+ attachmentId: attachmentId,
37014
+ stateId: stateId,
37015
+ value: value,
37016
+ parentUuid: parentUuid || null
37017
+ });
36843
37018
  }
36844
37019
 
36845
37020
  /**
@@ -36872,7 +37047,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36872
37047
  value: function update() {
36873
37048
  if (!this.tooltipEl || !this.selectedObject) return;
36874
37049
  this._positionTooltip();
36875
- this._refreshStateDisplays();
36876
37050
  }
36877
37051
 
36878
37052
  /**
@@ -36931,21 +37105,6 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36931
37105
  this._styleInjected = false;
36932
37106
  }
36933
37107
 
36934
- /**
36935
- * Generate a scoped attachment key that includes the parent component UUID.
36936
- * This ensures each instance of a smart component has isolated IO device state.
36937
- * @param {string} attachmentId - The original attachment ID from the component data
36938
- * @param {string|null} parentUuid - The UUID of the parent smart component instance
36939
- * @returns {string} A scoped key in the format "parentUuid::attachmentId" or just attachmentId if no parent
36940
- * @private
36941
- */
36942
- }, {
36943
- key: "_getScopedAttachmentKey",
36944
- value: function _getScopedAttachmentKey(attachmentId, parentUuid) {
36945
- if (!parentUuid) return attachmentId;
36946
- return "".concat(parentUuid, "::").concat(attachmentId);
36947
- }
36948
-
36949
37108
  /**
36950
37109
  * Gather I/O device children from a component's Three.js hierarchy.
36951
37110
  * Returns richer data including attachmentId and data point definitions.
@@ -36961,12 +37120,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36961
37120
  object.traverse(function (child) {
36962
37121
  var _child$userData;
36963
37122
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
36964
- var _this3$sceneViewer$ma, _this3$sceneViewer;
36965
37123
  var attachmentId = child.userData.attachmentId || '';
36966
37124
 
36967
37125
  // Use only data points from the animate window (behaviorConfig).
36968
37126
  // The static ioConfig.states[] snapshot on userData is intentionally ignored.
36969
- 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.ioBehaviorManager) === null || _this3$sceneViewer === void 0 ? void 0 : _this3$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this3$sceneViewer$ma !== void 0 ? _this3$sceneViewer$ma : [];
37127
+ var dataPoints = resolveDataPoints(parentUuid, attachmentId, child.userData, getIoBehaviorManager(_this3.sceneViewer));
36970
37128
 
36971
37129
  // When data points come from behaviorConfig they already carry direction:'input'.
36972
37130
  // Pass null so _buildDataPointRow uses the per-dp direction instead of the
@@ -36976,7 +37134,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
36976
37134
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
36977
37135
  deviceId: child.userData.deviceId || '',
36978
37136
  attachmentId: attachmentId,
36979
- scopedAttachmentId: _this3._getScopedAttachmentKey(attachmentId, parentUuid),
37137
+ scopedAttachmentId: getScopedAttachmentKey(attachmentId, parentUuid),
36980
37138
  dataPoints: dataPoints,
36981
37139
  direction: deviceDirection
36982
37140
  });
@@ -37122,11 +37280,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37122
37280
  }, {
37123
37281
  key: "_positionTooltip",
37124
37282
  value: function _positionTooltip() {
37125
- var _this$sceneViewer4, _this$sceneViewer5;
37283
+ var _this$sceneViewer5, _this$sceneViewer6;
37126
37284
  if (!this.tooltipEl || !this.selectedObject) return;
37127
37285
  var container = this._getContainer();
37128
- var camera = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.camera;
37129
- var renderer = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.renderer;
37286
+ var camera = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.camera;
37287
+ var renderer = (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.renderer;
37130
37288
  if (!container || !camera || !renderer) return;
37131
37289
 
37132
37290
  // Compute bounding box to position above the component
@@ -37163,8 +37321,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37163
37321
  }, {
37164
37322
  key: "_getContainer",
37165
37323
  value: function _getContainer() {
37166
- var _this$sceneViewer6;
37167
- 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;
37324
+ var _this$sceneViewer7;
37325
+ return ((_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 || (_this$sceneViewer7 = _this$sceneViewer7.renderer) === null || _this$sceneViewer7 === void 0 || (_this$sceneViewer7 = _this$sceneViewer7.domElement) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.parentElement) || null;
37168
37326
  }
37169
37327
 
37170
37328
  // -----------------------------------------------------------------------
@@ -37204,10 +37362,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37204
37362
  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;
37205
37363
  if (isInput) {
37206
37364
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
37207
- var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer;
37208
- (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
37365
+ var _this5$selectedObject;
37209
37366
  var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
37210
- (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.ioBehaviorManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
37367
+ _this5._dispatchIoState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
37211
37368
  });
37212
37369
  row.appendChild(ctrl);
37213
37370
  this._stateElements.set(key, {
@@ -37216,9 +37373,9 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37216
37373
  isInput: true
37217
37374
  });
37218
37375
  } else {
37219
- var _dp$stateConfig2;
37376
+ var _dp$stateConfig4;
37220
37377
  // unit suffix (optional, shown between name and badge)
37221
- var unit = (_dp$stateConfig2 = dp.stateConfig) === null || _dp$stateConfig2 === void 0 ? void 0 : _dp$stateConfig2.unit;
37378
+ var unit = (_dp$stateConfig4 = dp.stateConfig) === null || _dp$stateConfig4 === void 0 ? void 0 : _dp$stateConfig4.unit;
37222
37379
  if (unit) {
37223
37380
  var unitEl = document.createElement('span');
37224
37381
  unitEl.className = 'cp-tooltip__dp-unit';
@@ -37379,6 +37536,242 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37379
37536
  }]);
37380
37537
  }(BaseDisposable);
37381
37538
 
37539
+ /**
37540
+ * Shared behavior schema helpers for runtime normalization and UI authoring.
37541
+ * Used by IoBehaviorManager and sandbox BehaviorDialog.
37542
+ */
37543
+
37544
+ /**
37545
+ * Normalize behavior from shorthand to full format.
37546
+ * Supports:
37547
+ * - input: "attachment.state" → { attachment, state }
37548
+ * - outputs: ["attachment.state", ...] → converted to _outputs array
37549
+ *
37550
+ * @param {Object} behavior - Raw behavior from scene JSON
37551
+ * @returns {Object} Normalized behavior
37552
+ */
37553
+ function normalizeBehavior(behavior) {
37554
+ var normalized = _objectSpread2({}, behavior);
37555
+ if (typeof behavior.input === 'string') {
37556
+ var _behavior$input$split = behavior.input.split('.'),
37557
+ _behavior$input$split2 = _slicedToArray(_behavior$input$split, 2),
37558
+ attachment = _behavior$input$split2[0],
37559
+ state = _behavior$input$split2[1];
37560
+ normalized.input = {
37561
+ attachment: attachment,
37562
+ state: state
37563
+ };
37564
+ }
37565
+ if (behavior.outputs) {
37566
+ normalized._outputs = behavior.outputs.map(function (out) {
37567
+ if (typeof out === 'string') {
37568
+ var _out$split = out.split('.'),
37569
+ _out$split2 = _slicedToArray(_out$split, 2),
37570
+ _attachment = _out$split2[0],
37571
+ _state = _out$split2[1];
37572
+ return {
37573
+ attachment: _attachment,
37574
+ state: _state
37575
+ };
37576
+ }
37577
+ return out;
37578
+ });
37579
+ delete normalized.outputs;
37580
+ } else if (typeof behavior.output === 'string') {
37581
+ var _behavior$output$spli = behavior.output.split('.'),
37582
+ _behavior$output$spli2 = _slicedToArray(_behavior$output$spli, 2),
37583
+ _attachment2 = _behavior$output$spli2[0],
37584
+ _state2 = _behavior$output$spli2[1];
37585
+ normalized.output = {
37586
+ attachment: _attachment2,
37587
+ state: _state2
37588
+ };
37589
+ }
37590
+ return normalized;
37591
+ }
37592
+
37593
+ /**
37594
+ * Build intra-component shorthand behavior from form fields.
37595
+ */
37596
+ function buildIntraBehavior(_ref) {
37597
+ var id = _ref.id,
37598
+ inputAttachment = _ref.inputAttachment,
37599
+ inputState = _ref.inputState,
37600
+ outputs = _ref.outputs;
37601
+ return {
37602
+ id: id || "behavior-".concat(Date.now()),
37603
+ input: "".concat(inputAttachment, ".").concat(inputState),
37604
+ outputs: outputs.filter(function (o) {
37605
+ return o.attachment && o.state;
37606
+ }).map(function (o) {
37607
+ return "".concat(o.attachment, ".").concat(o.state);
37608
+ })
37609
+ };
37610
+ }
37611
+
37612
+ /**
37613
+ * Parse intra-component shorthand into form fields.
37614
+ */
37615
+ function parseIntraBehavior(behavior) {
37616
+ var inputAttachment = null;
37617
+ var inputState = null;
37618
+ if (typeof behavior.input === 'string') {
37619
+ var _behavior$input$split3 = behavior.input.split('.');
37620
+ var _behavior$input$split4 = _slicedToArray(_behavior$input$split3, 2);
37621
+ inputAttachment = _behavior$input$split4[0];
37622
+ inputState = _behavior$input$split4[1];
37623
+ } else if (behavior.input && _typeof(behavior.input) === 'object') {
37624
+ inputAttachment = behavior.input.attachment || null;
37625
+ inputState = behavior.input.state || null;
37626
+ }
37627
+ var outputs = (behavior.outputs || []).map(function (out) {
37628
+ if (typeof out === 'string') {
37629
+ var _out$split3 = out.split('.'),
37630
+ _out$split4 = _slicedToArray(_out$split3, 2),
37631
+ attachment = _out$split4[0],
37632
+ state = _out$split4[1];
37633
+ return {
37634
+ attachment: attachment,
37635
+ state: state
37636
+ };
37637
+ }
37638
+ return {
37639
+ attachment: (out === null || out === void 0 ? void 0 : out.attachment) || null,
37640
+ state: (out === null || out === void 0 ? void 0 : out.state) || null
37641
+ };
37642
+ });
37643
+ return {
37644
+ id: behavior.id || '',
37645
+ inputAttachment: inputAttachment || null,
37646
+ inputState: inputState || null,
37647
+ outputs: outputs.length ? outputs : [{
37648
+ attachment: null,
37649
+ state: null
37650
+ }]
37651
+ };
37652
+ }
37653
+
37654
+ /**
37655
+ * Build cross-component behavior with component instance scoping.
37656
+ */
37657
+ function buildCrossBehavior(_ref2) {
37658
+ var id = _ref2.id,
37659
+ inputEndpoint = _ref2.inputEndpoint,
37660
+ inputState = _ref2.inputState,
37661
+ outputs = _ref2.outputs;
37662
+ var _split = (inputEndpoint || '').split('::'),
37663
+ _split2 = _slicedToArray(_split, 2),
37664
+ inputComponent = _split2[0],
37665
+ inputAttachment = _split2[1];
37666
+ return {
37667
+ id: id || "behavior-".concat(Date.now()),
37668
+ input: {
37669
+ component: inputComponent,
37670
+ attachment: inputAttachment,
37671
+ state: inputState
37672
+ },
37673
+ outputs: outputs.filter(function (o) {
37674
+ return o.endpoint && o.state;
37675
+ }).map(function (o) {
37676
+ var _o$endpoint$split = o.endpoint.split('::'),
37677
+ _o$endpoint$split2 = _slicedToArray(_o$endpoint$split, 2),
37678
+ component = _o$endpoint$split2[0],
37679
+ attachment = _o$endpoint$split2[1];
37680
+ return {
37681
+ component: component,
37682
+ attachment: attachment,
37683
+ state: o.state
37684
+ };
37685
+ })
37686
+ };
37687
+ }
37688
+
37689
+ /**
37690
+ * Parse cross-component behavior into form fields.
37691
+ */
37692
+ function parseCrossBehavior(behavior) {
37693
+ var inputComponent, inputAttachment, inputState;
37694
+ if (typeof behavior.input === 'string') {
37695
+ var _behavior$input$split5 = behavior.input.split('.'),
37696
+ _behavior$input$split6 = _slicedToArray(_behavior$input$split5, 2),
37697
+ attachment = _behavior$input$split6[0],
37698
+ state = _behavior$input$split6[1];
37699
+ inputAttachment = attachment;
37700
+ inputState = state;
37701
+ inputComponent = null;
37702
+ } else if (behavior.input) {
37703
+ inputComponent = behavior.input.component;
37704
+ inputAttachment = behavior.input.attachment;
37705
+ inputState = behavior.input.state;
37706
+ }
37707
+ var rawOutputs = behavior.outputs || (behavior.output ? [behavior.output] : behavior._outputs || []);
37708
+ var outputs = rawOutputs.map(function (out) {
37709
+ if (typeof out === 'string') {
37710
+ var _out$split5 = out.split('.'),
37711
+ _out$split6 = _slicedToArray(_out$split5, 2),
37712
+ _attachment3 = _out$split6[0],
37713
+ _state3 = _out$split6[1];
37714
+ return {
37715
+ endpoint: _attachment3 ? "::".concat(_attachment3) : null,
37716
+ state: _state3
37717
+ };
37718
+ }
37719
+ var endpoint = out.component && out.attachment ? "".concat(out.component, "::").concat(out.attachment) : out.attachment ? "::".concat(out.attachment) : null;
37720
+ return {
37721
+ endpoint: endpoint,
37722
+ state: out.state || null
37723
+ };
37724
+ });
37725
+ var inputEndpoint = inputComponent && inputAttachment ? "".concat(inputComponent, "::").concat(inputAttachment) : inputAttachment ? "::".concat(inputAttachment) : null;
37726
+ return {
37727
+ id: behavior.id || '',
37728
+ inputEndpoint: inputEndpoint,
37729
+ inputState: inputState || null,
37730
+ outputs: outputs.length ? outputs : [{
37731
+ endpoint: null,
37732
+ state: null
37733
+ }]
37734
+ };
37735
+ }
37736
+
37737
+ /**
37738
+ * Translation helpers for I/O device animations.
37739
+ * Offsets are expressed in the device/model root's local space so every child
37740
+ * mesh moves along the same predictable axes regardless of intermediate parents.
37741
+ */
37742
+
37743
+ /**
37744
+ * @param {THREE.Object3D} modelRoot
37745
+ * @param {{ x?: number, y?: number, z?: number }|THREE.Vector3} offset
37746
+ * @returns {THREE.Vector3}
37747
+ */
37748
+ function modelOffsetToWorldDelta(modelRoot, offset) {
37749
+ var _offset$x, _offset$y, _offset$z;
37750
+ var v = offset instanceof THREE__namespace.Vector3 ? offset.clone() : new THREE__namespace.Vector3((_offset$x = offset === null || offset === void 0 ? void 0 : offset.x) !== null && _offset$x !== void 0 ? _offset$x : 0, (_offset$y = offset === null || offset === void 0 ? void 0 : offset.y) !== null && _offset$y !== void 0 ? _offset$y : 0, (_offset$z = offset === null || offset === void 0 ? void 0 : offset.z) !== null && _offset$z !== void 0 ? _offset$z : 0);
37751
+ var q = new THREE__namespace.Quaternion();
37752
+ modelRoot.getWorldQuaternion(q);
37753
+ return v.applyQuaternion(q);
37754
+ }
37755
+
37756
+ /**
37757
+ * Apply a model-root-space translation from a mesh's rest local pose.
37758
+ *
37759
+ * @param {THREE.Object3D} mesh
37760
+ * @param {THREE.Object3D} modelRoot
37761
+ * @param {THREE.Vector3} origLocalPos
37762
+ * @param {{ x?: number, y?: number, z?: number }} modelOffset
37763
+ */
37764
+ function applyModelRootTranslation(mesh, modelRoot, origLocalPos, modelOffset) {
37765
+ if (!mesh || !modelRoot || !origLocalPos) return;
37766
+ mesh.position.copy(origLocalPos);
37767
+ mesh.updateMatrixWorld(true);
37768
+ var origWorldPos = new THREE__namespace.Vector3();
37769
+ mesh.getWorldPosition(origWorldPos);
37770
+ var newWorldPos = origWorldPos.add(modelOffsetToWorldDelta(modelRoot, modelOffset));
37771
+ if (mesh.parent) mesh.parent.worldToLocal(newWorldPos);
37772
+ mesh.position.copy(newWorldPos);
37773
+ }
37774
+
37382
37775
  var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37383
37776
  function IoBehaviorManager(sceneViewer) {
37384
37777
  var _this;
@@ -37396,12 +37789,11 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37396
37789
  */
37397
37790
  _this._entries = new Map();
37398
37791
  _this._crossComponentBehaviors = [];
37399
-
37400
- /**
37401
- * Map: `${componentUuid}` → Array<{ id, input, outputs }>
37402
- * Component-level behaviors for intra-component io-device linking
37403
- */
37404
37792
  _this._componentBehaviors = new Map();
37793
+ /** @type {Map<string, string>} parentUuid::attachmentId → parentUuid */
37794
+ _this._attachmentParentMap = new Map();
37795
+ /** @type {Map<string, Object[]>} cache key → data point definitions */
37796
+ _this._dataPointsCache = new Map();
37405
37797
 
37406
37798
  /**
37407
37799
  * Injected by the host application to read and write I/O device state.
@@ -37491,7 +37883,10 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37491
37883
  }
37492
37884
  if (entries.length) {
37493
37885
  this._entries.set(key, entries);
37494
- // Loaded ${entries.length} animation(s) for attachment "${attachmentId}"
37886
+ if (parentUuid && attachmentId) {
37887
+ this._attachmentParentMap.set(this._key(parentUuid, attachmentId), parentUuid);
37888
+ }
37889
+ this._invalidateDataPointsCache(parentUuid, attachmentId);
37495
37890
  } else {
37496
37891
  console.warn("[IoBehaviorManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 behaviorConfig had ").concat(anims.length, " entries but none matched a mesh"));
37497
37892
  }
@@ -37499,7 +37894,6 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37499
37894
 
37500
37895
  /**
37501
37896
  * Apply animations triggered by an IO device state change.
37502
- * Should be called in parallel with BehaviorManager.triggerState().
37503
37897
  *
37504
37898
  * @param {string} attachmentId - Raw attachment key (not scoped)
37505
37899
  * @param {string} dataPointId - The data point / state variable id that changed
@@ -37509,70 +37903,36 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37509
37903
  }, {
37510
37904
  key: "triggerState",
37511
37905
  value: function triggerState(attachmentId, dataPointId, value, parentUuid) {
37512
- // triggerState: ${attachmentId}.${dataPointId} = ${value}
37513
-
37514
- if (parentUuid) {
37515
- var key = this._key(parentUuid, attachmentId);
37516
- var entries = this._entries.get(key);
37517
- if (entries) {
37518
- var _iterator2 = _createForOfIteratorHelper(entries),
37519
- _step2;
37520
- try {
37521
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37522
- var entry = _step2.value;
37523
- if (entry.anim.stateVariable !== dataPointId) continue;
37524
- this._applyAnimation(entry, value);
37525
- }
37526
- } catch (err) {
37527
- _iterator2.e(err);
37528
- } finally {
37529
- _iterator2.f();
37530
- }
37531
- }
37532
- } else {
37533
- // Fallback when parentUuid is not provided: match by attachmentId suffix or exact key match
37534
- var suffix = "::".concat(attachmentId);
37535
- var _iterator3 = _createForOfIteratorHelper(this._entries.entries()),
37536
- _step3;
37906
+ var _this$_crossComponent;
37907
+ var resolvedParent = parentUuid || this._findComponentByAttachment(attachmentId);
37908
+ if (!resolvedParent) return;
37909
+ var key = this._key(resolvedParent, attachmentId);
37910
+ var entries = this._entries.get(key);
37911
+ if (entries) {
37912
+ var _iterator2 = _createForOfIteratorHelper(entries),
37913
+ _step2;
37537
37914
  try {
37538
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
37539
- var _step3$value = _slicedToArray(_step3.value, 2),
37540
- _key2 = _step3$value[0],
37541
- _entries = _step3$value[1];
37542
- if (_key2 === attachmentId || _key2.endsWith(suffix)) {
37543
- var _iterator4 = _createForOfIteratorHelper(_entries),
37544
- _step4;
37545
- try {
37546
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
37547
- var _entry = _step4.value;
37548
- if (_entry.anim.stateVariable !== dataPointId) continue;
37549
- this._applyAnimation(_entry, value);
37550
- }
37551
- } catch (err) {
37552
- _iterator4.e(err);
37553
- } finally {
37554
- _iterator4.f();
37555
- }
37556
- }
37915
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
37916
+ var entry = _step2.value;
37917
+ if (entry.anim.stateVariable !== dataPointId) continue;
37918
+ this._applyAnimation(entry, value);
37557
37919
  }
37558
37920
  } catch (err) {
37559
- _iterator3.e(err);
37921
+ _iterator2.e(err);
37560
37922
  } finally {
37561
- _iterator3.f();
37923
+ _iterator2.f();
37562
37924
  }
37563
37925
  }
37564
37926
 
37565
37927
  // Evaluate component-level behaviors (intra-component io-device linking)
37566
- if (parentUuid && this._componentBehaviors.has(parentUuid)) {
37567
- var componentBehaviors = this._componentBehaviors.get(parentUuid);
37568
- // Checking component-level behaviors (count: ${componentBehaviors.length})
37569
- this.triggerCrossComponentBehaviors(componentBehaviors, parentUuid, attachmentId, dataPointId, value);
37928
+ if (this._componentBehaviors.has(resolvedParent)) {
37929
+ var componentBehaviors = this._componentBehaviors.get(resolvedParent);
37930
+ this.triggerCrossComponentBehaviors(componentBehaviors, resolvedParent, attachmentId, dataPointId, value, {
37931
+ sameComponent: true
37932
+ });
37570
37933
  }
37571
-
37572
- // Evaluate cross-component behaviors if any are registered
37573
- // Checking cross-component behaviors (count: ${this._crossComponentBehaviors?.length || 0})
37574
- if (this._crossComponentBehaviors && this._crossComponentBehaviors.length > 0) {
37575
- this.triggerCrossComponentBehaviors(this._crossComponentBehaviors, parentUuid, attachmentId, dataPointId, value);
37934
+ if (((_this$_crossComponent = this._crossComponentBehaviors) === null || _this$_crossComponent === void 0 ? void 0 : _this$_crossComponent.length) > 0) {
37935
+ this.triggerCrossComponentBehaviors(this._crossComponentBehaviors, resolvedParent, attachmentId, dataPointId, value);
37576
37936
  }
37577
37937
  }
37578
37938
 
@@ -37585,9 +37945,8 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37585
37945
  }, {
37586
37946
  key: "setCrossComponentBehaviors",
37587
37947
  value: function setCrossComponentBehaviors(behaviors) {
37588
- var _this2 = this;
37589
37948
  this._crossComponentBehaviors = (behaviors || []).map(function (b) {
37590
- return _this2._normalizeBehavior(b);
37949
+ return normalizeBehavior(b);
37591
37950
  });
37592
37951
  // Loaded ${this._crossComponentBehaviors.length} cross-component behavior(s)
37593
37952
  }
@@ -37602,72 +37961,17 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37602
37961
  }, {
37603
37962
  key: "registerComponentBehaviors",
37604
37963
  value: function registerComponentBehaviors(componentUuid, behaviors) {
37605
- var _this3 = this;
37606
- if (!behaviors || behaviors.length === 0) return;
37964
+ if (!behaviors || behaviors.length === 0) {
37965
+ this._componentBehaviors.delete(componentUuid);
37966
+ return;
37967
+ }
37607
37968
  var normalized = behaviors.map(function (b) {
37608
- return _this3._normalizeBehavior(b);
37969
+ return normalizeBehavior(b);
37609
37970
  });
37610
37971
  this._componentBehaviors.set(componentUuid, normalized);
37611
37972
  // Registered ${normalized.length} component-level behavior(s) for component ${componentUuid}
37612
37973
  }
37613
37974
 
37614
- /**
37615
- * Normalize behavior from shorthand to full format.
37616
- * Supports:
37617
- * - input: "attachment.state" → { attachment, state }
37618
- * - outputs: ["attachment.state", ...] → converted to individual behaviors
37619
- *
37620
- * @param {Object} behavior - Raw behavior from scene JSON
37621
- * @returns {Object} Normalized behavior
37622
- */
37623
- }, {
37624
- key: "_normalizeBehavior",
37625
- value: function _normalizeBehavior(behavior) {
37626
- var normalized = _objectSpread2({}, behavior);
37627
-
37628
- // Parse shorthand input: "attachment.state"
37629
- if (typeof behavior.input === 'string') {
37630
- var _behavior$input$split = behavior.input.split('.'),
37631
- _behavior$input$split2 = _slicedToArray(_behavior$input$split, 2),
37632
- attachment = _behavior$input$split2[0],
37633
- state = _behavior$input$split2[1];
37634
- normalized.input = {
37635
- attachment: attachment,
37636
- state: state
37637
- };
37638
- }
37639
-
37640
- // Parse shorthand output/outputs
37641
- if (behavior.outputs) {
37642
- // Multiple outputs - expand to array
37643
- normalized._outputs = behavior.outputs.map(function (out) {
37644
- if (typeof out === 'string') {
37645
- var _out$split = out.split('.'),
37646
- _out$split2 = _slicedToArray(_out$split, 2),
37647
- _attachment = _out$split2[0],
37648
- _state = _out$split2[1];
37649
- return {
37650
- attachment: _attachment,
37651
- state: _state
37652
- };
37653
- }
37654
- return out;
37655
- });
37656
- delete normalized.outputs;
37657
- } else if (typeof behavior.output === 'string') {
37658
- // Single output string
37659
- var _behavior$output$spli = behavior.output.split('.'),
37660
- _behavior$output$spli2 = _slicedToArray(_behavior$output$spli, 2),
37661
- _attachment2 = _behavior$output$spli2[0],
37662
- _state2 = _behavior$output$spli2[1];
37663
- normalized.output = {
37664
- attachment: _attachment2,
37665
- state: _state2
37666
- };
37667
- }
37668
- return normalized;
37669
- }
37670
-
37671
37975
  /**
37672
37976
  * Find the parent component UUID for a given attachment ID.
37673
37977
  * Searches the scene tree for the io-device with this attachment ID,
@@ -37681,12 +37985,10 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37681
37985
  value: function _findComponentByAttachment(attachmentId) {
37682
37986
  var _this$sceneViewer;
37683
37987
  if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.scene)) return null;
37684
- var scene = this.sceneViewer.scene;
37685
37988
  var found = null;
37686
- scene.traverse(function (obj) {
37989
+ this.sceneViewer.scene.traverse(function (obj) {
37687
37990
  var _obj$userData, _obj$userData2;
37688
37991
  if (found) return;
37689
- // Find the io-device object with this attachmentId
37690
37992
  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) {
37691
37993
  found = obj.userData.parentComponentId;
37692
37994
  }
@@ -37730,20 +38032,21 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37730
38032
  * @param {string} triggerAttachmentId - Attachment ID of the triggering device
37731
38033
  * @param {string} triggerStateId - The state variable ID that changed
37732
38034
  * @param {*} value - The new state value
38035
+ * @param {{ sameComponent?: boolean }} [options] - Intra-component links share one parent instance
37733
38036
  */
37734
38037
  }, {
37735
38038
  key: "triggerCrossComponentBehaviors",
37736
38039
  value: function triggerCrossComponentBehaviors(behaviors, triggerParentUuid, triggerAttachmentId, triggerStateId, value) {
38040
+ var options = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
37737
38041
  if (!behaviors || !Array.isArray(behaviors)) {
37738
38042
  return;
37739
38043
  }
37740
-
37741
- // Evaluating ${behaviors.length} behavior(s)
37742
- var _iterator5 = _createForOfIteratorHelper(behaviors),
37743
- _step5;
38044
+ var sameComponent = options.sameComponent === true;
38045
+ var _iterator3 = _createForOfIteratorHelper(behaviors),
38046
+ _step3;
37744
38047
  try {
37745
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
37746
- var behavior = _step5.value;
38048
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
38049
+ var behavior = _step3.value;
37747
38050
  var input = behavior.input,
37748
38051
  output = behavior.output,
37749
38052
  _outputs = behavior._outputs,
@@ -37752,94 +38055,69 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37752
38055
  console.warn('[Behavior] Skipping behavior - missing input or output(s):', behavior);
37753
38056
  continue;
37754
38057
  }
37755
-
37756
- // Auto-lookup component if not specified
37757
- var inputComponent = input.component || this._findComponentByAttachment(input.attachment);
37758
- console.log("[Behavior] Checking behavior \"".concat(behavior.id, "\":"), {
37759
- inputMatch: inputComponent === triggerParentUuid,
37760
- attachmentMatch: input.attachment === triggerAttachmentId,
37761
- stateMatch: input.state === triggerStateId,
37762
- expected: {
37763
- component: inputComponent,
37764
- attachment: input.attachment,
37765
- state: input.state
37766
- },
37767
- actual: {
37768
- component: triggerParentUuid,
37769
- attachment: triggerAttachmentId,
37770
- state: triggerStateId
37771
- }
37772
- });
37773
-
37774
- // Verify that the input matches the triggering source
37775
- if (inputComponent === triggerParentUuid && input.attachment === triggerAttachmentId && input.state === triggerStateId) {
38058
+ var inputComponent = input.component || (sameComponent ? triggerParentUuid : this._findComponentByAttachment(input.attachment));
38059
+ var inputMatches = sameComponent ? input.attachment === triggerAttachmentId && input.state === triggerStateId : inputComponent === triggerParentUuid && input.attachment === triggerAttachmentId && input.state === triggerStateId;
38060
+ if (inputMatches) {
37776
38061
  // Behavior "${behavior.id}" matched
37777
38062
 
37778
38063
  // Collect all outputs (single or multiple)
37779
38064
  var outputs = _outputs || (output ? [output] : []);
37780
38065
 
37781
38066
  // Process each output
37782
- var _iterator6 = _createForOfIteratorHelper(outputs),
37783
- _step6;
38067
+ var _iterator4 = _createForOfIteratorHelper(outputs),
38068
+ _step4;
37784
38069
  try {
37785
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
37786
- var out = _step6.value;
38070
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
38071
+ var out = _step4.value;
37787
38072
  // NEW: State-to-state pass-through pattern
37788
38073
  if (out.state) {
37789
- // Auto-lookup output component if not specified
37790
- var outputComponent = out.component || this._findComponentByAttachment(out.attachment);
37791
-
37792
- // Direct state mapping without conditions
38074
+ var outputComponent = out.component || (sameComponent ? triggerParentUuid : this._findComponentByAttachment(out.attachment));
37793
38075
  if (this._stateAdapter) {
37794
- // Dispatching state-to-state: ${out.attachment}.${out.state} = ${value}
37795
- this._stateAdapter.setState(out.attachment, out.state, value);
37796
-
37797
- // Trigger animations on the output component
37798
- // triggerState(attachmentId, dataPointId, value, parentUuid)
38076
+ var scopedKey = getScopedAttachmentKey(out.attachment, outputComponent);
38077
+ this._stateAdapter.setState(scopedKey, out.state, value);
37799
38078
  if (outputComponent) {
37800
- // Triggering animations on output component
37801
38079
  this.triggerState(out.attachment, out.state, value, outputComponent);
37802
38080
  } else {
37803
38081
  console.warn("[Behavior] Could not find component for attachment \"".concat(out.attachment, "\""));
37804
38082
  }
37805
38083
  } else {
37806
- console.warn('[Behavior] State adapter not configured for state-to-state behavior');
38084
+ console.warn('[Behavior] State adapter not configured for state-to-state behavior');
37807
38085
  }
37808
38086
  }
37809
38087
  // LEGACY: Mesh-based pattern with conditions
37810
38088
  else if (conditions && out.child) {
37811
38089
  // Using legacy mesh-based pattern with conditions
37812
38090
  // Evaluate conditions
37813
- var _iterator7 = _createForOfIteratorHelper(conditions),
37814
- _step7;
38091
+ var _iterator5 = _createForOfIteratorHelper(conditions),
38092
+ _step5;
37815
38093
  try {
37816
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
37817
- var condition = _step7.value;
38094
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
38095
+ var condition = _step5.value;
37818
38096
  if (this._evaluateCondition(condition.when, value)) {
37819
38097
  // Apply actions to the target output component/attachment/child mesh
37820
38098
  this._applyCrossComponentActions(out, condition.actions);
37821
38099
  }
37822
38100
  }
37823
38101
  } catch (err) {
37824
- _iterator7.e(err);
38102
+ _iterator5.e(err);
37825
38103
  } finally {
37826
- _iterator7.f();
38104
+ _iterator5.f();
37827
38105
  }
37828
38106
  } else {
37829
38107
  console.warn('[Behavior] Output has neither state nor (child + conditions):', out);
37830
38108
  }
37831
38109
  } // end outputs loop
37832
38110
  } catch (err) {
37833
- _iterator6.e(err);
38111
+ _iterator4.e(err);
37834
38112
  } finally {
37835
- _iterator6.f();
38113
+ _iterator4.f();
37836
38114
  }
37837
38115
  }
37838
38116
  }
37839
38117
  } catch (err) {
37840
- _iterator5.e(err);
38118
+ _iterator3.e(err);
37841
38119
  } finally {
37842
- _iterator5.f();
38120
+ _iterator3.f();
37843
38121
  }
37844
38122
  }
37845
38123
 
@@ -37909,17 +38187,17 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37909
38187
  }
37910
38188
 
37911
38189
  // 4. Apply actions to targetObj
37912
- var _iterator8 = _createForOfIteratorHelper(actions),
37913
- _step8;
38190
+ var _iterator6 = _createForOfIteratorHelper(actions),
38191
+ _step6;
37914
38192
  try {
37915
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
37916
- var action = _step8.value;
38193
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
38194
+ var action = _step6.value;
37917
38195
  this._applyCrossComponentAction(targetObj, action);
37918
38196
  }
37919
38197
  } catch (err) {
37920
- _iterator8.e(err);
38198
+ _iterator6.e(err);
37921
38199
  } finally {
37922
- _iterator8.f();
38200
+ _iterator6.f();
37923
38201
  }
37924
38202
  }
37925
38203
 
@@ -38011,8 +38289,12 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38011
38289
  }, {
38012
38290
  key: "getAnimationDataPoints",
38013
38291
  value: function getAnimationDataPoints(parentUuid, attachmentId) {
38014
- var _this4 = this;
38292
+ var _this2 = this;
38015
38293
  var hitMesh = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
38294
+ var cacheKey = "".concat(this._key(parentUuid, attachmentId), "::").concat((hitMesh === null || hitMesh === void 0 ? void 0 : hitMesh.uuid) || '');
38295
+ if (this._dataPointsCache.has(cacheKey)) {
38296
+ return this._dataPointsCache.get(cacheKey);
38297
+ }
38016
38298
  var key = this._key(parentUuid, attachmentId);
38017
38299
  var entries = this._entries.get(key);
38018
38300
  if (!(entries !== null && entries !== void 0 && entries.length)) return [];
@@ -38023,35 +38305,35 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38023
38305
  var filtered = entries;
38024
38306
  if (hitMesh) {
38025
38307
  var matching = entries.filter(function (e) {
38026
- return _this4._isMeshOrDescendant(hitMesh, e.mesh);
38308
+ return _this2._isMeshOrDescendant(hitMesh, e.mesh);
38027
38309
  });
38028
38310
  if (matching.length > 0) filtered = matching;
38029
38311
  }
38030
38312
 
38031
38313
  // Collapse multiple mesh entries that share the same stateVariable
38032
38314
  var seen = new Map(); // stateVariable → anim
38033
- var _iterator9 = _createForOfIteratorHelper(filtered),
38034
- _step9;
38315
+ var _iterator7 = _createForOfIteratorHelper(filtered),
38316
+ _step7;
38035
38317
  try {
38036
- for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
38037
- var anim = _step9.value.anim;
38318
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
38319
+ var anim = _step7.value.anim;
38038
38320
  if (!seen.has(anim.stateVariable)) {
38039
38321
  seen.set(anim.stateVariable, anim);
38040
38322
  }
38041
38323
  }
38042
38324
  } catch (err) {
38043
- _iterator9.e(err);
38325
+ _iterator7.e(err);
38044
38326
  } finally {
38045
- _iterator9.f();
38327
+ _iterator7.f();
38046
38328
  }
38047
38329
  var dps = [];
38048
- var _iterator0 = _createForOfIteratorHelper(seen),
38049
- _step0;
38330
+ var _iterator8 = _createForOfIteratorHelper(seen),
38331
+ _step8;
38050
38332
  try {
38051
- for (_iterator0.s(); !(_step0 = _iterator0.n()).done;) {
38052
- var _step0$value = _slicedToArray(_step0.value, 2),
38053
- stateVar = _step0$value[0],
38054
- _anim = _step0$value[1];
38333
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
38334
+ var _step8$value = _slicedToArray(_step8.value, 2),
38335
+ stateVar = _step8$value[0],
38336
+ _anim = _step8$value[1];
38055
38337
  // Normalise stateType from AnimateDevicesDialog variants
38056
38338
  var stateType = void 0;
38057
38339
  var raw = (_anim.stateType || '').toLowerCase();
@@ -38102,10 +38384,11 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38102
38384
  });
38103
38385
  }
38104
38386
  } catch (err) {
38105
- _iterator0.e(err);
38387
+ _iterator8.e(err);
38106
38388
  } finally {
38107
- _iterator0.f();
38389
+ _iterator8.f();
38108
38390
  }
38391
+ this._dataPointsCache.set(cacheKey, dps);
38109
38392
  return dps;
38110
38393
  }
38111
38394
 
@@ -38139,25 +38422,46 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38139
38422
  key: "unloadForComponent",
38140
38423
  value: function unloadForComponent(parentUuid) {
38141
38424
  var prefix = "".concat(parentUuid, "::");
38142
- var _iterator1 = _createForOfIteratorHelper(this._entries.keys()),
38143
- _step1;
38144
- try {
38145
- for (_iterator1.s(); !(_step1 = _iterator1.n()).done;) {
38146
- var key = _step1.value;
38147
- if (key.startsWith(prefix)) {
38148
- this._entries.delete(key);
38149
- }
38425
+ for (var _i = 0, _arr = _toConsumableArray(this._entries.keys()); _i < _arr.length; _i++) {
38426
+ var key = _arr[_i];
38427
+ if (key.startsWith(prefix)) {
38428
+ this._attachmentParentMap.delete(key);
38429
+ this._entries.delete(key);
38150
38430
  }
38151
- } catch (err) {
38152
- _iterator1.e(err);
38153
- } finally {
38154
- _iterator1.f();
38155
38431
  }
38432
+ this._componentBehaviors.delete(parentUuid);
38433
+ this._invalidateDataPointsCacheForParent(parentUuid);
38434
+ }
38435
+
38436
+ /**
38437
+ * Remove animation entries for a single attachment on a component instance.
38438
+ */
38439
+ }, {
38440
+ key: "unloadForAttachment",
38441
+ value: function unloadForAttachment(parentUuid, attachmentId) {
38442
+ var key = this._key(parentUuid, attachmentId);
38443
+ this._entries.delete(key);
38444
+ this._attachmentParentMap.delete(key);
38445
+ this._invalidateDataPointsCache(parentUuid, attachmentId);
38446
+ }
38447
+
38448
+ /**
38449
+ * Clear all runtime behavior state when the scene is cleared or replaced.
38450
+ */
38451
+ }, {
38452
+ key: "resetForScene",
38453
+ value: function resetForScene() {
38454
+ this._entries.clear();
38455
+ this._componentBehaviors.clear();
38456
+ this._crossComponentBehaviors = [];
38457
+ this._attachmentParentMap.clear();
38458
+ this._dataPointsCache.clear();
38156
38459
  }
38157
38460
  }, {
38158
38461
  key: "dispose",
38159
38462
  value: function dispose() {
38160
- this._entries.clear();
38463
+ this.resetForScene();
38464
+ this._stateAdapter = null;
38161
38465
  _superPropGet(IoBehaviorManager, "dispose", this, 3)([]);
38162
38466
  }
38163
38467
 
@@ -38224,20 +38528,20 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38224
38528
  key: "_applyAnimation",
38225
38529
  value: function _applyAnimation(entry, value) {
38226
38530
  var anim = entry.anim,
38227
- mesh = entry.mesh,
38228
- origPos = entry.origPos;
38531
+ mesh = entry.mesh;
38532
+ entry.origPos;
38229
38533
  entry.origRot;
38230
38534
  var viewerMaxDim = entry.viewerMaxDim;
38231
38535
  var mapping = this._resolveMapping(anim, value);
38232
38536
  if (!mapping) return;
38233
38537
  var types = anim.transformTypes || [];
38234
- var _iterator10 = _createForOfIteratorHelper(types),
38235
- _step10;
38538
+ var _iterator9 = _createForOfIteratorHelper(types),
38539
+ _step9;
38236
38540
  try {
38237
- for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
38238
- var type = _step10.value;
38541
+ for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
38542
+ var type = _step9.value;
38239
38543
  if (type === 'translation') {
38240
- this._applyTranslation(mesh, origPos, mapping.transform);
38544
+ this._applyTranslation(entry, mapping.transform);
38241
38545
  } else if (type === 'rotation') {
38242
38546
  this._applyRotation(entry, anim, mapping.rotationTransform, viewerMaxDim);
38243
38547
  } else if (type === 'color') {
@@ -38245,9 +38549,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38245
38549
  }
38246
38550
  }
38247
38551
  } catch (err) {
38248
- _iterator10.e(err);
38552
+ _iterator9.e(err);
38249
38553
  } finally {
38250
- _iterator10.f();
38554
+ _iterator9.f();
38251
38555
  }
38252
38556
  }
38253
38557
 
@@ -38367,20 +38671,21 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38367
38671
  // ─────────────────────────────────────────────────────────────────────────
38368
38672
 
38369
38673
  /**
38370
- * Apply a position delta relative to the mesh's original position.
38371
- * @param {THREE.Object3D} mesh
38372
- * @param {THREE.Vector3} origPos
38373
- * @param {{ x, y, z }} transform - Deltas
38674
+ * Apply a translation offset in device-model-root space so every animated mesh
38675
+ * shares the same axis directions regardless of intermediate parent transforms.
38676
+ *
38677
+ * @param {{ mesh, origPos, deviceModelRoot }} entry
38678
+ * @param {{ x, y, z }} transform - Offset in model-root local space
38374
38679
  */
38375
38680
  }, {
38376
38681
  key: "_applyTranslation",
38377
- value: function _applyTranslation(mesh, origPos, transform) {
38378
- var _transform$x, _transform$y, _transform$z;
38682
+ value: function _applyTranslation(entry, transform) {
38379
38683
  if (!transform) return;
38380
- // X and Y are negated to match the sign convention used in the AnimateDevicesDialog
38381
- // preview (_syncViewerTransform negates x and y before calling setMeshPreviewOffset).
38382
- // Z is added directly (no negation) — matching the dialog's z handling.
38383
- mesh.position.set(origPos.x - ((_transform$x = transform.x) !== null && _transform$x !== void 0 ? _transform$x : 0), origPos.y - ((_transform$y = transform.y) !== null && _transform$y !== void 0 ? _transform$y : 0), origPos.z + ((_transform$z = transform.z) !== null && _transform$z !== void 0 ? _transform$z : 0));
38684
+ var mesh = entry.mesh,
38685
+ origPos = entry.origPos,
38686
+ deviceModelRoot = entry.deviceModelRoot;
38687
+ if (!mesh || !origPos || !deviceModelRoot) return;
38688
+ applyModelRootTranslation(mesh, deviceModelRoot, origPos, transform);
38384
38689
  }
38385
38690
 
38386
38691
  /**
@@ -38399,6 +38704,9 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38399
38704
  origRot = entry.origRot,
38400
38705
  deviceModelRoot = entry.deviceModelRoot;
38401
38706
  if (!mesh || !origPos || !origRot) return null;
38707
+ if (entry._restWorldCache) {
38708
+ return entry._restWorldCache;
38709
+ }
38402
38710
  var savedPos = mesh.position.clone();
38403
38711
  var savedQuat = mesh.quaternion.clone();
38404
38712
  mesh.position.copy(origPos);
@@ -38417,12 +38725,35 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38417
38725
  }
38418
38726
  mesh.position.copy(savedPos);
38419
38727
  mesh.quaternion.copy(savedQuat);
38420
- return {
38728
+ entry._restWorldCache = {
38421
38729
  origWorldPos: origWorldPos,
38422
38730
  origWorldQuat: origWorldQuat,
38423
38731
  origWorldCenter: origWorldCenter,
38424
38732
  deviceWorldQuat: deviceWorldQuat
38425
38733
  };
38734
+ return entry._restWorldCache;
38735
+ }
38736
+ }, {
38737
+ key: "_invalidateDataPointsCache",
38738
+ value: function _invalidateDataPointsCache(parentUuid, attachmentId) {
38739
+ var prefix = "".concat(this._key(parentUuid, attachmentId), "::");
38740
+ for (var _i2 = 0, _arr2 = _toConsumableArray(this._dataPointsCache.keys()); _i2 < _arr2.length; _i2++) {
38741
+ var key = _arr2[_i2];
38742
+ if (key.startsWith(prefix)) {
38743
+ this._dataPointsCache.delete(key);
38744
+ }
38745
+ }
38746
+ }
38747
+ }, {
38748
+ key: "_invalidateDataPointsCacheForParent",
38749
+ value: function _invalidateDataPointsCacheForParent(parentUuid) {
38750
+ var prefix = "".concat(parentUuid, "::");
38751
+ for (var _i3 = 0, _arr3 = _toConsumableArray(this._dataPointsCache.keys()); _i3 < _arr3.length; _i3++) {
38752
+ var key = _arr3[_i3];
38753
+ if (key.startsWith(prefix)) {
38754
+ this._dataPointsCache.delete(key);
38755
+ }
38756
+ }
38426
38757
  }
38427
38758
 
38428
38759
  /**
@@ -38955,6 +39286,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
38955
39286
  // Initialize the component tooltip manager (screen-space tooltip on selection)
38956
39287
  this.centralPlant.managers.componentTooltipManager = new ComponentTooltipManager(this.centralPlant.sceneViewer);
38957
39288
  this.centralPlant.sceneViewer.componentTooltipManager = this.centralPlant.managers.componentTooltipManager;
39289
+ this.centralPlant.sceneViewer.managers.componentTooltipManager = this.centralPlant.managers.componentTooltipManager;
38958
39290
  }
38959
39291
  }
38960
39292
 
@@ -39863,37 +40195,14 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39863
40195
  // Add attached IO device models for smart components
39864
40196
  if (componentData.isSmart && componentData.attachedDevices) {
39865
40197
  var _this$centralPlant$sc8;
39866
- attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
39867
-
39868
- // Register behavior configs so IoBehaviorManager can respond to state changes
39869
40198
  var ioBehavMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioBehaviorManager;
39870
- if (ioBehavMgr) {
39871
- var _loop = function _loop() {
39872
- var _modelPreloader$compo;
39873
- var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
39874
- attachmentId = _Object$entries$_i[0],
39875
- attachment = _Object$entries$_i[1];
39876
- var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
39877
- if (!(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return 1; // continue
39878
- var deviceRoot = null;
39879
- componentModel.traverse(function (obj) {
39880
- var _obj$userData2;
39881
- if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
39882
- });
39883
- if (deviceRoot) {
39884
- ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, componentId);
39885
- }
39886
- };
39887
- for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
39888
- if (_loop()) continue;
39889
- }
39890
-
39891
- // Register component-level behaviors (intra-component io-device linking)
39892
- if (componentData.behaviors && componentData.behaviors.length > 0) {
39893
- // Registering ${componentData.behaviors.length} component-level behavior(s)
39894
- ioBehavMgr.registerComponentBehaviors(componentId, componentData.behaviors);
40199
+ attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId).then(function () {
40200
+ if (ioBehavMgr) {
40201
+ registerBehaviorsForComponent(ioBehavMgr, componentId, componentData, componentModel, modelPreloader.componentDictionary);
39895
40202
  }
39896
- }
40203
+ }).catch(function (err) {
40204
+ console.error("\u274C Error attaching IO devices for ".concat(libraryId, ":"), err);
40205
+ });
39897
40206
  }
39898
40207
 
39899
40208
  // Notify the component manager about the new component
@@ -40023,8 +40332,8 @@ var CentralPlantInternals = /*#__PURE__*/function () {
40023
40332
  // the component (e.g., dynamically added but not yet synced to sceneData)
40024
40333
  if (connectorIds.size === 0 && threeScene) {
40025
40334
  threeScene.traverse(function (obj) {
40026
- var _obj$userData3, _obj$userData4;
40027
- if ((obj.uuid === resolvedUuid || ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.originalUuid) === resolvedUuid) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'component') {
40335
+ var _obj$userData2, _obj$userData3;
40336
+ if ((obj.uuid === resolvedUuid || ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.originalUuid) === resolvedUuid) && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'component') {
40028
40337
  obj.children.forEach(function (child) {
40029
40338
  var _child$userData3;
40030
40339
  if (((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'connector') {
@@ -40068,18 +40377,23 @@ var CentralPlantInternals = /*#__PURE__*/function () {
40068
40377
  // Step 5: Remove the component from the Three.js scene
40069
40378
  var success = componentManager.removeComponentFromScene(componentId);
40070
40379
  if (success) {
40380
+ var _this$centralPlant$sc10;
40381
+ var ioBehavMgr = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 || (_this$centralPlant$sc10 = _this$centralPlant$sc10.managers) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.ioBehaviorManager;
40382
+ if (ioBehavMgr !== null && ioBehavMgr !== void 0 && ioBehavMgr.unloadForComponent) {
40383
+ ioBehavMgr.unloadForComponent(resolvedUuid);
40384
+ }
40071
40385
  // Step 6: Directly remove SEGMENT-* and Gateway objects from Three.js whose
40072
40386
  // pathFrom/pathTo references one of this component's connectors.
40073
40387
  // This is surgical cleanup that works regardless of shouldUpdatePaths.
40074
40388
  if (connectorIds.size > 0 && threeScene) {
40075
40389
  var objectsToRemove = [];
40076
40390
  threeScene.traverse(function (obj) {
40077
- var _obj$uuid, _obj$userData5, _obj$uuid2, _obj$userData6;
40078
- var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
40079
- var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.isDeclared) !== true;
40391
+ var _obj$uuid, _obj$userData4, _obj$uuid2, _obj$userData5;
40392
+ var isComputedSegment = ((_obj$uuid = obj.uuid) === null || _obj$uuid === void 0 ? void 0 : _obj$uuid.startsWith('SEGMENT-')) && ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.isDeclared) !== true;
40393
+ var isComputedGateway = ((_obj$uuid2 = obj.uuid) === null || _obj$uuid2 === void 0 ? void 0 : _obj$uuid2.includes('Gateway')) && ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.isDeclared) !== true;
40080
40394
  if (isComputedSegment || isComputedGateway) {
40081
- var _obj$userData7, _obj$userData8;
40082
- if (connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathFrom) || connectorIds.has((_obj$userData8 = obj.userData) === null || _obj$userData8 === void 0 ? void 0 : _obj$userData8.pathTo)) {
40395
+ var _obj$userData6, _obj$userData7;
40396
+ if (connectorIds.has((_obj$userData6 = obj.userData) === null || _obj$userData6 === void 0 ? void 0 : _obj$userData6.pathFrom) || connectorIds.has((_obj$userData7 = obj.userData) === null || _obj$userData7 === void 0 ? void 0 : _obj$userData7.pathTo)) {
40083
40397
  objectsToRemove.push(obj);
40084
40398
  }
40085
40399
  }
@@ -40168,6 +40482,149 @@ var CentralPlantInternals = /*#__PURE__*/function () {
40168
40482
  }]);
40169
40483
  }();
40170
40484
 
40485
+ /**
40486
+ * Scene-level behavior helpers: scan endpoints, register behaviors, cross-component links.
40487
+ */
40488
+ function getComponentDictionary(centralPlant) {
40489
+ var _centralPlant$getUtil, _centralPlant$manager;
40490
+ return (centralPlant === null || centralPlant === void 0 || (_centralPlant$getUtil = centralPlant.getUtility) === null || _centralPlant$getUtil === void 0 || (_centralPlant$getUtil = _centralPlant$getUtil.call(centralPlant, 'modelPreloader')) === null || _centralPlant$getUtil === void 0 ? void 0 : _centralPlant$getUtil.componentDictionary) || (centralPlant === null || centralPlant === void 0 || (_centralPlant$manager = centralPlant.managers) === null || _centralPlant$manager === void 0 || (_centralPlant$manager = _centralPlant$manager.componentDataManager) === null || _centralPlant$manager === void 0 ? void 0 : _centralPlant$manager.componentDictionary) || null;
40491
+ }
40492
+ function statesFromBehaviorConfig(device) {
40493
+ if (!(device !== null && device !== void 0 && device.behaviorConfig) || !Array.isArray(device.behaviorConfig)) return [];
40494
+ return device.behaviorConfig.map(function (b) {
40495
+ return b.stateVariable;
40496
+ }).filter(Boolean).filter(function (v, i, arr) {
40497
+ return arr.indexOf(v) === i;
40498
+ });
40499
+ }
40500
+ function resolveStatesForDevice(ioObj, deviceId, componentDictionary) {
40501
+ var _ioObj$userData;
40502
+ var device = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[deviceId];
40503
+ var fromBehaviorConfig = statesFromBehaviorConfig(device);
40504
+ if (fromBehaviorConfig.length) return fromBehaviorConfig;
40505
+ return (((_ioObj$userData = ioObj.userData) === null || _ioObj$userData === void 0 ? void 0 : _ioObj$userData.dataPoints) || []).map(function (dp) {
40506
+ return dp.id || dp.name;
40507
+ }).filter(Boolean).filter(function (v, i, arr) {
40508
+ return arr.indexOf(v) === i;
40509
+ });
40510
+ }
40511
+
40512
+ /**
40513
+ * Scan the live Three.js scene for smart-component io-device endpoints.
40514
+ */
40515
+ function scanSceneIoEndpoints(centralPlant) {
40516
+ var _centralPlant$sceneVi;
40517
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi = centralPlant.sceneViewer) === null || _centralPlant$sceneVi === void 0 ? void 0 : _centralPlant$sceneVi.scene;
40518
+ if (!scene) return [];
40519
+ var dict = getComponentDictionary(centralPlant);
40520
+ var endpoints = [];
40521
+ scene.traverse(function (obj) {
40522
+ var _obj$userData, _parent$userData, _parent$userData2;
40523
+ if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) !== 'io-device') return;
40524
+ var parent = obj.parent;
40525
+ var parentUuid = obj.userData.parentComponentId || (parent === null || parent === void 0 ? void 0 : parent.uuid) || '';
40526
+ var parentName = (parent === null || parent === void 0 || (_parent$userData = parent.userData) === null || _parent$userData === void 0 ? void 0 : _parent$userData.hardcodedUuid) || (parent === null || parent === void 0 ? void 0 : parent.name) || parentUuid;
40527
+ var libraryId = (parent === null || parent === void 0 || (_parent$userData2 = parent.userData) === null || _parent$userData2 === void 0 ? void 0 : _parent$userData2.libraryId) || '';
40528
+ var attachmentId = obj.userData.attachmentId || obj.name || obj.uuid;
40529
+ var deviceId = obj.userData.deviceId || '';
40530
+ endpoints.push({
40531
+ key: getScopedAttachmentKey(attachmentId, parentUuid),
40532
+ parentUuid: parentUuid,
40533
+ parentName: parentName,
40534
+ libraryId: libraryId,
40535
+ attachmentId: attachmentId,
40536
+ attachmentLabel: obj.userData.attachmentLabel || attachmentId,
40537
+ deviceId: deviceId,
40538
+ states: resolveStatesForDevice(obj, deviceId, dict)
40539
+ });
40540
+ });
40541
+ return endpoints;
40542
+ }
40543
+
40544
+ /**
40545
+ * Load cross-component behaviors from scene data mirrors.
40546
+ */
40547
+ function loadCrossComponentBehaviors(centralPlant) {
40548
+ var _centralPlant$importe, _centralPlant$sceneVi2;
40549
+ return (centralPlant === null || centralPlant === void 0 || (_centralPlant$importe = centralPlant.importedSceneData) === null || _centralPlant$importe === void 0 ? void 0 : _centralPlant$importe.behaviors) || (centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi2 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi2 === void 0 || (_centralPlant$sceneVi2 = _centralPlant$sceneVi2.currentSceneData) === null || _centralPlant$sceneVi2 === void 0 ? void 0 : _centralPlant$sceneVi2.behaviors) || [];
40550
+ }
40551
+
40552
+ /**
40553
+ * Persist cross-component behaviors to scene data mirrors and the runtime manager.
40554
+ */
40555
+ function applyCrossComponentBehaviors(centralPlant, behaviors) {
40556
+ var normalized = behaviors || [];
40557
+ if (centralPlant.importedSceneData) {
40558
+ if (normalized.length) {
40559
+ centralPlant.importedSceneData.behaviors = normalized;
40560
+ } else {
40561
+ delete centralPlant.importedSceneData.behaviors;
40562
+ }
40563
+ }
40564
+ var sceneViewer = centralPlant.sceneViewer;
40565
+ if (sceneViewer && !sceneViewer.currentSceneData) {
40566
+ var _centralPlant$getConn;
40567
+ sceneViewer.currentSceneData = {
40568
+ version: '2.3',
40569
+ connections: ((_centralPlant$getConn = centralPlant.getConnections) === null || _centralPlant$getConn === void 0 ? void 0 : _centralPlant$getConn.call(centralPlant)) || [],
40570
+ scene: {
40571
+ children: []
40572
+ }
40573
+ };
40574
+ }
40575
+ var currentSceneData = sceneViewer === null || sceneViewer === void 0 ? void 0 : sceneViewer.currentSceneData;
40576
+ if (currentSceneData) {
40577
+ if (normalized.length) {
40578
+ currentSceneData.behaviors = normalized;
40579
+ } else {
40580
+ delete currentSceneData.behaviors;
40581
+ }
40582
+ }
40583
+ var ioBehavMgr = getIoBehaviorManager(sceneViewer);
40584
+ if (ioBehavMgr) {
40585
+ ioBehavMgr.setCrossComponentBehaviors(normalized);
40586
+ }
40587
+ }
40588
+
40589
+ /**
40590
+ * Re-register intra-component behaviors on all scene instances of a smart component.
40591
+ */
40592
+ function refreshSceneIntraBehaviors(centralPlant, libraryId, behaviors) {
40593
+ var _centralPlant$sceneVi3;
40594
+ var ioBehavMgr = getIoBehaviorManager(centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.sceneViewer);
40595
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi3 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi3 === void 0 ? void 0 : _centralPlant$sceneVi3.scene;
40596
+ if (!ioBehavMgr || !scene || !libraryId) return;
40597
+ scene.traverse(function (obj) {
40598
+ var _obj$userData2, _obj$userData3;
40599
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component' && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.libraryId) === libraryId) {
40600
+ ioBehavMgr.registerComponentBehaviors(obj.uuid, behaviors || []);
40601
+ }
40602
+ });
40603
+ }
40604
+
40605
+ /**
40606
+ * Re-register intra- and cross-component behaviors for all instances in the live scene.
40607
+ */
40608
+ function reregisterSceneBehaviors(centralPlant) {
40609
+ var _centralPlant$sceneVi4;
40610
+ var ioBehavMgr = getIoBehaviorManager(centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.sceneViewer);
40611
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi4 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi4 === void 0 ? void 0 : _centralPlant$sceneVi4.scene;
40612
+ var dict = getComponentDictionary(centralPlant);
40613
+ if (!ioBehavMgr || !scene) return;
40614
+ var crossBehaviors = loadCrossComponentBehaviors(centralPlant);
40615
+ ioBehavMgr.setCrossComponentBehaviors(crossBehaviors);
40616
+ if (!dict) return;
40617
+ scene.traverse(function (obj) {
40618
+ var _obj$userData4, _obj$userData5;
40619
+ if (((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) !== 'component') return;
40620
+ var libraryId = (_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.libraryId;
40621
+ if (!libraryId) return;
40622
+ var componentData = dict[libraryId];
40623
+ if (!componentData) return;
40624
+ registerBehaviorsForComponent(ioBehavMgr, obj.uuid, componentData, obj, dict);
40625
+ });
40626
+ }
40627
+
40171
40628
  // ─────────────────────────────────────────────────────────────────────────────
40172
40629
  // Flow-direction compatibility helper (module-level, no class dependency)
40173
40630
  // ─────────────────────────────────────────────────────────────────────────────
@@ -40194,7 +40651,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40194
40651
  * Initialize the CentralPlant manager
40195
40652
  *
40196
40653
  * @constructor
40197
- * @version 0.3.37
40654
+ * @version 0.3.39
40198
40655
  * @updated 2025-10-22
40199
40656
  *
40200
40657
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -40368,8 +40825,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40368
40825
  key: "setImportedSceneData",
40369
40826
  value: (function () {
40370
40827
  var _setImportedSceneData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(sceneData) {
40371
- var _this$importedSceneDa, _this$importedSceneDa2, _this$importedSceneDa3, _this$sceneViewer;
40372
- var ioBehavMgr;
40828
+ var _this$importedSceneDa, _this$importedSceneDa2, _this$importedSceneDa3;
40373
40829
  return _regenerator().w(function (_context2) {
40374
40830
  while (1) switch (_context2.n) {
40375
40831
  case 0:
@@ -40386,14 +40842,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40386
40842
 
40387
40843
  // Scene behaviors loaded
40388
40844
 
40389
- // Register behaviors with IoBehaviorManager
40390
- 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;
40391
- if (ioBehavMgr) {
40392
- // Setting cross-component behaviors
40393
- ioBehavMgr.setCrossComponentBehaviors(this.importedSceneData.behaviors || []);
40394
- } else {
40395
- console.warn('[Behavior] ioBehaviorManager not available!');
40396
- }
40845
+ // Cross-component behaviors are registered by sceneOperationsManager.loadSceneData()
40397
40846
 
40398
40847
  // Reset component counter based on imported components to avoid ID conflicts
40399
40848
  this.internals.resetComponentCounter();
@@ -40574,8 +41023,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40574
41023
  }, {
40575
41024
  key: "selectObject",
40576
41025
  value: function selectObject(objectOrId) {
40577
- var _this$sceneViewer2, _object;
40578
- if (!((_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.transformManager)) {
41026
+ var _this$sceneViewer, _object;
41027
+ if (!((_this$sceneViewer = this.sceneViewer) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.transformManager)) {
40579
41028
  console.warn('⚠️ Transform manager not initialized');
40580
41029
  return false;
40581
41030
  }
@@ -40621,8 +41070,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40621
41070
  }, {
40622
41071
  key: "toggleObject",
40623
41072
  value: function toggleObject(objectOrId) {
40624
- var _this$sceneViewer3, _object2;
40625
- if (!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformManager)) {
41073
+ var _this$sceneViewer2, _object2;
41074
+ if (!((_this$sceneViewer2 = this.sceneViewer) !== null && _this$sceneViewer2 !== void 0 && _this$sceneViewer2.transformManager)) {
40626
41075
  console.warn('⚠️ Transform manager not initialized');
40627
41076
  return false;
40628
41077
  }
@@ -40658,8 +41107,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40658
41107
  }, {
40659
41108
  key: "deselectObject",
40660
41109
  value: function deselectObject() {
40661
- var _this$sceneViewer4;
40662
- if (!((_this$sceneViewer4 = this.sceneViewer) !== null && _this$sceneViewer4 !== void 0 && _this$sceneViewer4.transformManager)) {
41110
+ var _this$sceneViewer3;
41111
+ if (!((_this$sceneViewer3 = this.sceneViewer) !== null && _this$sceneViewer3 !== void 0 && _this$sceneViewer3.transformManager)) {
40663
41112
  console.warn('⚠️ Transform manager not initialized');
40664
41113
  return false;
40665
41114
  }
@@ -41239,68 +41688,70 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41239
41688
  }
41240
41689
 
41241
41690
  /**
41242
- * Set the state of an I/O device instance in the Three.js scene.
41243
- *
41244
- * This is the primary public API for driving IO device state changes from
41245
- * external code (real-time data feeds, AI agents, automated tests, etc.).
41246
- * It performs three coordinated actions in order:
41247
- * 1. Persists the new value through the state adapter (Vuex in sandbox,
41248
- * or any custom adapter injected via componentTooltipManager.configure())
41249
- * so that the host application's store stays consistent.
41250
- * 2. Evaluates all behaviors whose input matches this (attachmentId, stateId)
41251
- * pair and applies the resulting property changes to Three.js meshes.
41252
- * 3. Emits an 'io-device-state-changed' event on the sceneViewer so that
41253
- * host applications without a Vuex store (e.g. cp3d-viewer) can react.
41254
- *
41255
- * @param {string} attachmentId - The attachment ID of the IO device (matches
41256
- * the `attachmentId` stored in the Three.js object's userData)
41257
- * @param {string} stateId - The data-point / state ID on the device (e.g. 'power', 'level')
41258
- * @param {*} value - The new state value (boolean, number, string, etc.)
41259
- * @param {string} [parentUuid] - UUID of the parent component instance.
41260
- * Required when multiple instances of the same smart component share the
41261
- * same attachmentId — prevents cross-instance state bleed.
41262
- * @returns {boolean} True if the behavior system was reached; false if unavailable.
41263
- * @example
41264
- * // Toggle a push-button on a specific pump instance
41265
- * centralPlant.setIoDeviceState('pump-push-button-01', 'power', true, pumpUuid)
41266
- *
41267
- * // Drive an analog level sensor (no parentUuid needed when only one instance)
41268
- * centralPlant.setIoDeviceState('chiller-level-sensor-01', 'level', 0.75)
41691
+ * Internal single path for I/O state changes: persist, animate, link, emit.
41692
+ * @private
41269
41693
  */
41270
41694
  }, {
41271
- key: "setIoDeviceState",
41272
- value: function setIoDeviceState(attachmentId, stateId, value, parentUuid) {
41273
- var _this$sceneViewer5, _this$sceneViewer6, _this$sceneViewer7;
41274
- // 1. Persist via state adapter if one has been configured
41275
- 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;
41695
+ key: "_dispatchIoState",
41696
+ value: function _dispatchIoState(attachmentId, stateId, value, parentUuid) {
41697
+ var _this$managers, _this$sceneViewer4, _this$sceneViewer5, _this$managers2, _this$sceneViewer6, _this$sceneViewer7, _this$sceneViewer8;
41698
+ var tooltipMgr = ((_this$managers = this.managers) === null || _this$managers === void 0 ? void 0 : _this$managers.componentTooltipManager) || ((_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.componentTooltipManager) || ((_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.componentTooltipManager);
41699
+ var stateAdapter = tooltipMgr === null || tooltipMgr === void 0 ? void 0 : tooltipMgr._stateAdapter;
41276
41700
  if (stateAdapter !== null && stateAdapter !== void 0 && stateAdapter.setState) {
41277
- var scopedKey = parentUuid ? "".concat(parentUuid, "::").concat(attachmentId) : attachmentId;
41701
+ var scopedKey = getScopedAttachmentKey(attachmentId, parentUuid);
41278
41702
  try {
41279
41703
  stateAdapter.setState(scopedKey, stateId, value);
41280
41704
  } catch (err) {
41281
- console.warn('⚠️ setIoDeviceState(): stateAdapter.setState() threw:', err);
41705
+ console.warn('⚠️ _dispatchIoState(): stateAdapter.setState() threw:', err);
41282
41706
  }
41283
41707
  }
41284
-
41285
- // 2. Apply io-behavior changes
41286
- 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;
41708
+ var ioBehavMgr = ((_this$managers2 = this.managers) === null || _this$managers2 === void 0 ? void 0 : _this$managers2.ioBehaviorManager) || ((_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.managers) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.ioBehaviorManager) || ((_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.ioBehaviorManager);
41287
41709
  if (ioBehavMgr) {
41288
- var _this$importedSceneDa4;
41289
41710
  ioBehavMgr.triggerState(attachmentId, stateId, value, parentUuid);
41290
-
41291
- // Evaluate cross-component behaviors if they exist in the imported scene
41292
- if ((_this$importedSceneDa4 = this.importedSceneData) !== null && _this$importedSceneDa4 !== void 0 && _this$importedSceneDa4.behaviors) {
41293
- ioBehavMgr.triggerCrossComponentBehaviors(this.importedSceneData.behaviors, parentUuid, attachmentId, stateId, value);
41294
- }
41295
41711
  }
41296
-
41297
- // 3. Emit event for host apps that don't use the state adapter (e.g. cp3d-viewer)
41298
- (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 || _this$sceneViewer7.emit('io-device-state-changed', {
41712
+ (_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 || _this$sceneViewer8.emit('io-device-state-changed', {
41299
41713
  attachmentId: attachmentId,
41300
41714
  stateId: stateId,
41301
41715
  value: value,
41302
41716
  parentUuid: parentUuid || null
41303
41717
  });
41718
+ }
41719
+
41720
+ /**
41721
+ * Configure the state adapter on both tooltip and behavior managers.
41722
+ * @param {{ getState: Function, setState: Function }} stateAdapter
41723
+ */
41724
+ }, {
41725
+ key: "configureStateAdapter",
41726
+ value: function configureStateAdapter(stateAdapter) {
41727
+ var _this$managers3, _this$sceneViewer9, _this$managers4, _this$sceneViewer1, _this$sceneViewer10;
41728
+ var tooltipMgr = ((_this$managers3 = this.managers) === null || _this$managers3 === void 0 ? void 0 : _this$managers3.componentTooltipManager) || ((_this$sceneViewer9 = this.sceneViewer) === null || _this$sceneViewer9 === void 0 ? void 0 : _this$sceneViewer9.componentTooltipManager);
41729
+ if (tooltipMgr !== null && tooltipMgr !== void 0 && tooltipMgr.configure) {
41730
+ var _this$sceneViewer0;
41731
+ tooltipMgr.configure(stateAdapter);
41732
+ if ((_this$sceneViewer0 = this.sceneViewer) !== null && _this$sceneViewer0 !== void 0 && _this$sceneViewer0.managers) {
41733
+ this.sceneViewer.managers.componentTooltipManager = tooltipMgr;
41734
+ }
41735
+ }
41736
+ var ioBehavMgr = ((_this$managers4 = this.managers) === null || _this$managers4 === void 0 ? void 0 : _this$managers4.ioBehaviorManager) || ((_this$sceneViewer1 = this.sceneViewer) === null || _this$sceneViewer1 === void 0 || (_this$sceneViewer1 = _this$sceneViewer1.managers) === null || _this$sceneViewer1 === void 0 ? void 0 : _this$sceneViewer1.ioBehaviorManager) || ((_this$sceneViewer10 = this.sceneViewer) === null || _this$sceneViewer10 === void 0 ? void 0 : _this$sceneViewer10.ioBehaviorManager);
41737
+ if (ioBehavMgr !== null && ioBehavMgr !== void 0 && ioBehavMgr.configure) {
41738
+ ioBehavMgr.configure(stateAdapter);
41739
+ }
41740
+ }
41741
+
41742
+ /**
41743
+ * Set the state of an I/O device instance in the Three.js scene.
41744
+ *
41745
+ * @param {string} attachmentId - IO device attachment ID
41746
+ * @param {string} stateId - Data-point / state ID (e.g. 'power', 'level')
41747
+ * @param {*} value - New state value
41748
+ * @param {string} [parentUuid] - Parent component instance UUID
41749
+ * @returns {boolean}
41750
+ */
41751
+ }, {
41752
+ key: "setIoDeviceState",
41753
+ value: function setIoDeviceState(attachmentId, stateId, value, parentUuid) {
41754
+ this._dispatchIoState(attachmentId, stateId, value, parentUuid);
41304
41755
  return true;
41305
41756
  }
41306
41757
 
@@ -41317,8 +41768,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41317
41768
  }, {
41318
41769
  key: "getSceneAttachments",
41319
41770
  value: function getSceneAttachments() {
41320
- var _this$sceneViewer8;
41321
- var scene = (_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 ? void 0 : _this$sceneViewer8.scene;
41771
+ var _this$sceneViewer11;
41772
+ var scene = (_this$sceneViewer11 = this.sceneViewer) === null || _this$sceneViewer11 === void 0 ? void 0 : _this$sceneViewer11.scene;
41322
41773
  if (!scene) return [];
41323
41774
  var results = [];
41324
41775
  scene.traverse(function (obj) {
@@ -42026,6 +42477,58 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42026
42477
  }
42027
42478
  return extendComponentDictionary;
42028
42479
  }()
42480
+ /**
42481
+ * Merge mesh animation configs into the component dictionary.
42482
+ * @param {Object<string, { behaviorConfig?: Array, meshNameMap?: Object }>} configsByAssetId
42483
+ * @returns {number} Count of dictionary entries updated
42484
+ */
42485
+ )
42486
+ }, {
42487
+ key: "mergeBehaviorConfigsIntoDictionary",
42488
+ value: function mergeBehaviorConfigsIntoDictionary(configsByAssetId) {
42489
+ if (!this.managers.componentDataManager) {
42490
+ console.warn('⚠️ mergeBehaviorConfigsIntoDictionary(): Component data manager not available');
42491
+ return 0;
42492
+ }
42493
+ return this.managers.componentDataManager.mergeBehaviorConfigsIntoDictionary(configsByAssetId);
42494
+ }
42495
+
42496
+ /**
42497
+ * Scan live scene for I/O device endpoints (for behavior authoring UIs).
42498
+ */
42499
+ }, {
42500
+ key: "scanSceneIoEndpoints",
42501
+ value: function scanSceneIoEndpoints$1() {
42502
+ return scanSceneIoEndpoints(this);
42503
+ }
42504
+
42505
+ /**
42506
+ * Apply cross-component behaviors to scene data and the runtime manager.
42507
+ */
42508
+ }, {
42509
+ key: "setSceneBehaviors",
42510
+ value: function setSceneBehaviors(behaviors) {
42511
+ applyCrossComponentBehaviors(this, behaviors);
42512
+ }
42513
+
42514
+ /**
42515
+ * Read cross-component behaviors from the current scene data mirrors.
42516
+ */
42517
+ }, {
42518
+ key: "getSceneBehaviors",
42519
+ value: function getSceneBehaviors() {
42520
+ return loadCrossComponentBehaviors(this);
42521
+ }
42522
+
42523
+ /**
42524
+ * Re-register all intra- and cross-component behaviors on placed instances.
42525
+ */
42526
+ }, {
42527
+ key: "reregisterSceneBehaviors",
42528
+ value: function reregisterSceneBehaviors$1() {
42529
+ reregisterSceneBehaviors(this);
42530
+ }
42531
+
42029
42532
  /**
42030
42533
  * Remove S3 components from component dictionary
42031
42534
  * @returns {Promise<boolean>} True if components were removed successfully, false otherwise
@@ -42038,7 +42541,6 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42038
42541
  * console.log('S3 components removed');
42039
42542
  * }
42040
42543
  */
42041
- )
42042
42544
  }, {
42043
42545
  key: "removeS3Components",
42044
42546
  value: (function () {
@@ -42945,9 +43447,9 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42945
43447
  }, {
42946
43448
  key: "clearScene",
42947
43449
  value: function clearScene() {
42948
- var _this$sceneViewer9;
43450
+ var _this$sceneViewer12;
42949
43451
  this.importedSceneData = null;
42950
- 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;
43452
+ var ioBehavMgr = (_this$sceneViewer12 = this.sceneViewer) === null || _this$sceneViewer12 === void 0 || (_this$sceneViewer12 = _this$sceneViewer12.managers) === null || _this$sceneViewer12 === void 0 ? void 0 : _this$sceneViewer12.ioBehaviorManager;
42951
43453
  if (ioBehavMgr) {
42952
43454
  ioBehavMgr.setCrossComponentBehaviors([]);
42953
43455
  }
@@ -48826,6 +49328,9 @@ exports.SceneOperationsManager = SceneOperationsManager;
48826
49328
  exports.SceneTooltipsManager = SceneTooltipsManager;
48827
49329
  exports.SnapshotManager = SnapshotManager;
48828
49330
  exports.ThreeJSResourceManager = ThreeJSResourceManager;
49331
+ exports.applyCrossComponentBehaviors = applyCrossComponentBehaviors;
49332
+ exports.buildCrossBehavior = buildCrossBehavior;
49333
+ exports.buildIntraBehavior = buildIntraBehavior;
48829
49334
  exports.cacheJsonData = cacheJsonData;
48830
49335
  exports.cacheManager = cacheManager;
48831
49336
  exports.cleanExpiredCache = cleanExpiredCache;
@@ -48851,8 +49356,10 @@ exports.getCurrentCacheName = getCurrentCacheName;
48851
49356
  exports.getGlobalCacheStats = getGlobalCacheStats;
48852
49357
  exports.getGlobalOnlyCacheStats = getGlobalOnlyCacheStats;
48853
49358
  exports.getHardcodedUuid = getHardcodedUuid;
49359
+ exports.getIoBehaviorManager = getIoBehaviorManager;
48854
49360
  exports.getObjectTypeName = getObjectTypeName;
48855
49361
  exports.getObjectsByType = getObjectsByType;
49362
+ exports.getScopedAttachmentKey = getScopedAttachmentKey;
48856
49363
  exports.getThumbnailKey = getThumbnailKey;
48857
49364
  exports.getUserOnlyCacheStats = getUserOnlyCacheStats;
48858
49365
  exports.isCached = isCached;
@@ -48864,17 +49371,27 @@ exports.isExportable = isExportable;
48864
49371
  exports.isGateway = isGateway;
48865
49372
  exports.isSegment = isSegment;
48866
49373
  exports.isThumbnailCached = isThumbnailCached;
49374
+ exports.loadCrossComponentBehaviors = loadCrossComponentBehaviors;
48867
49375
  exports.loadTextureSetAndCreateMaterial = loadTextureSetAndCreateMaterial;
48868
49376
  exports.markAsComputed = markAsComputed;
48869
49377
  exports.markAsDeclared = markAsDeclared;
48870
49378
  exports.measureS3Transfer = measureS3Transfer;
48871
49379
  exports.modelPreloader = modelPreloader;
49380
+ exports.normalizeBehavior = normalizeBehavior;
49381
+ exports.parseCrossBehavior = parseCrossBehavior;
49382
+ exports.parseIntraBehavior = parseIntraBehavior;
48872
49383
  exports.preloadLocalFiles = preloadLocalFiles;
48873
49384
  exports.preloadS3Objects = preloadS3Objects;
49385
+ exports.refreshSceneIntraBehaviors = refreshSceneIntraBehaviors;
49386
+ exports.registerBehaviorsForComponent = registerBehaviorsForComponent;
49387
+ exports.reloadBehaviorsForDeviceAsset = reloadBehaviorsForDeviceAsset;
48874
49388
  exports.removeCachedJsonData = removeCachedJsonData;
49389
+ exports.reregisterSceneBehaviors = reregisterSceneBehaviors;
48875
49390
  exports.resetCacheIdentity = resetCacheIdentity;
48876
49391
  exports.resetGlobalCacheStats = resetGlobalCacheStats;
49392
+ exports.resolveDataPoints = resolveDataPoints;
48877
49393
  exports.s3MetadataCache = s3MetadataCache;
49394
+ exports.scanSceneIoEndpoints = scanSceneIoEndpoints;
48878
49395
  exports.sceneViewer = sceneViewer;
48879
49396
  exports.shouldRemoveOnRegeneration = shouldRemoveOnRegeneration;
48880
49397
  exports.switchCachePartition = switchCachePartition;