@2112-lab/central-plant 0.3.11 → 0.3.13

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.
@@ -3497,7 +3497,12 @@ function computeFilteredBoundingBox(object) {
3497
3497
 
3498
3498
  // Build a Set for O(1) lookups
3499
3499
  var excludeSet = new Set(excludeTypes);
3500
- object.updateWorldMatrix(false, true);
3500
+
3501
+ // Force matrix updates to ensure world-space coordinates are accurate.
3502
+ // Using force=true ensures matrices are updated even if matrixWorldNeedsUpdate is false,
3503
+ // which can happen after positioning a model before the render loop runs.
3504
+ object.updateMatrix();
3505
+ object.updateMatrixWorld(true);
3501
3506
  object.traverse(function (child) {
3502
3507
  // Only process nodes with geometry (Mesh, SkinnedMesh, etc.)
3503
3508
  if (!child.geometry) return;
@@ -29818,6 +29823,15 @@ var ModelManager = /*#__PURE__*/function () {
29818
29823
  var jsonData = _ref2.jsonData,
29819
29824
  glbModel = _ref2.glbModel;
29820
29825
  if (!glbModel) return;
29826
+
29827
+ // CRITICAL: Force matrix updates before computing bbox.
29828
+ // After loadLibraryModel positions the model, the world matrices may not be
29829
+ // invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
29830
+ // which only updates if matrixWorldNeedsUpdate is true. Force the update here
29831
+ // to ensure the bbox is computed with correct world-space coordinates.
29832
+ glbModel.updateMatrix();
29833
+ glbModel.updateMatrixWorld(true);
29834
+
29821
29835
  // Use filtered bbox (excludes connectors + io-devices) so it matches
29822
29836
  // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
29823
29837
  var filteredBox = computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);
@@ -35440,6 +35454,9 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35440
35454
  // stacking up independent renderComponents() runs
35441
35455
  _this2._refreshPending = false;
35442
35456
 
35457
+ // Set of viewport keys pending refresh (collects keys across multiple refresh() calls in same frame)
35458
+ _this2._pendingRefreshKeys = new Set();
35459
+
35443
35460
  // Event listener reference for cleanup
35444
35461
  _this2._objectTransformedListener = null;
35445
35462
  console.log('🔲 Viewport2DManager initialized (multi-instance support)');
@@ -35529,8 +35546,10 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35529
35546
  viewport._instanceKey = key;
35530
35547
  this.viewports.set(key, viewport);
35531
35548
 
35532
- // Initialize the stage for this viewport
35533
- this.initializeStage(viewport);
35549
+ // Initialize the stage for this viewport (waits for DOM layout to settle)
35550
+ _context.n = 4;
35551
+ return this.initializeStage(viewport);
35552
+ case 4:
35534
35553
  return _context.a(2, viewport.isReady);
35535
35554
  }
35536
35555
  }, _callee, this);
@@ -35624,50 +35643,67 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35624
35643
  /**
35625
35644
  * Initialize Konva stage for a specific viewport instance
35626
35645
  * @param {Viewport2DInstance} viewport - The viewport instance to initialize
35646
+ * @returns {Promise<void>} Resolves when viewport is fully ready
35627
35647
  */
35628
35648
  )
