@2112-lab/central-plant 0.3.27 → 0.3.29

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.
@@ -11828,40 +11828,14 @@ var SceneExportManager = /*#__PURE__*/function () {
11828
11828
  }
11829
11829
  });
11830
11830
 
11831
- // Helper function to extract behaviors from current scene data
11832
- var extractBehaviors = function extractBehaviors() {
11833
- var _this$sceneViewer, _this$sceneViewer2;
11834
- // Only export behaviors that are NOT re-derivable from a component's
11835
- // defaultBehaviors[]. All component/device default behaviors are
11836
- // reconstructed at load time by Step B of _processBehaviors() using the
11837
- // component dictionary, so writing compact behaviorRef entries for them
11838
- // would be redundant. The scene JSON behaviors[] array is reserved for
11839
- // any future scene-level overrides that cannot be derived from the asset.
11840
- if ((_this$sceneViewer = _this.sceneViewer) !== null && _this$sceneViewer !== void 0 && (_this$sceneViewer = _this$sceneViewer.managers) !== null && _this$sceneViewer !== void 0 && _this$sceneViewer.behaviorManager) {
11841
- return _this.sceneViewer.managers.behaviorManager.getBehaviors().filter(function (b) {
11842
- return !b._isDefaultBehavior;
11843
- });
11844
- }
11845
- // Fallback when BehaviorManager is not available: exclude any entry that
11846
- // was already a behaviorRef (it was derivable from the component asset)
11847
- // and exclude the legacy _isDefaultBehavior marker if present.
11848
- return (((_this$sceneViewer2 = _this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.currentSceneData) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.behaviors) || []).filter(function (b) {
11849
- return !b.behaviorRef && !b._isDefaultBehavior;
11850
- });
11851
- };
11852
-
11853
11831
  // Build the complete export data structure (matching central-plant-input.json format)
11854
- var behaviors = extractBehaviors();
11855
- var exportData = _objectSpread2(_objectSpread2({
11832
+ var exportData = {
11856
11833
  version: '2.3',
11857
- connections: extractConnections()
11858
- }, behaviors.length > 0 ? {
11859
- behaviors: behaviors
11860
- } : {}), {}, {
11834
+ connections: extractConnections(),
11861
11835
  scene: {
11862
11836
  children: sceneChildren
11863
11837
  }
11864
- });
11838
+ };
11865
11839
  console.log('✅ Scene export completed:', exportData);
11866
11840
  console.log("\uD83D\uDCCA Exported ".concat(sceneChildren.length, " components and ").concat(exportData.connections.length, " connections"));
11867
11841
  return exportData;
@@ -29818,407 +29792,6 @@ var PathFlowManager = /*#__PURE__*/function (_BaseDisposable) {
29818
29792
  }]);
29819
29793
  }(BaseDisposable);
29820
29794
 
