@2112-lab/central-plant 0.3.46 → 0.3.47

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 (29) hide show
  1. package/dist/bundle/index.js +182 -92
  2. package/dist/cjs/src/core/centralPlant.js +3 -4
  3. package/dist/cjs/src/core/sceneViewer.js +0 -1
  4. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +1 -2
  5. package/dist/cjs/src/managers/components/componentDataManager.js +0 -1
  6. package/dist/cjs/src/managers/controls/componentDragManager.js +2 -7
  7. package/dist/cjs/src/managers/pathfinding/pathfindingManager.js +55 -1
  8. package/dist/cjs/src/managers/scene/collisionManager.js +1 -2
  9. package/dist/cjs/src/managers/scene/componentTooltipManager.js +2 -3
  10. package/dist/cjs/src/managers/scene/sceneExportManager.js +30 -11
  11. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +21 -1
  12. package/dist/cjs/src/utils/behaviorDispatch.js +11 -42
  13. package/dist/cjs/src/utils/boundingBoxUtils.js +54 -8
  14. package/dist/cjs/src/utils/ioDeviceUtils.js +3 -9
  15. package/dist/esm/src/core/centralPlant.js +3 -4
  16. package/dist/esm/src/core/sceneViewer.js +0 -1
  17. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +1 -2
  18. package/dist/esm/src/managers/components/componentDataManager.js +0 -1
  19. package/dist/esm/src/managers/controls/componentDragManager.js +2 -7
  20. package/dist/esm/src/managers/pathfinding/pathfindingManager.js +56 -2
  21. package/dist/esm/src/managers/scene/collisionManager.js +1 -2
  22. package/dist/esm/src/managers/scene/componentTooltipManager.js +2 -3
  23. package/dist/esm/src/managers/scene/sceneExportManager.js +31 -12
  24. package/dist/esm/src/managers/scene/sceneOperationsManager.js +21 -1
  25. package/dist/esm/src/utils/behaviorDispatch.js +11 -42
  26. package/dist/esm/src/utils/boundingBoxUtils.js +55 -10
  27. package/dist/esm/src/utils/ioDeviceUtils.js +3 -9
  28. package/dist/index.d.ts +0 -6
  29. package/package.json +1 -1