35629
35649
  }, {
35630
35650
  key: "initializeStage",
35631
35651
  value: function initializeStage(viewport) {
35632
- if (!this.Konva || !viewport.container) {
35633
- console.error('❌ Cannot initialize stage: Konva or container missing');
35634
- return;
35635
- }
35636
-
35637
- // Get container dimensions
35638
- var rect = viewport.container.getBoundingClientRect();
35639
- var width = rect.width || 800;
35640
- var height = rect.height || 600;
35641
- console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
35642
-
35643
- // Create Konva stage for this viewport
35644
- viewport.stage = new this.Konva.Stage({
35645
- container: viewport.container,
35646
- width: width,
35647
- height: height
35648
- });
35649
-
35650
- // Create separate layers for grid and components
35651
- viewport.gridLayer = new this.Konva.Layer();
35652
- viewport.componentLayer = new this.Konva.Layer();
35653
-
35654
- // Add layers to stage in order (grid first, then components)
35655
- viewport.stage.add(viewport.gridLayer);
35656
- viewport.stage.add(viewport.componentLayer);
35657
-
35658
- // Setup resize handling
35659
- this.setupResizeListener(viewport);
35652
+ var _this4 = this;
35653
+ return new Promise(function (resolve) {
35654
+ if (!_this4.Konva || !viewport.container) {
35655
+ console.error('❌ Cannot initialize stage: Konva or container missing');
35656
+ resolve();
35657
+ return;
35658
+ }
35660
35659
 
35661
- // Setup zoom and pan handlers
35662
- this.setupZoomAndPanHandlers(viewport);
35660
+ // Get container dimensions
35661
+ var rect = viewport.container.getBoundingClientRect();
35662
+ var width = rect.width || 800;
35663
+ var height = rect.height || 600;
35664
+ console.log("\uD83D\uDCD0 Initializing Konva stage (".concat(viewport.viewType, "): ").concat(width, "x").concat(height));
35663
35665
 
35664
- // Draw initial content
35665
- this.drawGrid(viewport);
35666
- this.renderComponents(viewport);
35666
+ // Create Konva stage for this viewport
35667
+ viewport.stage = new _this4.Konva.Stage({
35668
+ container: viewport.container,
35669
+ width: width,
35670
+ height: height
35671
+ });
35667
35672
 
35668
- // Mark as ready
35669
- viewport.isReady = true;
35670
- console.log("\u2705 Viewport2DManager stage initialized (".concat(viewport.viewType, ")"));
35673
+ // Create separate layers for grid and components
35674
+ viewport.gridLayer = new _this4.Konva.Layer();
35675
+ viewport.componentLayer = new _this4.Konva.Layer();
35676
+
35677
+ // Add layers to stage in order (grid first, then components)
35678
+ viewport.stage.add(viewport.gridLayer);
35679
+ viewport.stage.add(viewport.componentLayer);
35680
+
35681
+ // Setup resize handling
35682
+ _this4.setupResizeListener(viewport);
35683
+
35684
+ // Setup zoom and pan handlers
35685
+ _this4.setupZoomAndPanHandlers(viewport);
35686
+
35687
+ // Draw initial grid (lightweight, safe to do immediately)
35688
+ _this4.drawGrid(viewport);
35689
+
35690
+ // Defer component rendering to next frame to ensure DOM layout is finalized.
35691
+ // Without this, getBoundingClientRect() may return stale/zero dimensions
35692
+ // if the container hasn't been fully laid out by CSS yet.
35693
+ requestAnimationFrame(function () {
35694
+ // Re-check container dimensions after layout settles
35695
+ var finalRect = viewport.container.getBoundingClientRect();
35696
+ if (finalRect.width > 0 && finalRect.height > 0) {
35697
+ viewport.stage.width(finalRect.width);
35698
+ viewport.stage.height(finalRect.height);
35699
+ _this4.drawGrid(viewport);
35700
+ }
35701
+ _this4.renderComponents(viewport);
35702
+ viewport.isReady = true;
35703
+ console.log("\u2705 Viewport2DManager stage initialized (".concat(viewport.viewType, ")"));
35704
+ resolve();
35705
+ });
35706
+ });
35671
35707
  }
35672
35708
 
35673
35709
  /**
@@ -35677,12 +35713,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35677
35713
  }, {
35678
35714
  key: "setupResizeListener",
35679
35715
  value: function setupResizeListener(viewport) {
35680
- var _this4 = this;
35716
+ var _this5 = this;
35681
35717
  if (typeof window === 'undefined' || !window.ResizeObserver || !viewport.container) {
35682
35718
  return;
35683
35719
  }
35684
35720
  viewport._resizeObserver = new ResizeObserver(function () {
35685
- _this4.resizeStage(viewport);
35721
+ _this5.resizeStage(viewport);
35686
35722
  });
35687
35723
  viewport._resizeObserver.observe(viewport.container);
35688
35724
  }
@@ -35717,6 +35753,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35717
35753
  }, {
35718
35754
  key: "setupZoomAndPanHandlers",
35719
35755
  value: function setupZoomAndPanHandlers(viewport) {
35756
+ var _this6 = this;
35720
35757
  if (!viewport.stage) return;
35721
35758
 
35722
35759
  // Mouse wheel zoom
@@ -35742,6 +35779,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35742
35779
  y: pointer.y - mousePointTo.y * clampedScale
35743
35780
  };
35744
35781
  viewport.stage.position(newPos);
35782
+ _this6.drawGrid(viewport);
35745
35783
  viewport.stage.batchDraw();
35746
35784
  });
35747
35785
 
@@ -35761,6 +35799,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35761
35799
  viewport.stage.x(viewport.stage.x() + dx);
35762
35800
  viewport.stage.y(viewport.stage.y() + dy);
35763
35801
  viewport.lastPanPoint = pos;
35802
+ _this6.drawGrid(viewport);
35764
35803
  viewport.stage.batchDraw();
35765
35804
  });
35766
35805
  viewport.stage.on('mouseup', function () {
@@ -35785,33 +35824,40 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35785
35824
  var width = viewport.stage.width();
35786
35825
  var height = viewport.stage.height();
35787
35826
  var gridSize = viewport.PIXELS_PER_UNIT;
35788
-
35789
- // Center of the viewport represents world origin (0,0,0)
35790
- var centerX = width / 2;
35791
- var centerY = height / 2;
35827
+ var stageX = viewport.stage.x();
35828
+ var stageY = viewport.stage.y();
35829
+ var scale = viewport.stage.scaleX();
35830
+
35831
+ // World origin sits at (width/2, height/2) in stage-local coordinates
35832
+ var originX = width / 2;
35833
+ var originY = height / 2;
35834
+
35835
+ // Compute the visible area in stage-local coordinates, with 1-cell padding
35836
+ var visLeft = -stageX / scale - gridSize;
35837
+ var visTop = -stageY / scale - gridSize;
35838
+ var visRight = (width - stageX) / scale + gridSize;
35839
+ var visBottom = (height - stageY) / scale + gridSize;
35792
35840
  var gridGroup = new this.Konva.Group();
35793
-
35794
- // Draw vertical lines centered at origin
35795
- var startX = centerX % gridSize - gridSize;
35796
35841
  var gridColor = '#dddddd';
35797
- for (var x = startX; x < width; x += gridSize) {
35798
- var distFromCenter = Math.abs(x - centerX);
35799
- var isOriginLine = distFromCenter < 1;
35842
+
35843
+ // Draw vertical lines start at the first grid column left of the visible area
35844
+ var firstX = Math.floor((visLeft - originX) / gridSize) * gridSize + originX;
35845
+ for (var x = firstX; x <= visRight; x += gridSize) {
35846
+ var isOriginLine = Math.abs(x - originX) < 0.5;
35800
35847
  gridGroup.add(new this.Konva.Line({
35801
- points: [x, 0, x, height],
35848
+ points: [x, visTop, x, visBottom],
35802
35849
  stroke: gridColor,
35803
35850
  strokeWidth: isOriginLine ? 2 : 1,
35804
35851
  listening: false
35805
35852
  }));
35806
35853
  }
35807
35854
 
35808
- // Draw horizontal lines centered at origin
35809
- var startY = centerY % gridSize - gridSize;
35810
- for (var y = startY; y < height; y += gridSize) {
35811
- var _distFromCenter = Math.abs(y - centerY);
35812
- var _isOriginLine = _distFromCenter < 1;
35855
+ // Draw horizontal lines start at the first grid row above the visible area
35856
+ var firstY = Math.floor((visTop - originY) / gridSize) * gridSize + originY;
35857
+ for (var y = firstY; y <= visBottom; y += gridSize) {
35858
+ var _isOriginLine = Math.abs(y - originY) < 0.5;
35813
35859
  gridGroup.add(new this.Konva.Line({
35814
- points: [0, y, width, y],
35860
+ points: [visLeft, y, visRight, y],
35815
35861
  stroke: gridColor,
35816
35862
  strokeWidth: _isOriginLine ? 2 : 1,
35817
35863
  listening: false
@@ -35821,14 +35867,14 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35821
35867
  // Draw center crosshair at world origin
35822
35868
  var crosshairSize = gridSize;
35823
35869
  gridGroup.add(new this.Konva.Line({
35824
- points: [centerX, centerY - crosshairSize, centerX, centerY + crosshairSize],
35870
+ points: [originX, originY - crosshairSize, originX, originY + crosshairSize],
35825
35871
  stroke: '#333',
35826
35872
  strokeWidth: 1,
35827
35873
  listening: false,
35828
35874
  dash: [5, 5]
35829
35875
  }));
35830
35876
  gridGroup.add(new this.Konva.Line({
35831
- points: [centerX - crosshairSize, centerY, centerX + crosshairSize, centerY],
35877
+ points: [originX - crosshairSize, originY, originX + crosshairSize, originY],
35832
35878
  stroke: '#333',
35833
35879
  strokeWidth: 1,
35834
35880
  listening: false,
@@ -35845,7 +35891,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35845
35891
  }, {
35846
35892
  key: "renderComponents",
35847
35893
  value: function renderComponents(viewport) {
35848
- var _this5 = this;
35894
+ var _this7 = this;
35849
35895
  if (!viewport.componentLayer || !viewport.stage || !this.sceneViewer) return;
35850
35896
  viewport.componentLayer.destroyChildren();
35851
35897
 
@@ -35856,19 +35902,18 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35856
35902
  return;
35857
35903
  }
35858
35904
 
35859
- // Only log on significant changes
35860
- if (components.length !== viewport._lastComponentCount) {
35861
- console.log("\uD83D\uDCE6 Rendering ".concat(components.length, " components in 2D view (").concat(viewport.viewType, ")"));
35862
- viewport._lastComponentCount = components.length;
35863
- }
35905
+ // Track render count for debugging
35906
+ if (viewport._renderCount === undefined) viewport._renderCount = 0;
35907
+ viewport._renderCount++;
35864
35908
  var width = viewport.stage.width();
35865
35909
  var height = viewport.stage.height();
35866
35910
  var centerX = width / 2;
35867
35911
  var centerY = height / 2;
35868
35912
  var scale = viewport.PIXELS_PER_UNIT;
35913
+ console.log("\uD83C\uDFA8 RENDER #".concat(viewport._renderCount, " (").concat(viewport.viewType, "): ").concat(components.length, " components, stage=").concat(width, "x").concat(height, ", center=(").concat(centerX, ", ").concat(centerY, ")"));
35869
35914
  components.forEach(function (component) {
35870
35915
  try {
35871
- _this5.renderComponent(viewport, component, centerX, centerY, scale);
35916
+ _this7.renderComponent(viewport, component, centerX, centerY, scale);
35872
35917
  } catch (err) {
35873
35918
  console.warn('⚠️ Error rendering component in 2D:', component.name, err);
35874
35919
  }
@@ -35899,6 +35944,11 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35899
35944
  var screenX = centerX + posX * scale;
35900
35945
  var screenY = centerY - posY * scale; // Flip Y for screen coords
35901
35946
 
35947
+ // Debug: Log ALL component positions on first render only
35948
+ if (viewport._renderCount === 1) {
35949
+ console.log("\uD83D\uDD0D [".concat(viewport.viewType, "] ").concat(component.name, ": bbox.z=").concat(bboxCenter.z.toFixed(2), ", posY=").concat(posY.toFixed(2), ", screenY=").concat(screenY.toFixed(0), ", centerY=").concat(centerY.toFixed(0)));
35950
+ }
35951
+
35902
35952
  // Generate unique color for this component
35903
35953
  var colors = this.generateComponentColor(component.id);
35904
35954
 
@@ -35928,18 +35978,19 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35928
35978
  draggable: false
35929
35979
  });
35930
35980
 
35931
- // Add label
35981
+ // Add label (hidden by default; shown on hover)
35932
35982
  var label = new this.Konva.Text({
35933
35983
  x: -rectWidth / 2,
35934
35984
  y: -rectHeight / 2 - 20,
35935
35985
  text: component.name || 'Component',
35936
35986
  fontSize: 12,
35937
35987
  fill: '#333',
35938
- listening: false
35988
+ listening: false,
35989
+ visible: false
35939
35990
  });
35940
35991
 
35941
35992
  // Add mouse event handlers
35942
- this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter);
35993
+ this.addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter, label);
35943
35994
  componentGroup.add(rect);
35944
35995
  componentGroup.add(label);
35945
35996
  viewport.componentLayer.add(componentGroup);
@@ -35976,7 +36027,14 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
35976
36027
  if (this._bboxCache.has(object.uuid)) {
35977
36028
  return this._bboxCache.get(object.uuid);
35978
36029
  }
35979
- var box = computeFilteredBoundingBox(object, []);
36030
+
36031
+ // Force matrix updates before computing bbox to ensure world-space accuracy
36032
+ object.updateMatrix();
36033
+ object.updateMatrixWorld(true);
36034
+
36035
+ // Exclude io-devices and connectors to match the stored worldBoundingBox
36036
+ // computed in modelManager.replaceWithGLBModels()
36037
+ var box = computeFilteredBoundingBox(object, ['io-device', 'connector']);
35980
36038
  var result;
35981
36039
  if (box.isEmpty()) {
35982
36040
  // Object has no geometry; fall back to a point at world position
@@ -36106,8 +36164,8 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36106
36164
  */
36107
36165
  }, {
36108
36166
  key: "addComponentInteractions",
36109
- value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter) {
36110
- var _this6 = this;
36167
+ value: function addComponentInteractions(viewport, rect, componentGroup, component, worldWidth, worldDepth, worldHeight, bboxCenter, label) {
36168
+ var _this8 = this;
36111
36169
  if (!this.Konva) return;
36112
36170
  var colors = this.generateComponentColor(component.id);
36113
36171
 
@@ -36118,6 +36176,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36118
36176
  rect.stroke('#007bff');
36119
36177
  rect.strokeWidth(3);
36120
36178
  viewport.stage.container().style.cursor = 'grab';
36179
+ if (label) label.visible(true);
36121
36180
  viewport.componentLayer.batchDraw();
36122
36181
  }
36123
36182
  });
