@2112-lab/central-plant 0.2.5 → 0.2.10

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.
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
6
+ var baseDisposable = require('../../core/baseDisposable.js');
7
+ var pathfindingData = require('../../core/pathfindingData.js');
8
+
9
+ // ── Temperature visualisation constants ────────────────────────────────────
10
+
11
+ /** Minimum temperature in the colour ramp (maps to pure blue). */
12
+ var TEMP_MIN = 0;
13
+
14
+ /** Maximum temperature in the colour ramp (maps to pure red). */
15
+ var TEMP_MAX = 100;
16
+
17
+ /**
18
+ * Convert a temperature value to a CSS hex colour string using a blue→red HSL
19
+ * ramp. Values are clamped to [TEMP_MIN, TEMP_MAX].
20
+ *
21
+ * • TEMP_MIN → hsl(240, 80%, 50%) — blue
22
+ * • TEMP_MAX → hsl(0, 80%, 50%) — red
23
+ *
24
+ * @param {number} temp
25
+ * @returns {string} CSS hex colour string e.g. '#2255cc'
26
+ */
27
+ function temperatureToColor(temp) {
28
+ var clamped = Math.max(TEMP_MIN, Math.min(TEMP_MAX, temp));
29
+ var t = (clamped - TEMP_MIN) / (TEMP_MAX - TEMP_MIN); // 0 (cold) → 1 (hot)
30
+ // Map t=0 → hue 240 (blue), t=1 → hue 0 (red)
31
+ var hue = Math.round(240 - t * 240);
32
+ return "hsl(".concat(hue, ", 80%, 50%)");
33
+ }
34
+
35
+ // ── PathFlowManager ─────────────────────────────────────────────────────────
36
+
37
+ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
38
+ /**
39
+ * @param {Object} sceneViewer - The central sceneViewer hub
40
+ */
41
+ function PathFlowManager(sceneViewer) {
42
+ var _this;
43
+ _rollupPluginBabelHelpers.classCallCheck(this, PathFlowManager);
44
+ _this = _rollupPluginBabelHelpers.callSuper(this, PathFlowManager);
45
+ _this.sceneViewer = sceneViewer;
46
+
47
+ /**
48
+ * Runtime-only attribute overrides.
49
+ * Map<pathId, Record<attributeKey, value>>
50
+ * Cleared when the session ends or resetDerived() is called.
51
+ * @type {Map<string, Record<string, any>>}
52
+ */
53
+ _this._derivedStore = new Map();
54
+ return _this;
55
+ }
56
+
57
+ // ── Public API — attribute write ──────────────────────────────────────────
58
+
59
+ /**
60
+ * Set a declared (persistent) flow attribute on a path.
61
+ * The value is stored in PathData and will be serialised with the scene.
62
+ * Triggers a visual update.
63
+ *
64
+ * @param {string} pathId - e.g. "PUMP-1-CONN-1-->CHILLER-1-CONN-2"
65
+ * @param {string} key - One of FLOW_ATTRIBUTE_KEYS
66
+ * @param {any} value
67
+ */
68
+ _rollupPluginBabelHelpers.inherits(PathFlowManager, _BaseDisposable);
69
+ return _rollupPluginBabelHelpers.createClass(PathFlowManager, [{
70
+ key: "setDeclared",
71
+ value: function setDeclared(pathId, key, value) {
72
+ var pd = this._getPathData(pathId);
73
+ if (!pd) {
74
+ console.warn("PathFlowManager.setDeclared: no PathData found for \"".concat(pathId, "\""));
75
+ return;
76
+ }
77
+ pd.setFlowAttribute(key, value);
78
+ this.applyVisualizationForPath(pathId);
79
+ }
80
+
81
+ /**
82
+ * Set a derived (runtime) flow attribute on a path.
83
+ * Overrides the declared value during this session but is not persisted.
84
+ * Triggers a visual update.
85
+ *
86
+ * @param {string} pathId
87
+ * @param {string} key
88
+ * @param {any} value
89
+ */
90
+ }, {
91
+ key: "setDerived",
92
+ value: function setDerived(pathId, key, value) {
93
+ if (!this._derivedStore.has(pathId)) {
94
+ this._derivedStore.set(pathId, {});
95
+ }
96
+ this._derivedStore.get(pathId)[key] = value;
97
+ this.applyVisualizationForPath(pathId);
98
+ }
99
+
100
+ /**
101
+ * Clear derived overrides for one path or all paths, then re-apply
102
+ * visualizations so that declared values take effect again.
103
+ *
104
+ * @param {string} [pathId] - Omit to reset all paths.
105
+ */
106
+ }, {
107
+ key: "resetDerived",
108
+ value: function resetDerived(pathId) {
109
+ var _this2 = this;
110
+ if (pathId !== undefined) {
111
+ this._derivedStore.delete(pathId);
112
+ this.applyVisualizationForPath(pathId);
113
+ } else {
114
+ var affected = _rollupPluginBabelHelpers.toConsumableArray(this._derivedStore.keys());
115
+ this._derivedStore.clear();
116
+ affected.forEach(function (id) {
117
+ return _this2.applyVisualizationForPath(id);
118
+ });
119
+ }
120
+ }
121
+
122
+ // ── Public API — attribute read ───────────────────────────────────────────
123
+
124
+ /**
125
+ * Resolve the effective value of a single attribute for a path.
126
+ * Resolution order: derived > declared > null.
127
+ *
128
+ * @param {string} pathId
129
+ * @param {string} key
130
+ * @returns {any|null}
131
+ */
132
+ }, {
133
+ key: "resolve",
134
+ value: function resolve(pathId, key) {
135
+ var derived = this._derivedStore.get(pathId);
136
+ if (derived && Object.prototype.hasOwnProperty.call(derived, key)) {
137
+ return derived[key];
138
+ }
139
+ var pd = this._getPathData(pathId);
140
+ if (pd) {
141
+ return pd.getFlowAttribute(key);
142
+ }
143
+ return null;
144
+ }
145
+
146
+ /**
147
+ * Resolve all four flow attributes for a path as a plain object.
148
+ * Each key is either the effective value or null if unset.
149
+ *
150
+ * @param {string} pathId
151
+ * @returns {{ flowDirection: any, flowSpeed: any, flowTemperature: any, flowMaterial: any }}
152
+ */
153
+ }, {
154
+ key: "resolveAll",
155
+ value: function resolveAll(pathId) {
156
+ var _this3 = this;
157
+ return Object.fromEntries(pathfindingData.FLOW_ATTRIBUTE_KEYS.map(function (key) {
158
+ return [key, _this3.resolve(pathId, key)];
159
+ }));
160
+ }
161
+
162
+ // ── Visualization ─────────────────────────────────────────────────────────
163
+
164
+ /**
165
+ * Compute and apply the visual colour for a single path based on its resolved
166
+ * flow attributes. Currently maps flowTemperature to a blue→red colour ramp.
167
+ * No-ops gracefully if the path has no renderable attributes or no pipe
168
+ * material exists yet.
169
+ *
170
+ * @param {string} pathId
171
+ */
172
+ }, {
173
+ key: "applyVisualizationForPath",
174
+ value: function applyVisualizationForPath(pathId) {
175
+ var temp = this.resolve(pathId, 'flowTemperature');
176
+ if (temp === null || temp === undefined) {
177
+ return; // No temperature declared — leave default pipe color
178
+ }
179
+ var color = temperatureToColor(temp);
180
+ var renderingManager = this._getRenderingManager();
181
+ if (!renderingManager) return;
182
+
183
+ // pathId = "from-->to"; extract the two halves
184
+ var sepIdx = pathId.indexOf('-->');
185
+ if (sepIdx === -1) {
186
+ console.warn("PathFlowManager: malformed pathId \"".concat(pathId, "\""));
187
+ return;
188
+ }
189
+ var from = pathId.slice(0, sepIdx);
190
+ var to = pathId.slice(sepIdx + 3);
191
+ renderingManager.updatePathColor(from, to, color);
192
+ console.log("\uD83C\uDF21\uFE0F PathFlowManager: \"".concat(pathId, "\" \u2192 flowTemperature ").concat(temp, " \u2192 ").concat(color));
193
+ }
194
+
195
+ /**
196
+ * Apply visualizations for every known path (union of pathDataStore and derived store).
197
+ * Call this after scene load or after bulk attribute changes.
198
+ */
199
+ }, {
200
+ key: "applyAllVisualizations",
201
+ value: function applyAllVisualizations() {
202
+ var _this4 = this;
203
+ var pathIds = new Set();
204
+ var pfMgr = this._getPathfindingManager();
205
+ if (pfMgr !== null && pfMgr !== void 0 && pfMgr.pathDataStore) {
206
+ pfMgr.pathDataStore.forEach(function (_, id) {
207
+ return pathIds.add(id);
208
+ });
209
+ }
210
+ this._derivedStore.forEach(function (_, id) {
211
+ return pathIds.add(id);
212
+ });
213
+ pathIds.forEach(function (id) {
214
+ return _this4.applyVisualizationForPath(id);
215
+ });
216
+ console.log("\uD83C\uDF0A PathFlowManager.applyAllVisualizations: processed ".concat(pathIds.size, " path(s)"));
217
+ }
218
+
219
+ // ── Lifecycle ─────────────────────────────────────────────────────────────
220
+ }, {
221
+ key: "dispose",
222
+ value: function dispose() {
223
+ this._derivedStore.clear();
224
+ this.sceneViewer = null;
225
+ _rollupPluginBabelHelpers.superPropGet(PathFlowManager, "dispose", this, 3)([]);
226
+ }
227
+
228
+ // ── Private helpers ───────────────────────────────────────────────────────
229
+
230
+ /**
231
+ * @returns {import('../../core/pathfindingData.js').PathData|null}
232
+ * @private
233
+ */
234
+ }, {
235
+ key: "_getPathData",
236
+ value: function _getPathData(pathId) {
237
+ var _this$_getPathfinding, _this$_getPathfinding2;
238
+ return (_this$_getPathfinding = (_this$_getPathfinding2 = this._getPathfindingManager()) === null || _this$_getPathfinding2 === void 0 || (_this$_getPathfinding2 = _this$_getPathfinding2.pathDataStore) === null || _this$_getPathfinding2 === void 0 ? void 0 : _this$_getPathfinding2.get(pathId)) !== null && _this$_getPathfinding !== void 0 ? _this$_getPathfinding : null;
239
+ }
240
+
241
+ /**
242
+ * @returns {import('../pathfinding/pathfindingManager.js').PathfindingManager|null}
243
+ * @private
244
+ */
245
+ }, {
246
+ key: "_getPathfindingManager",
247
+ value: function _getPathfindingManager() {
248
+ var _this$sceneViewer$man, _this$sceneViewer;
249
+ return (_this$sceneViewer$man = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.pathfindingManager) !== null && _this$sceneViewer$man !== void 0 ? _this$sceneViewer$man : null;
250
+ }
251
+
252
+ /**
253
+ * @returns {import('../pathfinding/PathRenderingManager.js').PathRenderingManager|null}
254
+ * @private
255
+ */
256
+ }, {
257
+ key: "_getRenderingManager",
258
+ value: function _getRenderingManager() {
259
+ var _this$_getPathfinding3, _this$_getPathfinding4;
260
+ return (_this$_getPathfinding3 = (_this$_getPathfinding4 = this._getPathfindingManager()) === null || _this$_getPathfinding4 === void 0 ? void 0 : _this$_getPathfinding4.renderingManager) !== null && _this$_getPathfinding3 !== void 0 ? _this$_getPathfinding3 : null;
261
+ }
262
+ }]);
263
+ }(baseDisposable.BaseDisposable);
264
+
265
+ exports.PathFlowManager = PathFlowManager;
@@ -42,16 +42,58 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
42
42
  emissive: 0
