@2112-lab/central-plant 0.3.38 → 0.3.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/bundle/index.js +1078 -562
  2. package/dist/cjs/src/core/centralPlant.js +115 -68
  3. package/dist/cjs/src/core/centralPlantInternals.js +20 -36
  4. package/dist/cjs/src/index.js +19 -0
  5. package/dist/cjs/src/managers/behaviors/IoBehaviorManager.js +175 -234
  6. package/dist/cjs/src/managers/components/componentDataManager.js +63 -11
  7. package/dist/cjs/src/managers/scene/componentTooltipManager.js +95 -65
  8. package/dist/cjs/src/managers/scene/modelManager.js +93 -145
  9. package/dist/cjs/src/managers/scene/sceneOperationsManager.js +8 -3
  10. package/dist/cjs/src/utils/animationTransformUtils.js +66 -0
  11. package/dist/cjs/src/utils/behaviorDispatch.js +62 -0
  12. package/dist/cjs/src/utils/behaviorRegistration.js +76 -0
  13. package/dist/cjs/src/utils/behaviorSceneUtils.js +155 -0
  14. package/dist/cjs/src/utils/behaviorSchema.js +209 -0
  15. package/dist/esm/src/core/centralPlant.js +115 -68
  16. package/dist/esm/src/core/centralPlantInternals.js +21 -37
  17. package/dist/esm/src/index.js +4 -0
  18. package/dist/esm/src/managers/behaviors/IoBehaviorManager.js +176 -235
  19. package/dist/esm/src/managers/components/componentDataManager.js +63 -11
  20. package/dist/esm/src/managers/scene/componentTooltipManager.js +95 -65
  21. package/dist/esm/src/managers/scene/modelManager.js +94 -146
  22. package/dist/esm/src/managers/scene/sceneOperationsManager.js +8 -3
  23. package/dist/esm/src/utils/animationTransformUtils.js +41 -0
  24. package/dist/esm/src/utils/behaviorDispatch.js +56 -0
  25. package/dist/esm/src/utils/behaviorRegistration.js +71 -0
  26. package/dist/esm/src/utils/behaviorSceneUtils.js +147 -0
  27. package/dist/esm/src/utils/behaviorSchema.js +201 -0
  28. package/dist/index.d.ts +169 -1
  29. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