@@ -36127,6 +36186,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36127
36186
  rect.stroke(colors.stroke);
36128
36187
  rect.strokeWidth(2);
36129
36188
  viewport.stage.container().style.cursor = 'default';
36189
+ if (label) label.visible(false);
36130
36190
  viewport.componentLayer.batchDraw();
36131
36191
  }
36132
36192
  });
@@ -36134,12 +36194,12 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36134
36194
  // CLICK EVENT
36135
36195
  rect.on('click', function () {
36136
36196
  if (!viewport.isDragging) {
36137
- var _this6$sceneViewer;
36197
+ var _this8$sceneViewer;
36138
36198
  console.log("\uD83C\uDFAF Component clicked: ".concat(component.name));
36139
36199
 
36140
36200
  // Use centralPlant API to select component
36141
- if ((_this6$sceneViewer = _this6.sceneViewer) !== null && _this6$sceneViewer !== void 0 && _this6$sceneViewer.centralPlant && component.uuid) {
36142
- _this6.sceneViewer.centralPlant.selectComponent(component.uuid);
36201
+ if ((_this8$sceneViewer = _this8.sceneViewer) !== null && _this8$sceneViewer !== void 0 && _this8$sceneViewer.centralPlant && component.uuid) {
36202
+ _this8.sceneViewer.centralPlant.selectComponent(component.uuid);
36143
36203
  }
36144
36204
  }
36145
36205
  });
@@ -36169,7 +36229,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36169
36229
  var worldOriginY = stageHeight / 2;
36170
36230
 
36171
36231
  // Snap to grid
36172
- var snappedPos = _this6.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
36232
+ var snappedPos = _this8.snapScreenToGrid(viewport, currentPos.x, currentPos.y, scale, worldOriginX, worldOriginY);
36173
36233
  componentGroup.position(snappedPos);
36174
36234
  viewport.componentLayer.batchDraw();
36175
36235
  });