@@ -1190,11 +1190,11 @@ function getIoBehaviorManager(sceneViewer) {
1190
1190
 
1191
1191
  /**
1192
1192
  * Resolve tooltip/drag data points for an I/O device attachment.
1193
- * Prefers behaviorConfig-driven animation data points; merges legacy ioConfig snapshot.
1193
+ * Prefers behaviorConfig-driven animation data points.
1194
1194
  *
1195
1195
  * @param {string} parentUuid
1196
1196
  * @param {string} attachmentId
1197
- * @param {Object} userData - io-device userData (may include dataPoints snapshot)
1197
+ * @param {Object} userData - io-device userData
1198
1198
  * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager|null} ioBehaviorManager
1199
1199
  * @param {THREE.Object3D|null} [hitMesh]
1200
1200
  * @returns {Object[]}
@@ -1202,21 +1202,7 @@ function getIoBehaviorManager(sceneViewer) {
1202
1202
  function resolveDataPoints(parentUuid, attachmentId, userData, ioBehaviorManager) {
1203
1203
  var hitMesh = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
1204
1204
  var fromAnimations = (ioBehaviorManager === null || ioBehaviorManager === void 0 ? void 0 : ioBehaviorManager.getAnimationDataPoints(parentUuid, attachmentId, hitMesh)) || [];
1205
- if (fromAnimations.length) return fromAnimations;
1206
- var legacy = (userData === null || userData === void 0 ? void 0 : userData.dataPoints) || [];
1207
- return legacy.map(function (dp) {
1208
- var _dp$defaultValue;
1209
- return {
1210
- id: dp.id || dp.name,
1211
- name: dp.name || dp.id,
1212
- stateType: dp.stateType || 'binary',
1213
- stateConfig: dp.stateConfig || {},
1214
- defaultValue: (_dp$defaultValue = dp.defaultValue) !== null && _dp$defaultValue !== void 0 ? _dp$defaultValue : null,
1215
- direction: dp.direction || (userData === null || userData === void 0 ? void 0 : userData.ioDirection) || 'output'
1216
- };
1217
- }).filter(function (dp) {
1218
- return dp.id;
1219
- });
1205
+ return fromAnimations;
1220
1206
  }
1221
1207
 
1222
1208
  /**
@@ -1247,35 +1233,18 @@ function applyDefaultIoDeviceStates(centralPlant) {
1247
1233
  if (seen.has(scopedKey)) return;
1248
1234
  seen.add(scopedKey);
1249
1235
  var dps = ioBehavMgr.getAnimationDataPoints(objParentUuid, objAttachmentId) || [];
1250
- if (dps.length) {
1251
- var _iterator = _createForOfIteratorHelper(dps),
1252
- _step;
1253
- try {
1254
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
1255
- var dp = _step.value;
1256
- if (!(dp !== null && dp !== void 0 && dp.id) || dp.defaultValue === undefined || dp.defaultValue === null) continue;
1257
- centralPlant.setIoDeviceState(objAttachmentId, dp.id, dp.defaultValue, objParentUuid);
1258
- }
1259
- } catch (err) {
1260
- _iterator.e(err);
1261
- } finally {
1262
- _iterator.f();
1263
- }
1264
- return;
1265
- }
1266
- var _iterator2 = _createForOfIteratorHelper(obj.userData.dataPoints || []),
1267
- _step2;
1236
+ var _iterator = _createForOfIteratorHelper(dps),
1237
+ _step;
1268
1238
  try {
1269
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1270
- var _dp = _step2.value;
1271
- var dpId = _dp.id || _dp.name;
1272
- if (!dpId || _dp.defaultValue === undefined || _dp.defaultValue === null) continue;
1273
- centralPlant.setIoDeviceState(objAttachmentId, dpId, _dp.defaultValue, objParentUuid);
1239
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
1240
+ var dp = _step.value;
1241
+ if (!(dp !== null && dp !== void 0 && dp.id) || dp.defaultValue === undefined || dp.defaultValue === null) continue;
1242
+ centralPlant.setIoDeviceState(objAttachmentId, dp.id, dp.defaultValue, objParentUuid);
1274
1243
  }
1275
1244
  } catch (err) {
1276
- _iterator2.e(err);
1245
+ _iterator.e(err);
1277
1246
  } finally {
1278
- _iterator2.f();
1247
+ _iterator.f();
1279
1248
  }
1280
1249
  });
1281
1250
  }
@@ -3783,6 +3752,51 @@ function computeIODeviceBoundingBoxes(componentObject) {
3783
3752
  return results;
3784
3753
  }
3785
3754
 
3755
+ /**
3756
+ * Computes individual world-space bounding boxes for each connector child
3757
+ * of a component.
3758
+ *
3759
+ * @param {THREE.Object3D} componentObject - The component's Three.js object
3760
+ * @returns {Array<{uuid: string, userData: Object, worldBoundingBox: {min: number[], max: number[]}}>}
3761
+ * Array of connector bounding box descriptors ready for injection into scene data
3762
+ */
3763
+ function computeConnectorBoundingBoxes(componentObject) {
3764
+ var results = [];
3765
+ var _iterator2 = _createForOfIteratorHelper(componentObject.children),
3766
+ _step2;
3767
+ try {
3768
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
3769
+ var _child$userData2;
3770
+ var child = _step2.value;
3771
+ if (((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) !== 'connector') continue;
3772
+ var bbox = new THREE__namespace.Box3().setFromObject(child);
3773
+
3774
+ // Fallback if mesh is too small or empty (sometimes connectors are just points)
3775
+ if (bbox.isEmpty() || bbox.getSize(new THREE__namespace.Vector3()).length() < 0.01) {
3776
+ var worldPos = new THREE__namespace.Vector3();
3777
+ child.getWorldPosition(worldPos);
3778
+ var size = 0.1;
3779
+ bbox.setFromCenterAndSize(worldPos, new THREE__namespace.Vector3(size, size, size));
3780
+ }
3781
+ results.push({
3782
+ uuid: child.uuid,
3783
+ userData: _objectSpread2(_objectSpread2({}, child.userData), {}, {
3784
+ objectType: 'connector'
3785
+ }),
3786
+ worldBoundingBox: {
3787
+ min: [bbox.min.x, bbox.min.y, bbox.min.z],
3788
+ max: [bbox.max.x, bbox.max.y, bbox.max.z]
3789
+ }
3790
+ });
3791
+ }
3792
+ } catch (err) {
3793
+ _iterator2.e(err);
3794
+ } finally {
3795
+ _iterator2.f();
3796
+ }
3797
+ return results;
3798
+ }
3799
+
3786
3800
  /**
3787
3801
  * Creates bounding box helpers for a selected object. For smart components
3788
3802
  * (components with io-device children), this produces:
@@ -3841,8 +3855,8 @@ function createSelectionBoxHelpers(object) {
3841
3855
 
3842
3856
  // Check if this object has io-device children (smart component)
3843
3857
  var hasIODevices = (_object$children = object.children) === null || _object$children === void 0 ? void 0 : _object$children.some(function (child) {
3844
- var _child$userData2;
3845
- return ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'io-device';
3858
+ var _child$userData3;
3859
+ return ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'io-device';
3846
3860
  });
3847
3861
  if (hasIODevices) {
3848
3862
  // 1. Create filtered helper for the component body
@@ -3879,11 +3893,11 @@ function createSelectionBoxHelpers(object) {
3879
3893
  * @param {THREE.Scene} scene - The scene (for finding objects by uuid)
3880
3894
  */
3881
3895
  function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
3882
- var _iterator2 = _createForOfIteratorHelper(helpers),
3883
- _step2;
3896
+ var _iterator3 = _createForOfIteratorHelper(helpers),
3897
+ _step3;
3884
3898
  try {
3885
3899
  var _loop = function _loop() {
3886
- var helper = _step2.value;
3900
+ var helper = _step3.value;
3887
3901
  var _helper$userData = helper.userData,
3888
3902
  sourceObjectUuid = _helper$userData.sourceObjectUuid,
3889
3903
  isFiltered = _helper$userData.isFiltered,
@@ -3914,13 +3928,13 @@ function updateSelectionBoxHelpers(helpers, selectedObjects, scene) {
3914
3928
  helper.update();
3915
3929
  }
3916
3930
  };
3917
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
3931
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
3918
3932
  if (_loop()) continue;
3919
3933
  }
3920
3934
  } catch (err) {
3921
- _iterator2.e(err);
3935
+ _iterator3.e(err);
3922
3936
  } finally {
3923
- _iterator2.f();
3937
+ _iterator3.f();
3924
3938
  }