+
3
+ /**
4
+ * Register mesh animations and intra-component state links when a smart
5
+ * component is placed or imported into the scene.
6
+ *
7
+ * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager} ioBehavMgr
8
+ * @param {string} componentUuid - Instance UUID of the parent component
9
+ * @param {Object} componentData - Smart component dictionary entry
10
+ * @param {Object} componentModel - Root THREE.Object3D of the placed component
11
+ * @param {Object} componentDictionary - modelPreloader.componentDictionary
12
+ */
13
+ function registerBehaviorsForComponent(ioBehavMgr, componentUuid, componentData, componentModel, componentDictionary) {
14
+ var _componentData$behavi;
15
+ if (!ioBehavMgr || !componentData || !componentModel) {
16
+ return;
17
+ }
18
+ if (componentData.isSmart && componentData.attachedDevices) {
19
+ var _loop = function _loop() {
20
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
21
+ attachmentId = _Object$entries$_i[0],
22
+ attachment = _Object$entries$_i[1];
23
+ var deviceData = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[attachment.deviceId];
24
+ if (!(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return 1; // continue
25
+ var deviceRoot = null;
26
+ componentModel.traverse(function (obj) {
27
+ var _obj$userData;
28
+ if (!deviceRoot && ((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.attachmentId) === attachmentId) deviceRoot = obj;
29
+ });
30
+ if (deviceRoot) {
31
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, deviceRoot, componentUuid);
32
+ }
33
+ };
34
+ for (var _i = 0, _Object$entries = Object.entries(componentData.attachedDevices); _i < _Object$entries.length; _i++) {
35
+ if (_loop()) continue;
36
+ }
37
+ }
38
+ if (((_componentData$behavi = componentData.behaviors) === null || _componentData$behavi === void 0 ? void 0 : _componentData$behavi.length) > 0) {
39
+ ioBehavMgr.registerComponentBehaviors(componentUuid, componentData.behaviors);
40
+ } else if (componentData.isSmart) {
41
+ ioBehavMgr.registerComponentBehaviors(componentUuid, []);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Reload mesh behaviorConfig for every placed instance of an I/O device asset.
47
+ * Call after the animation wizard saves updated behaviorConfig to the dictionary.
48
+ *
49
+ * @param {import('../managers/behaviors/IoBehaviorManager.js').IoBehaviorManager} ioBehavMgr
50
+ * @param {string} deviceAssetId - Dictionary / asset id of the I/O device
51
+ * @param {Object} componentDictionary
52
+ * @param {Object} centralPlant
53
+ */
54
+ function reloadBehaviorsForDeviceAsset(ioBehavMgr, deviceAssetId, componentDictionary, centralPlant) {
55
+ var _centralPlant$sceneVi;
56
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi = centralPlant.sceneViewer) === null || _centralPlant$sceneVi === void 0 ? void 0 : _centralPlant$sceneVi.scene;
57
+ var deviceData = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[deviceAssetId];
58
+ if (!ioBehavMgr || !scene || !(deviceData !== null && deviceData !== void 0 && deviceData.behaviorConfig)) return;
59
+ scene.traverse(function (obj) {
60
+ var _obj$userData2, _obj$userData3, _obj$parent;
61
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) !== 'io-device') return;
62
+ if (((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.deviceId) !== deviceAssetId) return;
63
+ var parentUuid = obj.userData.parentComponentId || ((_obj$parent = obj.parent) === null || _obj$parent === void 0 ? void 0 : _obj$parent.uuid);
64
+ var attachmentId = obj.userData.attachmentId;
65
+ if (!parentUuid || !attachmentId) return;
66
+ ioBehavMgr.unloadForAttachment(parentUuid, attachmentId);
67
+ ioBehavMgr.loadBehaviors(attachmentId, deviceData.behaviorConfig, obj, parentUuid);
68
+ });
69
+ }
70
+
71
+ export { registerBehaviorsForComponent, reloadBehaviorsForDeviceAsset };
@@ -0,0 +1,147 @@
1
+ import { registerBehaviorsForComponent } from './behaviorRegistration.js';
2
+ import { getScopedAttachmentKey, getIoBehaviorManager } from './behaviorDispatch.js';
3
+
4
+ /**
5
+ * Scene-level behavior helpers: scan endpoints, register behaviors, cross-component links.
6
+ */
7
+ function getComponentDictionary(centralPlant) {
8
+ var _centralPlant$getUtil, _centralPlant$manager;
9
+ return (centralPlant === null || centralPlant === void 0 || (_centralPlant$getUtil = centralPlant.getUtility) === null || _centralPlant$getUtil === void 0 || (_centralPlant$getUtil = _centralPlant$getUtil.call(centralPlant, 'modelPreloader')) === null || _centralPlant$getUtil === void 0 ? void 0 : _centralPlant$getUtil.componentDictionary) || (centralPlant === null || centralPlant === void 0 || (_centralPlant$manager = centralPlant.managers) === null || _centralPlant$manager === void 0 || (_centralPlant$manager = _centralPlant$manager.componentDataManager) === null || _centralPlant$manager === void 0 ? void 0 : _centralPlant$manager.componentDictionary) || null;
10
+ }
11
+ function statesFromBehaviorConfig(device) {
12
+ if (!(device !== null && device !== void 0 && device.behaviorConfig) || !Array.isArray(device.behaviorConfig)) return [];
13
+ return device.behaviorConfig.map(function (b) {
14
+ return b.stateVariable;
15
+ }).filter(Boolean).filter(function (v, i, arr) {
16
+ return arr.indexOf(v) === i;
17
+ });
18
+ }
19
+ function resolveStatesForDevice(ioObj, deviceId, componentDictionary) {
20
+ var _ioObj$userData;
21
+ var device = componentDictionary === null || componentDictionary === void 0 ? void 0 : componentDictionary[deviceId];
22
+ var fromBehaviorConfig = statesFromBehaviorConfig(device);
23
+ if (fromBehaviorConfig.length) return fromBehaviorConfig;
24
+ return (((_ioObj$userData = ioObj.userData) === null || _ioObj$userData === void 0 ? void 0 : _ioObj$userData.dataPoints) || []).map(function (dp) {
25
+ return dp.id || dp.name;
26
+ }).filter(Boolean).filter(function (v, i, arr) {
27
+ return arr.indexOf(v) === i;
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Scan the live Three.js scene for smart-component io-device endpoints.
33
+ */
34
+ function scanSceneIoEndpoints(centralPlant) {
35
+ var _centralPlant$sceneVi;
36
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi = centralPlant.sceneViewer) === null || _centralPlant$sceneVi === void 0 ? void 0 : _centralPlant$sceneVi.scene;
37
+ if (!scene) return [];
38
+ var dict = getComponentDictionary(centralPlant);
39
+ var endpoints = [];
40
+ scene.traverse(function (obj) {
41
+ var _obj$userData, _parent$userData, _parent$userData2;
42
+ if (((_obj$userData = obj.userData) === null || _obj$userData === void 0 ? void 0 : _obj$userData.objectType) !== 'io-device') return;
43
+ var parent = obj.parent;
44
+ var parentUuid = obj.userData.parentComponentId || (parent === null || parent === void 0 ? void 0 : parent.uuid) || '';
45
+ var parentName = (parent === null || parent === void 0 || (_parent$userData = parent.userData) === null || _parent$userData === void 0 ? void 0 : _parent$userData.hardcodedUuid) || (parent === null || parent === void 0 ? void 0 : parent.name) || parentUuid;
46
+ var libraryId = (parent === null || parent === void 0 || (_parent$userData2 = parent.userData) === null || _parent$userData2 === void 0 ? void 0 : _parent$userData2.libraryId) || '';
47
+ var attachmentId = obj.userData.attachmentId || obj.name || obj.uuid;
48
+ var deviceId = obj.userData.deviceId || '';
49
+ endpoints.push({
50
+ key: getScopedAttachmentKey(attachmentId, parentUuid),
51
+ parentUuid: parentUuid,
52
+ parentName: parentName,
53
+ libraryId: libraryId,
54
+ attachmentId: attachmentId,
55
+ attachmentLabel: obj.userData.attachmentLabel || attachmentId,
56
+ deviceId: deviceId,
57
+ states: resolveStatesForDevice(obj, deviceId, dict)
58
+ });
59
+ });
60
+ return endpoints;
61
+ }
62
+
63
+ /**
64
+ * Load cross-component behaviors from scene data mirrors.
65
+ */
66
+ function loadCrossComponentBehaviors(centralPlant) {
67
+ var _centralPlant$importe, _centralPlant$sceneVi2;
68
+ return (centralPlant === null || centralPlant === void 0 || (_centralPlant$importe = centralPlant.importedSceneData) === null || _centralPlant$importe === void 0 ? void 0 : _centralPlant$importe.behaviors) || (centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi2 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi2 === void 0 || (_centralPlant$sceneVi2 = _centralPlant$sceneVi2.currentSceneData) === null || _centralPlant$sceneVi2 === void 0 ? void 0 : _centralPlant$sceneVi2.behaviors) || [];
69
+ }
70
+
71
+ /**
72
+ * Persist cross-component behaviors to scene data mirrors and the runtime manager.
73
+ */
74
+ function applyCrossComponentBehaviors(centralPlant, behaviors) {
75
+ var normalized = behaviors || [];
76
+ if (centralPlant.importedSceneData) {
77
+ if (normalized.length) {
78
+ centralPlant.importedSceneData.behaviors = normalized;
79
+ } else {
80
+ delete centralPlant.importedSceneData.behaviors;
81
+ }
82
+ }
83
+ var sceneViewer = centralPlant.sceneViewer;
84
+ if (sceneViewer && !sceneViewer.currentSceneData) {
85
+ var _centralPlant$getConn;
86
+ sceneViewer.currentSceneData = {
87
+ version: '2.3',
88
+ connections: ((_centralPlant$getConn = centralPlant.getConnections) === null || _centralPlant$getConn === void 0 ? void 0 : _centralPlant$getConn.call(centralPlant)) || [],
89
+ scene: {
90
+ children: []
91
+ }
92
+ };
93
+ }
94
+ var currentSceneData = sceneViewer === null || sceneViewer === void 0 ? void 0 : sceneViewer.currentSceneData;
95
+ if (currentSceneData) {
96
+ if (normalized.length) {
97
+ currentSceneData.behaviors = normalized;
98
+ } else {
99
+ delete currentSceneData.behaviors;
100
+ }
101
+ }
102
+ var ioBehavMgr = getIoBehaviorManager(sceneViewer);
103
+ if (ioBehavMgr) {
104
+ ioBehavMgr.setCrossComponentBehaviors(normalized);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Re-register intra-component behaviors on all scene instances of a smart component.
110
+ */
111
+ function refreshSceneIntraBehaviors(centralPlant, libraryId, behaviors) {
112
+ var _centralPlant$sceneVi3;
113
+ var ioBehavMgr = getIoBehaviorManager(centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.sceneViewer);
114
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi3 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi3 === void 0 ? void 0 : _centralPlant$sceneVi3.scene;
115
+ if (!ioBehavMgr || !scene || !libraryId) return;
116
+ scene.traverse(function (obj) {
117
+ var _obj$userData2, _obj$userData3;
118
+ if (((_obj$userData2 = obj.userData) === null || _obj$userData2 === void 0 ? void 0 : _obj$userData2.objectType) === 'component' && ((_obj$userData3 = obj.userData) === null || _obj$userData3 === void 0 ? void 0 : _obj$userData3.libraryId) === libraryId) {
119
+ ioBehavMgr.registerComponentBehaviors(obj.uuid, behaviors || []);
120
+ }
121
+ });
122
+ }
123
+
124
+ /**
125
+ * Re-register intra- and cross-component behaviors for all instances in the live scene.
126
+ */
127
+ function reregisterSceneBehaviors(centralPlant) {
128
+ var _centralPlant$sceneVi4;
129
+ var ioBehavMgr = getIoBehaviorManager(centralPlant === null || centralPlant === void 0 ? void 0 : centralPlant.sceneViewer);
130
+ var scene = centralPlant === null || centralPlant === void 0 || (_centralPlant$sceneVi4 = centralPlant.sceneViewer) === null || _centralPlant$sceneVi4 === void 0 ? void 0 : _centralPlant$sceneVi4.scene;
131
+ var dict = getComponentDictionary(centralPlant);
132
+ if (!ioBehavMgr || !scene) return;
133
+ var crossBehaviors = loadCrossComponentBehaviors(centralPlant);
134
+ ioBehavMgr.setCrossComponentBehaviors(crossBehaviors);
135
+ if (!dict) return;
136
+ scene.traverse(function (obj) {
137
+ var _obj$userData4, _obj$userData5;
138
+ if (((_obj$userData4 = obj.userData) === null || _obj$userData4 === void 0 ? void 0 : _obj$userData4.objectType) !== 'component') return;
139
+ var libraryId = (_obj$userData5 = obj.userData) === null || _obj$userData5 === void 0 ? void 0 : _obj$userData5.libraryId;
140
+ if (!libraryId) return;
141
+ var componentData = dict[libraryId];
142
+ if (!componentData) return;
143
+ registerBehaviorsForComponent(ioBehavMgr, obj.uuid, componentData, obj, dict);
144
+ });
145
+ }
146
+
147
+ export { applyCrossComponentBehaviors, loadCrossComponentBehaviors, refreshSceneIntraBehaviors, reregisterSceneBehaviors, scanSceneIoEndpoints };
@@ -0,0 +1,201 @@
1
+ import { objectSpread2 as _objectSpread2, slicedToArray as _slicedToArray, typeof as _typeof } from '../../_virtual/_rollupPluginBabelHelpers.js';
2
+
3
+ /**
4
+ * Shared behavior schema helpers for runtime normalization and UI authoring.
5
+ * Used by IoBehaviorManager and sandbox BehaviorDialog.
6
+ */
7
+
8
+ /**
9
+ * Normalize behavior from shorthand to full format.
10
+ * Supports:
11
+ * - input: "attachment.state" → { attachment, state }
12
+ * - outputs: ["attachment.state", ...] → converted to _outputs array
13
+ *
14
+ * @param {Object} behavior - Raw behavior from scene JSON
15
+ * @returns {Object} Normalized behavior
16
+ */
17
+ function normalizeBehavior(behavior) {
18
+ var normalized = _objectSpread2({}, behavior);
19
+ if (typeof behavior.input === 'string') {
20
+ var _behavior$input$split = behavior.input.split('.'),
21
+ _behavior$input$split2 = _slicedToArray(_behavior$input$split, 2),
22
+ attachment = _behavior$input$split2[0],
23
+ state = _behavior$input$split2[1];
24
+ normalized.input = {
25
+ attachment: attachment,
26
+ state: state
27
+ };
28
+ }
29
+ if (behavior.outputs) {
30
+ normalized._outputs = behavior.outputs.map(function (out) {
31
+ if (typeof out === 'string') {
32
+ var _out$split = out.split('.'),
33
+ _out$split2 = _slicedToArray(_out$split, 2),
34
+ _attachment = _out$split2[0],
35
+ _state = _out$split2[1];
36
+ return {
37
+ attachment: _attachment,
38
+ state: _state
39
+ };
40
+ }
41
+ return out;
42
+ });
43
+ delete normalized.outputs;
44
+ } else if (typeof behavior.output === 'string') {
45
+ var _behavior$output$spli = behavior.output.split('.'),
46
+ _behavior$output$spli2 = _slicedToArray(_behavior$output$spli, 2),
47
+ _attachment2 = _behavior$output$spli2[0],
48
+ _state2 = _behavior$output$spli2[1];
49
+ normalized.output = {
50
+ attachment: _attachment2,
51
+ state: _state2
52
+ };
53
+ }
54
+ return normalized;
55
+ }
56
+
57
+ /**
58
+ * Build intra-component shorthand behavior from form fields.
59
+ */
60
+ function buildIntraBehavior(_ref) {
61
+ var id = _ref.id,
62
+ inputAttachment = _ref.inputAttachment,
63
+ inputState = _ref.inputState,
64
+ outputs = _ref.outputs;
65
+ return {
66
+ id: id || "behavior-".concat(Date.now()),
67
+ input: "".concat(inputAttachment, ".").concat(inputState),
68
+ outputs: outputs.filter(function (o) {
69
+ return o.attachment && o.state;
70
+ }).map(function (o) {
71
+ return "".concat(o.attachment, ".").concat(o.state);
72
+ })
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Parse intra-component shorthand into form fields.
78
+ */
79
+ function parseIntraBehavior(behavior) {
80
+ var inputAttachment = null;
81
+ var inputState = null;
82
+ if (typeof behavior.input === 'string') {
83
+ var _behavior$input$split3 = behavior.input.split('.');
84
+ var _behavior$input$split4 = _slicedToArray(_behavior$input$split3, 2);
85
+ inputAttachment = _behavior$input$split4[0];
86
+ inputState = _behavior$input$split4[1];
87
+ } else if (behavior.input && _typeof(behavior.input) === 'object') {
88
+ inputAttachment = behavior.input.attachment || null;
89
+ inputState = behavior.input.state || null;
90
+ }
91
+ var outputs = (behavior.outputs || []).map(function (out) {
92
+ if (typeof out === 'string') {
93
+ var _out$split3 = out.split('.'),
94
+ _out$split4 = _slicedToArray(_out$split3, 2),
95
+ attachment = _out$split4[0],
96
+ state = _out$split4[1];
97
+ return {
98
+ attachment: attachment,
99
+ state: state
100
+ };
101
+ }
102
+ return {
103
+ attachment: (out === null || out === void 0 ? void 0 : out.attachment) || null,
104
+ state: (out === null || out === void 0 ? void 0 : out.state) || null
105
+ };
106
+ });
107
+ return {
108
+ id: behavior.id || '',
109
+ inputAttachment: inputAttachment || null,
110
+ inputState: inputState || null,
111
+ outputs: outputs.length ? outputs : [{
112
+ attachment: null,
113
+ state: null
114
+ }]
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Build cross-component behavior with component instance scoping.
120
+ */
121
+ function buildCrossBehavior(_ref2) {
122
+ var id = _ref2.id,
123
+ inputEndpoint = _ref2.inputEndpoint,
124
+ inputState = _ref2.inputState,
125
+ outputs = _ref2.outputs;
126
+ var _split = (inputEndpoint || '').split('::'),
127
+ _split2 = _slicedToArray(_split, 2),
128
+ inputComponent = _split2[0],
129
+ inputAttachment = _split2[1];
130
+ return {
131
+ id: id || "behavior-".concat(Date.now()),
132
+ input: {
133
+ component: inputComponent,
134
+ attachment: inputAttachment,
135
+ state: inputState
136
+ },
137
+ outputs: outputs.filter(function (o) {
138
+ return o.endpoint && o.state;
139
+ }).map(function (o) {
140
+ var _o$endpoint$split = o.endpoint.split('::'),
141
+ _o$endpoint$split2 = _slicedToArray(_o$endpoint$split, 2),
142
+ component = _o$endpoint$split2[0],
143
+ attachment = _o$endpoint$split2[1];
144
+ return {
145
+ component: component,
146
+ attachment: attachment,
147
+ state: o.state
148
+ };
149
+ })
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Parse cross-component behavior into form fields.
155
+ */
156
+ function parseCrossBehavior(behavior) {
157
+ var inputComponent, inputAttachment, inputState;
158
+ if (typeof behavior.input === 'string') {
159
+ var _behavior$input$split5 = behavior.input.split('.'),
160
+ _behavior$input$split6 = _slicedToArray(_behavior$input$split5, 2),
161
+ attachment = _behavior$input$split6[0],
162
+ state = _behavior$input$split6[1];
163
+ inputAttachment = attachment;
164
+ inputState = state;
165
+ inputComponent = null;
166
+ } else if (behavior.input) {
167
+ inputComponent = behavior.input.component;
168
+ inputAttachment = behavior.input.attachment;
169
+ inputState = behavior.input.state;
170
+ }
171
+ var rawOutputs = behavior.outputs || (behavior.output ? [behavior.output] : behavior._outputs || []);
172
+ var outputs = rawOutputs.map(function (out) {
173
+ if (typeof out === 'string') {
174
+ var _out$split5 = out.split('.'),
175
+ _out$split6 = _slicedToArray(_out$split5, 2),
176
+ _attachment3 = _out$split6[0],
177
+ _state3 = _out$split6[1];
178
+ return {
179
+ endpoint: _attachment3 ? "::".concat(_attachment3) : null,
180
+ state: _state3
181
+ };
182
+ }
183
+ var endpoint = out.component && out.attachment ? "".concat(out.component, "::").concat(out.attachment) : out.attachment ? "::".concat(out.attachment) : null;
184
+ return {
185
+ endpoint: endpoint,
186
+ state: out.state || null
187
+ };
188
+ });
189
+ var inputEndpoint = inputComponent && inputAttachment ? "".concat(inputComponent, "::").concat(inputAttachment) : inputAttachment ? "::".concat(inputAttachment) : null;
190
+ return {
191
+ id: behavior.id || '',
192
+ inputEndpoint: inputEndpoint,
193
+ inputState: inputState || null,
194
+ outputs: outputs.length ? outputs : [{
195
+ endpoint: null,
196
+ state: null
197
+ }]
198
+ };
199
+ }
200
+
201
+ export { buildCrossBehavior, buildIntraBehavior, normalizeBehavior, parseCrossBehavior, parseIntraBehavior };
package/dist/index.d.ts CHANGED
@@ -30,8 +30,9 @@ export interface IoDeviceState {
30
30
  id: string
31
31
  name: string
32
32
  defaultValue: any
33
- stateType: 'binary' | 'number' | 'enum' | 'float' | 'string'
33
+ stateType: 'binary' | 'number' | 'enum' | 'float' | 'string' | 'continuous' | 'boolean'
34
34
  stateConfig?: Record<string, any>
35
+ direction?: 'input' | 'output'
35
36
  }
36
37
 
37
38
  export interface IoDeviceConfig {
@@ -39,11 +40,57 @@ export interface IoDeviceConfig {
39
40
  states: IoDeviceState[]
40
41
  }
41
42
 
43
+ export interface BehaviorMapping {
44
+ stateValue: any
45
+ transform?: { x?: number; y?: number; z?: number }
46
+ rotationTransform?: number
47
+ colorTransform?: string
48
+ }
49
+
50
+ export interface BehaviorEntry {
51
+ name?: string
52
+ meshUuid?: string
53
+ meshName?: string
54
+ stateVariable: string
55
+ stateType?: 'binary' | 'enum' | 'continuous' | 'number' | 'boolean'
56
+ transformTypes?: string[]
57
+ rotAxis?: 'x' | 'y' | 'z'
58
+ rotAxisOffset?: { x: number; y: number; z: number }
59
+ mappingAxis?: 'x' | 'y' | 'z'
60
+ mappings?: BehaviorMapping[]
61
+ }
62
+
63
+ export interface CrossComponentBehavior {
64
+ id?: string
65
+ input: string | { component?: string; attachment: string; state: string }
66
+ output?: string | { component?: string; attachment: string; state: string; child?: string }
67
+ outputs?: Array<string | { component?: string; attachment: string; state: string; child?: string }>
68
+ conditions?: Array<{ expression: string }>
69
+ }
70
+
71
+ export interface IoStateAdapter {
72
+ getState(attachmentId: string, dataPointId: string): any
73
+ setState(attachmentId: string, dataPointId: string, value: any): void
74
+ }
75
+
76
+ export interface SceneIoEndpoint {
77
+ key: string
78
+ parentUuid: string
79
+ parentName: string
80
+ libraryId: string
81
+ attachmentId: string
82
+ attachmentLabel: string
83
+ deviceId: string
84
+ states: string[]
85
+ }
86
+
42
87
  export interface IoDeviceAsset {
43
88
  uuid: string
44
89
  name: string
45
90
  assetType: 'I/O Device'
46
91
  ioConfig: IoDeviceConfig
92
+ behaviorConfig?: BehaviorEntry[]
93
+ meshNameMap?: Record<string, string>
47
94
  }
48
95
 
49
96
  export interface AttachmentDescriptor {
@@ -68,9 +115,105 @@ export interface SmartComponentAsset {
68
115
  attachmentPoint: AttachmentDescriptor['attachmentPoint'] | null
69
116
  attachedAt: string
70
117
  }>
118
+ behaviors?: CrossComponentBehavior[]
71
119
  [key: string]: any
72
120
  }
73
121
 
122
+ export declare function normalizeBehavior(behavior: CrossComponentBehavior): CrossComponentBehavior
123
+ export declare function buildIntraBehavior(form: {
124
+ id?: string
125
+ inputAttachment: string
126
+ inputState: string
127
+ outputs: Array<{ attachment: string; state: string }>
128
+ }): CrossComponentBehavior
129
+ export declare function parseIntraBehavior(behavior: CrossComponentBehavior): {
130
+ id: string
131
+ inputAttachment: string
132
+ inputState: string
133
+ outputs: Array<{ attachment: string; state: string }>
134
+ }
135
+ export declare function buildCrossBehavior(form: {
136
+ id?: string
137
+ inputEndpoint: string
138
+ inputState: string
139
+ outputs: Array<{ endpoint: string; state: string }>
140
+ }): CrossComponentBehavior
141
+ export declare function parseCrossBehavior(behavior: CrossComponentBehavior): {
142
+ id: string
143
+ inputEndpoint: string
144
+ inputState: string
145
+ outputs: Array<{ endpoint: string; state: string }>
146
+ }
147
+
148
+ export declare function getScopedAttachmentKey(attachmentId: string, parentUuid?: string | null): string
149
+ export declare function getIoBehaviorManager(sceneViewer: any): IoBehaviorManager | null
150
+ export declare function resolveDataPoints(
151
+ parentUuid: string,
152
+ attachmentId: string,
153
+ userData: Record<string, any>,
154
+ ioBehaviorManager: IoBehaviorManager | null,
155
+ hitMesh?: any
156
+ ): IoDeviceState[]
157
+
158
+ export declare function registerBehaviorsForComponent(
159
+ ioBehavMgr: IoBehaviorManager,
160
+ componentUuid: string,
161
+ componentData: SmartComponentAsset,
162
+ componentModel: any,
163
+ componentDictionary: Record<string, any>
164
+ ): void
165
+
166
+ export declare function reloadBehaviorsForDeviceAsset(
167
+ ioBehavMgr: IoBehaviorManager,
168
+ deviceAssetId: string,
169
+ componentDictionary: Record<string, any>,
170
+ centralPlant: any
171
+ ): void
172
+
173
+ export declare function scanSceneIoEndpoints(centralPlant: any): SceneIoEndpoint[]
174
+ export declare function applyCrossComponentBehaviors(centralPlant: any, behaviors: CrossComponentBehavior[]): void
175
+ export declare function loadCrossComponentBehaviors(centralPlant: any): CrossComponentBehavior[]
176
+ export declare function refreshSceneIntraBehaviors(centralPlant: any, libraryId: string, behaviors: CrossComponentBehavior[]): void
177
+ export declare function reregisterSceneBehaviors(centralPlant: any): void
178
+
179
+ export declare class IoBehaviorManager {
180
+ constructor(sceneViewer: any)
181
+ loadBehaviors(attachmentId: string, behaviorConfig: BehaviorEntry[] | { behaviors: BehaviorEntry[] }, deviceModelRoot: any, parentUuid: string): void
182
+ triggerState(attachmentId: string, dataPointId: string, value: any, parentUuid?: string | null): void
183
+ setCrossComponentBehaviors(behaviors: CrossComponentBehavior[]): void
184
+ registerComponentBehaviors(componentUuid: string, behaviors: CrossComponentBehavior[]): void
185
+ configure(stateAdapter: IoStateAdapter): void
186
+ getAnimationDataPoints(parentUuid: string, attachmentId: string, hitMesh?: any): IoDeviceState[]
187
+ getAnimatedMeshes(parentUuid: string, attachmentId: string): any[]
188
+ unloadForComponent(parentUuid: string): void
189
+ unloadForAttachment(parentUuid: string, attachmentId: string): void
190
+ resetForScene(): void
191
+ dispose(): void
192
+ }
193
+
194
+ export declare class ComponentTooltipManager {
195
+ constructor(sceneViewer: any)
196
+ configure(stateAdapter: IoStateAdapter): void
197
+ onSelectionChanged(object: any | null): void
198
+ update(): void
199
+ hide(): void
200
+ dispose(): void
201
+ }
202
+
203
+ export interface CentralPlantBehaviorApi {
204
+ configureStateAdapter(adapter: IoStateAdapter): void
205
+ setIoDeviceState(attachmentId: string, stateId: string, value: any, parentUuid?: string | null): void
206
+ mergeBehaviorConfigsIntoDictionary(configsByAssetId: Record<string, {
207
+ behaviorConfig?: BehaviorEntry[]
208
+ meshNameMap?: Record<string, string>
209
+ behaviors?: CrossComponentBehavior[]
210
+ }>): number
211
+ scanSceneIoEndpoints(): SceneIoEndpoint[]
212
+ setSceneBehaviors(behaviors: CrossComponentBehavior[]): void
213
+ getSceneBehaviors(): CrossComponentBehavior[]
214
+ reregisterSceneBehaviors(): void
215
+ }
216
+
74
217
  export interface IoDeviceUsageEntry {
75
218
  componentId: string
76
219
  componentName: string
@@ -140,6 +283,31 @@ export declare class PathFlowManager {
140
283
 
141
284
  // ─── Re-exports ───────────────────────────────────────────────────────────────
142
285
  export { CentralPlant } from './core/centralPlant.js';
286
+ export { IoBehaviorManager } from './managers/behaviors/IoBehaviorManager.js';
287
+ export { ComponentTooltipManager } from './managers/scene/componentTooltipManager.js';
288
+ export {
289
+ normalizeBehavior,
290
+ buildIntraBehavior,
291
+ parseIntraBehavior,
292
+ buildCrossBehavior,
293
+ parseCrossBehavior
294
+ } from './utils/behaviorSchema.js';
295
+ export {
296
+ registerBehaviorsForComponent,
297
+ reloadBehaviorsForDeviceAsset
298
+ } from './utils/behaviorRegistration.js';
299
+ export {
300
+ getScopedAttachmentKey,
301
+ getIoBehaviorManager,
302
+ resolveDataPoints
303
+ } from './utils/behaviorDispatch.js';
304
+ export {
305
+ scanSceneIoEndpoints,
306
+ applyCrossComponentBehaviors,
307
+ loadCrossComponentBehaviors,
308
+ refreshSceneIntraBehaviors,
309
+ reregisterSceneBehaviors
310
+ } from './utils/behaviorSceneUtils.js';
143
311
  export { DebugLogger, logger, transformLogger, pathfinderLogger, modelLogger } from './core/debugLogger.js';
144
312
  export { generateUuidFromName, getHardcodedUuid, findObjectByHardcodedUuid } from './core/nameUtils.js';
145
313
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2112-lab/central-plant",
3
- "version": "0.3.38",
3
+ "version": "0.3.39",
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",