43
43
  });
44
44
  _this.registerDisposable(_this._gatewayGeometry, _this._gatewayMaterial);
45
+
46
+ /**
47
+ * Map of pathId -> THREE.Material for per-path color control.
48
+ * pathId format: "${from}-->${to}"
49
+ * All segments within a path share the same material instance.
50
+ * @type {Map<string, THREE.Material>}
51
+ */
52
+ _this._pathMaterials = new Map();
45
53
  return _this;
46
54
  }
47
55
 
48
56
  /**
49
- * Get path colors for visual distinction
50
- * @param {number} index - Path index
51
- * @returns {string} Hex color string
57
+ * Dispose all tracked per-path materials and clear the map.
58
+ * Called at the start of createPipePaths to clean up stale materials.
59
+ * @private
52
60
  */
53
61
  _rollupPluginBabelHelpers.inherits(PathRenderingManager, _BaseDisposable);
54
62
  return _rollupPluginBabelHelpers.createClass(PathRenderingManager, [{
63
+ key: "_clearPathMaterials",
64
+ value: function _clearPathMaterials() {
65
+ this._pathMaterials.forEach(function (mat) {
66
+ return mat.dispose();
67
+ });
68
+ this._pathMaterials.clear();
69
+ }
70
+
71
+ /**
72
+ * Update the color of all pipe segments belonging to a specific path.
73
+ * Operates on the shared per-path material — no scene traversal needed.
74
+ *
75
+ * @param {string} from - Source connector ID
76
+ * @param {string} to - Target connector ID
77
+ * @param {string|number} color - Any value accepted by THREE.Color.set()
78
+ */
79
+ }, {
80
+ key: "updatePathColor",
81
+ value: function updatePathColor(from, to, color) {
82
+ var pathId = "".concat(from, "-->").concat(to);
83
+ var mat = this._pathMaterials.get(pathId);
84
+ if (mat) {
85
+ mat.color.set(color);
86
+ } else {
87
+ console.warn("\u26A0\uFE0F PathRenderingManager.updatePathColor: no material found for path \"".concat(pathId, "\""));
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Get path colors for visual distinction
93
+ * @param {number} index - Path index
94
+ * @returns {string} Hex color string
95
+ */
96
+ }, {
55
97
  key: "getPathColor",
56
98
  value: function getPathColor(index) {
57
99
  var colors = [
@@ -206,7 +248,11 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
206
248
  var globalSegmentIndex = maxExistingIndex + 1;
207
249
  console.log("\uD83D\uDD22 Starting segment index at ".concat(globalSegmentIndex, " (max existing: ").concat(maxExistingIndex, ")"));
208
250
  var pipeRadius = 0.1;
209
- var pipeMaterial = this.createPipeMaterial(crosscubeTextureSet);
251
+ // Base material created once; per-path materials are cloned from it below.
252
+ var baseMaterial = this.createPipeMaterial(crosscubeTextureSet);
253
+
254
+ // Dispose previous path materials (they were attached to now-removed segments)
255
+ this._clearPathMaterials();
210
256
  paths.forEach(function (pathData, index) {
211
257
  if (pathData.path && pathData.path.length >= 2) {
212
258
  // Convert path points to Vector3 objects for consistent handling
@@ -223,6 +269,12 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
223
269
  }
224
270
  });
225
271
 
272
+ // Create a per-path material clone so each path can have its color updated independently.
273
+ var pathId = "".concat(pathData.from, "-->").concat(pathData.to);
274
+ var perPathMaterial = baseMaterial.clone();
275
+ _this3._pathMaterials.set(pathId, perPathMaterial);
276
+ console.log("\uD83C\uDFA8 Created per-path material for \"".concat(pathId, "\""));
277
+
226
278
  // Check if endpoints are component connectors (from pathfinder result)
227
279
  var fromIsComponentConnector = pathData.fromObjectType === 'component-connector';
228
280
  var toIsComponentConnector = pathData.toObjectType === 'component-connector';
@@ -247,13 +299,13 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
247
299
  var cylinderGeometry = new THREE__namespace.CylinderGeometry(pipeRadius, pipeRadius, length, 16, 1, false);
248
300
 
249
301
  // Determine material (debug red if rectified and in dev mode)
250
- var materialToUse = pipeMaterial;
302
+ var materialToUse = perPathMaterial;
251
303
 
252
304
  // Check for dev mode (strict localhost only)
253
305
  var isDev = typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
254
306
  if (isDev && pathData.rectifiedSegments && pathData.rectifiedSegments.includes(j)) {
255
- // Create red debug material by cloning the pipe material to match the look
256
- materialToUse = pipeMaterial.clone();
307
+ // Create red debug material by cloning the per-path material to match the look
308
+ materialToUse = perPathMaterial.clone();
257
309
  materialToUse.color.setHex(0xff0000);
258
310
  console.log("\uD83C\uDFA8 Coloring rectified segment ".concat(j, " red"));
259
311
  }
@@ -315,7 +367,7 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
315
367
  sceneViewer.scene.add(cylinder);
316
368
 
317
369
  // Add smooth elbow joints only at actual direction changes (not at every point)
318
- _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, pipeMaterial, pathData, index, cylinder // Pass the segment so elbow can be added as a child
370
+ _this3.createAndAddElbowIfNeeded(pathPoints, j, pipeRadius, perPathMaterial, pathData, index, cylinder // Pass the segment so elbow can be added as a child
319
371
  );
320
372
  }
321
373
  }
@@ -420,6 +472,9 @@ var PathRenderingManager = /*#__PURE__*/function (_BaseDisposable) {
420
472
  value: function dispose() {
421
473
  console.log('🗑️ Disposing PathRenderingManager...');
422
474
 
475
+ // Dispose per-path materials
476
+ this._clearPathMaterials();
477
+
423
478
  // Call parent dispose to clean up registered resources (shared gateway geometry/material)
424
479
  _rollupPluginBabelHelpers.superPropGet(PathRenderingManager, "dispose", this, 3)([]);
425
480
 
@@ -410,7 +410,11 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
410
410
  key: "_executePathfinding",
411
411
  value: (function () {
412
412
  var _executePathfinding2 = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee(sceneData, connections) {
413
- var _sceneDataCopy$childr, _sceneDataCopy$childr2, _pathfindingResult$pa, _pathfindingResult$pa2;
413
+ var _this4 = this,
414
+ _sceneDataCopy$childr,
415
+ _sceneDataCopy$childr2,
416
+ _pathfindingResult$pa,
417
+ _pathfindingResult$pa2;
414
418
  var options,
415
419
  _options$context,
416
420
  context,
@@ -471,6 +475,15 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
471
475
 
472
476
  // Create pipe paths with materials using the paths from pathfinder
473
477
  this.renderingManager.createPipePaths(pathfindingResult.paths, this.crosscubeTextureSet);
478
+
479
+ // ── Stage 3b: Ensure PathData entries exist for all rendered paths ──────
480
+ // This preserves any flowAttributes already set on existing entries.
481
+ if (pathfindingResult.paths) {
482
+ pathfindingResult.paths.forEach(function (path) {
483
+ var pathId = "".concat(path.from, "-->").concat(path.to);
484
+ _this4._getOrCreatePathData(pathId, path.from, path.to);
485
+ });
486
+ }
474
487
  timers.pathRendering = performance.now() - renderStart;
475
488
 
476
489
  // ── Performance Summary ────────────────────────────────────────────
@@ -508,6 +521,26 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
508
521
  return this.sceneDataManager.getSimplifiedSceneData();
509
522
  }
510
523
 
524
+ /**
525
+ * Return an existing PathData for pathId from the store, or create and register a new one.
526
+ * Preserves existing flowAttributes when the entry already exists.
527
+ *
528
+ * @param {string} pathId - Canonical path identifier "${from}-->${to}"
529
+ * @param {string} from - Source connector ID
530
+ * @param {string} to - Target connector ID
531
+ * @returns {PathData}
532
+ * @private
533
+ */
534
+ }, {
535
+ key: "_getOrCreatePathData",
536
+ value: function _getOrCreatePathData(pathId, from, to) {
537
+ if (!this.pathDataStore.has(pathId)) {
538
+ var pd = new pathfindingData.PathData(pathId, from, to);
539
+ this.pathDataStore.set(pathId, pd);
540
+ }
541
+ return this.pathDataStore.get(pathId);
542
+ }
543
+
511
544
  /**
512
545
  * Initialize pathfinder and create paths
513
546
  */
@@ -515,12 +548,33 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
515
548
  key: "initializePathfinder",
516
549
  value: (function () {
517
550
  var _initializePathfinder = _rollupPluginBabelHelpers.asyncToGenerator(/*#__PURE__*/_rollupPluginBabelHelpers.regenerator().m(function _callee2(data, crosscubeTextureSet) {
518
- var pathfindingResult;
551
+ var _this5 = this,
552
+ _this$sceneViewer;
553
+ var pathfindingResult, originalToSubPaths, fromEndpointAttrs, flowMgr;
519
554
  return _rollupPluginBabelHelpers.regenerator().w(function (_context2) {
520
555
  while (1) switch (_context2.n) {
521
556
  case 0:
522
557
  this.crosscubeTextureSet = crosscubeTextureSet;
523
558
 
559
+ // ── Pre-load declared flowAttributes from connections JSON ─────────────
560
+ // This must happen before pathfinding so that PathData entries carrying
561
+ // flowAttributes are available for visualization right after rendering.
562
+ if (Array.isArray(data.connections)) {
563
+ data.connections.forEach(function (conn) {
564
+ if (conn.flowAttributes && _rollupPluginBabelHelpers["typeof"](conn.flowAttributes) === 'object') {
565
+ var pathId = "".concat(conn.from, "-->").concat(conn.to);
566
+ var pd = _this5._getOrCreatePathData(pathId, conn.from, conn.to);
567
+ Object.entries(conn.flowAttributes).forEach(function (_ref) {
568
+ var _ref2 = _rollupPluginBabelHelpers.slicedToArray(_ref, 2),
569
+ key = _ref2[0],
570
+ value = _ref2[1];
571
+ pd.setFlowAttribute(key, value);
572
+ });
573
+ console.log("\uD83C\uDF0A Loaded flowAttributes for path \"".concat(pathId, "\":"), conn.flowAttributes);
574
+ }
575
+ });
576
+ }
577
+
524
578
  // Use shared pathfinding logic with gateway creation enabled
525
579
  _context2.n = 1;
526
580
  return this._executePathfinding(data.scene, data.connections, {
@@ -529,6 +583,83 @@ var PathfindingManager = /*#__PURE__*/function (_BaseDisposable) {
529
583
  });
530
584
  case 1:
531
585
  pathfindingResult = _context2.v;
586
+ // ── Propagate flowAttributes to gateway-split rendered paths ──────────
587
+ // The pathfinder may split a connection A→B through gateway waypoints,
588
+ // producing sub-paths like A→G and G→B with different pathIds.
589
+ // We need to copy the original connection's flowAttributes onto every
590
+ // rendered sub-path entry in pathDataStore so the visualisation can
591
+ // find them by their rendered pathId.
592
+ if (Array.isArray(data.connections) && pathfindingResult) {
593
+ // Pass 1: use the explicit gateway connection mappings to get a precise
594
+ // original→sub-path map.
595
+ originalToSubPaths = new Map(); // origPathId → Set<renderedPathId>
596
+ if (pathfindingResult.gateways) {
597
+ pathfindingResult.gateways.forEach(function (gateway) {
598
+ var _ref3 = gateway.connections || {},
599
+ removed = _ref3.removed,
600
+ added = _ref3.added;
601
+ if (!(removed !== null && removed !== void 0 && removed.length) || !(added !== null && added !== void 0 && added.length)) return;
602
+ removed.forEach(function (removedConn) {
603
+ var origId = "".concat(removedConn.from, "-->").concat(removedConn.to);
604
+ if (!originalToSubPaths.has(origId)) originalToSubPaths.set(origId, new Set());
605
+ added.forEach(function (addedConn) {
606
+ originalToSubPaths.get(origId).add("".concat(addedConn.from, "-->").concat(addedConn.to));
607
+ });
608
+ });
609
+ });
610
+ }
611
+ data.connections.forEach(function (conn) {
612
+ if (!conn.flowAttributes) return;
613
+ var origId = "".concat(conn.from, "-->").concat(conn.to);
614
+ var subPathIds = new Set(originalToSubPaths.get(origId) || []);
615
+ subPathIds.add(origId); // include the direct path if it wasn't rewired
616
+
617
+ subPathIds.forEach(function (subPathId) {
618
+ var pd = _this5.pathDataStore.get(subPathId);
619
+ if (pd && Object.keys(pd.flowAttributes).length === 0) {
620
+ Object.entries(conn.flowAttributes).forEach(function (_ref4) {
621
+ var _ref5 = _rollupPluginBabelHelpers.slicedToArray(_ref4, 2),
622
+ key = _ref5[0],
623
+ value = _ref5[1];
624
+ pd.setFlowAttribute(key, value);
625
+ });
626
+ console.log("\uD83C\uDF0A Propagated flowAttributes to sub-path \"".concat(subPathId, "\" from \"").concat(origId, "\""));
627
+ }
628
+ });
629
+ });
630
+
631
+ // Pass 2: endpoint fallback for any rendered path still missing attributes
632
+ // (covers Gateway→Gateway intermediate segments not captured by gateway.connections.added)
633
+ fromEndpointAttrs = new Map(); // connectorId → flowAttributes
634
+ data.connections.forEach(function (conn) {
635
+ if (conn.flowAttributes && !fromEndpointAttrs.has(conn.from)) {
636
+ fromEndpointAttrs.set(conn.from, conn.flowAttributes);
637
+ }
638
+ });
639
+ this.pathDataStore.forEach(function (pd, pathId) {
640
+ if (Object.keys(pd.flowAttributes).length > 0) return;
641
+ var sepIdx = pathId.indexOf('-->');
642
+ if (sepIdx === -1) return;
643
+ var from = pathId.slice(0, sepIdx);
644
+ var attrs = fromEndpointAttrs.get(from);
645
+ if (attrs) {
646
+ Object.entries(attrs).forEach(function (_ref6) {
647
+ var _ref7 = _rollupPluginBabelHelpers.slicedToArray(_ref6, 2),
648
+ key = _ref7[0],
649
+ value = _ref7[1];
650
+ return pd.setFlowAttribute(key, value);
651
+ });
652
+ console.log("\uD83C\uDF0A Endpoint-matched flowAttributes for path \"".concat(pathId, "\""));
653
+ }
654
+ });
655
+ }
656
+
657
+ // ── Apply flow visualizations now that paths are rendered ──────────────
658
+ flowMgr = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.pathFlowManager;
659
+ if (flowMgr) {
660
+ flowMgr.applyAllVisualizations();
661
+ }
662
+
532
663
  // Update connections with rewired connections
533
664
  if (pathfindingResult.rewiredConnections) {
534
665
  // data.connections = pathfindingResult.rewiredConnections;