3925
3939
  }
3926
3940
 
@@ -12005,11 +12019,11 @@ var SceneExportManager = /*#__PURE__*/function () {
12005
12019
  };
12006
12020
  }
12007
12021
 
12008
- // For components: no children exported connector positions are defined in the component
12009
- // dictionary GLB and will be re-injected at import time via _injectConnectorChildrenFromDictionary.
12010
- // Exporting them here would prevent that injection (it skips if children already exist)
12011
- // and would leave the pathfinder with connectors in incompatible local-position format.
12022
+ // For components: only export child connectors if they were manually added/defined.
12023
+ // Most connectors are injected from dictionary at import time, but some (like manually placed ones)
12024
+ // need to be persisted to maintain connections in the exported scene.
12012
12025
  if (threeObject.children && threeObject.children.length > 0) {
12026
+ var _threeObject$userData11;
12013
12027
  var exportableChildren = [];
12014
12028
  if (isManualSegment) {
12015
12029
  // For manual segments, export their connector children
@@ -12023,6 +12037,25 @@ var SceneExportManager = /*#__PURE__*/function () {
12023
12037
  }
12024
12038
  }
12025
12039
  });
12040
+ } else if (((_threeObject$userData11 = threeObject.userData) === null || _threeObject$userData11 === void 0 ? void 0 : _threeObject$userData11.objectType) === 'component') {
12041
+ // For components, only export connectors that have objectType='connector'
12042
+ // Standard dictionary-injected connectors should be exported so connections work on re-import
12043
+ threeObject.children.forEach(function (child) {
12044
+ var _child$userData2;
12045
+ if (((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'connector') {
12046
+ exportableChildren.push({
12047
+ uuid: child.uuid,
12048
+ name: child.name,
12049
+ type: 'Mesh',
12050
+ position: {
12051
+ x: roundIfClose(child.position.x),
12052
+ y: roundIfClose(child.position.y),
12053
+ z: roundIfClose(child.position.z)
12054
+ },
12055
+ userData: _objectSpread2({}, child.userData)
12056
+ });
12057
+ }
12058
+ });
12026
12059
  }
12027
12060
  if (exportableChildren.length > 0) {
12028
12061
  jsonObject.children = exportableChildren;
@@ -12037,9 +12070,9 @@ var SceneExportManager = /*#__PURE__*/function () {
12037
12070
  // Extract main scene objects (components and standalone connectors)
12038
12071
  var sceneChildren = [];
12039
12072
  this.sceneViewer.scene.children.forEach(function (child) {
12040
- var _child$userData2;
12073
+ var _child$userData3;
12041
12074
  // Only export components and connectors; skip segments, gateways, polylines, etc.
12042
- var objectType = (_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType;
12075
+ var objectType = (_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType;
12043
12076
  if (objectType !== 'component' && objectType !== 'connector') {
12044
12077
  return;
12045
12078
  }
@@ -12203,14 +12236,14 @@ var SceneExportManager = /*#__PURE__*/function () {
12203
12236
  BufferGeometryUtils$1 = BufferGeometryUtilsModule.BufferGeometryUtils || BufferGeometryUtilsModule.default || BufferGeometryUtilsModule; // Create a new scene for export instead of cloning
12204
12237
  exportScene = new _THREE.Scene(); // Helper function to check if an object should be exported
12205
12238
  shouldExport = function shouldExport(child) {
12206
- var _child$name, _child$userData3, _child$userData4, _child$userData5, _child$userData6;
12239
+ var _child$name, _child$userData4, _child$userData5, _child$userData6, _child$userData7;
12207
12240
  if ((_child$name = child.name) !== null && _child$name !== void 0 && _child$name.includes('Polyline')) return false; // Will handle separately
12208
12241
  if (child.name === 'fogPlane') return false; // Skip fog plane
12209
- if ((_child$userData3 = child.userData) !== null && _child$userData3 !== void 0 && _child$userData3.isBrickWall) return false; // Skip environment
12210
- if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBaseGround) return false; // Skip environment
12211
- if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGrid) return false; // Skip environment
12242
+ if ((_child$userData4 = child.userData) !== null && _child$userData4 !== void 0 && _child$userData4.isBrickWall) return false; // Skip environment
12243
+ if ((_child$userData5 = child.userData) !== null && _child$userData5 !== void 0 && _child$userData5.isBaseGround) return false; // Skip environment
12244
+ if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isBaseGrid) return false; // Skip environment
12212
12245
  if (child.isLight) return false; // Skip lights
12213
- if ((_child$userData6 = child.userData) !== null && _child$userData6 !== void 0 && _child$userData6.isTransformControls) return false; // Skip transform controls
12246
+ if ((_child$userData7 = child.userData) !== null && _child$userData7 !== void 0 && _child$userData7.isTransformControls) return false; // Skip transform controls
12214
12247
  if (child.isTransformControls) return false; // Skip transform controls
12215
12248
  if (child.type && child.type.includes('TransformControls')) return false;
12216
12249
  if (child.type && child.type.includes('Helper')) return false; // Skip helpers
@@ -21031,7 +21064,6 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
21031
21064
  };
21032
21065
  return _objectSpread2(_objectSpread2({}, baseData), {}, {
21033
21066
  metadata: component.metadata || {},
21034
- ioConfig: component.ioConfig || null,
21035
21067
  boundingBox: component.boundingBox || null,
21036
21068
  adaptedBoundingBox: component.adaptedBoundingBox || null,
21037
21069
  children: component.children || [],
@@ -29244,6 +29276,31 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
29244
29276
  }
29245
29277
  });
29246
29278
  }
29279
+
29280
+ // Also reinject connectors from cache
29281
+ if (_cached.connectorBBoxes && _cached.connectorBBoxes.length > 0) {
29282
+ if (!_enrichedChild.children) _enrichedChild.children = [];
29283
+ _cached.connectorBBoxes.forEach(function (connBBox) {
29284
+ var existingIndex = _enrichedChild.children.findIndex(function (c) {
29285
+ return c.uuid === connBBox.uuid;
29286
+ });
29287
+ if (existingIndex >= 0) {
29288
+ _enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
29289
+ userData: _objectSpread2(_objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
29290
+ worldBoundingBox: connBBox.worldBoundingBox
29291
+ })
29292
+ });
29293
+ } else {
29294
+ _enrichedChild.children.push({
29295
+ uuid: connBBox.uuid,
29296
+ userData: _objectSpread2(_objectSpread2({}, connBBox.userData), {}, {
29297
+ worldBoundingBox: connBBox.worldBoundingBox
29298
+ }),
29299
+ children: []
29300
+ });
29301
+ }
29302
+ });
29303
+ }
29247
29304
  return _enrichedChild;
29248
29305
  }