@@ -36177,7 +36237,7 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36177
36237
  // DRAG END
36178
36238
  componentGroup.on('dragend', function () {
36179
36239
  setTimeout(function () {
36180
- var _this6$sceneViewer2;
36240
+ var _this8$sceneViewer2;
36181
36241
  viewport.isDragging = false;
36182
36242
  var finalPos = componentGroup.position();
36183
36243
  var stageWidth = viewport.stage.width();
@@ -36187,17 +36247,17 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36187
36247
  var worldOriginY = stageHeight / 2;
36188
36248
 
36189
36249
  // Convert screen to world coordinates
36190
- var worldCoords = _this6.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
36250
+ var worldCoords = _this8.screenToWorldCoords(viewport, finalPos.x, finalPos.y, scale, worldOriginX, worldOriginY);
36191
36251
 
36192
36252
  // Calculate new position: delta from old bbox center to new bbox center
36193
36253
  var currentPos = component.position;
36194
- var newPosition = _this6.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
36254
+ var newPosition = _this8.worldCoordsToObjectPosition(viewport, worldCoords, currentPos, bboxCenter);
36195
36255
 
36196
36256
  // Apply translation via centralPlant API
36197
36257
  var deltaX = newPosition.x - currentPos.x;
36198
36258
  var deltaY = newPosition.y - currentPos.y;
36199
36259
  var deltaZ = newPosition.z - currentPos.z;
36200
- if ((_this6$sceneViewer2 = _this6.sceneViewer) !== null && _this6$sceneViewer2 !== void 0 && _this6$sceneViewer2.centralPlant && component.uuid) {
36260
+ if ((_this8$sceneViewer2 = _this8.sceneViewer) !== null && _this8$sceneViewer2 !== void 0 && _this8$sceneViewer2.centralPlant && component.uuid) {
36201
36261
  var success = true;
36202
36262
 
36203
36263
  // Suppress per-axis path updates so the pathfinder only runs once,
@@ -36205,32 +36265,32 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36205
36265
  // Running updatePaths() after each individual axis (the default
36206
36266
  // translateComponent behaviour) produces intermediate wrong results on
36207
36267
  // the first drag because no cached fingerprint exists yet to skip them.
36208
- var wasAutoUpdate = _this6.sceneViewer.shouldUpdatePaths;
36209
- _this6.sceneViewer.shouldUpdatePaths = false;
36268
+ var wasAutoUpdate = _this8.sceneViewer.shouldUpdatePaths;
36269
+ _this8.sceneViewer.shouldUpdatePaths = false;
36210
36270
  try {
36211
36271
  if (Math.abs(deltaX) > 0.01) {
36212
- success = success && _this6.sceneViewer.centralPlant.translate(component.uuid, 'x', deltaX);
36272
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'x', deltaX);
36213
36273
  }
36214
36274
  if (Math.abs(deltaY) > 0.01) {
36215
- success = success && _this6.sceneViewer.centralPlant.translate(component.uuid, 'y', deltaY);
36275
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'y', deltaY);
36216
36276
  }
36217
36277
  if (Math.abs(deltaZ) > 0.01) {
36218
- success = success && _this6.sceneViewer.centralPlant.translate(component.uuid, 'z', deltaZ);
36278
+ success = success && _this8.sceneViewer.centralPlant.translate(component.uuid, 'z', deltaZ);
36219
36279
  }
36220
36280
  } finally {
36221
- _this6.sceneViewer.shouldUpdatePaths = wasAutoUpdate;
36281
+ _this8.sceneViewer.shouldUpdatePaths = wasAutoUpdate;
36222
36282
  }
36223
- if (!success && _this6.dragStartPosition) {
36283
+ if (!success && _this8.dragStartPosition) {
36224
36284
  console.warn('⚠️ Failed to translate component, reverting position');
36225
- componentGroup.position(_this6.dragStartPosition);
36285
+ componentGroup.position(_this8.dragStartPosition);
36226
36286
  } else {
36227
36287
  console.log("\u2705 Component ".concat(component.name, " translated successfully in 2D viewport"));
36228
36288
 
36229
36289
  // Single path update with the final combined position
36230
- if (wasAutoUpdate && _this6.sceneViewer) {
36290
+ if (wasAutoUpdate && _this8.sceneViewer) {
36231
36291
  console.log('🔄 Auto-updating paths after 2D viewport translation...');
36232
36292
  try {
36233
- _this6.sceneViewer.updatePaths();
36293
+ _this8.sceneViewer.updatePaths();
36234
36294
  console.log('✅ Paths auto-updated successfully from 2D viewport');
36235
36295
  } catch (error) {
36236
36296
  console.error('❌ Error auto-updating paths from 2D viewport:', error);
@@ -36483,28 +36543,39 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36483
36543
  }, {
36484
36544
  key: "refresh",
36485
36545
  value: function refresh() {
36486
- var _this7 = this;
36546
+ var _this9 = this;
36487
36547
  var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
36548
+ // Collect viewport keys to refresh; null means "all viewports"
36549
+ if (key) {
36550
+ this._pendingRefreshKeys.add(key);
36551
+ } else {
36552
+ // null key means refresh all — mark with special sentinel
36553
+ this._pendingRefreshKeys.add('__ALL__');
36554
+ }
36555
+
36556
+ // If already scheduled, the rAF callback will process our newly-added key
36488
36557
  if (this._refreshPending) return;
36489
36558
  this._refreshPending = true;
36490
36559
  requestAnimationFrame(function () {
36491
- _this7._refreshPending = false;
36560
+ _this9._refreshPending = false;
36561
+
36492
36562
  // Clear per-cycle caches so each component is measured/traversed once per paint
36493
- _this7._bboxCache.clear();
36494
- _this7._componentListCache = null;
36495
- if (key) {
36496
- var viewport = _this7.viewports.get(key);
36497
- if (viewport && viewport.isReady) {
36498
- _this7.renderComponents(viewport);
36499
- }
36500
- } else {
36501
- var _iterator = _createForOfIteratorHelper(_this7.viewports.values()),
36563
+ _this9._bboxCache.clear();
36564
+ _this9._componentListCache = null;
36565
+
36566
+ // Check if we need to refresh all viewports
36567
+ var refreshAll = _this9._pendingRefreshKeys.has('__ALL__');
36568
+ var keysToRefresh = new Set(_this9._pendingRefreshKeys);
36569
+ _this9._pendingRefreshKeys.clear();
36570
+ if (refreshAll) {
36571
+ // Refresh all viewports
36572
+ var _iterator = _createForOfIteratorHelper(_this9.viewports.values()),
36502
36573
  _step;
36503
36574
  try {
36504
36575
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
36505
- var _viewport = _step.value;
36506
- if (_viewport.isReady) {
36507
- _this7.renderComponents(_viewport);
36576
+ var viewport = _step.value;
36577
+ if (viewport.isReady) {
36578
+ _this9.renderComponents(viewport);
36508
36579
  }
36509
36580
  }
36510
36581
  } catch (err) {
@@ -36512,6 +36583,23 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36512
36583
  } finally {
36513
36584
  _iterator.f();
36514
36585
  }
36586
+ } else {
36587
+ // Refresh only the specific viewports that were requested
36588
+ var _iterator2 = _createForOfIteratorHelper(keysToRefresh),
36589
+ _step2;
36590
+ try {
36591
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
36592
+ var viewportKey = _step2.value;
36593
+ var _viewport = _this9.viewports.get(viewportKey);
36594
+ if (_viewport && _viewport.isReady) {
36595
+ _this9.renderComponents(_viewport);
36596
+ }
36597
+ }
36598
+ } catch (err) {
36599
+ _iterator2.e(err);
36600
+ } finally {
36601
+ _iterator2.f();
36602
+ }
36515
36603
  }
36516
36604
  });
36517
36605
  }
@@ -36532,20 +36620,20 @@ var Viewport2DManager = /*#__PURE__*/function (_BaseDisposable2) {
36532
36620
  }
36533
36621
 
36534
36622
  // Dispose all viewport instances
36535
- var _iterator2 = _createForOfIteratorHelper(this.viewports.entries()),
36536
- _step2;
36623
+ var _iterator3 = _createForOfIteratorHelper(this.viewports.entries()),
36624
+ _step3;
36537
36625
  try {
36538
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
36539
- var _step2$value = _slicedToArray(_step2.value, 2),
36540
- key = _step2$value[0],
36541
- viewport = _step2$value[1];
36626
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
36627
+ var _step3$value = _slicedToArray(_step3.value, 2),
36628
+ key = _step3$value[0],
36629
+ viewport = _step3$value[1];
36542
36630
  console.log("\uD83D\uDDD1\uFE0F Disposing viewport: ".concat(key));
36543
36631
  viewport.dispose();
36544
36632
  }
36545
36633
  } catch (err) {
36546
- _iterator2.e(err);
36634
+ _iterator3.e(err);
36547
36635
  } finally {
36548
- _iterator2.f();
36636
+ _iterator3.f();
36549
36637
  }
36550
36638
  this.viewports.clear();
36551
36639
  this.Konva = null;
@@ -37946,7 +38034,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
37946
38034
  * Initialize the CentralPlant manager
37947
38035
  *
37948
38036
  * @constructor
37949
- * @version 0.3.11
38037
+ * @version 0.3.13
37950
38038
  * @updated 2025-10-22
37951
38039
  *
37952
38040
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -35,7 +35,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
35
35
  * Initialize the CentralPlant manager
36
36
  *
37
37
  * @constructor
38
- * @version 0.3.11
38
+ * @version 0.3.13
39
39
  * @updated 2025-10-22
40
40
  *
41
41
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -585,6 +585,15 @@ var ModelManager = /*#__PURE__*/function () {
585
585
  var jsonData = _ref2.jsonData,
586
586
  glbModel = _ref2.glbModel;
587
587
  if (!glbModel) return;
588
+
589
+ // CRITICAL: Force matrix updates before computing bbox.
590
+ // After loadLibraryModel positions the model, the world matrices may not be
591
+ // invalidated yet. computeFilteredBoundingBox uses updateWorldMatrix(false, true)
592
+ // which only updates if matrixWorldNeedsUpdate is true. Force the update here
593
+ // to ensure the bbox is computed with correct world-space coordinates.
594
+ glbModel.updateMatrix();
595
+ glbModel.updateMatrixWorld(true);
596
+
588
597
  // Use filtered bbox (excludes connectors + io-devices) so it matches
589
598
  // what pathfindingManager._enrichSceneDataWithBoundingBoxes produces
590
599
  var filteredBox = boundingBoxUtils.computeFilteredBoundingBox(glbModel, ['io-device', 'connector']);