@2112-lab/central-plant 0.1.86 → 0.1.88

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.
@@ -11375,6 +11375,12 @@ var SceneExportManager = /*#__PURE__*/function () {
11375
11375
  // Computed from geometry - not in input format
11376
11376
  'name',
11377
11377
  // Redundant with GLB node names - not in input format
11378
+ 'addedTimestamp',
11379
+ // Internal tracking - not needed in export
11380
+ 'addedBy',
11381
+ // Internal tracking - not needed in export
11382
+ 'initialPosition',
11383
+ // Internal tracking - not needed in export
11378
11384
  // Exclude internal segment tracking properties
11379
11385
  'segmentId',
11380
11386
  // Internal tracking
@@ -19786,6 +19792,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
19786
19792
  id: key,
19787
19793
  name: component.name,
19788
19794
  type: ((_component$metadata = component.metadata) === null || _component$metadata === void 0 ? void 0 : _component$metadata.type) || 'Component',
19795
+ assetType: component.assetType || null,
19789
19796
  category: component.category,
19790
19797
  modelKey: component.modelKey,
19791
19798
  modelType: component.modelType,
@@ -19794,10 +19801,12 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
19794
19801
  // Preserve S3 metadata
19795
19802
  isS3Component: component.isS3Component,
19796
19803
  s3Path: component.s3Path,
19797
- preLoad: component.preLoad
19804
+ preLoad: component.preLoad,
19805
+ preCache: component.preCache
19798
19806
  };