29249
29306
 
@@ -29301,11 +29358,40 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
29301
29358
  console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
29302
29359
  }
29303
29360
 
29361
+ // PHASE 2: Enrich Connectors (CRITICAL for pathfinding API)
29362
+ // Also compute world bounding boxes for connectors and inject into children.
29363
+ // This ensures endpoints have world coordinates in sceneDataCopy.
29364
+ var connectorBBoxes = computeConnectorBoundingBoxes(componentObject);
29365
+ if (connectorBBoxes.length > 0) {
29366
+ if (!enrichedChild.children) enrichedChild.children = [];
29367
+ connectorBBoxes.forEach(function (connBBox) {
29368
+ var existingIndex = enrichedChild.children.findIndex(function (c) {
29369
+ return c.uuid === connBBox.uuid;
29370
+ });
29371
+ if (existingIndex >= 0) {
29372
+ enrichedChild.children[existingIndex] = _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
29373
+ userData: _objectSpread2(_objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
29374
+ worldBoundingBox: connBBox.worldBoundingBox
29375
+ })
29376
+ });
29377
+ } else {
29378
+ enrichedChild.children.push({
29379
+ uuid: connBBox.uuid,
29380
+ userData: _objectSpread2(_objectSpread2({}, connBBox.userData), {}, {
29381
+ worldBoundingBox: connBBox.worldBoundingBox
29382
+ }),
29383
+ children: []
29384
+ });
29385
+ }
29386
+ });
29387
+ }
29388
+
29304
29389
  // Store in cache