29821
- var BehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
29822
- function BehaviorManager(sceneViewer) {
29823
- var _this;
29824
- _classCallCheck(this, BehaviorManager);
29825
- _this = _callSuper(this, BehaviorManager);
29826
- _this.sceneViewer = sceneViewer;
29827
-
29828
- /** @type {Array<Object>} Raw behavior definitions from the scene JSON */
29829
- _this._behaviors = [];
29830
-
29831
- /** @type {Map<string, THREE.Object3D>} Cache: attachmentId -> resolved Three.js object */
29832
- _this._resolvedOutputs = new Map();
29833
- return _this;
29834
- }
29835
-
29836
- // ─────────────────────────────────────────────────────────────────────────
29837
- // PUBLIC API
29838
- // ─────────────────────────────────────────────────────────────────────────
29839
-
29840
- /**
29841
- * Load and store behavior definitions, then resolve output objects.
29842
- * Should be called AFTER GLB models have been loaded so all meshes exist.
29843
- * @param {Array<Object>} behaviorsArray - Array of behavior definition objects
29844
- */
29845
- _inherits(BehaviorManager, _BaseDisposable);
29846
- return _createClass(BehaviorManager, [{
29847
- key: "loadBehaviors",
29848
- value: function loadBehaviors(behaviorsArray) {
29849
- if (!Array.isArray(behaviorsArray)) {
29850
- console.warn('⚠️ BehaviorManager.loadBehaviors(): expected an array, got', _typeof(behaviorsArray));
29851
- return;
29852
- }
29853
- this._behaviors = behaviorsArray;
29854
- this._resolvedOutputs.clear();
29855
- this._resolveOutputObjects();
29856
- console.log("\u2705 BehaviorManager: loaded ".concat(this._behaviors.length, " behavior(s)"));
29857
- }
29858
-
29859
- /**
29860
- * Returns all stored behavior definitions.
29861
- * @returns {Array<Object>}
29862
- */
29863
- }, {
29864
- key: "getBehaviors",
29865
- value: function getBehaviors() {
29866
- return _toConsumableArray(this._behaviors);
29867
- }
29868
-
29869
- /**
29870
- * Add a single behavior definition at runtime and resolve its output object.
29871
- * @param {Object} behaviorDef
29872
- */
29873
- }, {
29874
- key: "addBehavior",
29875
- value: function addBehavior(behaviorDef) {
29876
- if (!behaviorDef || !behaviorDef.id) {
29877
- console.warn('⚠️ BehaviorManager.addBehavior(): missing id', behaviorDef);
29878
- return false;
29879
- }
29880
- if (this._behaviors.find(function (b) {
29881
- return b.id === behaviorDef.id;
29882
- })) {
29883
- console.warn("\u26A0\uFE0F BehaviorManager.addBehavior(): id \"".concat(behaviorDef.id, "\" already exists"));
29884
- return false;
29885
- }
29886
- this._behaviors.push(behaviorDef);
29887
- this._resolveOutputForBehavior(behaviorDef);
29888
- console.log("\u2705 BehaviorManager: added behavior \"".concat(behaviorDef.id, "\""));
29889
- return true;
29890
- }
29891
-
29892
- /**
29893
- * Remove a behavior definition by id.
29894
- * @param {string} behaviorId
29895
- * @returns {boolean}
29896
- */
29897
- }, {
29898
- key: "removeBehavior",
29899
- value: function removeBehavior(behaviorId) {
29900
- var idx = this._behaviors.findIndex(function (b) {
29901
- return b.id === behaviorId;
29902
- });
29903
- if (idx === -1) {
29904
- console.warn("\u26A0\uFE0F BehaviorManager.removeBehavior(): id \"".concat(behaviorId, "\" not found"));
29905
- return false;
29906
- }
29907
- this._behaviors.splice(idx, 1);
29908
- // We keep the resolved cache entry — it is harmless if no behaviors reference it
29909
- console.log("\u2705 BehaviorManager: removed behavior \"".concat(behaviorId, "\""));
29910
- return true;
29911
- }
29912
-
29913
- /**
29914
- * Simulate an IO device state value arriving and evaluate all matching behaviors.
29915
- * Any behavior whose input.attachment + input.state matches will have its
29916
- * conditions tested and actions applied.
29917
- *
29918
- * @param {string} attachmentId - The attachment ID of the input io-device
29919
- * @param {string} stateId - The state ID on that device
29920
- * @param {*} value - Current value of the state
29921
- * @param {string} [parentUuid] - UUID of the parent component instance (used to
29922
- * disambiguate clones that share the same attachmentId)
29923
- */
29924
- }, {
29925
- key: "triggerState",
29926
- value: function triggerState(attachmentId, stateId, value, parentUuid) {
29927
- var _this2 = this;
29928
- var matching = this._behaviors.filter(function (b) {
29929
- var _b$input, _b$input2, _b$input3;
29930
- if (((_b$input = b.input) === null || _b$input === void 0 ? void 0 : _b$input.attachment) !== attachmentId) return false;
29931
- if (((_b$input2 = b.input) === null || _b$input2 === void 0 ? void 0 : _b$input2.state) !== stateId) return false;
29932
- // If both the behavior and the caller carry a component UUID, they must match.
29933
- // If either side has no component context, fall back to attachment-only matching.
29934
- if ((_b$input3 = b.input) !== null && _b$input3 !== void 0 && _b$input3.component && parentUuid && b.input.component !== parentUuid) return false;
29935
- return true;
29936
- });
29937
- if (matching.length === 0) {
29938
- console.log("\u2139\uFE0F BehaviorManager.triggerState(): no behaviors for \"".concat(attachmentId, "/").concat(stateId, "\""));
29939
- return;
29940
- }
29941
- matching.forEach(function (behavior) {
29942
- _this2._evaluateBehavior(behavior, value);
29943
- });
29944
- }
29945
-
29946
- // ─────────────────────────────────────────────────────────────────────────
29947
- // PRIVATE HELPERS
29948
- // ─────────────────────────────────────────────────────────────────────────
29949
-
29950
- /**
29951
- * Walk all behaviors and resolve each output object once.
29952
- */
29953
- }, {
29954
- key: "_resolveOutputObjects",
29955
- value: function _resolveOutputObjects() {
29956
- var _this3 = this;
29957
- this._behaviors.forEach(function (b) {
29958
- return _this3._resolveOutputForBehavior(b);
29959
- });
29960
- }
29961
-
29962
- /**
29963
- * Resolve a single behavior's output object and cache it by behavior id.
29964
- * Looks for an object in the scene whose userData.attachmentId matches
29965
- * behavior.output.attachment. If behavior.output.child is also set, it
29966
- * then searches within that object for a descendant whose name matches.
29967
- *
29968
- * @param {Object} behavior
29969
- */
29970
- }, {
29971
- key: "_resolveOutputForBehavior",
29972
- value: function _resolveOutputForBehavior(behavior) {
29973
- var _this$sceneViewer, _behavior$output, _behavior$output2, _behavior$output3;
29974
- var cacheKey = this._outputCacheKey(behavior);
29975
- if (this._resolvedOutputs.has(cacheKey)) return; // already resolved
29976
-
29977
- var scene = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.scene;
29978
- if (!scene) {
29979
- console.warn('⚠️ BehaviorManager: scene not available for output resolution');
29980
- return;
29981
- }
29982
- var attachmentId = (_behavior$output = behavior.output) === null || _behavior$output === void 0 ? void 0 : _behavior$output.attachment;
29983
- var childName = (_behavior$output2 = behavior.output) === null || _behavior$output2 === void 0 ? void 0 : _behavior$output2.child;
29984
- var componentUuid = (_behavior$output3 = behavior.output) === null || _behavior$output3 === void 0 ? void 0 : _behavior$output3.component;
29985
- if (!attachmentId) {
29986
- console.warn("\u26A0\uFE0F BehaviorManager: behavior \"".concat(behavior.id, "\" has no output.attachment"));
29987
- return;
29988
- }
29989
-
29990
- // Find the attachment root in the scene, scoped to the correct component
29991
- // instance when a componentUuid is specified (prevents cross-clone bleed).
29992
- var attachmentRoot = null;
29993
- scene.traverse(function (obj) {
29994
- var _obj$userData, _obj$parent;
29995
- if (attachmentRoot) return;
29996
- if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.attachmentId) !== attachmentId) return;
29997
- // If a component UUID is specified, the io-device's parent must match it
29998
- if (componentUuid && ((_obj$parent = obj.parent) === null || _obj$parent === void 0 ? void 0 : _obj$parent.uuid) !== componentUuid) return;
29999
- attachmentRoot = obj;
30000
- });
30001
- if (!attachmentRoot) {
30002
- console.warn("\u26A0\uFE0F BehaviorManager: could not find attachment \"".concat(attachmentId, "\" in scene"));
30003
- return;
30004
- }
30005
- var targetObject = attachmentRoot;
30006
-
30007
- // If a specific child mesh name is specified, drill into it
30008
- if (childName) {
30009
- var foundChild = null;
30010
- attachmentRoot.traverse(function (obj) {
30011
- if (!foundChild && obj.name === childName) {
30012
- foundChild = obj;
30013
- }
30014
- });
30015
- if (!foundChild) {
30016
- console.warn("\u26A0\uFE0F BehaviorManager: child \"".concat(childName, "\" not found under attachment \"").concat(attachmentId, "\""));
30017
- return;
30018
- }
30019
- targetObject = foundChild;
30020
- } else {
30021
- // No child specified: if the root has no material (e.g. it's a Group),
30022
- // fall through to the first Mesh descendant so that material property
30023
- // actions (e.g. material.emissiveIntensity) work without needing an
30024
- // explicit child selection.
30025
- if (!attachmentRoot.isMesh) {
30026
- var firstMesh = null;
30027
- attachmentRoot.traverse(function (obj) {
30028
- if (!firstMesh && obj.isMesh) firstMesh = obj;
30029
- });
30030
- if (firstMesh) {
30031
- targetObject = firstMesh;
30032
- console.log("\uD83D\uDD17 BehaviorManager: no child specified \u2014 auto-resolved to first mesh \"".concat(firstMesh.name || firstMesh.uuid, "\" under \"").concat(attachmentId, "\""));
30033
- }
30034
- }
30035
- }
30036
- this._resolvedOutputs.set(cacheKey, targetObject);
30037
- console.log("\uD83D\uDD17 BehaviorManager: resolved output for \"".concat(behavior.id, "\" \u2192 ").concat(targetObject.name || targetObject.uuid));
30038
- }
30039
-
30040
- /**
30041
- * Build a stable cache key for a behavior's output.
30042
- * @param {Object} behavior
30043
- * @returns {string}
30044
- */
30045
- }, {
30046
- key: "_outputCacheKey",
30047
- value: function _outputCacheKey(behavior) {
30048
- var _behavior$output4, _behavior$output5;
30049
- return "".concat(behavior.id, "::").concat((_behavior$output4 = behavior.output) === null || _behavior$output4 === void 0 ? void 0 : _behavior$output4.attachment, "::").concat(((_behavior$output5 = behavior.output) === null || _behavior$output5 === void 0 ? void 0 : _behavior$output5.child) || '');
30050
- }
30051
-
30052
- /**
30053
- * Evaluate a behavior's conditions against the current data-point value
30054
- * and apply matching actions.
30055
- *
30056
- * @param {Object} behavior
30057
- * @param {*} value - Current data-point value
30058
- */
30059
- }, {
30060
- key: "_evaluateBehavior",
30061
- value: function _evaluateBehavior(behavior, value) {
30062
- var _this4 = this;
30063
- if (!Array.isArray(behavior.conditions) || behavior.conditions.length === 0) return;
30064
- var cacheKey = this._outputCacheKey(behavior);
30065
- var targetObject = this._resolvedOutputs.get(cacheKey);
30066
- if (!targetObject) {
30067
- // Attempt late resolution (model may have loaded after behavior was added)
30068
- this._resolveOutputForBehavior(behavior);
30069
- var resolved = this._resolvedOutputs.get(cacheKey);
30070
- if (!resolved) {
30071
- console.warn("\u26A0\uFE0F BehaviorManager: no resolved output for behavior \"".concat(behavior.id, "\" \u2014 skipping"));
30072
- return;
30073
- }
30074
- }
30075
- var output = this._resolvedOutputs.get(cacheKey);
30076
- behavior.conditions.forEach(function (condition) {
30077
- if (_this4._testCondition(condition.when, value)) {
30078
- if (Array.isArray(condition.actions)) {
30079
- condition.actions.forEach(function (action) {
30080
- _this4._applyAction(output, action.set, action.value, action.relative === true);
30081
- });
30082
- }
30083
- }
30084
- });
30085
- }
30086
-
30087
- /**
30088
- * Safely test a condition expression against a value.
30089
- * Supports a fixed set of patterns — no arbitrary eval().
30090
- *
30091
- * Supported patterns:
30092
- * "state.value === true" → value === true
30093
- * "state.value === false" → value === false
30094
- * "state.value === <num>" → value === num
30095
- * "state.value > <num>" → value > num
30096
- * "state.value < <num>" → value < num
30097
- * "state.value >= <num>" → value >= num
30098
- * "state.value <= <num>" → value <= num
30099
- *
30100
- * @param {string} whenExpr
30101
- * @param {*} value
30102
- * @returns {boolean}
30103
- */
30104
- }, {
30105
- key: "_testCondition",
30106
- value: function _testCondition(whenExpr, value) {
30107
- if (typeof whenExpr !== 'string') return false;
30108
- var expr = whenExpr.trim();
30109
-
30110
- // Strip the "state.value " prefix if present (also accept legacy "dataPoint.value" prefix)
30111
- var stripped = expr.replace(/^(state|dataPoint)\.value\s*/, '');
30112
-
30113
- // Parse operator and RHS
30114
- var match = stripped.match(/^(===|!==|>=|<=|>|<)\s*(.+)$/);
30115
- if (!match) {
30116
- console.warn("\u26A0\uFE0F BehaviorManager: unrecognised condition expression: \"".concat(whenExpr, "\""));
30117
- return false;
30118
- }
30119
- var operator = match[1];
30120
- var rawRhs = match[2].trim();
30121
-
30122
- // Parse RHS to the appropriate JS type
30123
- var rhs;
30124
- if (rawRhs === 'true') rhs = true;else if (rawRhs === 'false') rhs = false;else if (rawRhs === 'null') rhs = null;else if (!isNaN(rawRhs)) rhs = parseFloat(rawRhs);else rhs = rawRhs; // treat as string
30125
-
30126
- switch (operator) {
30127
- case '===':
30128
- // Treat 0/1 as false/true when the RHS is a boolean (binary toggle)
30129
- if (typeof rhs === 'boolean') return Boolean(value) === rhs;
30130
- return value === rhs;
30131
- case '!==':
30132
- if (typeof rhs === 'boolean') return Boolean(value) !== rhs;
30133
- return value !== rhs;
30134
- case '>':
30135
- return value > rhs;
30136
- case '<':
30137
- return value < rhs;
30138
- case '>=':
30139
- return value >= rhs;
30140
- case '<=':
30141
- return value <= rhs;
30142
- default:
30143
- return false;
30144
- }
30145
- }
30146
-
30147
- /**
30148
- * Apply a single action to a Three.js object by navigating a dot-notation path.
30149
- * Example: set = "material.emissiveIntensity", value = 1.0
30150
- *
30151
- * @param {THREE.Object3D} object
30152
- * @param {string} propertyPath - Dot-notation path relative to the object
30153
- * @param {*} value
30154
- */
30155
- }, {
30156
- key: "_applyAction",
30157
- value: function _applyAction(object, propertyPath, value) {
30158
- var relative = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
30159
- if (!object || typeof propertyPath !== 'string') return;
30160
- var parts = propertyPath.split('.');
30161
-
30162
- // Clone shared material onto this mesh before mutating any material property.
30163
- // Three.js reuses the same Material instance across all meshes loaded from the
30164
- // same GLB, so without cloning a color/emissive change would bleed to every
30165
- // other component instance that shares the model.
30166
- if (parts[0] === 'material' && object.isMesh && object.material && !object.userData._behaviorMaterialCloned) {
30167
- object.material = object.material.clone();
30168
- object.userData._behaviorMaterialCloned = true;
30169
- }
30170
- var target = object;
30171
- for (var i = 0; i < parts.length - 1; i++) {
30172
- if (target == null) {
30173
- console.warn("\u26A0\uFE0F BehaviorManager._applyAction(): path \"".concat(propertyPath, "\" broken at \"").concat(parts[i], "\""));
30174
- return;
30175
- }
30176
- target = target[parts[i]];
30177
- }
30178
- var lastKey = parts[parts.length - 1];
30179
- if (target == null || !(lastKey in target)) {
30180
- console.warn("\u26A0\uFE0F BehaviorManager._applyAction(): property \"".concat(propertyPath, "\" not found on"), object);
30181
- return;
30182
- }
30183
-
30184
- // If relative, capture the mesh's original value on first execution and offset from it.
30185
- if (relative) {
30186
- var baselineKey = "_baseline_".concat(propertyPath.replace(/\./g, '_'));
30187
- if (!(baselineKey in object.userData)) {
30188
- object.userData[baselineKey] = target[lastKey];
30189
- }
30190
- value = object.userData[baselineKey] + parseFloat(value);
30191
- }
30192
-
30193
- // THREE.Color objects must be mutated via .set() rather than replaced
30194
- var existing = target[lastKey];
30195
- if (existing && existing.isColor && typeof value === 'string') {
30196
- existing.set(value);
30197
- } else {
30198
- // Coerce numeric strings to numbers for non-color properties
30199
- target[lastKey] = typeof value === 'string' && value !== '' && !isNaN(value) ? parseFloat(value) : value;
30200
- }
30201
- console.log("\u2705 BehaviorManager: applied \"".concat(propertyPath, "\" = ").concat(value, " on \"").concat(object.name || object.uuid, "\""));
30202
-
30203
- // If we touched a material property, mark it needs update
30204
- if (parts[0] === 'material' && target.needsUpdate !== undefined) {
30205
- target.needsUpdate = true;
30206
- }
30207
- }
30208
-
30209
- // ─────────────────────────────────────────────────────────────────────────
30210
- // DISPOSAL
30211
- // ─────────────────────────────────────────────────────────────────────────
30212
- }, {
30213
- key: "dispose",
30214
- value: function dispose() {
30215
- this._behaviors = [];
30216
- this._resolvedOutputs.clear();
30217
- _superPropGet(BehaviorManager, "dispose", this, 3)([]);
30218
- }
30219
- }]);
30220
- }(BaseDisposable);
30221
-
30222
29795
  /**
30223
29796
  * IO Device Utilities
30224
29797
  * Shared utility functions for attaching IO devices to smart components.
@@ -31736,7 +31309,7 @@ var ModelManager = /*#__PURE__*/function () {
31736
31309
  key: "loadLibraryModel",
31737
31310
  value: function () {
31738
31311
  var _loadLibraryModel = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(targetMesh, jsonEntry, componentData) {
31739
- var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioAnimMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
31312
+ var component, _jsonEntry$userData, _jsonEntry$userData2, _jsonEntry$userData3, originalProps, connectorChildren, gltfScene, libraryModel, _this$sceneViewer, ioBehavMgr, _loop, _i, _Object$entries, warmFn, _jsonEntry$userData4, _t;
31740
31313
  return _regenerator().w(function (_context2) {
31741
31314
  while (1) switch (_context2.n) {
31742
31315
  case 0:
@@ -31779,22 +31352,22 @@ var ModelManager = /*#__PURE__*/function () {
31779
31352
  _context2.n = 4;
31780
31353
  return attachIODevicesToComponent(libraryModel, componentData, modelPreloader, originalProps.uuid);
31781
31354
  case 4:
31782
- // Register animation configs for each attached device
31783
- ioAnimMgr = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.ioAnimationManager;
31784
- if (!ioAnimMgr) {
31355
+ // Register behavior configs for each attached device
31356
+ ioBehavMgr = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.ioBehaviorManager;
31357
+ if (!ioBehavMgr) {
31785
31358
  _context2.n = 8;
31786
31359
  break;
31787
31360
  }
31788
31361
  _loop = /*#__PURE__*/_regenerator().m(function _loop() {
31789
- var _modelPreloader$compo, _deviceData$animation;
31362
+ var _modelPreloader$compo, _deviceData$behaviorC;
31790
31363
  var _Object$entries$_i, attachmentId, attachment, deviceData, deviceRoot;
31791
31364
  return _regenerator().w(function (_context) {
31792
31365
  while (1) switch (_context.n) {
31793
31366
  case 0:
31794
31367
  _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), attachmentId = _Object$entries$_i[0], attachment = _Object$entries$_i[1];
31795
31368
  deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
31796
- console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 animationConfig:"), (_deviceData$animation = deviceData === null || deviceData === void 0 ? void 0 : deviceData.animationConfig) !== null && _deviceData$animation !== void 0 ? _deviceData$animation : 'NONE');
31797
- if (deviceData !== null && deviceData !== void 0 && deviceData.animationConfig) {
31369
+ console.log("[ModelManager] attachment \"".concat(attachmentId, "\" \u2192 deviceId \"").concat(attachment.deviceId, "\" \u2192 behaviorConfig:"), (_deviceData$behaviorC = deviceData === null || deviceData === void 0 ? void 0 : deviceData.behaviorConfig) !== null && _deviceData$behaviorC !== void 0 ? _deviceData$behaviorC : 'NONE');
31370
+ if (deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig) {
31798
31371
  _context.n = 1;
31799
31372
  break;
31800
31373
  }
@@ -31806,7 +31379,7 @@ var ModelManager = /*#__PURE__*/function () {
31806
31379
  if (!deviceRoot && ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.attachmentId) === attachmentId) deviceRoot = obj;
31807
31380
  });
31808
31381
  if (deviceRoot) {
31809
- ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, originalProps.uuid);
31382
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, originalProps.uuid);
31810
31383
  }
31811
31384
  case 2:
31812
31385
  return _context.a(2);
@@ -33765,13 +33338,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
33765
33338
  phaseStart = performance.now();
33766
33339
  this._finalizeScene(data, crosscubeTextureSet, isImported);
33767
33340
  timers.phase5_finalize = performance.now() - phaseStart;
33768
-
33769
- // Phase 6: Load behaviors (after GLB models are present so output objects can be resolved)
33770
- phaseStart = performance.now();
33771
- this._processBehaviors(data);
33772
- timers.phase6_behaviors = performance.now() - phaseStart;
33773
33341
  totalTime = performance.now() - totalStart;
33774
- console.log("\u23F1\uFE0F Scene Loading Performance:\n Phase 1 (Prepare) : ".concat(timers.phase1_prepare.toFixed(0), "ms\n Phase 2 (Create Objects): ").concat(timers.phase2_createObjects.toFixed(0), "ms\n Phase 3 (GLB Models) : ").concat(timers.phase3_glbModels.toFixed(0), "ms\n Phase 4 (Pathfinding) : ").concat(timers.phase4_pathfinding.toFixed(0), "ms\n Phase 5 (Finalize) : ").concat(timers.phase5_finalize.toFixed(0), "ms\n Phase 6 (Behaviors) : ").concat(timers.phase6_behaviors.toFixed(0), "ms\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Total : ").concat(totalTime.toFixed(0), "ms"));
33342
+ console.log("\u23F1\uFE0F Scene Loading Performance:\n Phase 1 (Prepare) : ".concat(timers.phase1_prepare.toFixed(0), "ms\n Phase 2 (Create Objects): ").concat(timers.phase2_createObjects.toFixed(0), "ms\n Phase 3 (GLB Models) : ").concat(timers.phase3_glbModels.toFixed(0), "ms\n Phase 4 (Pathfinding) : ").concat(timers.phase4_pathfinding.toFixed(0), "ms\n Phase 5 (Finalize) : ").concat(timers.phase5_finalize.toFixed(0), "ms\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Total : ").concat(totalTime.toFixed(0), "ms"));
33775
33343
  console.log('✅ Scene loaded successfully');
33776
33344
 
33777
33345
  // Notify UI components (e.g. SceneHierarchy) that the scene is fully loaded
@@ -34129,278 +33697,6 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34129
33697
  }
34130
33698
  }
34131
33699
 
34132
- /**
34133
- * Process behaviors from the scene data, expand any defaultBehaviors defined
34134
- * on component dictionary entries, resolve behaviorRef entries against device
34135
- * assets, inject per-instance component UUIDs, and hand the flat resolved
34136
- * array to BehaviorManager.
34137
- *
34138
- * Resolution cascade:
34139
- * 1. Explicit behaviors in data.behaviors — passed through as-is (behaviorRef
34140
- * entries are resolved against the component/device dictionaries).
34141
- * 2. defaultBehaviors[] on each placed smart component's dictionary entry —
34142
- * expanded per instance with the instance UUID injected. Compact
34143
- * behaviorRef-derived entries are tagged _isDefaultBehavior:true (the
34144
- * exporter can re-derive them); full L2 behavior objects are tagged false
34145
- * so they are exported verbatim and survive round-trips.
34146
- *
34147
- * @param {Object} data - Scene JSON data
34148
- */
34149
- }, {
34150
- key: "_processBehaviors",
34151
- value: function _processBehaviors(data) {
34152
- var _this$sceneViewer2,
34153
- _this$sceneViewer$cen2,
34154
- _this5 = this,
34155
- _data$scene3;
34156
- var behaviorManager = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.behaviorManager;
34157
- if (!behaviorManager) {
34158
- console.warn('⚠️ _processBehaviors: BehaviorManager not available');
34159
- return;
34160
- }
34161
-
34162
- // Obtain the component dictionary (extended = includes S3/smart components)
34163
- var componentDictionary = ((_this$sceneViewer$cen2 = this.sceneViewer.centralPlant) === null || _this$sceneViewer$cen2 === void 0 || (_this$sceneViewer$cen2 = _this$sceneViewer$cen2.managers) === null || _this$sceneViewer$cen2 === void 0 || (_this$sceneViewer$cen2 = _this$sceneViewer$cen2.componentDataManager) === null || _this$sceneViewer$cen2 === void 0 ? void 0 : _this$sceneViewer$cen2.componentDictionary) || {};
34164
-
34165
- // ── Step A: Resolve any behaviorRef entries from data.behaviors ─────────
34166
- var explicitBehaviors = [];
34167
- if (Array.isArray(data === null || data === void 0 ? void 0 : data.behaviors)) {
34168
- data.behaviors.forEach(function (entry) {
34169
- if (entry.behaviorRef) {
34170
- var resolved = _this5._resolveBehaviorRef(entry, componentDictionary);
34171
- if (resolved) explicitBehaviors.push(resolved);
34172
- } else {
34173
- explicitBehaviors.push(entry);
34174
- }
34175
- });
34176
- }
34177
-
34178
- // Build a set of explicit behavior ids for deduplication
34179
- var explicitIds = new Set(explicitBehaviors.map(function (b) {
34180
- return b.id;
34181
- }));
34182
-
34183
- // Build a set of (component::attachment::state) tuples covered by explicit
34184
- // behaviors. If an expanded default behavior would produce the SAME input
34185
- // tuple, the explicit behavior is treated as the intentional override and
34186
- // the default expansion is skipped. This prevents a component's built-in
34187
- // switch→LED wiring from doubling-up when the user has deliberately authored
34188
- // cross-component behaviors that re-wire the same switch.
34189
- var explicitInputTuples = new Set(explicitBehaviors.filter(function (b) {
34190
- var _b$input, _b$input2, _b$input3;
34191
- return ((_b$input = b.input) === null || _b$input === void 0 ? void 0 : _b$input.component) && ((_b$input2 = b.input) === null || _b$input2 === void 0 ? void 0 : _b$input2.attachment) && ((_b$input3 = b.input) === null || _b$input3 === void 0 ? void 0 : _b$input3.state);
34192
- }).map(function (b) {
34193
- return "".concat(b.input.component, "::").concat(b.input.attachment, "::").concat(b.input.state);
34194
- }));
34195
-
34196
- // Build a set of instanceUuids already covered by behaviorRef entries in
34197
- // data.behaviors (written by the exporter for smart component defaults).
34198
- // Step B skips these instances to avoid loading the same behaviors twice —
34199
- // once via Step A (resolved from data.behaviors refs) and once via Step B
34200
- // (auto-expanded from the component dictionary).
34201
- var coveredInstances = new Set();
34202
- if (Array.isArray(data === null || data === void 0 ? void 0 : data.behaviors)) {
34203
- data.behaviors.forEach(function (entry) {
34204
- if (entry.behaviorRef && entry.component) {
34205
- coveredInstances.add(entry.component);
34206
- }
34207
- });
34208
- }
34209
-
34210
- // ── Step B: Walk placed components and expand their defaultBehaviors ─────
34211
- var instanceBehaviors = [];
34212
- if (Array.isArray(data === null || data === void 0 || (_data$scene3 = data.scene) === null || _data$scene3 === void 0 ? void 0 : _data$scene3.children)) {
34213
- data.scene.children.forEach(function (child) {
34214
- var _child$userData0, _compData$defaultBeha;
34215
- var libraryId = (_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.libraryId;
34216
- if (!libraryId) return;
34217
- var instanceUuid = child.uuid;
34218
- // Skip instances whose defaults were already resolved by Step A
34219
- // (they have explicit behaviorRef entries in data.behaviors).
34220
- if (coveredInstances.has(instanceUuid)) return;
34221
- var compData = componentDictionary[libraryId];
34222
- if (!(compData !== null && compData !== void 0 && (_compData$defaultBeha = compData.defaultBehaviors) !== null && _compData$defaultBeha !== void 0 && _compData$defaultBeha.length)) return;
34223
- compData.defaultBehaviors.forEach(function (template) {
34224
- var _expanded$input, _expanded$input2, _expanded$input3;
34225
- var expanded = _this5._expandDefaultBehavior(template, instanceUuid, componentDictionary);
34226
- if (!expanded) return;
34227
- // Skip if an explicit scene behavior already covers this id
34228
- if (explicitIds.has(expanded.id)) return;
34229
- // Skip if an explicit scene behavior already covers the same
34230
- // (component, attachment, state) input tuple. This prevents a
34231
- // component's built-in default wiring (e.g. switch → own LED) from
34232
- // double-firing when the user has authored a cross-component override
34233
- // that rewires the same switch to a different target.
34234
- if ((_expanded$input = expanded.input) !== null && _expanded$input !== void 0 && _expanded$input.component && (_expanded$input2 = expanded.input) !== null && _expanded$input2 !== void 0 && _expanded$input2.attachment && (_expanded$input3 = expanded.input) !== null && _expanded$input3 !== void 0 && _expanded$input3.state) {
34235
- var tuple = "".concat(expanded.input.component, "::").concat(expanded.input.attachment, "::").concat(expanded.input.state);
34236
- if (explicitInputTuples.has(tuple)) return;
34237
- }
34238
- // All component defaultBehaviors are re-derivable from the component
34239
- // asset at export time (via compact behaviorRef), so mark them all.
34240
- expanded._isDefaultBehavior = true;
34241
- instanceBehaviors.push(expanded);
34242
- });
34243
- });
34244
- }
34245
- var allBehaviors = [].concat(explicitBehaviors, instanceBehaviors);
34246
- if (allBehaviors.length === 0) return;
34247
- behaviorManager.loadBehaviors(allBehaviors);
34248
- console.log("\u2705 _processBehaviors: loaded ".concat(explicitBehaviors.length, " explicit + ").concat(instanceBehaviors.length, " default-expanded behavior(s)"));
34249
- }
34250
-
34251
- /**
34252
- * Register the defaultBehaviors of a single newly placed component instance
34253
- * into the BehaviorManager. Called from addComponent() (drag-drop), where
34254
- * _processBehaviors() (scene-load path) is not invoked.
34255
- *
34256
- * @param {Object} componentData - Entry from the component dictionary
34257
- * @param {string} instanceUuid - UUID of the placed component (componentModel.uuid)
34258
- */
34259
- }, {
34260
- key: "registerBehaviorsForComponentInstance",
34261
- value: function registerBehaviorsForComponentInstance(componentData, instanceUuid) {
34262
- var _this$sceneViewer3,
34263
- _componentData$defaul,
34264
- _this$sceneViewer$cen3,
34265
- _this6 = this;
34266
- var behaviorManager = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.behaviorManager;
34267
- if (!behaviorManager) return;
34268
- if (!(componentData !== null && componentData !== void 0 && (_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length)) return;
34269
- var componentDictionary = ((_this$sceneViewer$cen3 = this.sceneViewer.centralPlant) === null || _this$sceneViewer$cen3 === void 0 || (_this$sceneViewer$cen3 = _this$sceneViewer$cen3.managers) === null || _this$sceneViewer$cen3 === void 0 || (_this$sceneViewer$cen3 = _this$sceneViewer$cen3.componentDataManager) === null || _this$sceneViewer$cen3 === void 0 ? void 0 : _this$sceneViewer$cen3.componentDictionary) || {};
34270
- var registered = 0;
34271
- componentData.defaultBehaviors.forEach(function (template) {
34272
- var expanded = _this6._expandDefaultBehavior(template, instanceUuid, componentDictionary);
34273
- if (!expanded) return;
34274
- expanded._isDefaultBehavior = true;
34275
- behaviorManager.addBehavior(expanded);
34276
- registered++;
34277
- });
34278
- if (registered > 0) {
34279
- console.log("\u2705 registerBehaviorsForComponentInstance: registered ".concat(registered, " behavior(s) for instance ").concat(instanceUuid));
34280
- }
34281
- }
34282
-
34283
- /**
34284
- * Resolve a scene-level or component-level behaviorRef entry into a full
34285
- * behavior definition by looking up the referenced template in the device
34286
- * asset's defaultBehaviors[] and substituting 'self' with the real
34287
- * attachmentId.
34288
- *
34289
- * Ref shape: { behaviorRef, deviceId, attachment, component? }
34290
- *
34291
- * @param {Object} ref
34292
- * @param {Object} componentDictionary
34293
- * @returns {Object|null} Resolved behavior or null if not found
34294
- */
34295
- }, {
34296
- key: "_resolveBehaviorRef",
34297
- value: function _resolveBehaviorRef(ref, componentDictionary) {
34298
- var _resolved$input, _resolved$output;
34299
- if (!ref.behaviorRef) {
34300
- console.warn('⚠️ _resolveBehaviorRef: missing behaviorRef', ref);
34301
- return null;
34302
- }
34303
-
34304
- // ── Component-level L2 ref: { behaviorRef, libraryId, component } ──────
34305
- // The full behavior template lives on the smart component's defaultBehaviors[].
34306
- // No 'self' substitution needed — attachment IDs are already real.
34307
- if (ref.libraryId) {
34308
- var compData = componentDictionary[ref.libraryId];
34309
- if (!compData) {
34310
- console.warn("\u26A0\uFE0F _resolveBehaviorRef: component \"".concat(ref.libraryId, "\" not in dictionary"));
34311
- return null;
34312
- }
34313
- var _template = (compData.defaultBehaviors || []).find(function (b) {
34314
- return b.id === ref.behaviorRef;
34315
- });
34316
- if (!_template) {
34317
- console.warn("\u26A0\uFE0F _resolveBehaviorRef: behavior \"".concat(ref.behaviorRef, "\" not found on component \"").concat(ref.libraryId, "\""));
34318
- return null;
34319
- }
34320
- var _resolved = JSON.parse(JSON.stringify(_template));
34321
- _resolved.id = "".concat(_resolved.id, "::").concat(ref.component);
34322
- if (ref.component) {
34323
- _resolved.input = _objectSpread2(_objectSpread2({}, _resolved.input), {}, {
34324
- component: ref.component
34325
- });
34326
- _resolved.output = _objectSpread2(_objectSpread2({}, _resolved.output), {}, {
34327
- component: ref.component
34328
- });
34329
- }
34330
- return _resolved;
34331
- }
34332
-
34333
- // ── Device-level L1 ref: { behaviorRef, deviceId, attachment, component } ─
34334
- if (!ref.deviceId || !ref.attachment) {
34335
- console.warn('⚠️ _resolveBehaviorRef: incomplete ref', ref);
34336
- return null;
34337
- }
34338
- var deviceData = componentDictionary[ref.deviceId];
34339
- if (!deviceData) {
34340
- console.warn("\u26A0\uFE0F _resolveBehaviorRef: device \"".concat(ref.deviceId, "\" not in dictionary"));
34341
- return null;
34342
- }
34343
- var template = (deviceData.defaultBehaviors || []).find(function (b) {
34344
- return b.id === ref.behaviorRef;
34345
- });
34346
- if (!template) {
34347
- console.warn("\u26A0\uFE0F _resolveBehaviorRef: behavior \"".concat(ref.behaviorRef, "\" not found on device \"").concat(ref.deviceId, "\""));
34348
- return null;
34349
- }
34350
- // Deep clone and substitute 'self' -> real attachmentId
34351
- var resolved = JSON.parse(JSON.stringify(template));
34352
- resolved.id = "".concat(resolved.id, "::").concat(ref.attachment);
34353
- if (((_resolved$input = resolved.input) === null || _resolved$input === void 0 ? void 0 : _resolved$input.attachment) === 'self') resolved.input.attachment = ref.attachment;
34354
- if (((_resolved$output = resolved.output) === null || _resolved$output === void 0 ? void 0 : _resolved$output.attachment) === 'self') resolved.output.attachment = ref.attachment;
34355
- // Inject component UUID guard if the ref carries one
34356
- if (ref.component) {
34357
- resolved.input = _objectSpread2(_objectSpread2({}, resolved.input), {}, {
34358
- component: ref.component
34359
- });
34360
- resolved.output = _objectSpread2(_objectSpread2({}, resolved.output), {}, {
34361
- component: ref.component
34362
- });
34363
- }
34364
- return resolved;
34365
- }
34366
-
34367
- /**
34368
- * Expand a single defaultBehaviors[] template entry for a specific component
34369
- * instance placed in the scene.
34370
- *
34371
- * If the entry is a behaviorRef, it is first resolved against the device
34372
- * library; otherwise it is treated as a full behavior definition.
34373
- * In both cases the instanceUuid is injected as input.component and
34374
- * output.component, and the behavior id is suffixed with ::instanceUuid.
34375
- *
34376
- * @param {Object} template - Entry from compData.defaultBehaviors[]
34377
- * @param {string} instanceUuid - UUID of the placed component instance
34378
- * @param {Object} componentDictionary
34379
- * @returns {Object|null}
34380
- */
34381
- }, {
34382
- key: "_expandDefaultBehavior",
34383
- value: function _expandDefaultBehavior(template, instanceUuid, componentDictionary) {
34384
- var base;
34385
- if (template.behaviorRef) {
34386
- // Resolve the device ref first (substitutes 'self' -> attachment)
34387
- base = this._resolveBehaviorRef(template, componentDictionary);
34388
- if (!base) return null;
34389
- } else {
34390
- base = JSON.parse(JSON.stringify(template));
34391
- }
34392
- // Suffix the id so each instance gets a unique behavior id
34393
- base.id = "".concat(base.id, "::").concat(instanceUuid);
34394
- // Inject the instance UUID as the component scope guard
34395
- base.input = _objectSpread2(_objectSpread2({}, base.input), {}, {
34396
- component: instanceUuid
34397
- });
34398
- base.output = _objectSpread2(_objectSpread2({}, base.output), {}, {
34399
- component: instanceUuid
34400
- });
34401
- return base;
34402
- }
34403
-
34404
33700
  /**
34405
33701
  * Save original world matrices for direction calculations
34406
33702
  */
@@ -34469,7 +33765,7 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34469
33765
  key: "loadSceneFromData",
34470
33766
  value: (function () {
34471
33767
  var _loadSceneFromData = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9(data) {
34472
- var _data$scene4, _data$scene5, _data$scene6, _data$scene7;
33768
+ var _data$scene3, _data$scene4, _data$scene5, _data$scene6;
34473
33769
  return _regenerator().w(function (_context9) {
34474
33770
  while (1) switch (_context9.n) {
34475
33771
  case 0:
@@ -34479,10 +33775,10 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34479
33775
  dataType: _typeof(data),
34480
33776
  hasScene: !!(data !== null && data !== void 0 && data.scene),
34481
33777
  sceneType: _typeof(data === null || data === void 0 ? void 0 : data.scene),
34482
- hasChildren: !!(data !== null && data !== void 0 && (_data$scene4 = data.scene) !== null && _data$scene4 !== void 0 && _data$scene4.children),
34483
- childrenType: data !== null && data !== void 0 && (_data$scene5 = data.scene) !== null && _data$scene5 !== void 0 && _data$scene5.children ? _typeof(data.scene.children) : 'undefined',
34484
- isArray: Array.isArray(data === null || data === void 0 || (_data$scene6 = data.scene) === null || _data$scene6 === void 0 ? void 0 : _data$scene6.children),
34485
- childrenLength: data === null || data === void 0 || (_data$scene7 = data.scene) === null || _data$scene7 === void 0 || (_data$scene7 = _data$scene7.children) === null || _data$scene7 === void 0 ? void 0 : _data$scene7.length,
33778
+ hasChildren: !!(data !== null && data !== void 0 && (_data$scene3 = data.scene) !== null && _data$scene3 !== void 0 && _data$scene3.children),
33779
+ childrenType: data !== null && data !== void 0 && (_data$scene4 = data.scene) !== null && _data$scene4 !== void 0 && _data$scene4.children ? _typeof(data.scene.children) : 'undefined',
33780
+ isArray: Array.isArray(data === null || data === void 0 || (_data$scene5 = data.scene) === null || _data$scene5 === void 0 ? void 0 : _data$scene5.children),
33781
+ childrenLength: data === null || data === void 0 || (_data$scene6 = data.scene) === null || _data$scene6 === void 0 || (_data$scene6 = _data$scene6.children) === null || _data$scene6 === void 0 ? void 0 : _data$scene6.length,
34486
33782
  dataKeys: data ? Object.keys(data) : [],
34487
33783
  sceneKeys: data !== null && data !== void 0 && data.scene ? Object.keys(data.scene) : []
34488
33784
  });
@@ -34613,8 +33909,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34613
33909
  // Process children (connectors, etc.) if they exist
34614
33910
  if (componentModel.children && componentModel.children.length > 0) {
34615
33911
  componentModel.children.forEach(function (child) {
34616
- var _child$userData1, _child$userData10;
34617
- var childType = ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType) || ((_child$userData10 = child.userData) === null || _child$userData10 === void 0 ? void 0 : _child$userData10.objectType);
33912
+ var _child$userData0, _child$userData1;
33913
+ var childType = ((_child$userData0 = child.userData) === null || _child$userData0 === void 0 ? void 0 : _child$userData0.objectType) || ((_child$userData1 = child.userData) === null || _child$userData1 === void 0 ? void 0 : _child$userData1.objectType);
34618
33914
  if (childType === 'connector') {
34619
33915
  var _child$geometry;
34620
33916
  var childBoundingBox = new THREE__namespace.Box3().setFromObject(child);
@@ -34699,8 +33995,8 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34699
33995
  if (segment.children && segment.children.length > 0) {
34700
33996
  var childrenToRemove = _toConsumableArray(segment.children);
34701
33997
  childrenToRemove.forEach(function (child) {
34702
- var _child$userData11;
34703
- if ((_child$userData11 = child.userData) !== null && _child$userData11 !== void 0 && _child$userData11.isPipeElbow) {
33998
+ var _child$userData10;
33999
+ if ((_child$userData10 = child.userData) !== null && _child$userData10 !== void 0 && _child$userData10.isPipeElbow) {
34704
34000
  console.log("\uD83D\uDDD1\uFE0F Removing elbow child from segment before manualization: ".concat(child.uuid));
34705
34001
  segment.remove(child);
34706
34002
  if (child.geometry) child.geometry.dispose();
@@ -34851,7 +34147,7 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34851
34147
  value: function _convertConnectedGatewaysToManual(connectors, currentSceneData) {
34852
34148
  var _connectors$,
34853
34149
  _segment$userData2,
34854
- _this7 = this;
34150
+ _this5 = this;
34855
34151
  console.log('🔍 Checking for connected gateways to convert to manual...');
34856
34152
  var sceneViewer = this.sceneViewer;
34857
34153
  var convertedGateways = [];
@@ -34888,7 +34184,7 @@ var SceneOperationsManager = /*#__PURE__*/function () {
34888
34184
  console.log("\uD83D\uDD27 Found computed gateway at endpoint: ".concat(endpointObject.uuid, " - converting to manual"));
34889
34185
 
34890
34186
  // Convert gateway to manual (declared) using manualizeGateway for consistency
34891
- _this7.manualizeGateway(endpointObject, currentSceneData);
34187
+ _this5.manualizeGateway(endpointObject, currentSceneData);
34892
34188
  convertedGateways.push(endpointObject);
34893
34189
  } else if (((_endpointObject$userD5 = endpointObject.userData) === null || _endpointObject$userD5 === void 0 ? void 0 : _endpointObject$userD5.objectType) === 'gateway' && ((_endpointObject$userD6 = endpointObject.userData) === null || _endpointObject$userD6 === void 0 ? void 0 : _endpointObject$userD6.isDeclared) === true) {
34894
34190
  console.log("\u2139\uFE0F Gateway ".concat(endpointObject.uuid, " is already declared (manual), skipping conversion"));
@@ -37324,7 +36620,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37324
36620
  }, {
37325
36621
  key: "toggleIODeviceBinaryState",
37326
36622
  value: function toggleIODeviceBinaryState(ioDeviceObject) {
37327
- var _ref, _this$sceneViewer, _this$sceneViewer2;
36623
+ var _ref, _this$sceneViewer;
37328
36624
  if (!ioDeviceObject || !this._stateAdapter) return;
37329
36625
  var ud = ioDeviceObject.userData;
37330
36626
  var attachmentId = ud === null || ud === void 0 ? void 0 : ud.attachmentId;
@@ -37358,8 +36654,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37358
36654
  var currentVal = (_ref = storedVal !== null && storedVal !== void 0 ? storedVal : binaryState.defaultValue) !== null && _ref !== void 0 ? _ref : false;
37359
36655
  var newVal = !Boolean(currentVal);
37360
36656
  this._stateAdapter.setState(scopedAttachmentId, dpId, newVal);
37361
- (_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);
37362
- (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.ioAnimationManager) === null || _this$sceneViewer2 === void 0 || _this$sceneViewer2.triggerState(attachmentId, dpId, newVal, parentUuid);
36657
+ (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.managers) === null || _this$sceneViewer === void 0 || (_this$sceneViewer = _this$sceneViewer.ioBehaviorManager) === null || _this$sceneViewer === void 0 || _this$sceneViewer.triggerState(attachmentId, dpId, newVal, parentUuid);
37363
36658
  console.log("\uD83D\uDD04 [IODevice] Toggled ".concat(scopedAttachmentId, ".").concat(dpId, ": ").concat(currentVal, " \u2192 ").concat(newVal));
37364
36659
  }
37365
36660
 
@@ -37375,7 +36670,7 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37375
36670
  }, {
37376
36671
  key: "startIODeviceDrag",
37377
36672
  value: function startIODeviceDrag(ioDeviceObject) {
37378
- var _this$sceneViewer3,
36673
+ var _this$sceneViewer2,
37379
36674
  _this2 = this;
37380
36675
  if (!ioDeviceObject || !this._stateAdapter) return;
37381
36676
  var ud = ioDeviceObject.userData;
@@ -37392,8 +36687,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37392
36687
  obj = obj.parent;
37393
36688
  }
37394
36689
  var scopedAttachmentId = this._getScopedAttachmentKey(attachmentId, parentUuid);
37395
- var ioAnimMgr = (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.ioAnimationManager;
37396
- var dataPoints = ((ioAnimMgr === null || ioAnimMgr === void 0 ? void 0 : ioAnimMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
36690
+ var ioBehavMgr = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 || (_this$sceneViewer2 = _this$sceneViewer2.managers) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.ioBehaviorManager;
36691
+ var dataPoints = ((ioBehavMgr === null || ioBehavMgr === void 0 ? void 0 : ioBehavMgr.getAnimationDataPoints(parentUuid, attachmentId)) || []).concat((ud === null || ud === void 0 ? void 0 : ud.dataPoints) || [])
37397
36692
  // deduplicate by id
37398
36693
  .filter(function (dp, i, arr) {
37399
36694
  return arr.findIndex(function (d) {
@@ -37515,15 +36810,14 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37515
36810
  }, {
37516
36811
  key: "_applyDpState",
37517
36812
  value: function _applyDpState(_ref2, newVal) {
37518
- var _this$_stateAdapter, _this$sceneViewer4, _this$sceneViewer5;
36813
+ var _this$_stateAdapter, _this$sceneViewer3;
37519
36814
  var scopedAttachmentId = _ref2.scopedAttachmentId,
37520
36815
  attachmentId = _ref2.attachmentId,
37521
36816
  parentUuid = _ref2.parentUuid,
37522
36817
  dp = _ref2.dp;
37523
36818
  var dpId = dp.id;
37524
36819
  (_this$_stateAdapter = this._stateAdapter) === null || _this$_stateAdapter === void 0 || _this$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
37525
- (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.behaviorManager) === null || _this$sceneViewer4 === void 0 || _this$sceneViewer4.triggerState(attachmentId, dpId, newVal, parentUuid);
37526
- (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioAnimationManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, dpId, newVal, parentUuid);
36820
+ (_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.managers) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.ioBehaviorManager) === null || _this$sceneViewer3 === void 0 || _this$sceneViewer3.triggerState(attachmentId, dpId, newVal, parentUuid);
37527
36821
  }
37528
36822
 
37529
36823
  /**
@@ -37648,11 +36942,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37648
36942
  var _this3$sceneViewer$ma, _this3$sceneViewer;
37649
36943
  var attachmentId = child.userData.attachmentId || '';
37650
36944
 
37651
- // Use only data points from the animate window (animationConfig).
36945
+ // Use only data points from the animate window (behaviorConfig).
37652
36946
  // The static ioConfig.states[] snapshot on userData is intentionally ignored.
37653
- var dataPoints = (_this3$sceneViewer$ma = (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.ioAnimationManager) === null || _this3$sceneViewer === void 0 ? void 0 : _this3$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this3$sceneViewer$ma !== void 0 ? _this3$sceneViewer$ma : [];
36947
+ var dataPoints = (_this3$sceneViewer$ma = (_this3$sceneViewer = _this3.sceneViewer) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.managers) === null || _this3$sceneViewer === void 0 || (_this3$sceneViewer = _this3$sceneViewer.ioBehaviorManager) === null || _this3$sceneViewer === void 0 ? void 0 : _this3$sceneViewer.getAnimationDataPoints(parentUuid, attachmentId)) !== null && _this3$sceneViewer$ma !== void 0 ? _this3$sceneViewer$ma : [];
37654
36948
 
37655
- // When data points come from animationConfig they already carry direction:'input'.
36949
+ // When data points come from behaviorConfig they already carry direction:'input'.
37656
36950
  // Pass null so _buildDataPointRow uses the per-dp direction instead of the
37657
36951
  // device-level ioDirection (which may be 'output' and would hide controls).
37658
36952
  var deviceDirection = dataPoints.length > 0 ? null : child.userData.ioDirection || 'output';
@@ -37806,11 +37100,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37806
37100
  }, {
37807
37101
  key: "_positionTooltip",
37808
37102
  value: function _positionTooltip() {
37809
- var _this$sceneViewer6, _this$sceneViewer7;
37103
+ var _this$sceneViewer4, _this$sceneViewer5;
37810
37104
  if (!this.tooltipEl || !this.selectedObject) return;
37811
37105
  var container = this._getContainer();
37812
- var camera = (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.camera;
37813
- var renderer = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.renderer;
37106
+ var camera = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.camera;
37107
+ var renderer = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.renderer;
37814
37108
  if (!container || !camera || !renderer) return;
37815
37109
 
37816
37110
  // Compute bounding box to position above the component
@@ -37847,8 +37141,8 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37847
37141
  }, {
37848
37142
  key: "_getContainer",
37849
37143
  value: function _getContainer() {
37850
- var _this$sceneViewer8;
37851
- return ((_this$sceneViewer8 = this.sceneViewer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.renderer) === null || _this$sceneViewer8 === void 0 || (_this$sceneViewer8 = _this$sceneViewer8.domElement) === null || _this$sceneViewer8 === void 0 ? void 0 : _this$sceneViewer8.parentElement) || null;
37144
+ var _this$sceneViewer6;
37145
+ return ((_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.renderer) === null || _this$sceneViewer6 === void 0 || (_this$sceneViewer6 = _this$sceneViewer6.domElement) === null || _this$sceneViewer6 === void 0 ? void 0 : _this$sceneViewer6.parentElement) || null;
37852
37146
  }
37853
37147
 
37854
37148
  // -----------------------------------------------------------------------
@@ -37888,15 +37182,10 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
37888
37182
  var currentVal = (_ref3 = (_this$_stateAdapter$g = (_this$_stateAdapter2 = this._stateAdapter) === null || _this$_stateAdapter2 === void 0 ? void 0 : _this$_stateAdapter2.getState(scopedAttachmentId, dpId)) !== null && _this$_stateAdapter$g !== void 0 ? _this$_stateAdapter$g : dp.defaultValue) !== null && _ref3 !== void 0 ? _ref3 : null;
37889
37183
  if (isInput) {
37890
37184
  var ctrl = this._buildInputControl(dp, currentVal, function (newVal) {
37891
- var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer, _this5$sceneViewer2;
37185
+ var _this5$_stateAdapter, _this5$selectedObject, _this5$sceneViewer;
37892
37186
  (_this5$_stateAdapter = _this5._stateAdapter) === null || _this5$_stateAdapter === void 0 || _this5$_stateAdapter.setState(scopedAttachmentId, dpId, newVal);
37893
- // Also fire BehaviorManager so any wired behaviors react immediately.
37894
- // Pass the parent component UUID so behaviors scoped to a specific instance
37895
- // don't bleed across clones that share the same attachmentId.
37896
- // Use originalAttachmentId for behavior triggering as behaviors are keyed by original ID
37897
37187
  var parentUuid = ((_this5$selectedObject = _this5.selectedObject) === null || _this5$selectedObject === void 0 ? void 0 : _this5$selectedObject.uuid) || null;
37898
- (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.behaviorManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
37899
- (_this5$sceneViewer2 = _this5.sceneViewer) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.managers) === null || _this5$sceneViewer2 === void 0 || (_this5$sceneViewer2 = _this5$sceneViewer2.ioAnimationManager) === null || _this5$sceneViewer2 === void 0 || _this5$sceneViewer2.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
37188
+ (_this5$sceneViewer = _this5.sceneViewer) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.managers) === null || _this5$sceneViewer === void 0 || (_this5$sceneViewer = _this5$sceneViewer.ioBehaviorManager) === null || _this5$sceneViewer === void 0 || _this5$sceneViewer.triggerState(originalAttachmentId || scopedAttachmentId, dpId, newVal, parentUuid);
37900
37189
  });
37901
37190
  row.appendChild(ctrl);
37902
37191
  this._stateElements.set(key, {
@@ -38068,11 +37357,11 @@ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
38068
37357
  }]);
38069
37358
  }(BaseDisposable);
38070
37359
 
38071
- var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
38072
- function IoAnimationManager(sceneViewer) {
37360
+ var IoBehaviorManager = /*#__PURE__*/function (_BaseDisposable) {
37361
+ function IoBehaviorManager(sceneViewer) {
38073
37362
  var _this;
38074
- _classCallCheck(this, IoAnimationManager);
38075
- _this = _callSuper(this, IoAnimationManager);
37363
+ _classCallCheck(this, IoBehaviorManager);
37364
+ _this = _callSuper(this, IoBehaviorManager);
38076
37365
  _this.sceneViewer = sceneViewer;
38077
37366
 
38078
37367
  /**
@@ -38097,18 +37386,18 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
38097
37386
  * so that mesh references are live.
38098
37387
  *
38099
37388
  * @param {string} attachmentId - The attachment key (e.g. 'attch-switch-01')
38100
- * @param {Object|Array} animationConfig - Serialized config; either the full object
38101
- * { animations: [...] } or a plain array of animation entries.
37389
+ * @param {Object|Array} behaviorConfig - Serialized config; either the full object
37390
+ * { behaviors: [...] } or a plain array of behavior entries.
38102
37391
  * @param {THREE.Object3D} deviceModelRoot - The device's root Object3D as added to the scene
38103
37392
  * @param {string} parentUuid - UUID of the host component Object3D
38104
37393
  */
38105
- _inherits(IoAnimationManager, _BaseDisposable);
38106
- return _createClass(IoAnimationManager, [{
38107
- key: "loadAnimations",
38108
- value: function loadAnimations(attachmentId, animationConfig, deviceModelRoot, parentUuid) {
38109
- var _animationConfig$anim;
38110
- if (!animationConfig || !deviceModelRoot) return;
38111
- var anims = Array.isArray(animationConfig) ? animationConfig : (_animationConfig$anim = animationConfig.animations) !== null && _animationConfig$anim !== void 0 ? _animationConfig$anim : [];
37394
+ _inherits(IoBehaviorManager, _BaseDisposable);
37395
+ return _createClass(IoBehaviorManager, [{
37396
+ key: "loadBehaviors",
37397
+ value: function loadBehaviors(attachmentId, behaviorConfig, deviceModelRoot, parentUuid) {
37398
+ var _behaviorConfig$behav;
37399
+ if (!behaviorConfig || !deviceModelRoot) return;
37400
+ var anims = Array.isArray(behaviorConfig) ? behaviorConfig : (_behaviorConfig$behav = behaviorConfig.behaviors) !== null && _behaviorConfig$behav !== void 0 ? _behaviorConfig$behav : [];
38112
37401
  if (!anims.length) return;
38113
37402
  var key = this._key(parentUuid, attachmentId);
38114
37403
  var entries = [];
@@ -38119,7 +37408,7 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
38119
37408
  var anim = _step.value;
38120
37409
  var mesh = this._resolveMesh(anim, deviceModelRoot);
38121
37410
  if (!mesh) {
38122
- console.warn("[IoAnimationManager] Could not find mesh for animation \"".concat(anim.name || anim.stateVariable, "\" (uuid: ").concat(anim.meshUuid, ", name: \"").concat(anim.meshName, "\")"));
37411
+ console.warn("[IoBehaviorManager] Could not find mesh for animation \"".concat(anim.name || anim.stateVariable, "\" (uuid: ").concat(anim.meshUuid, ", name: \"").concat(anim.meshName, "\")"));
38123
37412
  continue;
38124
37413
  }
38125
37414
  entries.push({
@@ -38136,11 +37425,11 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
38136
37425
  }
38137
37426
  if (entries.length) {
38138
37427
  this._entries.set(key, entries);
38139
- console.log("[IoAnimationManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
37428
+ console.log("[IoBehaviorManager] Loaded ".concat(entries.length, " animation(s) for attachment \"").concat(attachmentId, "\" (parent: ").concat(parentUuid, ") \u2014 stateVariables: ").concat(entries.map(function (e) {
38140
37429
  return e.anim.stateVariable;
38141
37430
  }).join(', ')));
38142
37431
  } else {
38143
- console.warn("[IoAnimationManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 animationConfig had ").concat(anims.length, " entries but none matched a mesh"));
37432
+ console.warn("[IoBehaviorManager] No mesh entries resolved for attachment \"".concat(attachmentId, "\" \u2014 behaviorConfig had ").concat(anims.length, " entries but none matched a mesh"));
38144
37433
  }
38145
37434
  }
38146
37435
 
@@ -38334,7 +37623,7 @@ var IoAnimationManager = /*#__PURE__*/function (_BaseDisposable) {
38334
37623
  key: "dispose",
38335
37624
  value: function dispose() {
38336
37625
  this._entries.clear();
38337
- _superPropGet(IoAnimationManager, "dispose", this, 3)([]);
37626
+ _superPropGet(IoBehaviorManager, "dispose", this, 3)([]);
38338
37627
  }
38339
37628
 
38340
37629
  // ─────────────────────────────────────────────────────────────────────────
@@ -38954,13 +38243,12 @@ var CentralPlantInternals = /*#__PURE__*/function () {
38954
38243
  this.centralPlant.managers.keyboardControlsManager = new KeyboardControlsManager(this.centralPlant.sceneViewer);
38955
38244
  this.centralPlant.managers.pathfindingManager = new PathfindingManager(this.centralPlant.sceneViewer);
38956
38245
  this.centralPlant.managers.pathFlowManager = new PathFlowManager(this.centralPlant.sceneViewer);
38957
- this.centralPlant.managers.behaviorManager = new BehaviorManager(this.centralPlant.sceneViewer);
38958
38246
  this.centralPlant.managers.sceneOperationsManager = new SceneOperationsManager(this.centralPlant.sceneViewer);
38959
38247
  this.centralPlant.managers.animationManager = new AnimationManager(this.centralPlant.sceneViewer);
38960
38248
  this.centralPlant.managers.cameraControlsManager = new CameraControlsManager(this.centralPlant.sceneViewer);
38961
38249
  this.centralPlant.managers.componentDragManager = new ComponentDragManager(this.centralPlant.sceneViewer);
38962
38250
  this.centralPlant.managers.viewport2DManager = new Viewport2DManager(this.centralPlant.sceneViewer);
38963
- this.centralPlant.managers.ioAnimationManager = new IoAnimationManager(this.centralPlant.sceneViewer);
38251
+ this.centralPlant.managers.ioBehaviorManager = new IoBehaviorManager(this.centralPlant.sceneViewer);
38964
38252
  this.centralPlant.managers.ioOutlineManager = new IoOutlineManager(this.centralPlant.sceneViewer);
38965
38253
 
38966
38254
  // All managers are now stored in the managers collection and will be attached via attachToComponent()
@@ -39732,7 +39020,7 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39732
39020
  return false;
39733
39021
  }
39734
39022
  try {
39735
- var _componentData$childr, _componentData$childr2, _this$centralPlant$sc7, _componentData$childr3, _componentData$defaul;
39023
+ var _componentData$childr, _componentData$childr2, _this$centralPlant$sc7, _componentData$childr3;
39736
39024
  // Generate a unique component ID if not provided
39737
39025
  var componentId = options.customId || this.generateUniqueComponentId(libraryId);
39738
39026
 
@@ -39939,23 +39227,23 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39939
39227
  var _this$centralPlant$sc8;
39940
39228
  attachIODevicesToComponent(componentModel, componentData, modelPreloader, componentId);
39941
39229
 
39942
- // Register animation configs so IoAnimationManager can respond to state changes
39943
- var ioAnimMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioAnimationManager;
39944
- if (ioAnimMgr) {
39230
+ // Register behavior configs so IoBehaviorManager can respond to state changes
39231
+ var ioBehavMgr = (_this$centralPlant$sc8 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc8 === void 0 || (_this$centralPlant$sc8 = _this$centralPlant$sc8.managers) === null || _this$centralPlant$sc8 === void 0 ? void 0 : _this$centralPlant$sc8.ioBehaviorManager;
39232
+ if (ioBehavMgr) {
39945
39233
  var _loop = function _loop() {
39946
39234
  var _modelPreloader$compo;
39947
39235
  var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
39948
39236
  attachmentId = _Object$entries$_i[0],
39949
39237
  attachment = _Object$entries$_i[1];
39950
39238
  var deviceData = (_modelPreloader$compo = modelPreloader.componentDictionary) === null || _modelPreloader$compo === void 0 ? void 0 : _modelPreloader$compo[attachment.deviceId];
39951
- if (!(deviceData !== null && deviceData !== void 0 && deviceData.animationConfig)) return 1; // continue
39239
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return 1; // continue
39952
39240
  var deviceRoot = null;
39953
39241
  componentModel.traverse(function (obj) {
39954
39242
  var _obj$userData2;
39955
39243
  if (!deviceRoot && ((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.attachmentId) === attachmentId) deviceRoot = obj;
39956
39244
  });
39957
39245
  if (deviceRoot) {
39958
- ioAnimMgr.loadAnimations(attachmentId, deviceData.animationConfig, deviceRoot, componentId);
39246
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, componentId);
39959
39247
  }
39960
39248
  };
39961
39249
  for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
@@ -39964,15 +39252,6 @@ var CentralPlantInternals = /*#__PURE__*/function () {
39964
39252
  }
39965
39253
  }
39966
39254
 
39967
- // Register default behaviors for smart components so the BehaviorManager
39968
- // responds to tooltip-driven state changes immediately after drop.
39969
- // (The scene-load path uses _processBehaviors instead, which runs on loadSceneData.)
39970
- if ((_componentData$defaul = componentData.defaultBehaviors) !== null && _componentData$defaul !== void 0 && _componentData$defaul.length) {
39971
- var _this$centralPlant$sc9, _som$registerBehavior;
39972
- var som = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.sceneOperationsManager;
39973
- som === null || som === void 0 || (_som$registerBehavior = som.registerBehaviorsForComponentInstance) === null || _som$registerBehavior === void 0 || _som$registerBehavior.call(som, componentData, componentId);
39974
- }
39975
-
39976
39255
  // Notify the component manager about the new component
39977
39256
  if (componentManager.registerComponent) {
39978
39257
  componentManager.registerComponent(componentModel);
@@ -40045,18 +39324,18 @@ var CentralPlantInternals = /*#__PURE__*/function () {
40045
39324
  }, {
40046
39325
  key: "deleteComponent",
40047
39326
  value: function deleteComponent(componentId) {
40048
- var _this$centralPlant$sc0;
39327
+ var _this$centralPlant$sc9;
40049
39328
  // Check if component manager is available
40050
- var componentManager = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.componentManager;
39329
+ var componentManager = (_this$centralPlant$sc9 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc9 === void 0 ? void 0 : _this$centralPlant$sc9.componentManager;
40051
39330
  if (!componentManager) {
40052
39331
  console.error('❌ deleteComponent(): Component manager not available');
40053
39332
  return false;
40054
39333
  }
40055
39334
  try {
40056
- var _this$centralPlant$sc1, _this$centralPlant$sc10, _sceneData$scene2, _sceneData$scene3;
39335
+ var _this$centralPlant$sc0, _this$centralPlant$sc1, _sceneData$scene2, _sceneData$scene3;
40057
39336
  console.log("\uD83D\uDDD1\uFE0F deleteComponent(): Deleting component ".concat(componentId));
40058
- var threeScene = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.scene;
40059
- var sceneData = (_this$centralPlant$sc10 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc10 === void 0 ? void 0 : _this$centralPlant$sc10.currentSceneData;
39337
+ var threeScene = (_this$centralPlant$sc0 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc0 === void 0 ? void 0 : _this$centralPlant$sc0.scene;
39338
+ var sceneData = (_this$centralPlant$sc1 = this.centralPlant.sceneViewer) === null || _this$centralPlant$sc1 === void 0 ? void 0 : _this$centralPlant$sc1.currentSceneData;
40060
39339
 
40061
39340
  // Step 1: Resolve the actual Three.js UUID from componentId.
40062
39341
  // The UI emits object.name (e.g. "Pump (PUMP-1)") as the selection ID, but
@@ -40271,7 +39550,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
40271
39550
  * Initialize the CentralPlant manager
40272
39551
  *
40273
39552
  * @constructor
40274
- * @version 0.3.27
39553
+ * @version 0.3.29
40275
39554
  * @updated 2025-10-22
40276
39555
  *
40277
39556
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -41305,107 +40584,6 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41305
40584
  };
41306
40585
  }
41307
40586
 
41308
- // ─────────────────────────────────────────────────────────────────────────
41309
- // BEHAVIORS API
41310
- // ─────────────────────────────────────────────────────────────────────────
41311
-
41312
- /**
41313
- * Get all behavior definitions currently stored in the BehaviorManager.
41314
- * @returns {Array<Object>} Array of behavior definition objects, or empty array.
41315
- * @example
41316
- * const behaviors = centralPlant.getBehaviors()
41317
- * behaviors.forEach(b => console.log(b.id, b.input, b.output))
41318
- */
41319
- }, {
41320
- key: "getBehaviors",
41321
- value: function getBehaviors() {
41322
- var _this$sceneViewer4;
41323
- var bm = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4.behaviorManager;
41324
- if (!bm) {
41325
- console.warn('⚠️ getBehaviors(): BehaviorManager not available');
41326
- return [];
41327
- }
41328
- return bm.getBehaviors();
41329
- }
41330
-
41331
- /**
41332
- * Add a behavior definition at runtime.
41333
- * @param {Object} behaviorDef - Full behavior definition object (must have unique `id`)
41334
- * @returns {boolean} True if added successfully, false otherwise
41335
- * @example
41336
- * centralPlant.addBehavior({
41337
- * id: 'my-behavior',
41338
- * input: { attachment: 'device-01', dataPoint: 'boolean-status-01' },
41339
- * output: { attachment: 'light-01', child: 'indicator-mesh-01' },
41340
- * conditions: [{ when: 'dataPoint.value === true', actions: [{ set: 'material.emissiveIntensity', value: 1.0 }] }]
41341
- * })
41342
- */
41343
- }, {
41344
- key: "addBehavior",
41345
- value: function addBehavior(behaviorDef) {
41346
- var _this$sceneViewer5, _this$sceneViewer6;
41347
- var bm = (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 ? void 0 : _this$sceneViewer5.behaviorManager;
41348
- if (!bm) {
41349
- console.warn('⚠️ addBehavior(): BehaviorManager not available');
41350
- return false;
41351
- }
41352
- // Also persist into currentSceneData so export includes it
41353
- if ((_this$sceneViewer6 = this.sceneViewer) !== null && _this$sceneViewer6 !== void 0 && _this$sceneViewer6.currentSceneData) {
41354
- if (!Array.isArray(this.sceneViewer.currentSceneData.behaviors)) {
41355
- this.sceneViewer.currentSceneData.behaviors = [];
41356
- }
41357
- this.sceneViewer.currentSceneData.behaviors.push(behaviorDef);
41358
- }
41359
- return bm.addBehavior(behaviorDef);
41360
- }
41361
-
41362
- /**
41363
- * Remove a behavior definition by id.
41364
- * @param {string} behaviorId
41365
- * @returns {boolean} True if removed, false if not found
41366
- * @example
41367
- * centralPlant.removeBehavior('my-behavior')
41368
- */
41369
- }, {
41370
- key: "removeBehavior",
41371
- value: function removeBehavior(behaviorId) {
41372
- var _this$sceneViewer7, _this$sceneViewer8;
41373
- var bm = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 || (_this$sceneViewer7 = _this$sceneViewer7.managers) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.behaviorManager;
41374
- if (!bm) {
41375
- console.warn('⚠️ removeBehavior(): BehaviorManager not available');
41376
- return false;
41377
- }
41378
- // Also remove from currentSceneData
41379
- if ((_this$sceneViewer8 = this.sceneViewer) !== null && _this$sceneViewer8 !== void 0 && (_this$sceneViewer8 = _this$sceneViewer8.currentSceneData) !== null && _this$sceneViewer8 !== void 0 && _this$sceneViewer8.behaviors) {
41380
- var idx = this.sceneViewer.currentSceneData.behaviors.findIndex(function (b) {
41381
- return b.id === behaviorId;
41382
- });
41383
- if (idx !== -1) this.sceneViewer.currentSceneData.behaviors.splice(idx, 1);
41384
- }
41385
- return bm.removeBehavior(behaviorId);
41386
- }
41387
-
41388
- /**
41389
- * Simulate an IO device state value arriving and trigger any matching behaviors.
41390
- * Useful for live testing in the UI or for integration with real data feeds.
41391
- * @param {string} attachmentId - The attachment id of the input io-device
41392
- * @param {string} stateId - The state id on that device
41393
- * @param {*} value - The new state value
41394
- * @example
41395
- * centralPlant.triggerState('pump-push-button-01', 'power', true)
41396
- */
41397
- }, {
41398
- key: "triggerState",
41399
- value: function triggerState(attachmentId, stateId, value, parentUuid) {
41400
- var _this$sceneViewer9;
41401
- var bm = (_this$sceneViewer9 = this.sceneViewer) === null || _this$sceneViewer9 === void 0 || (_this$sceneViewer9 = _this$sceneViewer9.managers) === null || _this$sceneViewer9 === void 0 ? void 0 : _this$sceneViewer9.behaviorManager;
41402
- if (!bm) {
41403
- console.warn('⚠️ triggerState(): BehaviorManager not available');
41404
- return;
41405
- }
41406
- bm.triggerState(attachmentId, stateId, value, parentUuid);
41407
- }
41408
-
41409
40587
  /**
41410
40588
  * Set the state of an I/O device instance in the Three.js scene.
41411
40589
  *
@@ -41438,15 +40616,9 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41438
40616
  }, {
41439
40617
  key: "setIoDeviceState",
41440
40618
  value: function setIoDeviceState(attachmentId, stateId, value, parentUuid) {
41441
- var _this$sceneViewer0, _this$sceneViewer1, _this$sceneViewer10;
41442
- var bm = (_this$sceneViewer0 = this.sceneViewer) === null || _this$sceneViewer0 === void 0 || (_this$sceneViewer0 = _this$sceneViewer0.managers) === null || _this$sceneViewer0 === void 0 ? void 0 : _this$sceneViewer0.behaviorManager;
41443
- if (!bm) {
41444
- console.warn('⚠️ setIoDeviceState(): BehaviorManager not available');
41445
- return false;
41446
- }
41447
-
40619
+ var _this$sceneViewer4, _this$sceneViewer5, _this$sceneViewer6;
41448
40620
  // 1. Persist via state adapter if one has been configured
41449
- var stateAdapter = (_this$sceneViewer1 = this.sceneViewer) === null || _this$sceneViewer1 === void 0 || (_this$sceneViewer1 = _this$sceneViewer1.managers) === null || _this$sceneViewer1 === void 0 || (_this$sceneViewer1 = _this$sceneViewer1.componentTooltipManager) === null || _this$sceneViewer1 === void 0 ? void 0 : _this$sceneViewer1._stateAdapter;
40621
+ var stateAdapter = (_this$sceneViewer4 = this.sceneViewer) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.managers) === null || _this$sceneViewer4 === void 0 || (_this$sceneViewer4 = _this$sceneViewer4.componentTooltipManager) === null || _this$sceneViewer4 === void 0 ? void 0 : _this$sceneViewer4._stateAdapter;
41450
40622
  if (stateAdapter !== null && stateAdapter !== void 0 && stateAdapter.setState) {
41451
40623
  var scopedKey = parentUuid ? "".concat(parentUuid, "::").concat(attachmentId) : attachmentId;
41452
40624
  try {
@@ -41456,11 +40628,11 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41456
40628
  }
41457
40629
  }
41458
40630
 
41459
- // 2. Apply Three.js behavior changes
41460
- bm.triggerState(attachmentId, stateId, value, parentUuid);
40631
+ // 2. Apply io-behavior changes
40632
+ (_this$sceneViewer5 = this.sceneViewer) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.managers) === null || _this$sceneViewer5 === void 0 || (_this$sceneViewer5 = _this$sceneViewer5.ioBehaviorManager) === null || _this$sceneViewer5 === void 0 || _this$sceneViewer5.triggerState(attachmentId, stateId, value, parentUuid);
41461
40633
 
41462
40634
  // 3. Emit event for host apps that don't use the state adapter (e.g. cp3d-viewer)
41463
- (_this$sceneViewer10 = this.sceneViewer) === null || _this$sceneViewer10 === void 0 || _this$sceneViewer10.emit('io-device-state-changed', {
40635
+ (_this$sceneViewer6 = this.sceneViewer) === null || _this$sceneViewer6 === void 0 || _this$sceneViewer6.emit('io-device-state-changed', {
41464
40636
  attachmentId: attachmentId,
41465
40637
  stateId: stateId,
41466
40638
  value: value,
@@ -41482,8 +40654,8 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
41482
40654
  }, {
41483
40655
  key: "getSceneAttachments",
41484
40656
  value: function getSceneAttachments() {
41485
- var _this$sceneViewer11;
41486
- var scene = (_this$sceneViewer11 = this.sceneViewer) === null || _this$sceneViewer11 === void 0 ? void 0 : _this$sceneViewer11.scene;
40657
+ var _this$sceneViewer7;
40658
+ var scene = (_this$sceneViewer7 = this.sceneViewer) === null || _this$sceneViewer7 === void 0 ? void 0 : _this$sceneViewer7.scene;
41487
40659
  if (!scene) return [];
41488
40660
  var results = [];
41489
40661
  scene.traverse(function (obj) {
@@ -43288,7 +42460,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
43288
42460
  this.centralPlant.attachToComponent();
43289
42461
 
43290
42462
  // Sync our managers tracking object after attachment
43291
- managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'behaviorManager', 'ioAnimationManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
42463
+ managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'pathFlowManager', 'ioBehaviorManager', 'ioOutlineManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
43292
42464
  managerKeys.forEach(function (key) {
43293
42465
  if (_this2[key]) {
43294
42466
  _this2.managers[key] = _this2[key];
@@ -43620,10 +42792,10 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
43620
42792
  },
43621
42793
  onIODeviceDrag: function onIODeviceDrag(ioDeviceObject, signedDelta, isStart) {
43622
42794
  if (isStart) {
43623
- var _ioDeviceObject$userD, _this4$managers$ioAni, _this4$managers, _this4$managers2;
42795
+ var _ioDeviceObject$userD, _this4$managers$ioBeh, _this4$managers, _this4$managers2;
43624
42796
  // Resolve parentUuid by walking up to the host component.
43625
42797
  // Use userData.originalUuid (the custom componentId) because that
43626
- // is what IoAnimationManager uses as the map key — NOT obj.uuid.
42798
+ // is what IoBehaviorManager uses as the map key — NOT obj.uuid.
43627
42799
  var parentUuid = null;
43628
42800
  var obj = ioDeviceObject.parent;
43629
42801
  while (obj) {
@@ -43639,7 +42811,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
43639
42811
  // silhouette is isolated and the outline ring is visible around
43640
42812
  // them specifically (not swallowed by the larger device body).
43641
42813
  // Fall back to the whole device group when none are registered.
43642
- var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioAni = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioAnimationManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioAni !== void 0 ? _this4$managers$ioAni : [] : [];
42814
+ var animatedMeshes = attachmentId && parentUuid ? (_this4$managers$ioBeh = (_this4$managers = _this4.managers) === null || _this4$managers === void 0 || (_this4$managers = _this4$managers.ioBehaviorManager) === null || _this4$managers === void 0 ? void 0 : _this4$managers.getAnimatedMeshes(parentUuid, attachmentId)) !== null && _this4$managers$ioBeh !== void 0 ? _this4$managers$ioBeh : [] : [];
43643
42815
  var targets = animatedMeshes.length > 0 ? animatedMeshes : [ioDeviceObject];
43644
42816
  (_this4$managers2 = _this4.managers) === null || _this4$managers2 === void 0 || (_this4$managers2 = _this4$managers2.ioOutlineManager) === null || _this4$managers2 === void 0 || _this4$managers2.setTargets(targets);
43645
42817
  }
@@ -49006,7 +48178,6 @@ var rendering3D = /*#__PURE__*/Object.freeze({
49006
48178
  });
49007
48179
 
49008
48180
  exports.AnimationManager = AnimationManager;
49009
- exports.BehaviorManager = BehaviorManager;
49010
48181
  exports.CACHE_EXPIRY = CACHE_EXPIRY;
49011
48182
  exports.CACHE_NAME_PREFIX = CACHE_NAME_PREFIX;
49012
48183
  exports.CacheManager = CacheManager;
@@ -49019,7 +48190,7 @@ exports.ComponentTooltipManager = ComponentTooltipManager;
49019
48190
  exports.EnvironmentManager = EnvironmentManager;
49020
48191
  exports.FLOW_ATTRIBUTE_KEYS = FLOW_ATTRIBUTE_KEYS;
49021
48192
  exports.GLOBAL_CACHE_NAME = GLOBAL_CACHE_NAME;
49022
- exports.IoAnimationManager = IoAnimationManager;
48193
+ exports.IoBehaviorManager = IoBehaviorManager;
49023
48194
  exports.KeyboardControlsManager = KeyboardControlsManager;
49024
48195
  exports.ModelManager = ModelManager;
49025
48196
  exports.OperationHistoryManager = OperationHistoryManager;