19799
19807
  return _objectSpread2(_objectSpread2({}, baseData), {}, {
19800
19808
  metadata: component.metadata || {},
19809
+ ioConfig: component.ioConfig || null,
19801
19810
  boundingBox: component.boundingBox || null,
19802
19811
  adaptedBoundingBox: component.adaptedBoundingBox || null,
19803
19812
  children: component.children || [],
@@ -33790,24 +33799,14 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
33790
33799
  }, {
33791
33800
  key: "toggleIODeviceBinaryState",
33792
33801
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
33793
- var _this$sceneViewer;
33802
+ var _ref, _this$sceneViewer;
33794
33803
  if (!ioDeviceObject || !this._stateAdapter) return;
33795
33804
  var ud = ioDeviceObject.userData;
33796
33805
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
33797
33806
  var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
33798
33807
  if (!attachmentId) return;
33799
33808
 
33800
- // Find the first binary state
33801
- var binaryState = dataPoints.find(function (dp) {
33802
- return dp.stateType === 'binary' || dp.type === 'binary';
33803
- });
33804
- if (!binaryState) return;
33805
- var dpId = binaryState.id;
33806
- var currentVal = this._stateAdapter.getState(attachmentId, dpId);
33807
- var newVal = !Boolean(currentVal);
33808
- this._stateAdapter.setState(attachmentId, dpId, newVal);
33809
-
33810
- // Walk up to find parent component UUID for scoped behavior triggering
33809
+ // Walk up to find parent component UUID for scoped state/behavior handling
33811
33810
  var parentUuid = null;
33812
33811
  var obj = ioDeviceObject.parent;
33813
33812
  while (obj) {
@@ -33818,8 +33817,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
33818
33817
  }
33819
33818
  obj = obj.parent;
33820
33819
  }
33820
+
33821
+ // Create a scoped attachment key to prevent state sharing between instances
33822
+ // of the same smart component that share the same attachmentId
33823
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
33824
+
33825
+ // Find the first binary state
33826
+ var binaryState = dataPoints.find(function (dp) {
33827
+ return dp.stateType === 'binary' || dp.type === 'binary';
33828
+ });
33829
+ if (!binaryState) return;
33830
+ var dpId = binaryState.id;
33831
+ var storedVal = this._stateAdapter.getState(scopedAttachmentId, dpId);
33832
+ // Fall back to defaultValue when state is uninitialized (null/undefined)
33833
+ var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
33834
+ var newVal = !Boolean(currentVal);
33835
+ this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
33821
33836
  (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.behaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
33822
- console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(attachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
33837
+ console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
33823
33838
  }
33824
33839
 
33825
33840
  /**
@@ -33911,23 +33926,42 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
33911
33926
  this._styleInjected = false;
33912
33927
  }
33913
33928
 
33929
+ /**
33930
+ * Generate a scoped attachment key that includes the parent component UUID.
33931
+ * This ensures each instance of a smart component has isolated IO device state.
33932
+ * @param {string} attachmentId - The original attachment ID from the component data
33933
+ * @param {string|null} parentUuid - The UUID of the parent smart component instance
33934
+ * @returns {string} A scoped key in the format "parentUuid::attachmentId" or just attachmentId if no parent
33935
+ * @private
33936
+ */
33937
+ }, {
33938
+ key: "_getScopedAttachmentKey",
33939
+ value: function _getScopedAttachmentKey(attachmentId, parentUuid) {
33940
+ if (!parentUuid) return attachmentId;
33941
+ return "".concat(parentUuid, "::").concat(attachmentId);
33942
+ }
33943
+
33914
33944
  /**
33915
33945
  * Gather I/O device children from a component's Three.js hierarchy.
33916
33946
  * Returns richer data including attachmentId and data point definitions.
33917
- * @param {THREE.Object3D} object
33918
- * @returns {{ label: string, deviceId: string, attachmentId: string, dataPoints: Array }[]}
33947
+ * @param {THREE.Object3D} object - The parent component object
33948
+ * @returns {{ label: string, deviceId: string, attachmentId: string, scopedAttachmentId: string, dataPoints: Array }[]}
33919
33949
  */
33920
33950
  }, {
33921
33951
  key: "_getIODevices",
33922
33952
  value: function _getIODevices(object) {
33953
+ var _this2 = this;
33923
33954
  var devices = [];
33955
+ var parentUuid = object.uuid; // The component's own UUID
33924
33956
  object.traverse(function (child) {
33925
33957
  var _child$userData;
33926
33958
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
33959
+ var attachmentId = child.userData.attachmentId || '';
33927
33960
  devices.push({
33928
33961
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
33929
33962
  deviceId: child.userData.deviceId || '',
33930
- attachmentId: child.userData.attachmentId || '',
33963
+ attachmentId: attachmentId,
33964
+ scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
33931
33965
  dataPoints: child.userData.dataPoints || [],
33932
33966
  direction: child.userData.ioDirection || 'output'
33933
33967
  });
@@ -33943,7 +33977,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
33943
33977
  }, {
33944
33978
  key: "_buildTooltip",
33945
33979
  value: function _buildTooltip(object) {
33946
- var _this2 = this;
33980
+ var _this3 = this;
33947
33981
  // Remove any existing tooltip first
33948
33982
  this.hide();
33949
33983
  // Re-assign selected object since hide() clears it
@@ -34011,9 +34045,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34011
34045
  list.appendChild(item);
34012
34046
 
34013
34047
  // Data point rows (one per data point definition stored in userData)
34014
- if (device.attachmentId && device.dataPoints.length > 0) {
34048
+ // Use scopedAttachmentId to ensure state is isolated per component instance
34049
+ if (device.scopedAttachmentId && device.dataPoints.length > 0) {
34015
34050
  device.dataPoints.forEach(function (dp) {
34016
- var row = _this2._buildDataPointRow(device.attachmentId, dp, device.direction);
34051
+ var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
34017
34052
  list.appendChild(row);
34018
34053
  });
34019
34054
  }
@@ -34024,11 +34059,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34024
34059
  // Hover expand/collapse
34025
34060
  trigger.addEventListener('mouseenter', function () {
34026
34061
  ioSection.classList.add('expanded');
34027
- _this2._ioExpanded = true;
34062
+ _this3._ioExpanded = true;
34028
34063
  });
34029
34064
  ioSection.addEventListener('mouseleave', function () {
34030
34065
  ioSection.classList.remove('expanded');
34031
- _this2._ioExpanded = false;
34066
+ _this3._ioExpanded = false;
34032
34067
  });
34033
34068
  card.appendChild(ioSection);
34034
34069
  } else {
@@ -34127,18 +34162,19 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34127
34162
  * Output / read-only direction → shows a state badge (updated each frame).
34128
34163
  * Input / bidirectional → shows an interactive control.
34129
34164
  *
34130
- * @param {string} attachmentId
34165
+ * @param {string} scopedAttachmentId - Scoped attachment ID (parentUuid::attachmentId) for state isolation
34131
34166
  * @param {Object} dp - data point definition from ioConfig.dataPoints
34132
34167
  * @param {string} [deviceDirection] - device-level direction ('input'|'output'), overrides dp.direction
34168
+ * @param {string} [originalAttachmentId] - Original attachment ID for behavior triggering
34133
34169
  * @returns {HTMLElement}
34134
34170
  */
34135
34171
  }, {
34136
34172
  key: "_buildDataPointRow",
34137
- value: function _buildDataPointRow(attachmentId, dp, deviceDirection) {
34138
- var _ref,
34173
+ value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
34174
+ var _ref2,
34139
34175
  _this$_stateAdapter$g,
34140
34176
  _this$_stateAdapter,
34141
- _this3 = this;
34177
+ _this4 = this;
34142
34178
  var row = document.createElement('div');
34143
34179
  row.className = 'cp-tooltip__dp-row';
34144
34180
  var nameEl = document.createElement('span');
@@ -34146,20 +34182,21 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34146
34182
  nameEl.textContent = dp.name || dp.id || '?';
34147
34183
  row.appendChild(nameEl);
34148
34184
  var dpId = dp.id || dp.name;
34149
- var key = "".concat(attachmentId, "::").concat(dpId);
34185
+ var key = "".concat(scopedAttachmentId, "::").concat(dpId);
34150
34186
  // Device-level direction takes precedence; fall back to per-dp direction
34151
34187
  var resolvedDirection = deviceDirection || dp.direction || 'output';
34152
34188
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
34153
- var currentVal = (_ref = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(attachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref !== void 0 ? _ref : null;
34189
+ var currentVal = (_ref2 = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : null;
34154
34190
  if (isInput) {
34155
34191
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
34156
- var _this3$_stateAdapter, _this3$selectedObject, _this3$sceneViewer;
34157
- (_this3$_stateAdapter = _this3._stateAdapter) === null || _this3$_stateAdapter === void 0 || _this3$_stateAdapter.setState(attachmentId, dpId, newVal);
34192
+ var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer;
34193
+ (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
34158
34194
  // Also fire BehaviorManager so any wired behaviors react immediately.
34159
34195
  // Pass the parent component UUID so behaviors scoped to a specific instance
34160
34196
  // don't bleed across clones that share the same attachmentId.
34161
- var parentUuid = ((_this3$selectedObject = _this3.selectedObject) === null || _this3$selectedObject === void 0 ? void 0 : _this3$selectedObject.uuid) || null;
34162
- (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.behaviorManager) === null || _this3$sceneViewer === void 0 || _this3$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
34197
+ // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
34198
+ var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
34199
+ (_this4$sceneViewer = _this4.sceneViewer) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.managers) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.behaviorManager) === null || _this4$sceneViewer === void 0 || _this4$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
34163
34200
  });
34164
34201
  row.appendChild(ctrl);
34165
34202
  this._stateElements.set(key, {
@@ -34309,20 +34346,23 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34309
34346
  /**
34310
34347
  * Re-read all tracked read-only badge values from the state adapter.
34311
34348
  * Called each frame from update() — skips if no adapter is configured.
34349
+ * Key format is scopedAttachmentId::dataPointId where scopedAttachmentId
34350
+ * can be parentUuid::attachmentId, resulting in parentUuid::attachmentId::dataPointId
34312
34351
  */
34313
34352
  }, {
34314
34353
  key: "_refreshStateDisplays",
34315
34354
  value: function _refreshStateDisplays() {
34316
- var _this4 = this;
34355
+ var _this5 = this;
34317
34356
  if (!this._stateAdapter || !this._stateElements.size) return;
34318
34357
  this._stateElements.forEach(function (entry, key) {
34319
34358
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
34320
- var sepIdx = key.indexOf('::');
34359
+ // Use lastIndexOf since scopedAttachmentId may contain '::' (parentUuid::attachmentId)
34360
+ var sepIdx = key.lastIndexOf('::');
34321
34361
  if (sepIdx === -1) return;
34322
- var attachmentId = key.slice(0, sepIdx);
34362
+ var scopedAttachmentId = key.slice(0, sepIdx);
34323
34363
  var dataPointId = key.slice(sepIdx + 2);
34324
- var val = _this4._stateAdapter.getState(attachmentId, dataPointId);
34325
- _this4._applyBadgeValue(entry.el, val, entry.dp);
34364
+ var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
34365
+ _this5._applyBadgeValue(entry.el, val, entry.dp);
34326
34366
  });
34327
34367
  }
34328
34368
  }]);
@@ -36734,7 +36774,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
36734
36774
  * Initialize the CentralPlant manager
36735
36775
  *
36736
36776
  * @constructor
36737
- * @version 0.1.86
36777
+ * @version 0.1.88
36738
36778
  * @updated 2025-10-22
36739
36779
  *
36740
36780
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -19,7 +19,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
19
19
  * Initialize the CentralPlant manager
20
20
  *
21
21
  * @constructor
22
- * @version 0.1.86
22
+ * @version 0.1.88
23
23
  * @updated 2025-10-22
24
24
  *
25
25
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -845,6 +845,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
845
845
  id: key,
846
846
  name: component.name,
847
847
  type: ((_component$metadata = component.metadata) === null || _component$metadata === void 0 ? void 0 : _component$metadata.type) || 'Component',
848
+ assetType: component.assetType || null,
848
849
  category: component.category,
849
850
  modelKey: component.modelKey,
850
851
  modelType: component.modelType,
@@ -853,10 +854,12 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
853
854
  // Preserve S3 metadata
854
855
  isS3Component: component.isS3Component,
855
856
  s3Path: component.s3Path,
856
- preLoad: component.preLoad
857
+ preLoad: component.preLoad,
858
+ preCache: component.preCache
857
859
  };
858
860
  return _rollupPluginBabelHelpers.objectSpread2(_rollupPluginBabelHelpers.objectSpread2({}, baseData), {}, {
859
861
  metadata: component.metadata || {},
862
+ ioConfig: component.ioConfig || null,
860
863
  boundingBox: component.boundingBox || null,
861
864
  adaptedBoundingBox: component.adaptedBoundingBox || null,
862
865
  children: component.children || [],
@@ -129,24 +129,14 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
129
129
  }, {
130
130
  key: "toggleIODeviceBinaryState",
131
131
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
132
- var _this$sceneViewer;
132
+ var _ref, _this$sceneViewer;
133
133
  if (!ioDeviceObject || !this._stateAdapter) return;
134
134
  var ud = ioDeviceObject.userData;
135
135
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
136
136
  var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
137
137
  if (!attachmentId) return;
138
138
 
139
- // Find the first binary state
140
- var binaryState = dataPoints.find(function (dp) {
141
- return dp.stateType === 'binary' || dp.type === 'binary';
142
- });
143
- if (!binaryState) return;
144
- var dpId = binaryState.id;
145
- var currentVal = this._stateAdapter.getState(attachmentId, dpId);
146
- var newVal = !Boolean(currentVal);
147
- this._stateAdapter.setState(attachmentId, dpId, newVal);
148
-
149
- // Walk up to find parent component UUID for scoped behavior triggering
139
+ // Walk up to find parent component UUID for scoped state/behavior handling
150
140
  var parentUuid = null;
151
141
  var obj = ioDeviceObject.parent;
152
142
  while (obj) {
@@ -157,8 +147,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
157
147
  }
158
148
  obj = obj.parent;
159
149
  }
150
+
151
+ // Create a scoped attachment key to prevent state sharing between instances
152
+ // of the same smart component that share the same attachmentId
153
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
154
+
155
+ // Find the first binary state
156
+ var binaryState = dataPoints.find(function (dp) {
157
+ return dp.stateType === 'binary' || dp.type === 'binary';
158
+ });
159
+ if (!binaryState) return;
160
+ var dpId = binaryState.id;
161
+ var storedVal = this._stateAdapter.getState(scopedAttachmentId, dpId);
162
+ // Fall back to defaultValue when state is uninitialized (null/undefined)
163
+ var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
164
+ var newVal = !Boolean(currentVal);
165
+ this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
160
166
  (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.behaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
161
- console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(attachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
167
+ console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
162
168
  }
163
169
 
164
170
  /**
@@ -250,23 +256,42 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
250
256
  this._styleInjected = false;
251
257
  }
252
258
 
259
+ /**
260
+ * Generate a scoped attachment key that includes the parent component UUID.
261
+ * This ensures each instance of a smart component has isolated IO device state.
262
+ * @param {string} attachmentId - The original attachment ID from the component data
263
+ * @param {string|null} parentUuid - The UUID of the parent smart component instance
264
+ * @returns {string} A scoped key in the format "parentUuid::attachmentId" or just attachmentId if no parent
265
+ * @private
266
+ */
267
+ }, {
268
+ key: "_getScopedAttachmentKey",
269
+ value: function _getScopedAttachmentKey(attachmentId, parentUuid) {
270
+ if (!parentUuid) return attachmentId;
271
+ return "".concat(parentUuid, "::").concat(attachmentId);
272
+ }
273
+
253
274
  /**
254
275
  * Gather I/O device children from a component's Three.js hierarchy.
255
276
  * Returns richer data including attachmentId and data point definitions.
256
- * @param {THREE.Object3D} object
257
- * @returns {{ label: string, deviceId: string, attachmentId: string, dataPoints: Array }[]}
277
+ * @param {THREE.Object3D} object - The parent component object
278
+ * @returns {{ label: string, deviceId: string, attachmentId: string, scopedAttachmentId: string, dataPoints: Array }[]}
258
279
  */
259
280
  }, {
260
281
  key: "_getIODevices",
261
282
  value: function _getIODevices(object) {
283
+ var _this2 = this;
262
284
  var devices = [];
285
+ var parentUuid = object.uuid; // The component's own UUID
263
286
  object.traverse(function (child) {
264
287
  var _child$userData;
265
288
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
289
+ var attachmentId = child.userData.attachmentId || '';
266
290
  devices.push({
267
291
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
268
292
  deviceId: child.userData.deviceId || '',
269
- attachmentId: child.userData.attachmentId || '',
293
+ attachmentId: attachmentId,
294
+ scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
270
295
  dataPoints: child.userData.dataPoints || [],
271
296
  direction: child.userData.ioDirection || 'output'
272
297
  });
@@ -282,7 +307,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
282
307
  }, {
283
308
  key: "_buildTooltip",
284
309
  value: function _buildTooltip(object) {
285
- var _this2 = this;
310
+ var _this3 = this;
286
311
  // Remove any existing tooltip first
287
312
  this.hide();
288
313
  // Re-assign selected object since hide() clears it
@@ -350,9 +375,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
350
375
  list.appendChild(item);
351
376
 
352
377
  // Data point rows (one per data point definition stored in userData)
353
- if (device.attachmentId && device.dataPoints.length > 0) {
378
+ // Use scopedAttachmentId to ensure state is isolated per component instance
379
+ if (device.scopedAttachmentId && device.dataPoints.length > 0) {
354
380
  device.dataPoints.forEach(function (dp) {
355
- var row = _this2._buildDataPointRow(device.attachmentId, dp, device.direction);
381
+ var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
356
382
  list.appendChild(row);
357
383
  });
358
384
  }
@@ -363,11 +389,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
363
389
  // Hover expand/collapse
364
390
  trigger.addEventListener('mouseenter', function () {
365
391
  ioSection.classList.add('expanded');
366
- _this2._ioExpanded = true;
392
+ _this3._ioExpanded = true;
367
393
  });
368
394
  ioSection.addEventListener('mouseleave', function () {
369
395
  ioSection.classList.remove('expanded');
370
- _this2._ioExpanded = false;
396
+ _this3._ioExpanded = false;
371
397
  });
372
398
  card.appendChild(ioSection);
373
399
  } else {
@@ -466,18 +492,19 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
466
492
  * Output / read-only direction → shows a state badge (updated each frame).
467
493
  * Input / bidirectional → shows an interactive control.
468
494
  *
469
- * @param {string} attachmentId
495
+ * @param {string} scopedAttachmentId - Scoped attachment ID (parentUuid::attachmentId) for state isolation
470
496
  * @param {Object} dp - data point definition from ioConfig.dataPoints
471
497
  * @param {string} [deviceDirection] - device-level direction ('input'|'output'), overrides dp.direction
498
+ * @param {string} [originalAttachmentId] - Original attachment ID for behavior triggering
472
499
  * @returns {HTMLElement}
473
500
  */
474
501
  }, {
475
502
  key: "_buildDataPointRow",
476
- value: function _buildDataPointRow(attachmentId, dp, deviceDirection) {
477
- var _ref,
503
+ value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
504
+ var _ref2,
478
505
  _this$_stateAdapter$g,
479
506
  _this$_stateAdapter,
480
- _this3 = this;
507
+ _this4 = this;
481
508
  var row = document.createElement('div');
482
509
  row.className = 'cp-tooltip__dp-row';
483
510
  var nameEl = document.createElement('span');
@@ -485,20 +512,21 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
485
512
  nameEl.textContent = dp.name || dp.id || '?';
486
513
  row.appendChild(nameEl);
487
514
  var dpId = dp.id || dp.name;
488
- var key = "".concat(attachmentId, "::").concat(dpId);
515
+ var key = "".concat(scopedAttachmentId, "::").concat(dpId);
489
516
  // Device-level direction takes precedence; fall back to per-dp direction
490
517
  var resolvedDirection = deviceDirection || dp.direction || 'output';
491
518
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
492
- var currentVal = (_ref = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(attachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref !== void 0 ? _ref : null;
519
+ var currentVal = (_ref2 = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : null;
493
520
  if (isInput) {
494
521
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
495
- var _this3$_stateAdapter, _this3$selectedObject, _this3$sceneViewer;
496
- (_this3$_stateAdapter = _this3._stateAdapter) === null || _this3$_stateAdapter === void 0 || _this3$_stateAdapter.setState(attachmentId, dpId, newVal);
522
+ var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer;
523
+ (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
497
524
  // Also fire BehaviorManager so any wired behaviors react immediately.
498
525
  // Pass the parent component UUID so behaviors scoped to a specific instance
499
526
  // don't bleed across clones that share the same attachmentId.
500
- var parentUuid = ((_this3$selectedObject = _this3.selectedObject) === null || _this3$selectedObject === void 0 ? void 0 : _this3$selectedObject.uuid) || null;
501
- (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.behaviorManager) === null || _this3$sceneViewer === void 0 || _this3$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
527
+ // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
528
+ var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
529
+ (_this4$sceneViewer = _this4.sceneViewer) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.managers) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.behaviorManager) === null || _this4$sceneViewer === void 0 || _this4$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
502
530
  });
503
531
  row.appendChild(ctrl);
504
532
  this._stateElements.set(key, {
@@ -648,20 +676,23 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
648
676
  /**
649
677
  * Re-read all tracked read-only badge values from the state adapter.
650
678
  * Called each frame from update() — skips if no adapter is configured.
679
+ * Key format is scopedAttachmentId::dataPointId where scopedAttachmentId
680
+ * can be parentUuid::attachmentId, resulting in parentUuid::attachmentId::dataPointId
651
681
  */
652
682
  }, {
653
683
  key: "_refreshStateDisplays",
654
684
  value: function _refreshStateDisplays() {
655
- var _this4 = this;
685
+ var _this5 = this;
656
686
  if (!this._stateAdapter || !this._stateElements.size) return;
657
687
  this._stateElements.forEach(function (entry, key) {
658
688
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
659
- var sepIdx = key.indexOf('::');
689
+ // Use lastIndexOf since scopedAttachmentId may contain '::' (parentUuid::attachmentId)
690
+ var sepIdx = key.lastIndexOf('::');
660
691
  if (sepIdx === -1) return;
661
- var attachmentId = key.slice(0, sepIdx);
692
+ var scopedAttachmentId = key.slice(0, sepIdx);
662
693
  var dataPointId = key.slice(sepIdx + 2);
663
- var val = _this4._stateAdapter.getState(attachmentId, dataPointId);
664
- _this4._applyBadgeValue(entry.el, val, entry.dp);
694
+ var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
695
+ _this5._applyBadgeValue(entry.el, val, entry.dp);
665
696
  });
666
697
  }
667
698
  }]);
@@ -160,6 +160,12 @@ var SceneExportManager = /*#__PURE__*/function () {
160
160
  // Computed from geometry - not in input format
161
161
  'name',
162
162
  // Redundant with GLB node names - not in input format
163
+ 'addedTimestamp',
164
+ // Internal tracking - not needed in export
165
+ 'addedBy',
166
+ // Internal tracking - not needed in export
167
+ 'initialPosition',
168
+ // Internal tracking - not needed in export
163
169
  // Exclude internal segment tracking properties
164
170
  'segmentId',
165
171
  // Internal tracking
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.86
18
+ * @version 0.1.88
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -821,6 +821,7 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
821
821
  id: key,
822
822
  name: component.name,
823
823
  type: ((_component$metadata = component.metadata) === null || _component$metadata === void 0 ? void 0 : _component$metadata.type) || 'Component',
824
+ assetType: component.assetType || null,
824
825
  category: component.category,
825
826
  modelKey: component.modelKey,
826
827
  modelType: component.modelType,
@@ -829,10 +830,12 @@ var ComponentDataManager = /*#__PURE__*/function (_BaseDisposable) {
829
830
  // Preserve S3 metadata
830
831
  isS3Component: component.isS3Component,
831
832
  s3Path: component.s3Path,
832
- preLoad: component.preLoad
833
+ preLoad: component.preLoad,
834
+ preCache: component.preCache
833
835
  };
834
836
  return _objectSpread2(_objectSpread2({}, baseData), {}, {
835
837
  metadata: component.metadata || {},
838
+ ioConfig: component.ioConfig || null,
836
839
  boundingBox: component.boundingBox || null,
837
840
  adaptedBoundingBox: component.adaptedBoundingBox || null,
838
841
  children: component.children || [],
@@ -105,24 +105,14 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
105
105
  }, {
106
106
  key: "toggleIODeviceBinaryState",
107
107
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
108
- var _this$sceneViewer;
108
+ var _ref, _this$sceneViewer;
109
109
  if (!ioDeviceObject || !this._stateAdapter) return;
110
110
  var ud = ioDeviceObject.userData;
111
111
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
112
112
  var dataPoints = (ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [];
113
113
  if (!attachmentId) return;
114
114
 
115
- // Find the first binary state
116
- var binaryState = dataPoints.find(function (dp) {
117
- return dp.stateType === 'binary' || dp.type === 'binary';
118
- });
119
- if (!binaryState) return;
120
- var dpId = binaryState.id;
121
- var currentVal = this._stateAdapter.getState(attachmentId, dpId);
122
- var newVal = !Boolean(currentVal);
123
- this._stateAdapter.setState(attachmentId, dpId, newVal);
124
-
125
- // Walk up to find parent component UUID for scoped behavior triggering
115
+ // Walk up to find parent component UUID for scoped state/behavior handling
126
116
  var parentUuid = null;
127
117
  var obj = ioDeviceObject.parent;
128
118
  while (obj) {
@@ -133,8 +123,24 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
133
123
  }
134
124
  obj = obj.parent;
135
125
  }
126
+
127
+ // Create a scoped attachment key to prevent state sharing between instances
128
+ // of the same smart component that share the same attachmentId
129
+ var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
130
+
131
+ // Find the first binary state
132
+ var binaryState = dataPoints.find(function (dp) {
133
+ return dp.stateType === 'binary' || dp.type === 'binary';
134
+ });
135
+ if (!binaryState) return;
136
+ var dpId = binaryState.id;
137
+ var storedVal = this._stateAdapter.getState(scopedAttachmentId, dpId);
138
+ // Fall back to defaultValue when state is uninitialized (null/undefined)
139
+ var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
140
+ var newVal = !Boolean(currentVal);
141
+ this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
136
142
  (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.behaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
137
- console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(attachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
143
+ console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
138
144
  }
139
145
 
140
146
  /**
@@ -226,23 +232,42 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
226
232
  this._styleInjected = false;
227
233
  }
228
234
 
235
+ /**
236
+ * Generate a scoped attachment key that includes the parent component UUID.
237
+ * This ensures each instance of a smart component has isolated IO device state.
238
+ * @param {string} attachmentId - The original attachment ID from the component data
239
+ * @param {string|null} parentUuid - The UUID of the parent smart component instance
240
+ * @returns {string} A scoped key in the format "parentUuid::attachmentId" or just attachmentId if no parent
241
+ * @private
242
+ */
243
+ }, {
244
+ key: "_getScopedAttachmentKey",
245
+ value: function _getScopedAttachmentKey(attachmentId, parentUuid) {
246
+ if (!parentUuid) return attachmentId;
247
+ return "".concat(parentUuid, "::").concat(attachmentId);
248
+ }
249
+
229
250
  /**
230
251
  * Gather I/O device children from a component's Three.js hierarchy.
231
252
  * Returns richer data including attachmentId and data point definitions.
232
- * @param {THREE.Object3D} object
233
- * @returns {{ label: string, deviceId: string, attachmentId: string, dataPoints: Array }[]}
253
+ * @param {THREE.Object3D} object - The parent component object
254
+ * @returns {{ label: string, deviceId: string, attachmentId: string, scopedAttachmentId: string, dataPoints: Array }[]}
234
255
  */
235
256
  }, {
236
257
  key: "_getIODevices",
237
258
  value: function _getIODevices(object) {
259
+ var _this2 = this;
238
260
  var devices = [];
261
+ var parentUuid = object.uuid; // The component's own UUID
239
262
  object.traverse(function (child) {
240
263
  var _child$userData;
241
264
  if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
265
+ var attachmentId = child.userData.attachmentId || '';
242
266
  devices.push({
243
267
  label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
244
268
  deviceId: child.userData.deviceId || '',
245
- attachmentId: child.userData.attachmentId || '',
269
+ attachmentId: attachmentId,
270
+ scopedAttachmentId: _this2._getScopedAttachmentKey(attachmentId, parentUuid),
246
271
  dataPoints: child.userData.dataPoints || [],
247
272
  direction: child.userData.ioDirection || 'output'
248
273
  });
@@ -258,7 +283,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
258
283
  }, {
259
284
  key: "_buildTooltip",
260
285
  value: function _buildTooltip(object) {
261
- var _this2 = this;
286
+ var _this3 = this;
262
287
  // Remove any existing tooltip first
263
288
  this.hide();
264
289
  // Re-assign selected object since hide() clears it
@@ -326,9 +351,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
326
351
  list.appendChild(item);
327
352
 
328
353
  // Data point rows (one per data point definition stored in userData)
329
- if (device.attachmentId && device.dataPoints.length > 0) {
354
+ // Use scopedAttachmentId to ensure state is isolated per component instance
355
+ if (device.scopedAttachmentId && device.dataPoints.length > 0) {
330
356
  device.dataPoints.forEach(function (dp) {
331
- var row = _this2._buildDataPointRow(device.attachmentId, dp, device.direction);
357
+ var row = _this3._buildDataPointRow(device.scopedAttachmentId, dp, device.direction, device.attachmentId);
332
358
  list.appendChild(row);
333
359
  });
334
360
  }
@@ -339,11 +365,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
339
365
  // Hover expand/collapse
340
366
  trigger.addEventListener('mouseenter', function () {
341
367
  ioSection.classList.add('expanded');
342
- _this2._ioExpanded = true;
368
+ _this3._ioExpanded = true;
343
369
  });
344
370
  ioSection.addEventListener('mouseleave', function () {
345
371
  ioSection.classList.remove('expanded');
346
- _this2._ioExpanded = false;
372
+ _this3._ioExpanded = false;
347
373
  });
348
374
  card.appendChild(ioSection);
349
375
  } else {
@@ -442,18 +468,19 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
442
468
  * Output / read-only direction → shows a state badge (updated each frame).
443
469
  * Input / bidirectional → shows an interactive control.
444
470
  *
445
- * @param {string} attachmentId
471
+ * @param {string} scopedAttachmentId - Scoped attachment ID (parentUuid::attachmentId) for state isolation
446
472
  * @param {Object} dp - data point definition from ioConfig.dataPoints
447
473
  * @param {string} [deviceDirection] - device-level direction ('input'|'output'), overrides dp.direction
474
+ * @param {string} [originalAttachmentId] - Original attachment ID for behavior triggering
448
475
  * @returns {HTMLElement}
449
476
  */
450
477
  }, {
451
478
  key: "_buildDataPointRow",
452
- value: function _buildDataPointRow(attachmentId, dp, deviceDirection) {
453
- var _ref,
479
+ value: function _buildDataPointRow(scopedAttachmentId, dp, deviceDirection, originalAttachmentId) {
480
+ var _ref2,
454
481
  _this$_stateAdapter$g,
455
482
  _this$_stateAdapter,
456
- _this3 = this;
483
+ _this4 = this;
457
484
  var row = document.createElement('div');
458
485
  row.className = 'cp-tooltip__dp-row';
459
486
  var nameEl = document.createElement('span');
@@ -461,20 +488,21 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
461
488
  nameEl.textContent = dp.name || dp.id || '?';
462
489
  row.appendChild(nameEl);
463
490
  var dpId = dp.id || dp.name;
464
- var key = "".concat(attachmentId, "::").concat(dpId);
491
+ var key = "".concat(scopedAttachmentId, "::").concat(dpId);
465
492
  // Device-level direction takes precedence; fall back to per-dp direction
466
493
  var resolvedDirection = deviceDirection || dp.direction || 'output';
467
494
  var isInput = resolvedDirection === 'input' || resolvedDirection === 'bidirectional';
468
- var currentVal = (_ref = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(attachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref !== void 0 ? _ref : null;
495
+ var currentVal = (_ref2 = (_this$_stateAdapter$g = (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 ? void 0 : _this$_stateAdapter.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref2 !== void 0 ? _ref2 : null;
469
496
  if (isInput) {
470
497
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
471
- var _this3$_stateAdapter, _this3$selectedObject, _this3$sceneViewer;
472
- (_this3$_stateAdapter = _this3._stateAdapter) === null || _this3$_stateAdapter === void 0 || _this3$_stateAdapter.setState(attachmentId, dpId, newVal);
498
+ var _this4$_stateAdapter, _this4$selectedObject, _this4$sceneViewer;
499
+ (_this4$_stateAdapter = _this4._stateAdapter) === null || _this4$_stateAdapter === void 0 || _this4$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
473
500
  // Also fire BehaviorManager so any wired behaviors react immediately.
474
501
  // Pass the parent component UUID so behaviors scoped to a specific instance
475
502
  // don't bleed across clones that share the same attachmentId.
476
- var parentUuid = ((_this3$selectedObject = _this3.selectedObject) === null || _this3$selectedObject === void 0 ? void 0 : _this3$selectedObject.uuid) || null;
477
- (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.behaviorManager) === null || _this3$sceneViewer === void 0 || _this3$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
503
+ // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
504
+ var parentUuid = ((_this4$selectedObject = _this4.selectedObject) === null || _this4$selectedObject === void 0 ? void 0 : _this4$selectedObject.uuid) || null;
505
+ (_this4$sceneViewer = _this4.sceneViewer) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.managers) === null || _this4$sceneViewer === void 0 || (_this4$sceneViewer = _this4$sceneViewer.behaviorManager) === null || _this4$sceneViewer === void 0 || _this4$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
478
506
  });
479
507
  row.appendChild(ctrl);
480
508
  this._stateElements.set(key, {
@@ -624,20 +652,23 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
624
652
  /**
625
653
  * Re-read all tracked read-only badge values from the state adapter.
626
654
  * Called each frame from update() — skips if no adapter is configured.
655
+ * Key format is scopedAttachmentId::dataPointId where scopedAttachmentId
656
+ * can be parentUuid::attachmentId, resulting in parentUuid::attachmentId::dataPointId
627
657
  */
628
658
  }, {
629
659
  key: "_refreshStateDisplays",
630
660
  value: function _refreshStateDisplays() {
631
- var _this4 = this;
661
+ var _this5 = this;
632
662
  if (!this._stateAdapter || !this._stateElements.size) return;
633
663
  this._stateElements.forEach(function (entry, key) {
634
664
  if (entry.isInput) return; // interactive controls are user-driven; don't overwrite
635
- var sepIdx = key.indexOf('::');
665
+ // Use lastIndexOf since scopedAttachmentId may contain '::' (parentUuid::attachmentId)
666
+ var sepIdx = key.lastIndexOf('::');
636
667
  if (sepIdx === -1) return;
637
- var attachmentId = key.slice(0, sepIdx);
668
+ var scopedAttachmentId = key.slice(0, sepIdx);
638
669
  var dataPointId = key.slice(sepIdx + 2);
639
- var val = _this4._stateAdapter.getState(attachmentId, dataPointId);
640
- _this4._applyBadgeValue(entry.el, val, entry.dp);
670
+ var val = _this5._stateAdapter.getState(scopedAttachmentId, dataPointId);
671
+ _this5._applyBadgeValue(entry.el, val, entry.dp);
641
672
  });
642
673
  }
643
674
  }]);
@@ -138,6 +138,12 @@ var SceneExportManager = /*#__PURE__*/function () {
138
138
  // Computed from geometry - not in input format
139
139
  'name',
140
140
  // Redundant with GLB node names - not in input format
141
+ 'addedTimestamp',
142
+ // Internal tracking - not needed in export
143
+ 'addedBy',
144
+ // Internal tracking - not needed in export
145
+ 'initialPosition',
146
+ // Internal tracking - not needed in export
141
147
  // Exclude internal segment tracking properties
142
148
  'segmentId',
143
149
  // Internal tracking
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.1.86",
3
+ "version": "0.1.88",
4
4
  "description": "Utility modules for the Central Plant Application",
5
5
  "main": "dist/bundle/index.js",
6
6
  "module": "dist/esm/src/index.js",