@2112-lab/central-plant 0.1.78 → 0.1.79

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.
@@ -25,6 +25,8 @@ function _interopNamespace(e) {
25
25
 
26
26
  var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
27
27
 
28
+ /** Maximum anisotropy to apply (caps GPU memory for mip storage) */
29
+ var MAX_ANISOTROPY = 4;
28
30
  var TEXTURE_SETS = {
29
31
  // Light metallic texture using the gravel_embedded_concrete with metallic properties
30
32
  light_metal: {
@@ -161,10 +163,29 @@ function _loadTextureSet() {
161
163
  return Promise.all(promises);
162
164
  case 2:
163
165
  loadedTextures = _context2.v;
164
- textureMap = Object.fromEntries(loadedTextures); // Apply texture settings
165
- Object.values(textureMap).forEach(function (texture) {
166
+ textureMap = Object.fromEntries(loadedTextures); // Apply texture settings with memory-conscious defaults
167
+ Object.entries(textureMap).forEach(function (_ref4) {
168
+ var _ref5 = _rollupPluginBabelHelpers.slicedToArray(_ref4, 2),
169
+ type = _ref5[0],
170
+ texture = _ref5[1];
166
171
  texture.wrapS = texture.wrapT = THREE__namespace.RepeatWrapping;
167
172
  texture.repeat.set(textureSet.repeat.x, textureSet.repeat.y);
173
+
174
+ // Correct colour-space: diffuse is sRGB, everything else is linear data
175
+ if (type === 'diffuse') {
176
+ texture.colorSpace = THREE__namespace.SRGBColorSpace;
177
+ } else {
178
+ texture.colorSpace = THREE__namespace.LinearSRGBColorSpace;
179
+ }
180
+
181
+ // Roughness maps don't benefit much from mipmaps — skip them to save GPU memory
182
+ if (type === 'roughness') {
183
+ texture.generateMipmaps = false;
184
+ texture.minFilter = THREE__namespace.LinearFilter;
185
+ }
186
+
187
+ // Cap anisotropy to balance quality vs memory
188
+ texture.anisotropy = MAX_ANISOTROPY;
168
189
  });
169
190
  console.log("Texture set ".concat(setName, " loaded successfully"));
170
191
  return _context2.a(2, {
@@ -74,6 +74,11 @@ var AnimationManager = /*#__PURE__*/function (_BaseDisposable) {
74
74
  sceneViewer.tooltipsManager.render();
75
75
  }
76
76
 
77
+ // Update component tooltip screen position
78
+ if (sceneViewer.componentTooltipManager) {
79
+ sceneViewer.componentTooltipManager.update();
80
+ }
81
+
77
82
  // End performance monitoring frame
78
83
  if (sceneViewer.performanceMonitorManager) {
79
84
  sceneViewer.performanceMonitorManager.endFrame();
@@ -0,0 +1,340 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var _rollupPluginBabelHelpers = require('../../../_virtual/_rollupPluginBabelHelpers.js');
6
+ var THREE = require('three');
7
+ var baseDisposable = require('../../core/baseDisposable.js');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n["default"] = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Inline styles (injected once into the document head)
31
+ // ---------------------------------------------------------------------------
32
+ var TOOLTIP_STYLES = "\n.cp-tooltip {\n position: absolute;\n pointer-events: auto;\n z-index: 10000;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 13px;\n line-height: 1.4;\n user-select: none;\n min-width: 200px;\n max-width: 280px;\n transform: translate(-50%, -100%);\n margin-top: -12px;\n transition: opacity 0.15s ease;\n}\n\n.cp-tooltip__card {\n background: rgba(28, 32, 40, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 10px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);\n overflow: hidden;\n}\n\n/* \u2500\u2500 Header \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\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\u2500\u2500\u2500\u2500\u2500 */\n.cp-tooltip__header {\n padding: 10px 14px;\n font-weight: 600;\n font-size: 14px;\n color: #e8ecf1;\n background: rgba(255, 255, 255, 0.04);\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n letter-spacing: 0.2px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* \u2500\u2500 I/O Devices section \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.cp-tooltip__io-section {\n position: relative;\n}\n\n.cp-tooltip__io-trigger {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 14px;\n color: #a4adba;\n cursor: pointer;\n transition: background 0.12s ease, color 0.12s ease;\n}\n\n.cp-tooltip__io-trigger:hover {\n background: rgba(255, 255, 255, 0.06);\n color: #e8ecf1;\n}\n\n.cp-tooltip__io-trigger-label {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 0.6px;\n font-weight: 500;\n}\n\n.cp-tooltip__io-arrow {\n font-size: 10px;\n transition: transform 0.2s ease;\n}\n\n.cp-tooltip__io-section.expanded .cp-tooltip__io-arrow {\n transform: rotate(90deg);\n}\n\n/* \u2500\u2500 Device list \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\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.cp-tooltip__device-list {\n max-height: 0;\n overflow: hidden;\n transition: max-height 0.25s ease;\n}\n\n.cp-tooltip__io-section.expanded .cp-tooltip__device-list {\n max-height: 300px;\n}\n\n.cp-tooltip__device-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 14px 6px 20px;\n color: #c1c8d1;\n font-size: 12px;\n border-top: 1px solid rgba(255, 255, 255, 0.04);\n transition: background 0.1s ease;\n}\n\n.cp-tooltip__device-item:hover {\n background: rgba(255, 255, 255, 0.04);\n}\n\n.cp-tooltip__device-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #4fc3f7;\n flex-shrink: 0;\n}\n\n.cp-tooltip__device-name {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* \u2500\u2500 Empty state \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\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.cp-tooltip__no-devices {\n padding: 8px 14px;\n color: #6b7280;\n font-size: 12px;\n font-style: italic;\n}\n\n/* \u2500\u2500 Caret arrow pointing down \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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.cp-tooltip__caret {\n width: 0;\n height: 0;\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-top: 7px solid rgba(28, 32, 40, 0.95);\n margin: 0 auto;\n position: relative;\n top: -1px;\n}\n";
33
+ var ComponentTooltipManager = /*#__PURE__*/function (_BaseDisposable) {
34
+ /**
35
+ * @param {Object} sceneViewer - The sceneViewer instance
36
+ */
37
+ function ComponentTooltipManager(sceneViewer) {
38
+ var _this;
39
+ _rollupPluginBabelHelpers.classCallCheck(this, ComponentTooltipManager);
40
+ _this = _rollupPluginBabelHelpers.callSuper(this, ComponentTooltipManager);
41
+ _this.sceneViewer = sceneViewer;
42
+ _this.selectedObject = null;
43
+ _this.tooltipEl = null;
44
+ _this._styleInjected = false;
45
+ _this._ioExpanded = false;
46
+ _this._injectStyles();
47
+ return _this;
48
+ }
49
+
50
+ // -----------------------------------------------------------------------
51
+ // Lifecycle
52
+ // -----------------------------------------------------------------------
53
+
54
+ /**
55
+ * Called automatically by BaseDisposable.dispose()
56
+ * @override
57
+ */
58
+ _rollupPluginBabelHelpers.inherits(ComponentTooltipManager, _BaseDisposable);
59
+ return _rollupPluginBabelHelpers.createClass(ComponentTooltipManager, [{
60
+ key: "_doDispose",
61
+ value: function _doDispose() {
62
+ this.hide();
63
+ this._removeStyleTag();
64
+ this.nullifyProperties('sceneViewer', 'selectedObject', 'tooltipEl');
65
+ }
66
+
67
+ // -----------------------------------------------------------------------
68
+ // Public API
69
+ // -----------------------------------------------------------------------
70
+
71
+ /**
72
+ * Should be called when an object is selected or deselected.
73
+ * @param {THREE.Object3D|null} object
74
+ */
75
+ }, {
76
+ key: "onSelectionChanged",
77
+ value: function onSelectionChanged(object) {
78
+ var _object$userData;
79
+ if (!object) {
80
+ this.hide();
81
+ return;
82
+ }
83
+ var objectType = (_object$userData = object.userData) === null || _object$userData === void 0 ? void 0 : _object$userData.objectType;
84
+ if (objectType !== 'component') {
85
+ this.hide();
86
+ return;
87
+ }
88
+ this.selectedObject = object;
89
+ this._ioExpanded = false;
90
+ this._buildTooltip(object);
91
+ }
92
+
93
+ /**
94
+ * Update tooltip screen position. Call once per frame (from the animation loop).
95
+ */
96
+ }, {
97
+ key: "update",
98
+ value: function update() {
99
+ if (!this.tooltipEl || !this.selectedObject) return;
100
+ this._positionTooltip();
101
+ }
102
+
103
+ /**
104
+ * Remove the tooltip from the DOM.
105
+ */
106
+ }, {
107
+ key: "hide",
108
+ value: function hide() {
109
+ if (this.tooltipEl) {
110
+ this.tooltipEl.remove();
111
+ this.tooltipEl = null;
112
+ }
113
+ this.selectedObject = null;
114
+ this._ioExpanded = false;
115
+ }
116
+
117
+ // -----------------------------------------------------------------------
118
+ // Internal — build
119
+ // -----------------------------------------------------------------------
120
+
121
+ /** Inject the stylesheet once into the document head */
122
+ }, {
123
+ key: "_injectStyles",
124
+ value: function _injectStyles() {
125
+ if (this._styleInjected) return;
126
+ this._styleTag = document.createElement('style');
127
+ this._styleTag.setAttribute('data-cp-tooltip', '');
128
+ this._styleTag.textContent = TOOLTIP_STYLES;
129
+ document.head.appendChild(this._styleTag);
130
+ this._styleInjected = true;
131
+ this.registerDOMElement(this._styleTag);
132
+ }
133
+
134
+ /** Remove the injected stylesheet */
135
+ }, {
136
+ key: "_removeStyleTag",
137
+ value: function _removeStyleTag() {
138
+ if (this._styleTag && this._styleTag.parentNode) {
139
+ this._styleTag.parentNode.removeChild(this._styleTag);
140
+ }
141
+ this._styleTag = null;
142
+ this._styleInjected = false;
143
+ }
144
+
145
+ /**
146
+ * Gather I/O device children from a component's Three.js hierarchy.
147
+ * @param {THREE.Object3D} object
148
+ * @returns {{ label: string, deviceId: string }[]}
149
+ */
150
+ }, {
151
+ key: "_getIODevices",
152
+ value: function _getIODevices(object) {
153
+ var devices = [];
154
+ object.traverse(function (child) {
155
+ var _child$userData;
156
+ if (((_child$userData = child.userData) === null || _child$userData === void 0 ? void 0 : _child$userData.objectType) === 'io-device') {
157
+ devices.push({
158
+ label: child.userData.attachmentLabel || child.name || child.userData.deviceId || 'Unknown Device',
159
+ deviceId: child.userData.deviceId || ''
160
+ });
161
+ }
162
+ });
163
+ return devices;
164
+ }
165
+
166
+ /**
167
+ * Build and show the tooltip for the given object.
168
+ * @param {THREE.Object3D} object
169
+ */
170
+ }, {
171
+ key: "_buildTooltip",
172
+ value: function _buildTooltip(object) {
173
+ var _this2 = this;
174
+ // Remove any existing tooltip first
175
+ this.hide();
176
+ // Re-assign selected object since hide() clears it
177
+ this.selectedObject = object;
178
+ var container = this._getContainer();
179
+ if (!container) return;
180
+
181
+ // Ensure the container supports absolute positioning
182
+ var containerStyle = window.getComputedStyle(container);
183
+ if (containerStyle.position === 'static') {
184
+ container.style.position = 'relative';
185
+ }
186
+
187
+ // Gather data — extract the friendly name, stripping the " (component-id)" suffix
188
+ var rawName = object.name || '';
189
+ var componentName = rawName.replace(/\s*\([^)]*\)\s*$/, '') || 'Unnamed Component';
190
+ var devices = this._getIODevices(object);
191
+ var isSmart = devices.length > 0;
192
+
193
+ // Root element
194
+ var root = document.createElement('div');
195
+ root.className = 'cp-tooltip';
196
+
197
+ // Card
198
+ var card = document.createElement('div');
199
+ card.className = 'cp-tooltip__card';
200
+
201
+ // Header
202
+ var header = document.createElement('div');
203
+ header.className = 'cp-tooltip__header';
204
+ header.textContent = componentName;
205
+ card.appendChild(header);
206
+
207
+ // I/O Devices section
208
+ if (isSmart) {
209
+ var ioSection = document.createElement('div');
210
+ ioSection.className = 'cp-tooltip__io-section';
211
+
212
+ // Trigger row
213
+ var trigger = document.createElement('div');
214
+ trigger.className = 'cp-tooltip__io-trigger';
215
+ var label = document.createElement('span');
216
+ label.className = 'cp-tooltip__io-trigger-label';
217
+ label.textContent = "I/O Devices (".concat(devices.length, ")");
218
+ var arrow = document.createElement('span');
219
+ arrow.className = 'cp-tooltip__io-arrow';
220
+ arrow.textContent = '▶';
221
+ trigger.appendChild(label);
222
+ trigger.appendChild(arrow);
223
+
224
+ // Device list
225
+ var list = document.createElement('div');
226
+ list.className = 'cp-tooltip__device-list';
227
+ devices.forEach(function (device) {
228
+ var item = document.createElement('div');
229
+ item.className = 'cp-tooltip__device-item';
230
+ var dot = document.createElement('span');
231
+ dot.className = 'cp-tooltip__device-dot';
232
+ var name = document.createElement('span');
233
+ name.className = 'cp-tooltip__device-name';
234
+ name.textContent = device.label;
235
+ item.appendChild(dot);
236
+ item.appendChild(name);
237
+ list.appendChild(item);
238
+ });
239
+ ioSection.appendChild(trigger);
240
+ ioSection.appendChild(list);
241
+
242
+ // Hover expand/collapse
243
+ trigger.addEventListener('mouseenter', function () {
244
+ ioSection.classList.add('expanded');
245
+ _this2._ioExpanded = true;
246
+ });
247
+ ioSection.addEventListener('mouseleave', function () {
248
+ ioSection.classList.remove('expanded');
249
+ _this2._ioExpanded = false;
250
+ });
251
+ card.appendChild(ioSection);
252
+ } else {
253
+ // Non-smart: show empty state
254
+ var noDevices = document.createElement('div');
255
+ noDevices.className = 'cp-tooltip__no-devices';
256
+ noDevices.textContent = 'No I/O devices attached';
257
+ card.appendChild(noDevices);
258
+ }
259
+ root.appendChild(card);
260
+
261
+ // Caret
262
+ var caret = document.createElement('div');
263
+ caret.className = 'cp-tooltip__caret';
264
+ root.appendChild(caret);
265
+
266
+ // Prevent clicks on tooltip from propagating to the scene
267
+ root.addEventListener('pointerdown', function (e) {
268
+ return e.stopPropagation();
269
+ });
270
+ root.addEventListener('click', function (e) {
271
+ return e.stopPropagation();
272
+ });
273
+
274
+ // Add to the renderer container so coordinates are relative to the viewport
275
+ container.appendChild(root);
276
+ this.tooltipEl = root;
277
+
278
+ // Initial positioning
279
+ this._positionTooltip();
280
+ }
281
+
282
+ // -----------------------------------------------------------------------
283
+ // Internal — positioning
284
+ // -----------------------------------------------------------------------
285
+
286
+ /**
287
+ * Project the selected object's world position to screen space and
288
+ * position the tooltip element accordingly.
289
+ */
290
+ }, {
291
+ key: "_positionTooltip",
292
+ value: function _positionTooltip() {
293
+ var _this$sceneViewer, _this$sceneViewer2;
294
+ if (!this.tooltipEl || !this.selectedObject) return;
295
+ var container = this._getContainer();
296
+ var camera = (_this$sceneViewer = this.sceneViewer) === null || _this$sceneViewer === void 0 ? void 0 : _this$sceneViewer.camera;
297
+ var renderer = (_this$sceneViewer2 = this.sceneViewer) === null || _this$sceneViewer2 === void 0 ? void 0 : _this$sceneViewer2.renderer;
298
+ if (!container || !camera || !renderer) return;
299
+
300
+ // Compute bounding box to position above the component
301
+ var box = new THREE__namespace.Box3().setFromObject(this.selectedObject);
302
+ var center = box.getCenter(new THREE__namespace.Vector3());
303
+ // Use top of bounding box (Z-up)
304
+ var topZ = box.max.z;
305
+ var worldPos = new THREE__namespace.Vector3(center.x, center.y, topZ);
306
+
307
+ // Project to NDC
308
+ var ndc = worldPos.clone().project(camera);
309
+
310
+ // NDC to pixel coords relative to the container
311
+ var rect = container.getBoundingClientRect();
312
+ var x = (ndc.x + 1) / 2 * rect.width;
313
+ var y = (-ndc.y + 1) / 2 * rect.height;
314
+
315
+ // Hide if behind camera
316
+ if (ndc.z > 1) {
317
+ this.tooltipEl.style.display = 'none';
318
+ return;
319
+ }
320
+ this.tooltipEl.style.display = '';
321
+ this.tooltipEl.style.left = "".concat(x, "px");
322
+ this.tooltipEl.style.top = "".concat(y, "px");
323
+ }
324
+
325
+ /**
326
+ * Get the DOM element where the Three.js canvas currently lives.
327
+ * In QuadViewport the canvas is moved out of the original hidden
328
+ * container, so we must use the renderer's actual parent element.
329
+ * @returns {HTMLElement|null}
330
+ */
331
+ }, {
332
+ key: "_getContainer",
333
+ value: function _getContainer() {
334
+ var _this$sceneViewer3;
335
+ return ((_this$sceneViewer3 = this.sceneViewer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.renderer) === null || _this$sceneViewer3 === void 0 || (_this$sceneViewer3 = _this$sceneViewer3.domElement) === null || _this$sceneViewer3 === void 0 ? void 0 : _this$sceneViewer3.parentElement) || null;
336
+ }
337
+ }]);
338
+ }(baseDisposable.BaseDisposable);
339
+
340
+ exports.ComponentTooltipManager = ComponentTooltipManager;
@@ -15,7 +15,7 @@ var CentralPlant = /*#__PURE__*/function (_BaseDisposable) {
15
15
  * Initialize the CentralPlant manager
16
16
  *
17
17
  * @constructor
18
- * @version 0.1.78
18
+ * @version 0.1.79
19
19
  * @updated 2025-10-22
20
20
  *
21
21
  * @description Creates a new CentralPlant instance and initializes internal managers and utilities.
@@ -19,6 +19,7 @@ import { AnimationManager } from '../managers/scene/animationManager.js';
19
19
  import { CameraControlsManager } from '../managers/controls/cameraControlsManager.js';
20
20
  import { ComponentDragManager } from '../managers/controls/componentDragManager.js';
21
21
  import { SceneTooltipsManager } from '../managers/scene/sceneTooltipsManager.js';
22
+ import { ComponentTooltipManager } from '../managers/scene/componentTooltipManager.js';
22
23
  import { Viewport2DManager } from '../managers/scene/viewport2DManager.js';
23
24
  import { generateUuidFromName, getHardcodedUuid, findObjectByHardcodedUuid, generateUniqueComponentId } from '../utils/nameUtils.js';
24
25
  import modelPreloader from '../rendering/modelPreloader.js';
@@ -151,6 +152,11 @@ var CentralPlantInternals = /*#__PURE__*/function () {
151
152
  this.centralPlant.managers.tooltipsManager = new SceneTooltipsManager(this.centralPlant.sceneViewer.$refs.container, this.centralPlant.sceneViewer.camera, this.centralPlant.sceneViewer.scene);
152
153
  this.centralPlant.sceneViewer.tooltipsManager = this.centralPlant.managers.tooltipsManager;
153
154
  console.log('🔍 Tooltip manager initialized:', this.centralPlant.managers.tooltipsManager);
155
+
156
+ // Initialize the component tooltip manager (screen-space tooltip on selection)
157
+ this.centralPlant.managers.componentTooltipManager = new ComponentTooltipManager(this.centralPlant.sceneViewer);
158
+ this.centralPlant.sceneViewer.componentTooltipManager = this.centralPlant.managers.componentTooltipManager;
159
+ console.log('🔍 Component tooltip manager initialized');
154
160
  }
155
161
  }
156
162
 
@@ -98,7 +98,7 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
98
98
  this.centralPlant.attachToComponent();
99
99
 
100
100
  // Sync our managers tracking object after attachment
101
- managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager']; // Populate our managers tracking object
101
+ managerKeys = ['threeJSResourceManager', 'performanceMonitorManager', 'settingsManager', 'sceneExportManager', 'componentManager', 'sceneInitializationManager', 'environmentManager', 'keyboardControlsManager', 'pathfindingManager', 'sceneOperationsManager', 'animationManager', 'cameraControlsManager', 'componentDragManager', 'tooltipsManager', 'componentTooltipManager']; // Populate our managers tracking object
102
102
  managerKeys.forEach(function (key) {
103
103
  if (_this2[key]) {
104
104
  _this2.managers[key] = _this2[key];
@@ -376,6 +376,10 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
376
376
  this.transformManager.on({
377
377
  onModeChange: this.onModeChange.bind(this),
378
378
  onSelectionChanged: function onSelectionChanged(object) {
379
+ // Update the component tooltip on selection changes
380
+ if (_this4.componentTooltipManager) {
381
+ _this4.componentTooltipManager.onSelectionChanged(object);
382
+ }
379
383
  if (object) {
380
384
  var _object$userData;
381
385
  // Object selected
@@ -801,6 +805,12 @@ var sceneViewer = /*#__PURE__*/function (_BaseDisposable) {
801
805
  this.tooltipsManager = null;
802
806
  }
803
807
 
808
+ // Clean up component tooltip manager
809
+ if (this.componentTooltipManager) {
810
+ this.componentTooltipManager.dispose('ComponentTooltipManager');
811
+ this.componentTooltipManager = null;
812
+ }
813
+
804
814
  // Fallback cleanup if Three.js resource manager not available
805
815
  if (!this.threeJSResourceManager) {
806
816
  // Fallback cleanup if disposal manager not available
@@ -7,6 +7,7 @@ export { SceneInitializationManager } from './managers/scene/sceneInitialization
7
7
  export { SceneOperationsManager } from './managers/scene/sceneOperationsManager.js';
8
8
  export { SceneExportManager } from './managers/scene/sceneExportManager.js';
9
9
  export { SceneTooltipsManager } from './managers/scene/sceneTooltipsManager.js';
10
+ export { ComponentTooltipManager } from './managers/scene/componentTooltipManager.js';
10
11
  export { SceneHierarchyManager } from './managers/scene/sceneHierarchyManager.js';
11
12
  export { ComponentManager } from './managers/components/componentManager.js';
12
13
  export { AnimationManager } from './managers/scene/animationManager.js';