@2112-lab/central-plant 0.3.46 → 0.3.48

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 (39) hide show
  1. package/dist/bundle/index.js +624 -425
  2. package/dist/cjs/src/core/centralPlant.js +46 -46
  3. package/dist/cjs/src/core/centralPlantInternals.js +4 -2
  4. package/dist/cjs/src/core/sceneViewer.js +1 -2
  5. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +1 -2
  6. package/dist/cjs/src/managers/components/componentDataManager.js +0 -1
  7. package/dist/cjs/src/managers/components/componentManager.js +11 -3
  8. package/dist/cjs/src/managers/components/transformOperationsManager.js +9 -11
  9. package/dist/cjs/src/managers/controls/componentDragManager.js +2 -7
  10. package/dist/cjs/src/managers/controls/transformControlsManager.js +83 -44
  11. package/dist/cjs/src/managers/pathfinding/pathfindingManager.js +274 -192
  12. package/dist/cjs/src/managers/scene/collisionManager.js +1 -2
  13. package/dist/cjs/src/managers/scene/componentTooltipManager.js +2 -3
  14. package/dist/cjs/src/managers/scene/modelManager.js +33 -10
  15. package/dist/cjs/src/managers/scene/sceneExportManager.js +42 -12
  16. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +26 -1
  17. package/dist/cjs/src/utils/behaviorDispatch.js +11 -42
  18. package/dist/cjs/src/utils/boundingBoxUtils.js +79 -36
  19. package/dist/cjs/src/utils/ioDeviceUtils.js +3 -9
  20. package/dist/esm/src/core/centralPlant.js +46 -46
  21. package/dist/esm/src/core/centralPlantInternals.js +4 -2
  22. package/dist/esm/src/core/sceneViewer.js +1 -2
  23. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +1 -2
  24. package/dist/esm/src/managers/components/componentDataManager.js +0 -1
  25. package/dist/esm/src/managers/components/componentManager.js +11 -3
  26. package/dist/esm/src/managers/components/transformOperationsManager.js +9 -11
  27. package/dist/esm/src/managers/controls/componentDragManager.js +2 -7
  28. package/dist/esm/src/managers/controls/transformControlsManager.js +83 -44
  29. package/dist/esm/src/managers/pathfinding/pathfindingManager.js +276 -194
  30. package/dist/esm/src/managers/scene/collisionManager.js +1 -2
  31. package/dist/esm/src/managers/scene/componentTooltipManager.js +2 -3
  32. package/dist/esm/src/managers/scene/modelManager.js +33 -10
  33. package/dist/esm/src/managers/scene/sceneExportManager.js +41 -13
  34. package/dist/esm/src/managers/scene/sceneOperationsManager.js +26 -1
  35. package/dist/esm/src/utils/behaviorDispatch.js +11 -42
  36. package/dist/esm/src/utils/boundingBoxUtils.js +80 -38
  37. package/dist/esm/src/utils/ioDeviceUtils.js +3 -9
  38. package/dist/index.d.ts +0 -6
  39. package/package.json +1 -1
@@ -33,7 +33,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
33
33
  * Initialize the CentralPlant manager
34
34
  *
35
35
  * @constructor
36
- * @version 0.3.46
36
+ * @version 0.3.48
37
37
  * @updated 2025-10-22
38
38
  *