29305
29390
  _this3._bboxCache.set(child.uuid, {
29306
29391
  matrixHash: _hash,
29307
29392
  filteredBBox: _bboxData,
29308
- ioDeviceBBoxes: ioDeviceBBoxes
29393
+ ioDeviceBBoxes: ioDeviceBBoxes,
29394
+ connectorBBoxes: connectorBBoxes
29309
29395
  });
29310
29396
  return enrichedChild;
29311
29397
  } else {
@@ -30074,7 +30160,7 @@ function attachIODevicesToComponent(_x, _x2, _x3, _x4) {
30074
30160
  }
30075
30161
  function _attachIODevicesToComponent() {
30076
30162
  _attachIODevicesToComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(componentModel, componentData, modelPreloader, parentComponentId) {
30077
- var attachedDevices, _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t, _t2;
30163
+ var attachedDevices, _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t, _t2;
30078
30164
  return _regenerator().w(function (_context) {
30079
30165
  while (1) switch (_context.n) {
30080
30166
  case 0:
@@ -30152,20 +30238,14 @@ function _attachIODevicesToComponent() {
30152
30238
  // Name the device model
30153
30239
  deviceModel.name = "".concat(attachment.attachmentLabel || 'IO Device', " (").concat(attachmentId, ")");
30154
30240
 
30155
- // Set user data for identification — include ioConfig data points so the
30156
- // component tooltip can render state displays without an extra lookup.
30241
+ // Set user data for identification
30157
30242
  deviceModel.userData = {
30158
30243
  objectType: 'io-device',
30159
30244
  deviceId: attachment.deviceId,
30160
30245
  attachmentId: attachmentId,
30161
30246
  attachmentLabel: attachment.attachmentLabel,
30162
30247
  parentComponentId: parentComponentId,
30163
- deviceName: deviceData.name || '',
30164
- // Snapshot of the device's data point definitions (stateType, stateConfig, direction, etc.)
30165
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
30166
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
30167
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
30168
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output'
30248
+ deviceName: deviceData.name || ''
30169
30249
  };
30170
30250
 
30171
30251
  // Position at the attachment point
@@ -34128,9 +34208,29 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34128
34208
  min: boundingBox.min.toArray(),
34129
34209
  max: boundingBox.max.toArray()
34130
34210
  }
34131
- })
34211
+ }),
34212
+ children: [] // Initialize children array
34132
34213
  };
34133
34214
 
34215
+ // Collect children that are connectors
34216
+ if (componentModel.children) {
34217
+ componentModel.children.forEach(function (child) {
34218
+ if (child.userData && child.userData.objectType === 'connector') {
34219
+ componentSceneData.children.push({
34220
+ uuid: child.uuid,
34221
+ name: child.name,
34222
+ type: 'Mesh',
34223
+ position: {
34224
+ x: child.position.x,
34225
+ y: child.position.y,
34226
+ z: child.position.z
34227
+ },
34228
+ userData: _objectSpread2({}, child.userData)
34229
+ });
34230
+ }
34231
+ });
34232
+ }
34233
+
34134
34234
  // Add the component to the scene data
34135
34235
  if (!currentSceneData.scene.children) {
34136
34236
  currentSceneData.scene.children = [];
@@ -34539,8 +34639,7 @@ var CollisionManager = /*#__PURE__*/function () {
34539
34639
  if (child === object || _this._isDescendantOf(object, child)) return;
34540
34640
 
34541
34641
  // Filter by CP object types at the root level (skip internal meshes during traverse)
34542
- var isCPRootObject = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'component' || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment' || ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'gateway' || ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) === 'connector' || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.ioConfig); // IO Device
34543
-
34642
+ var isCPRootObject = ((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'component' || ((_child$userData2 = child.userData) === null || _child$userData2 === void 0 ? void 0 : _child$userData2.objectType) === 'segment' || ((_child$userData3 = child.userData) === null || _child$userData3 === void 0 ? void 0 : _child$userData3.objectType) === 'gateway' || ((_child$userData4 = child.userData) === null || _child$userData4 === void 0 ? void 0 : _child$userData4.objectType) === 'connector' || ((_child$userData5 = child.userData) === null || _child$userData5 === void 0 ? void 0 : _child$userData5.objectType) === 'io-device';
34544
34643
  if (isCPRootObject && !_this._shouldExclude(child, excludeTypes)) {
34545
34644
  // Use cached worldBoundingBox if available, otherwise compute it (and cache it)
34546
34645
  var childBBox;
@@ -35168,7 +35267,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
35168
35267
  key: "_attachIODeviceModelsToPreview",
35169
35268
  value: (function () {
35170
35269
  var _attachIODeviceModelsToPreview2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
35171
- var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
35270
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
35172
35271
  return _regenerator().w(function (_context3) {
35173
35272
  while (1) switch (_context3.n) {
35174
35273
  case 0:
@@ -35231,12 +35330,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
35231
35330
  deviceId: attachment.deviceId,
35232
35331
  attachmentId: attachmentId,
35233
35332
  attachmentLabel: attachment.attachmentLabel,
35234
- deviceName: deviceData.name || '',
35235
- // Snapshot of data point definitions so the tooltip can render state
35236
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
35237
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
35238
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
35239
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output'
35333
+ deviceName: deviceData.name || ''
35240
35334
  };
35241
35335
  if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
35242
35336
  pos = attachment.attachmentPoint.position;
@@ -37272,8 +37366,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37272
37366
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
37273
37367
  var attachmentId = child.userData.attachmentId || '';
37274
37368
 
37275
- // Use only data points from the animate window (behaviorConfig).
37276
- // The static ioConfig.states[] snapshot on userData is intentionally ignored.
37369
+ // Use data points from the animate window (behaviorConfig).
37277
37370
  var dataPoints = resolveDataPoints(parentUuid, attachmentId, child.userData, getIoBehaviorManager(_this3.sceneViewer));
37278
37371
 
37279
37372
  // When data points come from behaviorConfig they already carry direction:'input'.
@@ -37486,7 +37579,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37486
37579
  * Input / bidirectional → shows an interactive control.
37487
37580
  *
37488
37581
  * @param {string} scopedAttachmentId - Scoped attachment ID (parentUuid::attachmentId) for state isolation
37489
- * @param {Object} dp - data point definition from ioConfig.dataPoints
37582
+ * @param {Object} dp - data point definition
37490
37583
  * @param {string} [deviceDirection] - device-level direction ('input'|'output'), overrides dp.direction
37491
37584
  * @param {string} [originalAttachmentId] - Original attachment ID for behavior triggering
37492
37585
  * @returns {HTMLElement}
@@ -38502,8 +38595,7 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
38502
38595
  /**
38503
38596
  * Return tooltip-compatible data point definitions derived from the loaded
38504
38597
  * animation entries for a given attachment. Used by componentTooltipManager
38505
- * to replace the static ioConfig.states[] snapshot with the richer animation
38506
- * state definitions created in the Animate window.
38598
+ * to provide the richer animation state definitions created in the Animate window.
38507
38599
  *
38508
38600
  * One dp object is emitted per unique stateVariable; multiple mesh entries
38509
38601
  * that share the same stateVariable are collapsed into one.
@@ -40919,7 +41011,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40919
41011
  * Initialize the CentralPlant manager
40920
41012
  *
40921
41013
  * @constructor
40922
- * @version 0.3.46
41014
+ * @version 0.3.47
40923
41015
  * @updated 2025-10-22
40924
41016
  *
40925
41017
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -42219,7 +42311,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42219
42311
  * List all available I/O Device assets from the component dictionary.
42220
42312
  * @param {Object} [options={}]
42221
42313
  * @param {'all'|'bundled'|'user'} [options.source='all'] - Filter by asset origin
42222
- * @returns {Array<{uuid: string, name: string, assetType: string, ioConfig: Object}>}
42314
+ * @returns {Array<{uuid: string, name: string, assetType: string}>}
42223
42315
  * @example
42224
42316
  * const devices = centralPlant.getIoDevices({ source: 'all' })
42225
42317
  * const bundledOnly = centralPlant.getIoDevices({ source: 'bundled' })
@@ -42246,8 +42338,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
42246
42338
  return {
42247
42339
  uuid: entry.uuid || entry.id,
42248
42340
  name: entry.name,
42249
- assetType: entry.assetType,
42250
- ioConfig: entry.ioConfig || {}
42341
+ assetType: entry.assetType
42251
42342
  };
42252
42343
  });
42253
42344
  }
@@ -44568,7 +44659,6 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
44568
44659
  }, {
44569
44660
  key: "updatePaths",
44570
44661
  value: function updatePaths() {
44571
- console.log("updatePaths started");
44572
44662
  // Delegate pathfinding update to PathfindingManager
44573
44663
  if (this.pathfindingManager) {
44574
44664
  this.pathfindingManager.updatePathfindingAfterTransform(this.currentSceneData);
@@ -37,7 +37,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
37
37
  * Initialize the CentralPlant manager
38
38
  *
39
39
  * @constructor
40
- * @version 0.3.46
40
+ * @version 0.3.47
41
41
  * @updated 2025-10-22
42
42
  *
43
43
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -1337,7 +1337,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1337
1337
  * List all available I/O Device assets from the component dictionary.
1338
1338
  * @param {Object} [options={}]
1339
1339
  * @param {'all'|'bundled'|'user'} [options.source='all'] - Filter by asset origin
1340
- * @returns {Array<{uuid: string, name: string, assetType: string, ioConfig: Object}>}
1340
+ * @returns {Array<{uuid: string, name: string, assetType: string}>}
1341
1341
  * @example
1342
1342
  * const devices = centralPlant.getIoDevices({ source: 'all' })
1343
1343
  * const bundledOnly = centralPlant.getIoDevices({ source: 'bundled' })
@@ -1364,8 +1364,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1364
1364
  return {
1365
1365
  uuid: entry.uuid || entry.id,
1366
1366
  name: entry.name,
1367
- assetType: entry.assetType,
1368
- ioConfig: entry.ioConfig || {}
1367
+ assetType: entry.assetType
1369
1368
  };
1370
1369
  });
1371
1370
  }
@@ -647,7 +647,6 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
647
647
  }, {
648
648
  key: "updatePaths",
649
649
  value: function updatePaths() {
650
- console.log("updatePaths started");
651
650
  // Delegate pathfinding update to PathfindingManager
652
651
  if (this.pathfindingManager) {
653
652
  this.pathfindingManager.updatePathfindingAfterTransform(this.currentSceneData);
@@ -537,8 +537,7 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
537
537
  /**
538
538
  * Return tooltip-compatible data point definitions derived from the loaded
539
539
  * animation entries for a given attachment. Used by componentTooltipManager
540
- * to replace the static ioConfig.states[] snapshot with the richer animation
541
- * state definitions created in the Animate window.
540
+ * to provide the richer animation state definitions created in the Animate window.
542
541
  *
543
542
  * One dp object is emitted per unique stateVariable; multiple mesh entries
544
543
  * that share the same stateVariable are collapsed into one.
@@ -888,7 +888,6 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
888
888
  };
889
889
  return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, baseData), {}, {
890
890
  metadata: component.metadata || {},
891
- ioConfig: component.ioConfig || null,
892
891
  boundingBox: component.boundingBox || null,
893
892
  adaptedBoundingBox: component.adaptedBoundingBox || null,
894
893
  children: component.children || [],
@@ -345,7 +345,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
345
345
  key: "_attachIODeviceModelsToPreview",
346
346
  value: (function () {
347
347
  var _attachIODeviceModelsToPreview2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
348
- var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _deviceData$ioConfig, _deviceData$ioConfig2, _deviceData$ioConfig3, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
348
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
349
349
  return _rollupPluginBabelHelpers.regenerator().w(function (_context3) {
350
350
  while (1) switch (_context3.n) {
351
351
  case 0:
@@ -408,12 +408,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
408
408
  deviceId: attachment.deviceId,
409
409
  attachmentId: attachmentId,
410
410
  attachmentLabel: attachment.attachmentLabel,
411
- deviceName: deviceData.name || '',
412
- // Snapshot of data point definitions so the tooltip can render state
413
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
414
- dataPoints: ((_deviceData$ioConfig = deviceData.ioConfig) === null || _deviceData$ioConfig === void 0 ? void 0 : _deviceData$ioConfig.states) || ((_deviceData$ioConfig2 = deviceData.ioConfig) === null || _deviceData$ioConfig2 === void 0 ? void 0 : _deviceData$ioConfig2.dataPoints) || [],
415
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
416
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output'
411
+ deviceName: deviceData.name || ''
417
412
  };
418
413
  if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
419
414
  pos = attachment.attachmentPoint.position;
@@ -328,6 +328,31 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
328
328
  }
329
329
  });
330
330
  }
331
+
332
+ // Also reinject connectors from cache
333
+ if (_cached.connectorBBoxes && _cached.connectorBBoxes.length > 0) {
334
+ if (!_enrichedChild.children) _enrichedChild.children = [];
335
+ _cached.connectorBBoxes.forEach(function (connBBox) {
336
+ var existingIndex = _enrichedChild.children.findIndex(function (c) {
337
+ return c.uuid === connBBox.uuid;
338
+ });
339
+ if (existingIndex >= 0) {
340
+ _enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex]), {}, {
341
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, _enrichedChild.children[existingIndex].userData), {}, {
342
+ worldBoundingBox: connBBox.worldBoundingBox
343
+ })
344
+ });
345
+ } else {
346
+ _enrichedChild.children.push({
347
+ uuid: connBBox.uuid,
348
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, connBBox.userData), {}, {
349
+ worldBoundingBox: connBBox.worldBoundingBox
350
+ }),
351
+ children: []
352
+ });
353
+ }
354
+ });
355
+ }
331
356
  return _enrichedChild;
332
357
  }
333
358
 
@@ -385,11 +410,40 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
385
410
  console.log("\uD83D\uDCE6 Injected ".concat(ioDeviceBBoxes.length, " io-device bounding box(es) for component ").concat(child.uuid));
386
411
  }
387
412
 
413
+ // PHASE 2: Enrich Connectors (CRITICAL for pathfinding API)
414
+ // Also compute world bounding boxes for connectors and inject into children.
415
+ // This ensures endpoints have world coordinates in sceneDataCopy.
416
+ var connectorBBoxes = boundingBoxUtils.computeConnectorBoundingBoxes(componentObject);
417
+ if (connectorBBoxes.length > 0) {
418
+ if (!enrichedChild.children) enrichedChild.children = [];
419
+ connectorBBoxes.forEach(function (connBBox) {
420
+ var existingIndex = enrichedChild.children.findIndex(function (c) {
421
+ return c.uuid === connBBox.uuid;
422
+ });
423
+ if (existingIndex >= 0) {
424
+ enrichedChild.children[existingIndex] = _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex]), {}, {
425
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, enrichedChild.children[existingIndex].userData), {}, {
426
+ worldBoundingBox: connBBox.worldBoundingBox
427
+ })
428
+ });
429
+ } else {
430
+ enrichedChild.children.push({
431
+ uuid: connBBox.uuid,
432
+ userData: _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, connBBox.userData), {}, {
433
+ worldBoundingBox: connBBox.worldBoundingBox
434
+ }),
435
+ children: []
436
+ });
437
+ }
438
+ });
439
+ }
440
+
388
441
  // Store in cache
389
442
  _this3._bboxCache.set(child.uuid, {
390
443
  matrixHash: _hash,
391
444
  filteredBBox: _bboxData,
392
- ioDeviceBBoxes: ioDeviceBBoxes
445
+ ioDeviceBBoxes: ioDeviceBBoxes,
446
+ connectorBBoxes: connectorBBoxes
393
447
  });
394
448
  return enrichedChild;
395
449
  } else {