39
39
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -363,7 +363,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
363
363
  }, {
364
364
  key: "translate",
365
365
  value: function translate(componentId, axis, value) {
366
- return this.internals.translateComponent(componentId, axis, value);
366
+ var skipPathUpdate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
367
+ return this.internals.translateComponent(componentId, axis, value, skipPathUpdate);
367
368
  }
368
369
 
369
370
  /**
@@ -1061,18 +1062,19 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1061
1062
  return [];
1062
1063
  }
1063
1064
 
1064
- // Traverse through all components in the scene data
1065
- sceneData.scene.children.forEach(function (component) {
1066
- // Each component may have connector children
1067
- if (component.children && Array.isArray(component.children)) {
1068
- component.children.forEach(function (child) {
1069
- // Check if this child is a connector
1070
- if (child.userData && child.userData.objectType === 'connector' && child.uuid) {
1071
- allConnectorIds.push(child.uuid);
1072
- }
1073
- });
1074
- }
1075
- });
1065
+ // Recursive search for connector IDs
1066
+ var _findConnectorIds = function findConnectorIds(nodes) {
1067
+ if (!nodes || !Array.isArray(nodes)) return;
1068
+ nodes.forEach(function (node) {
1069
+ if (node.userData && node.userData.objectType === 'connector' && node.uuid) {
1070
+ allConnectorIds.push(node.uuid);
1071
+ }
1072
+ if (node.children && Array.isArray(node.children)) {
1073
+ _findConnectorIds(node.children);
1074
+ }
1075
+ });
1076
+ };
1077
+ _findConnectorIds(sceneData.scene.children);
1076
1078
 
1077
1079
  // Get existing connections
1078
1080
  var existingConnections = this.getConnections();
@@ -1102,28 +1104,28 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1102
1104
  * const infos = centralPlant.getAvailableConnectionsInfo()
1103
1105
  * // [{ id: 'PUMP-1-CONNECTOR-1', flow: 'out' }, ...]
1104
1106
  */
1107
+ /**
1108
+ * Get all connectors in the scene that are currently available (not used in a connection).
1109
+ * Searches the active Three.js scene directly for the most accurate results.
1110
+ * @returns {Array<{id: string, flow: string}>} Array of connector info objects
1111
+ * @since 0.1.72 (Updated to search Three.js scene directly)
1112
+ */
1105
1113
  }, {
1106
1114
  key: "getAvailableConnectionsInfo",
1107
1115
  value: function getAvailableConnectionsInfo() {
1108
- if (!this.sceneViewer || !this.sceneViewer.currentSceneData) {
1109
- console.warn('⚠️ getAvailableConnectionsInfo(): Scene viewer or current scene data not available');
1110
- return [];
1111
- }
1112
- var sceneData = this.sceneViewer.currentSceneData;
1113
- if (!sceneData.scene || !sceneData.scene.children) {
1114
- console.warn('⚠️ getAvailableConnectionsInfo(): Invalid scene data structure');
1116
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
1117
+ console.warn('⚠️ getAvailableConnectionsInfo(): Scene viewer or scene not available');
1115
1118
  return [];
1116
1119
  }
1117
1120
  var allConnectorInfos = [];
1118
- sceneData.scene.children.forEach(function (component) {
1119
- if (component.children && Array.isArray(component.children)) {
1120
- component.children.forEach(function (child) {
1121
- if (child.userData && child.userData.objectType === 'connector' && child.uuid) {
1122
- allConnectorInfos.push({
1123
- id: child.uuid,
1124
- flow: child.userData.flow || 'bi'
1125
- });
1126
- }
1121
+
1122
+ // Search the Three.js scene directly for connectors
1123
+ // This is more reliable than searching currentSceneData which might be stale
1124
+ this.sceneViewer.scene.traverse(function (node) {
1125
+ if (node.userData && node.userData.objectType === 'connector' && node.uuid) {
1126
+ allConnectorInfos.push({
1127
+ id: node.uuid,
1128
+ flow: node.userData.flow || 'bi'
1127
1129
  });
1128
1130
  }
1129
1131
  });
@@ -1145,30 +1147,29 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1145
1147
  * const result = centralPlant.validateConnections()
1146
1148
  * result.invalid.forEach(({ connection, reason }) => console.warn(reason, connection))
1147
1149
  */
1150
+ /**
1151
+ * Validate all connections in the current scene for flow direction compatibility.
1152
+ * Searches the active Three.js scene for connector flow markers.
1153
+ * @returns {{ valid: Array<Object>, invalid: Array<{connection: Object, reason: string}> }}
1154
+ * @since 0.1.72 (Updated to search Three.js scene directly)
1155
+ */
1148
1156
  }, {
1149
1157
  key: "validateConnections",
1150
1158
  value: function validateConnections() {
1151
- if (!this.sceneViewer || !this.sceneViewer.currentSceneData) {
1152
- console.warn('⚠️ validateConnections(): Scene viewer or current scene data not available');
1159
+ if (!this.sceneViewer || !this.sceneViewer.scene) {
1160
+ console.warn('⚠️ validateConnections(): Scene viewer or scene not available');
1153
1161
  return {
1154
1162
  valid: [],
1155
1163
  invalid: []
1156
1164
  };
1157
1165
  }
1158
1166
  var connections = this.getConnections();
1159
- var sceneData = this.sceneViewer.currentSceneData;
1160
1167
 
1161
- // Build lookup map: connectorId → flow
1168
+ // Build lookup map: connectorId → flow from the Three.js scene
1162
1169
  var flowMap = {};
1163
- var scene = sceneData.scene || {};
1164
- var children = scene.children || [];
1165
- children.forEach(function (component) {
1166
- if (component.children && Array.isArray(component.children)) {
1167
- component.children.forEach(function (child) {
1168
- if (child.userData && child.userData.objectType === 'connector' && child.uuid) {
1169
- flowMap[child.uuid] = child.userData.flow || 'bi';
1170
- }
1171
- });
1170
+ this.sceneViewer.scene.traverse(function (node) {
1171
+ if (node.userData && node.userData.objectType === 'connector' && node.uuid) {
1172
+ flowMap[node.uuid] = node.userData.flow || 'bi';
1172
1173
  }
1173
1174
  });
1174
1175
  var valid = [];
@@ -1333,7 +1334,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1333
1334
  * List all available I/O Device assets from the component dictionary.
1334
1335
  * @param {Object} [options={}]
1335
1336
  * @param {'all'|'bundled'|'user'} [options.source='all'] - Filter by asset origin
1336
- * @returns {Array<{uuid: string, name: string, assetType: string, ioConfig: Object}>}
1337
+ * @returns {Array<{uuid: string, name: string, assetType: string}>}
1337
1338
  * @example
1338
1339
  * const devices = centralPlant.getIoDevices({ source: 'all' })
1339
1340
  * const bundledOnly = centralPlant.getIoDevices({ source: 'bundled' })
@@ -1360,8 +1361,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
1360
1361
  return {
1361
1362
  uuid: entry.uuid || entry.id,
1362
1363
  name: entry.name,
1363
- assetType: entry.assetType,
1364
- ioConfig: entry.ioConfig || {}
1364
+ assetType: entry.assetType
1365
1365
  };
1366
1366
  });
1367
1367
  }
@@ -450,17 +450,19 @@ var CentralPlantInternals = /*#__PURE__*/function () {
450
450
  * @param {string} componentId - The UUID of the component to translate
451
451
  * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
452
452
  * @param {number} value - The value to translate by
453
+ * @param {boolean} [skipPathUpdate=false] - Whether to skip triggering pathfinding
453
454
  * @returns {boolean} True if translation was successful, false otherwise
454
455
  */
455
456
  }, {
456
457
  key: "translateComponent",
457
458
  value: function translateComponent(componentId, axis, value) {
458
459
  var _this$centralPlant$ma;
460
+ var skipPathUpdate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
459
461
  if (!((_this$centralPlant$ma = this.centralPlant.managers) !== null && _this$centralPlant$ma !== void 0 && _this$centralPlant$ma.transformOperationsManager)) {
460
462
  console.error('❌ translateComponent(): TransformOperationsManager not available');
461
463
  return false;
462
464
  }
463
- return this.centralPlant.managers.transformOperationsManager.translateComponent(componentId, axis, value);
465
+ return this.centralPlant.managers.transformOperationsManager.translateComponent(componentId, axis, value, skipPathUpdate);
464
466
  }
465
467
 
466
468
  /**
@@ -552,7 +554,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
552
554
  } else if (objectType === 'gateway') {
553
555
  success = this.translateGateway(objectId, axis, value);
554
556
  } else if (objectType === 'component') {
555
- success = this.translateComponent(objectId, axis, value);
557
+ success = this.translateComponent(objectId, axis, value, skipPathUpdate);
556
558
  } else {
557
559
  result.errors.push("Unknown object type: ".concat(objectType, " (").concat(objectId, ")"));
558
560
  console.warn("\u26A0\uFE0F Unknown object type: ".concat(objectType, " for ").concat(objectId));
@@ -98,7 +98,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
98
98
  this.centralPlant.attachToComponent();
99
99
 
100
100
  // Sync our managers tracking object after attachment
101
- managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'ioBehaviorManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
101
+ managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'transformOperationsManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'ioBehaviorManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
102
102
  managerKeys.forEach(function (key) {
103
103
  if (_this2[key]) {
104
104
  _this2.managers[key] = _this2[key];
@@ -643,7 +643,6 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
643
643
  }, {
644
644
  key: "updatePaths",
645
645
  value: function updatePaths() {
646
- console.log("updatePaths started");
647
646
  // Delegate pathfinding update to PathfindingManager
648
647
  if (this.pathfindingManager) {
649
648
  this.pathfindingManager.updatePathfindingAfterTransform(this.currentSceneData);
@@ -513,8 +513,7 @@ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
513
513
  /**
514
514
  * Return tooltip-compatible data point definitions derived from the loaded
515
515
  * animation entries for a given attachment. Used by componentTooltipManager
516
- * to replace the static ioConfig.states[] snapshot with the richer animation
517
- * state definitions created in the Animate window.
516
+ * to provide the richer animation state definitions created in the Animate window.
518
517
  *
519
518
  * One dp object is emitted per unique stateVariable; multiple mesh entries
520
519
  * that share the same stateVariable are collapsed into one.
@@ -864,7 +864,6 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
864
864
  };
865
865
  return _objectSpread2(_objectSpread2({}, baseData), {}, {
866
866
  metadata: component.metadata || {},
867
- ioConfig: component.ioConfig || null,
868
867
  boundingBox: component.boundingBox || null,
869
868
  adaptedBoundingBox: component.adaptedBoundingBox || null,
870
869
  children: component.children || [],
@@ -20,7 +20,7 @@ var ComponentManager = /*#__PURE__*/function () {
20
20
  key: "addComponentToScene",
21
21
  value: (function () {
22
22
  var _addComponentToScene = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(componentData) {
23
- var _gltfScene$userData, uuid, componentDictionary, libraryComponent, position, gltfScene, componentMesh, event, _t, _t2;
23
+ var _gltfScene$userData, uuid, componentDictionary, libraryComponent, position, gltfScene, componentMesh, connectorIndex, event, _t, _t2;
24
24
  return _regenerator().w(function (_context) {
25
25
  while (1) switch (_context.n) {
26
26
  case 0:
@@ -124,6 +124,7 @@ var ComponentManager = /*#__PURE__*/function () {
124
124
  }
125
125
 
126
126
  // Enable shadows for all meshes in the component
127
+ connectorIndex = 0;
127
128
  componentMesh.traverse(function (child) {
128
129
  if (child.isMesh) {
129
130
  child.castShadow = true;
@@ -133,12 +134,19 @@ var ComponentManager = /*#__PURE__*/function () {
133
134
  }
134
135
 
135
136
  // Set connector properties for connectors within the component
136
- if (child.name && child.name.toLowerCase().includes('connector')) {
137
+ if (child.name && child.name.toLowerCase().includes('connector') || child.userData && child.userData.objectType === 'connector') {
138
+ connectorIndex++;
137
139
  if (!child.userData) {
138
140
  child.userData = {};
139
141
  }
140
142
  child.userData.objectType = 'connector';
141
- console.log("\uD83D\uDD27 Set objectType='connector' for: ".concat(child.name));
143
+
144
+ // Assign a predictable, stable UUID to the connector
145
+ // This ensures connections made in the sandbox survive export/import
146
+ var connectorUuid = "".concat(uuid, "-CONNECTOR-").concat(connectorIndex);
147
+ child.uuid = connectorUuid;
148
+ child.userData.originalUuid = connectorUuid;
149
+ console.log("\uD83D\uDD27 Set predictable connector UUID: ".concat(connectorUuid, " for: ").concat(child.name));
142
150
  }
143
151
  }
144
152
  });
@@ -16,12 +16,14 @@ var TransformOperationsManager = /*#__PURE__*/function () {
16
16
  * @param {string} componentId - The UUID of the component to translate
17
17
  * @param {string} axis - The axis to translate on ('x', 'y', or 'z')
18
18
  * @param {number} value - The value to translate by
19
+ * @param {boolean} [skipPathUpdate=false] - Whether to skip triggering pathfinding after translation
19
20
  * @returns {boolean} True if translation was successful, false otherwise
20
21
  */
21
22
  return _createClass(TransformOperationsManager, [{
22
23
  key: "translateComponent",
23
24
  value: function translateComponent(componentId, axis, value) {
24
25
  var _this$sceneViewer$man, _this$sceneViewer;
26
+ var skipPathUpdate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
25
27
  // Store transform parameters using the OperationHistoryManager
26
28
  if ((_this$sceneViewer$man = this.sceneViewer.managers) !== null && _this$sceneViewer$man !== void 0 && _this$sceneViewer$man.operationHistoryManager) {
27
29
  this.sceneViewer.managers.operationHistoryManager.addToOperationHistory('translateComponent', {
@@ -89,7 +91,7 @@ var TransformOperationsManager = /*#__PURE__*/function () {
89
91
  }
90
92
 
91
93
  // Auto-update paths if enabled (matches behavior of translateSegment and translateGateway)
92
- if (this.sceneViewer.shouldUpdatePaths) {
94
+ if (!skipPathUpdate && this.sceneViewer.shouldUpdatePaths) {
93
95
  try {
94
96
  if (this.sceneViewer && typeof this.sceneViewer.updatePaths === 'function') {
95
97
  this.sceneViewer.updatePaths();
@@ -100,6 +102,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
100
102
  } catch (error) {
101
103
  console.error('❌ Error auto-updating paths:', error);
102
104
  }
105
+ } else if (skipPathUpdate) {
106
+ console.log('ℹ️ skipPathUpdate is true: skipping automatic path update');
103
107
  }
104
108
 
105
109
  // Emit transform event if available
@@ -1918,11 +1922,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1918
1922
  sceneDataComponent.children.forEach(function (child) {
1919
1923
  var _child$userData21;
1920
1924
  if (((_child$userData21 = child.userData) === null || _child$userData21 === void 0 ? void 0 : _child$userData21.objectType) === 'connector') {
1921
- // Find the actual connector object in the scene
1922
- var connectorObj = component.children.find(function (c) {
1923
- var _c$userData;
1924
- return c.uuid === child.uuid || ((_c$userData = c.userData) === null || _c$userData === void 0 ? void 0 : _c$userData.originalUuid) === child.uuid;
1925
- });
1925
+ // Find the actual connector object in the scene (recursive search for nested connectors)
1926
+ var connectorObj = component.getObjectByProperty('uuid', child.uuid) || component.getObjectByProperty('name', child.uuid);
1926
1927
  if (connectorObj) {
1927
1928
  // Get world position of connector
1928
1929
  var worldPos = new THREE.Vector3();
@@ -1981,11 +1982,8 @@ var TransformOperationsManager = /*#__PURE__*/function () {
1981
1982
  sceneDataComponent.children.forEach(function (child) {
1982
1983
  var _child$userData22;
1983
1984
  if (((_child$userData22 = child.userData) === null || _child$userData22 === void 0 ? void 0 : _child$userData22.objectType) === 'connector') {
1984
- // Find the actual connector object in the scene
1985
- var connectorObj = component.children.find(function (c) {
1986
- var _c$userData2;
1987
- return c.uuid === child.uuid || ((_c$userData2 = c.userData) === null || _c$userData2 === void 0 ? void 0 : _c$userData2.originalUuid) === child.uuid;
1988
- });
1985
+ // Find the actual connector object in the scene (recursive search for nested connectors)
1986
+ var connectorObj = component.getObjectByProperty('uuid', child.uuid) || component.getObjectByProperty('name', child.uuid);
1989
1987
  if (connectorObj) {
1990
1988
  var _connectorObj$userDat;
1991
1989
  // Get world position of connector
@@ -321,7 +321,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
321
321
  key: "_attachIODeviceModelsToPreview",
322
322
  value: (function () {
323
323
  var _attachIODeviceModelsToPreview2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(parentObject, componentData, modelPreloader) {
324
- 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;
324
+ var _i, _Object$entries, _Object$entries$_i, attachmentId, attachment, _modelPreloader$compo, _attachment$attachmen, _attachment$attachmen2, deviceData, cachedDevice, _modelPreloader$loadi, deviceModel, pos, rot, deg2rad, _t3;
325
325
  return _regenerator().w(function (_context3) {
326
326
  while (1) switch (_context3.n) {
327
327
  case 0:
@@ -384,12 +384,7 @@ var ComponentDragManager = /*#__PURE__*/function (_BaseDisposable) {
384
384
  deviceId: attachment.deviceId,
385
385
  attachmentId: attachmentId,
386
386
  attachmentLabel: attachment.attachmentLabel,
387
- deviceName: deviceData.name || '',
388
- // Snapshot of data point definitions so the tooltip can render state
389
- // ioConfig can use either 'states' (preferred) or legacy 'dataPoints' as the array key
390
- 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) || [],
391
- // Device-level I/O direction: 'input' means the user can write state via the tooltip
392
- ioDirection: ((_deviceData$ioConfig3 = deviceData.ioConfig) === null || _deviceData$ioConfig3 === void 0 ? void 0 : _deviceData$ioConfig3.direction) || 'output'
387
+ deviceName: deviceData.name || ''
393
388
  };
394
389
  if ((_attachment$attachmen = attachment.attachmentPoint) !== null && _attachment$attachmen !== void 0 && _attachment$attachmen.position) {
395
390
  pos = attachment.attachmentPoint.position;
@@ -40,7 +40,7 @@ var TransformControlsManager = /*#__PURE__*/function () {
40
40
  this.forceInvisible = false;
41
41
 
42
42
  // SceneViewer reference for event listening
43
- this.sceneViewer = null;
43
+ this.sceneViewer = centralPlant ? centralPlant.sceneViewer : null;
44
44
  this._objectTransformedListener = null;
45
45
 
46
46
  // Event handlers storage
@@ -242,23 +242,28 @@ var TransformControlsManager = /*#__PURE__*/function () {
242
242
  _this2.transformState.isTransforming = true;
243
243
 
244
244
  // Store initial transforms for all selected objects
245
- if (_this2.selectedObjects.length > 0 && _this2.multiSelectionGroup) {
245
+ if (_this2.selectedObjects.length > 0) {
246
246
  _this2.selectedObjects.forEach(function (obj) {
247
- obj.userData._multiSelectOriginalPosition = obj.position.clone();
248
- obj.userData._multiSelectOriginalRotation = obj.rotation.clone();
249
- obj.userData._multiSelectOriginalScale = obj.scale.clone();
250
- });
251
-
252
- // Snapshot group position and helper geometry vertices so we can do
253
- // cheap per-frame bbox translation without re-traversing the mesh hierarchy.
254
- _this2._dragStartGroupPosition = _this2.multiSelectionGroup.position.clone();
255
- _this2.boundingBoxHelpers.forEach(function (helper) {
256
- var _helper$geometry;
257
- var posAttr = (_helper$geometry = helper.geometry) === null || _helper$geometry === void 0 || (_helper$geometry = _helper$geometry.attributes) === null || _helper$geometry === void 0 ? void 0 : _helper$geometry.position;
258
- if (posAttr) {
259
- helper.userData._dragStartPositions = new Float32Array(posAttr.array);
247
+ obj.userData._dragStartRotation = obj.rotation.clone();
248
+ obj.userData._dragStartPosition = obj.position.clone();
249
+ if (_this2.multiSelectionGroup) {
250
+ obj.userData._multiSelectOriginalPosition = obj.position.clone();
251
+ obj.userData._multiSelectOriginalRotation = obj.rotation.clone();
252
+ obj.userData._multiSelectOriginalScale = obj.scale.clone();
260
253
  }
261
254
  });
255
+ if (_this2.multiSelectionGroup) {
256
+ // Snapshot group position and helper geometry vertices so we can do
257
+ // cheap per-frame bbox translation without re-traversing the mesh hierarchy.
258
+ _this2._dragStartGroupPosition = _this2.multiSelectionGroup.position.clone();
259
+ _this2.boundingBoxHelpers.forEach(function (helper) {
260
+ var _helper$geometry;
261
+ var posAttr = (_helper$geometry = helper.geometry) === null || _helper$geometry === void 0 || (_helper$geometry = _helper$geometry.attributes) === null || _helper$geometry === void 0 ? void 0 : _helper$geometry.position;
262
+ if (posAttr) {
263
+ helper.userData._dragStartPositions = new Float32Array(posAttr.array);
264
+ }
265
+ });
266
+ }
262
267
  }
263
268
 
264
269
  // Disable orbit controls during transformation
@@ -275,7 +280,7 @@ var TransformControlsManager = /*#__PURE__*/function () {
275
280
 
276
281
  // Transform end event
277
282
  this.eventHandlers.transformEnd = /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
278
- var hasComponents, _this2$selectedObject, sceneCompleteEvent;
283
+ var isTranslate, isRotate, hasComponents, _this2$selectedObject, sceneCompleteEvent;
279
284
  return _regenerator().w(function (_context) {
280
285
  while (1) switch (_context.n) {
281
286
  case 0:
@@ -297,29 +302,52 @@ var TransformControlsManager = /*#__PURE__*/function () {
297
302
  console.error('❌ Error in applyMultiSelectionTransform:', error);
298
303
  });
299
304
  case 1:
300
- // applyMultiSelectionTransform() already calls updatePaths() once at the end
301
- // so we skip the additional updatePaths() call below
302
- console.log('✅ Multi-selection transform complete (updatePaths already called)');
305
+ console.log('✅ Multi-selection transform complete');
303
306
  _context.n = 3;
304
307
  break;
305
308
  case 2:
306
- if (_this2.currentMode === 'translate' && _this2.sceneViewer && typeof _this2.sceneViewer.updatePaths === 'function') {
307
- // Update paths after translation is complete
308
- // Only if we translated components (not segments/gateways - they handle paths internally)
309
- // This branch only executes when NOT in multi-selection mode
310
- hasComponents = _this2.selectedObjects.some(function (obj) {
311
- return !isSegment(obj) && !isGateway(obj);
312
- });
313
- if (hasComponents) {
314
- console.log('🔄 Updating paths after component translation...');
315
- try {
316
- _this2.sceneViewer.updatePaths();
317
- console.log('✅ Paths updated successfully after translation');
318
- } catch (error) {
319
- console.error('❌ Error updating paths after translation:', error);
309
+ if (_this2.sceneViewer && typeof _this2.sceneViewer.updatePaths === 'function') {
310
+ // Update paths after transformation is complete (translation or rotation)
311
+ isTranslate = _this2.currentMode === 'translate';
312
+ isRotate = _this2.currentMode === 'rotate';
313
+ if (isTranslate || isRotate) {
314
+ hasComponents = _this2.selectedObjects.some(function (obj) {
315
+ return !isSegment(obj) && !isGateway(obj);
316
+ });
317
+ if (hasComponents) {
318
+ console.log("\uD83D\uDD04 Updating paths after component ".concat(_this2.currentMode, "..."));
319
+ try {
320
+ // Ensure scene data is synced before updating paths
321
+ _this2.selectedObjects.forEach(function (obj) {
322
+ var _obj$userData;
323
+ if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
324
+ var _this2$sceneViewer$ma;
325
+ var transformMgr = (_this2$sceneViewer$ma = _this2.sceneViewer.managers) === null || _this2$sceneViewer$ma === void 0 ? void 0 : _this2$sceneViewer$ma.transformOperationsManager;
326
+ if (transformMgr) {
327
+ if (isTranslate) {
328
+ transformMgr.updateComponentPositionInSceneData(obj);
329
+ }
330
+ if (isRotate) {
331
+ transformMgr.updateComponentRotationInSceneData(obj);
332
+
333
+ // Calculate rotation delta for direction vector patching
334
+ var startRot = obj.userData._dragStartRotation || obj.rotation;
335
+ var deltaRad = obj.rotation.z - startRot.z;
336
+ var deltaDeg = Math.round(THREE.MathUtils.radToDeg(deltaRad) / 90) * 90;
337
+ if (deltaDeg !== 0) {
338
+ console.log("\uD83E\uDDED Patching connector directions by ".concat(deltaDeg, " degrees after gizmo rotation"));
339
+ transformMgr.updateConnectorDirections(obj, 'z', deltaDeg);
340
+ }
341
+ }
342
+ }
343
+ }
344
+ });
345
+ _this2.sceneViewer.updatePaths();
346
+ console.log("\u2705 Paths updated successfully after ".concat(_this2.currentMode));
347
+ } catch (error) {
348
+ console.error("\u274C Error updating paths after ".concat(_this2.currentMode, ":"), error);
349
+ }
320
350
  }
321
- } else {
322
- console.log('ℹ️ Skipping updatePaths - segments/gateways handle their own path updates');
323
351
  }
324
352
  }
325
353
  case 3:
@@ -450,8 +478,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
450
478
  var hit = _step.value;
451
479
  var _obj = hit.object;
452
480
  while (_obj) {
453
- var _obj$userData2;
454
- if (((_obj$userData2 = _obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'io-device') {
481
+ var _obj$userData3;
482
+ if (((_obj$userData3 = _obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'io-device') {
455
483
  ioDeviceObject = _obj;
456
484
  break;
457
485
  }
@@ -474,8 +502,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
474
502
  var parentUuid = null;
475
503
  var obj = ioDeviceObject.parent;
476
504
  while (obj) {
477
- var _obj$userData;
478
- if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) === 'component') {
505
+ var _obj$userData2;
506
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component') {
479
507
  parentUuid = obj.userData.originalUuid || obj.uuid;
480
508
  break;
481
509
  }
@@ -552,8 +580,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
552
580
  var hit = _step2.value;
553
581
  var obj = hit.object;
554
582
  while (obj) {
555
- var _obj$userData3;
556
- if (((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.objectType) === 'io-device') {
583
+ var _obj$userData4;
584
+ if (((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) === 'io-device') {
557
585
  _this4.callbacks.onIODeviceClick(obj);
558
586
  return;
559
587
  }
@@ -2214,8 +2242,8 @@ var TransformControlsManager = /*#__PURE__*/function () {
2214
2242
  key: "_updateSegmentReference",
2215
2243
  value: function _updateSegmentReference(oldSegment, newSegment, index) {
2216
2244
  var selectedIndex = this.selectedObjects.findIndex(function (obj) {
2217
- var _obj$userData4;
2218
- return obj.uuid === oldSegment.uuid || ((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.originalUuid) === oldSegment.uuid;
2245
+ var _obj$userData5;
2246
+ return obj.uuid === oldSegment.uuid || ((_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.originalUuid) === oldSegment.uuid;
2219
2247
  });
2220
2248
  if (selectedIndex !== -1 && newSegment) {
2221
2249
  // Clear bounding box cache
@@ -2323,7 +2351,7 @@ var TransformControlsManager = /*#__PURE__*/function () {
2323
2351
  componentId = component.uuid || ((_component$userData = component.userData) === null || _component$userData === void 0 ? void 0 : _component$userData.originalUuid);
2324
2352
  _context6.n = 3;
2325
2353
  return this._translateObjectOnAxes(componentId, deltaX, deltaY, deltaZ, threshold, throttleDelay, AXIS_THROTTLE, function (id, axis, delta) {
2326
- return _this0.centralPlant.translate(id, axis, delta);
2354
+ return _this0.centralPlant.translate(id, axis, delta, true);
2327
2355
  });
2328
2356
  case 3:
2329
2357
  console.log("\uD83D\uDD27 Component ".concat(component.name, " translated (").concat(i + 1, "/").concat(components.length, ")"));
@@ -2422,6 +2450,17 @@ var TransformControlsManager = /*#__PURE__*/function () {
2422
2450
  if (this.selectedObjects.length > 0) {
2423
2451
  this.updateMultiSelection();
2424
2452
  }
2453
+
2454
+ // Trigger pathfinding update once after all objects in the batch are translated
2455
+ if (this.sceneViewer && typeof this.sceneViewer.updatePaths === 'function') {
2456
+ var hasComponentsOrSegments = this.selectedObjects.some(function (obj) {
2457
+ return !isGateway(obj);
2458
+ });
2459
+ if (hasComponentsOrSegments) {
2460
+ console.log('🔄 Triggering batched pathfinding update after multi-object translation...');
2461
+ this.sceneViewer.updatePaths();
2462
+ }
2463
+ }
2425
2464
  console.log("\u2705 Multi-selection transform applied");
2426
2465
  }
2427
2466