claude-mpm 4.2.44__py3-none-any.whl → 4.2.51__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +43 -1
- claude_mpm/agents/INSTRUCTIONS.md +75 -1
- claude_mpm/agents/WORKFLOW.md +46 -1
- claude_mpm/agents/frontmatter_validator.py +20 -12
- claude_mpm/agents/templates/nextjs_engineer.json +277 -0
- claude_mpm/agents/templates/python_engineer.json +289 -0
- claude_mpm/agents/templates/react_engineer.json +11 -3
- claude_mpm/agents/templates/security.json +50 -9
- claude_mpm/cli/commands/agents.py +2 -2
- claude_mpm/cli/commands/uninstall.py +1 -2
- claude_mpm/cli/interactive/agent_wizard.py +3 -3
- claude_mpm/cli/parsers/agent_manager_parser.py +3 -3
- claude_mpm/cli/parsers/agents_parser.py +1 -1
- claude_mpm/constants.py +1 -1
- claude_mpm/core/error_handler.py +2 -4
- claude_mpm/core/file_utils.py +4 -12
- claude_mpm/core/log_manager.py +8 -5
- claude_mpm/core/logger.py +1 -1
- claude_mpm/core/logging_utils.py +6 -6
- claude_mpm/core/unified_agent_registry.py +18 -4
- claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
- claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
- claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
- claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
- claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
- claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
- claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
- claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
- claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
- claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
- claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
- claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
- claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
- claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
- claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
- claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
- claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
- claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
- claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
- claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
- claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
- claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
- claude_mpm/dashboard/static/built/components/code-viewer.js +2 -1076
- claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
- claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
- claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
- claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
- claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
- claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
- claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -465
- claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/built/connection-manager.js +536 -0
- claude_mpm/dashboard/static/built/dashboard.js +1 -1
- claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/built/react/events.js +30 -0
- claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
- claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
- claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
- claude_mpm/dashboard/static/built/shared/logger.js +385 -0
- claude_mpm/dashboard/static/built/shared/page-structure.js +251 -0
- claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
- claude_mpm/dashboard/static/built/socket-client.js +1 -1
- claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/css/dashboard.css +28 -5
- claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
- claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
- claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
- claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
- claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
- claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
- claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
- claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
- claude_mpm/dashboard/static/dist/dashboard.js +1 -1
- claude_mpm/dashboard/static/dist/react/events.js +30 -0
- claude_mpm/dashboard/static/dist/socket-client.js +1 -1
- claude_mpm/dashboard/static/events.html +607 -0
- claude_mpm/dashboard/static/index.html +713 -0
- claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
- claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
- claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
- claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
- claude_mpm/dashboard/static/js/components/code-viewer.js +306 -66
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
- claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
- claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
- claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +285 -85
- claude_mpm/dashboard/static/js/components/working-directory.js +3 -0
- claude_mpm/dashboard/static/js/dashboard.js +61 -33
- claude_mpm/dashboard/static/js/socket-client.js +12 -8
- claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
- claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
- claude_mpm/dashboard/static/legacy/activity.html +736 -0
- claude_mpm/dashboard/static/legacy/agents.html +786 -0
- claude_mpm/dashboard/static/legacy/files.html +747 -0
- claude_mpm/dashboard/static/legacy/tools.html +831 -0
- claude_mpm/dashboard/static/monitors-index.html +218 -0
- claude_mpm/dashboard/static/monitors.html +431 -0
- claude_mpm/dashboard/static/production/events.html +659 -0
- claude_mpm/dashboard/static/production/main.html +715 -0
- claude_mpm/dashboard/static/production/monitors.html +483 -0
- claude_mpm/dashboard/static/socket.io.min.js +7 -0
- claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
- claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
- claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
- claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
- claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
- claude_mpm/dashboard/templates/index.html +79 -9
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +1 -1
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +25 -8
- claude_mpm/services/agents/deployment/agent_validator.py +3 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +13 -4
- claude_mpm/services/agents/local_template_manager.py +2 -6
- claude_mpm/services/monitor/daemon.py +1 -2
- claude_mpm/services/monitor/daemon_manager.py +2 -5
- claude_mpm/services/monitor/event_emitter.py +2 -2
- claude_mpm/services/monitor/handlers/code_analysis.py +4 -6
- claude_mpm/services/monitor/handlers/hooks.py +2 -4
- claude_mpm/services/monitor/server.py +27 -4
- claude_mpm/tools/code_tree_analyzer.py +2 -2
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/METADATA +1 -1
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/RECORD +146 -81
- claude_mpm/dashboard/static/test-browser-monitor.html +0 -470
- claude_mpm/dashboard/static/test-simple.html +0 -97
- /claude_mpm/dashboard/static/{test_debug.html → test-archive/test_debug.html} +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/WHEEL +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.2.44.dist-info → claude_mpm-4.2.51.dist-info}/top_level.txt +0 -0
@@ -41,6 +41,14 @@ class EventViewer {
|
|
41
41
|
this.socketClient.onEventUpdate((events, sessions) => {
|
42
42
|
// Ensure we always have a valid events array
|
43
43
|
this.events = Array.isArray(events) ? events : [];
|
44
|
+
console.log('[EventViewer] Events updated - received:', this.events.length);
|
45
|
+
|
46
|
+
// Update debug metrics
|
47
|
+
const debugReceivedEl = document.getElementById('debug-events-received');
|
48
|
+
if (debugReceivedEl) {
|
49
|
+
debugReceivedEl.textContent = this.events.length;
|
50
|
+
}
|
51
|
+
|
44
52
|
this.updateDisplay();
|
45
53
|
});
|
46
54
|
}
|
@@ -221,8 +229,29 @@ class EventViewer {
|
|
221
229
|
* Render events in the UI
|
222
230
|
*/
|
223
231
|
renderEvents() {
|
224
|
-
|
225
|
-
|
232
|
+
// CRITICAL FIX: Use the container passed to constructor, not hardcoded events-list
|
233
|
+
// This prevents events from being rendered in the wrong tab
|
234
|
+
const eventsList = this.container;
|
235
|
+
if (!eventsList) {
|
236
|
+
console.warn('[EventViewer] Container not found, skipping render');
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
|
240
|
+
// SAFETY: Basic check to ensure we're rendering to the correct container
|
241
|
+
if (eventsList.id !== 'events-list') {
|
242
|
+
console.error('[EventViewer] CRITICAL: Attempting to render to wrong container:', eventsList.id);
|
243
|
+
return;
|
244
|
+
}
|
245
|
+
|
246
|
+
// Check if events tab exists and render regardless of active state
|
247
|
+
// This allows events to be pre-rendered when tab becomes active
|
248
|
+
const eventsTab = document.getElementById('events-tab');
|
249
|
+
if (!eventsTab) {
|
250
|
+
console.warn('[EventViewer] Events tab not found in DOM');
|
251
|
+
return;
|
252
|
+
}
|
253
|
+
|
254
|
+
console.log('[EventViewer] Rendering events - count:', this.filteredEvents.length);
|
226
255
|
|
227
256
|
// Check if user is at bottom BEFORE rendering (for autoscroll decision)
|
228
257
|
const wasAtBottom = (eventsList.scrollTop + eventsList.clientHeight >= eventsList.scrollHeight - 10);
|
@@ -268,6 +297,14 @@ class EventViewer {
|
|
268
297
|
// Update filtered elements reference
|
269
298
|
this.filteredEventElements = Array.from(eventsList.querySelectorAll('.event-item'));
|
270
299
|
|
300
|
+
console.log('[EventViewer] Events rendered - filtered:', this.filteredEvents.length, 'elements:', this.filteredEventElements.length);
|
301
|
+
|
302
|
+
// Update debug metrics
|
303
|
+
const debugRenderedEl = document.getElementById('debug-events-rendered');
|
304
|
+
if (debugRenderedEl) {
|
305
|
+
debugRenderedEl.textContent = this.filteredEvents.length;
|
306
|
+
}
|
307
|
+
|
271
308
|
// Update Dashboard navigation items if we're in the events tab
|
272
309
|
if (window.dashboard && window.dashboard.currentTab === 'events' &&
|
273
310
|
window.dashboard.tabNavigation && window.dashboard.tabNavigation.events) {
|
@@ -316,14 +316,24 @@ class FileToolTracker {
|
|
316
316
|
* @returns {boolean} - True if tool operation
|
317
317
|
*/
|
318
318
|
isToolOperation(event) {
|
319
|
-
// Tool operations have tool_name
|
319
|
+
// Tool operations have tool_name - be more inclusive about event types
|
320
320
|
// Check both top-level and data.tool_name for compatibility
|
321
321
|
const hasToolName = event.tool_name || (event.data && event.data.tool_name);
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
322
|
+
|
323
|
+
// Accept multiple event types that might contain tool operations
|
324
|
+
const validEventTypes = ['hook', 'tool_use', 'tool', 'agent', 'response'];
|
325
|
+
const isValidEventType = validEventTypes.includes(event.type) ||
|
326
|
+
(event.type && event.type.includes('tool'));
|
327
|
+
|
328
|
+
// Check for tool-related subtypes or any indication this is a tool operation
|
329
|
+
const isToolSubtype = event.subtype === 'pre_tool' ||
|
330
|
+
event.subtype === 'post_tool' ||
|
331
|
+
(event.subtype && typeof event.subtype === 'string' && event.subtype.includes('tool')) ||
|
332
|
+
event.type === 'tool_use' ||
|
333
|
+
event.type === 'tool';
|
334
|
+
|
335
|
+
// If it has a tool_name and either a valid event type or tool subtype, it's a tool operation
|
336
|
+
return hasToolName && (isValidEventType || isToolSubtype);
|
327
337
|
}
|
328
338
|
|
329
339
|
/**
|
@@ -332,14 +342,20 @@ class FileToolTracker {
|
|
332
342
|
* @returns {boolean} - True if file operation
|
333
343
|
*/
|
334
344
|
isFileOperation(event) {
|
335
|
-
// File operations are
|
345
|
+
// File operations are events with file-related tools - be more inclusive
|
336
346
|
// Check both top-level and data for tool_name
|
337
347
|
let toolName = event.tool_name || (event.data && event.data.tool_name) || '';
|
348
|
+
|
349
|
+
// If no tool name, not a file operation
|
350
|
+
if (!toolName) {
|
351
|
+
return false;
|
352
|
+
}
|
353
|
+
|
338
354
|
toolName = toolName.toLowerCase();
|
339
|
-
|
355
|
+
|
340
356
|
// Check case-insensitively since tool names can come in different cases
|
341
357
|
const fileTools = ['read', 'write', 'edit', 'grep', 'multiedit', 'glob', 'ls', 'bash', 'notebookedit'];
|
342
|
-
|
358
|
+
|
343
359
|
// Get tool parameters from either location
|
344
360
|
const toolParams = event.tool_parameters || (event.data && event.data.tool_parameters);
|
345
361
|
|
@@ -352,7 +368,8 @@ class FileToolTracker {
|
|
352
368
|
}
|
353
369
|
}
|
354
370
|
|
355
|
-
|
371
|
+
// If it's a file tool, it's a file operation regardless of event type
|
372
|
+
return fileTools.includes(toolName);
|
356
373
|
}
|
357
374
|
|
358
375
|
/**
|
@@ -702,3 +719,6 @@ class FileToolTracker {
|
|
702
719
|
// ES6 Module export
|
703
720
|
export { FileToolTracker };
|
704
721
|
export default FileToolTracker;
|
722
|
+
|
723
|
+
// Make FileToolTracker globally available for dist/dashboard.js
|
724
|
+
window.FileToolTracker = FileToolTracker;
|
@@ -362,3 +362,7 @@ class SocketManager {
|
|
362
362
|
// ES6 Module export
|
363
363
|
export { SocketManager };
|
364
364
|
export default SocketManager;
|
365
|
+
|
366
|
+
// Make SocketManager globally available for the dist/dashboard.js
|
367
|
+
// This ensures compatibility with the minified version
|
368
|
+
window.SocketManager = SocketManager;
|
@@ -14,6 +14,9 @@
|
|
14
14
|
*/
|
15
15
|
class UIStateManager {
|
16
16
|
constructor() {
|
17
|
+
// Switching lock to prevent race conditions
|
18
|
+
this._switching = false;
|
19
|
+
|
17
20
|
// Hash to tab mapping
|
18
21
|
this.hashToTab = {
|
19
22
|
'#events': 'events',
|
@@ -78,6 +81,7 @@ class UIStateManager {
|
|
78
81
|
*/
|
79
82
|
setupEventHandlers() {
|
80
83
|
this.setupHashNavigation();
|
84
|
+
this.setupTabClickHandlers(); // Add explicit tab click handlers
|
81
85
|
this.setupUnifiedKeyboardNavigation();
|
82
86
|
}
|
83
87
|
|
@@ -103,8 +107,23 @@ class UIStateManager {
|
|
103
107
|
*/
|
104
108
|
handleHashChange() {
|
105
109
|
const hash = window.location.hash || '';
|
110
|
+
console.log('[Hash Navigation] DETAILED DEBUG:');
|
111
|
+
console.log('[Hash Navigation] - Current hash:', hash);
|
112
|
+
console.log('[Hash Navigation] - hashToTab mapping:', this.hashToTab);
|
113
|
+
console.log('[Hash Navigation] - Direct lookup result:', this.hashToTab[hash]);
|
114
|
+
console.log('[Hash Navigation] - Is hash in mapping?', hash in this.hashToTab);
|
115
|
+
console.log('[Hash Navigation] - Hash length:', hash.length);
|
116
|
+
console.log('[Hash Navigation] - Hash char codes:', hash.split('').map(c => c.charCodeAt(0)));
|
117
|
+
|
106
118
|
const tabName = this.hashToTab[hash] || 'events';
|
107
|
-
console.log('[Hash Navigation]
|
119
|
+
console.log('[Hash Navigation] Final resolved tab name:', tabName);
|
120
|
+
|
121
|
+
// Special logging for File Tree tab
|
122
|
+
if (tabName === 'claude-tree' || hash === '#file_tree') {
|
123
|
+
console.log('[UIStateManager] FILE TREE TAB SELECTED via hash:', hash);
|
124
|
+
console.log('[UIStateManager] Tab name resolved to:', tabName);
|
125
|
+
}
|
126
|
+
|
108
127
|
this.switchTab(tabName, false); // false = don't update hash (we're responding to hash change)
|
109
128
|
}
|
110
129
|
|
@@ -116,6 +135,36 @@ class UIStateManager {
|
|
116
135
|
console.log('[Hash Navigation] setupTabNavigation is deprecated - using hash navigation instead');
|
117
136
|
}
|
118
137
|
|
138
|
+
/**
|
139
|
+
* Set up explicit click handlers for tab buttons to ensure proper routing
|
140
|
+
* This ensures tab clicks work even if other modules interfere
|
141
|
+
*/
|
142
|
+
setupTabClickHandlers() {
|
143
|
+
document.querySelectorAll('.tab-button').forEach(button => {
|
144
|
+
button.addEventListener('click', (e) => {
|
145
|
+
console.log('[UIStateManager] Tab button clicked:', e.target);
|
146
|
+
|
147
|
+
// Prevent default only if we're going to handle it
|
148
|
+
const tabName = this.getTabNameFromButton(e.target);
|
149
|
+
console.log('[UIStateManager] Resolved tab name:', tabName);
|
150
|
+
|
151
|
+
if (tabName) {
|
152
|
+
// Let the href attribute update the hash naturally, which will trigger our hashchange handler
|
153
|
+
// But also explicitly trigger the switch in case href doesn't work
|
154
|
+
setTimeout(() => {
|
155
|
+
const expectedHash = this.tabToHash[tabName];
|
156
|
+
if (window.location.hash !== expectedHash && expectedHash) {
|
157
|
+
console.log('[UIStateManager] Hash not updated, forcing update:', expectedHash);
|
158
|
+
window.location.hash = expectedHash;
|
159
|
+
}
|
160
|
+
}, 10);
|
161
|
+
}
|
162
|
+
});
|
163
|
+
});
|
164
|
+
|
165
|
+
console.log('[UIStateManager] Tab click handlers set up for', document.querySelectorAll('.tab-button').length, 'buttons');
|
166
|
+
}
|
167
|
+
|
119
168
|
/**
|
120
169
|
* Set up unified keyboard navigation across all tabs
|
121
170
|
*/
|
@@ -145,122 +194,270 @@ class UIStateManager {
|
|
145
194
|
* @returns {string} - Tab name
|
146
195
|
*/
|
147
196
|
getTabNameFromButton(button) {
|
197
|
+
console.log('[getTabNameFromButton] DEBUG: button object:', button);
|
198
|
+
console.log('[getTabNameFromButton] DEBUG: button.nodeType:', button.nodeType);
|
199
|
+
console.log('[getTabNameFromButton] DEBUG: button.tagName:', button.tagName);
|
200
|
+
|
201
|
+
// CRITICAL FIX: Make sure we're dealing with the actual button element
|
202
|
+
// Sometimes the click target might be a child element (like the emoji icon)
|
203
|
+
let targetButton = button;
|
204
|
+
if (button && button.closest && button.closest('.tab-button')) {
|
205
|
+
targetButton = button.closest('.tab-button');
|
206
|
+
console.log('[getTabNameFromButton] DEBUG: Used closest() to find actual button');
|
207
|
+
}
|
208
|
+
|
148
209
|
// First check for data-tab attribute
|
149
|
-
const dataTab =
|
150
|
-
|
210
|
+
const dataTab = targetButton ? targetButton.getAttribute('data-tab') : null;
|
211
|
+
console.log('[getTabNameFromButton] DEBUG: data-tab attribute:', dataTab);
|
212
|
+
console.log('[getTabNameFromButton] DEBUG: dataTab truthy:', !!dataTab);
|
213
|
+
|
214
|
+
// CRITICAL: Specifically handle the File Tree case
|
215
|
+
if (dataTab === 'claude-tree') {
|
216
|
+
console.log('[getTabNameFromButton] DEBUG: Found claude-tree data-tab, returning it');
|
217
|
+
return 'claude-tree';
|
218
|
+
}
|
219
|
+
|
220
|
+
if (dataTab) {
|
221
|
+
console.log('[getTabNameFromButton] DEBUG: Returning dataTab:', dataTab);
|
222
|
+
return dataTab;
|
223
|
+
}
|
151
224
|
|
152
225
|
// Fallback to text content matching
|
153
|
-
const text =
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
if (text.includes('file tree')
|
226
|
+
const text = targetButton ? targetButton.textContent.toLowerCase() : '';
|
227
|
+
console.log('[getTabNameFromButton] DEBUG: text content:', text);
|
228
|
+
console.log('[getTabNameFromButton] DEBUG: text includes file tree:', text.includes('file tree'));
|
229
|
+
console.log('[getTabNameFromButton] DEBUG: text includes events:', text.includes('events'));
|
230
|
+
|
231
|
+
// CRITICAL: Check File Tree FIRST since it's the problematic one
|
232
|
+
if (text.includes('file tree') || text.includes('📝')) {
|
233
|
+
console.log('[getTabNameFromButton] DEBUG: Matched file tree, returning claude-tree');
|
234
|
+
return 'claude-tree';
|
235
|
+
}
|
236
|
+
if (text.includes('activity') || text.includes('🌳')) return 'activity';
|
237
|
+
if (text.includes('agents') || text.includes('🤖')) return 'agents';
|
238
|
+
if (text.includes('tools') || text.includes('🔧')) return 'tools';
|
239
|
+
if (text.includes('files') || text.includes('📁')) return 'files';
|
160
240
|
if (text.includes('code')) return 'code';
|
161
241
|
if (text.includes('sessions')) return 'sessions';
|
162
242
|
if (text.includes('system')) return 'system';
|
243
|
+
if (text.includes('events') || text.includes('📊')) return 'events';
|
244
|
+
|
245
|
+
console.log('[getTabNameFromButton] DEBUG: No match, falling back to events');
|
163
246
|
return 'events';
|
164
247
|
}
|
165
248
|
|
166
249
|
/**
|
167
|
-
* Switch to specified tab
|
250
|
+
* Switch to specified tab - BULLETPROOF VERSION
|
168
251
|
* @param {string} tabName - Name of tab to switch to
|
169
252
|
* @param {boolean} updateHash - Whether to update URL hash (default: true)
|
170
253
|
*/
|
171
254
|
switchTab(tabName, updateHash = true) {
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if (window.location.hash !== newHash) {
|
178
|
-
console.log(`[Hash Navigation] Updating hash to: ${newHash}`);
|
179
|
-
window.location.hash = newHash;
|
180
|
-
return; // The hashchange event will trigger switchTab again
|
181
|
-
}
|
255
|
+
// CRITICAL: Prevent race conditions by using a switching lock
|
256
|
+
if (this._switching) {
|
257
|
+
console.log(`[UIStateManager] Tab switch already in progress, queuing: ${tabName}`);
|
258
|
+
setTimeout(() => this.switchTab(tabName, updateHash), 50);
|
259
|
+
return;
|
182
260
|
}
|
261
|
+
this._switching = true;
|
183
262
|
|
184
|
-
|
185
|
-
this.currentTab = tabName;
|
263
|
+
console.log(`[UIStateManager] BULLETPROOF switchTab: ${tabName}, updateHash: ${updateHash}`);
|
186
264
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
// Now add active class ONLY to the selected tab
|
194
|
-
allTabButtons.forEach(btn => {
|
195
|
-
const btnTabName = this.getTabNameFromButton(btn);
|
196
|
-
if (btnTabName === tabName) {
|
197
|
-
btn.classList.add('active');
|
198
|
-
console.log(`[DEBUG] Set active on button with data-tab: ${btn.getAttribute('data-tab')}`);
|
265
|
+
try {
|
266
|
+
// Extra logging for File Tree debugging
|
267
|
+
if (tabName === 'claude-tree') {
|
268
|
+
console.log('[UIStateManager] SWITCHING TO FILE TREE TAB');
|
269
|
+
console.log('[UIStateManager] Current tab before switch:', this.currentTab);
|
199
270
|
}
|
271
|
+
|
272
|
+
// Update URL hash if requested (when triggered by user action, not hash change)
|
273
|
+
if (updateHash && this.tabToHash[tabName]) {
|
274
|
+
const newHash = this.tabToHash[tabName];
|
275
|
+
if (window.location.hash !== newHash) {
|
276
|
+
console.log(`[UIStateManager] Updating hash to: ${newHash}`);
|
277
|
+
this._switching = false; // Release lock before hash change
|
278
|
+
window.location.hash = newHash;
|
279
|
+
return; // The hashchange event will trigger switchTab again
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
const previousTab = this.currentTab;
|
284
|
+
this.currentTab = tabName;
|
285
|
+
|
286
|
+
// STEP 1: NUCLEAR RESET - Remove ALL active states unconditionally
|
287
|
+
this._removeAllActiveStates();
|
288
|
+
|
289
|
+
// STEP 2: Set the ONE correct tab as active
|
290
|
+
this._setActiveTab(tabName);
|
291
|
+
|
292
|
+
// STEP 3: Show ONLY the correct content
|
293
|
+
this._showTabContent(tabName);
|
294
|
+
|
295
|
+
// STEP 4: Cleanup and validation
|
296
|
+
this._validateTabState(tabName);
|
297
|
+
|
298
|
+
// Clear previous selections when switching tabs
|
299
|
+
this.clearUnifiedSelection();
|
300
|
+
|
301
|
+
// Trigger tab change event for other modules
|
302
|
+
document.dispatchEvent(new CustomEvent('tabChanged', {
|
303
|
+
detail: {
|
304
|
+
newTab: tabName,
|
305
|
+
previousTab: previousTab
|
306
|
+
}
|
307
|
+
}));
|
308
|
+
|
309
|
+
// Auto-scroll to bottom after a brief delay to ensure content is rendered
|
310
|
+
setTimeout(() => {
|
311
|
+
if (this.autoScroll) {
|
312
|
+
this.scrollCurrentTabToBottom();
|
313
|
+
}
|
314
|
+
|
315
|
+
// Special handling for File Tree tab - trigger the tree render
|
316
|
+
// But DON'T let it manipulate tabs itself
|
317
|
+
if (tabName === 'claude-tree' && window.CodeViewer) {
|
318
|
+
// Call a new method that only renders content, not tab switching
|
319
|
+
if (window.CodeViewer.renderContent) {
|
320
|
+
window.CodeViewer.renderContent();
|
321
|
+
} else {
|
322
|
+
// Fallback to show() but it should be fixed to not switch tabs
|
323
|
+
window.CodeViewer.show();
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}, 100);
|
327
|
+
|
328
|
+
} finally {
|
329
|
+
// ALWAYS release the lock
|
330
|
+
setTimeout(() => {
|
331
|
+
this._switching = false;
|
332
|
+
}, 200);
|
333
|
+
}
|
334
|
+
}
|
335
|
+
|
336
|
+
/**
|
337
|
+
* NUCLEAR RESET: Remove ALL active states from ALL elements
|
338
|
+
* This ensures no stale states remain
|
339
|
+
*/
|
340
|
+
_removeAllActiveStates() {
|
341
|
+
// Remove active class from ALL tab buttons
|
342
|
+
document.querySelectorAll('.tab-button').forEach(btn => {
|
343
|
+
btn.classList.remove('active');
|
344
|
+
// Also remove any inline styling that might interfere
|
345
|
+
btn.style.removeProperty('border-bottom');
|
346
|
+
btn.style.removeProperty('color');
|
200
347
|
});
|
201
348
|
|
202
|
-
//
|
203
|
-
|
204
|
-
allTabContents.forEach(content => {
|
349
|
+
// Remove active class from ALL tab content
|
350
|
+
document.querySelectorAll('.tab-content').forEach(content => {
|
205
351
|
content.classList.remove('active');
|
352
|
+
// Clear any inline display styles
|
353
|
+
content.style.removeProperty('display');
|
354
|
+
|
355
|
+
// CRITICAL: Clean leaked content in non-events tabs
|
356
|
+
if (content.id !== 'events-tab') {
|
357
|
+
this._cleanLeakedEventContent(content);
|
358
|
+
}
|
206
359
|
});
|
207
360
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
361
|
+
console.log('[UIStateManager] NUCLEAR: All active states removed');
|
362
|
+
}
|
363
|
+
|
364
|
+
/**
|
365
|
+
* Set ONLY the specified tab as active
|
366
|
+
*/
|
367
|
+
_setActiveTab(tabName) {
|
368
|
+
const targetTab = document.querySelector(`[data-tab="${tabName}"]`);
|
369
|
+
if (targetTab) {
|
370
|
+
targetTab.classList.add('active');
|
371
|
+
console.log(`[UIStateManager] Set active: ${tabName}`);
|
372
|
+
} else {
|
373
|
+
console.error(`[UIStateManager] Could not find tab button for: ${tabName}`);
|
374
|
+
}
|
375
|
+
}
|
376
|
+
|
377
|
+
/**
|
378
|
+
* Show ONLY the specified tab content
|
379
|
+
*/
|
380
|
+
_showTabContent(tabName) {
|
381
|
+
const targetContent = document.getElementById(`${tabName}-tab`);
|
382
|
+
if (targetContent) {
|
383
|
+
targetContent.classList.add('active');
|
384
|
+
console.log(`[UIStateManager] Showing content: ${tabName}-tab`);
|
385
|
+
|
386
|
+
// Special handling for File Tree tab
|
215
387
|
if (tabName === 'claude-tree') {
|
216
|
-
|
217
|
-
if (claudeTreeContainer) {
|
218
|
-
// Check if events list somehow got into this container
|
219
|
-
const eventsList = claudeTreeContainer.querySelector('#events-list');
|
220
|
-
if (eventsList) {
|
221
|
-
console.warn('[UIStateManager] Found events-list in File Tree container, removing it!');
|
222
|
-
eventsList.remove();
|
223
|
-
}
|
224
|
-
|
225
|
-
// Check for event items
|
226
|
-
const eventItems = claudeTreeContainer.querySelectorAll('.event-item');
|
227
|
-
if (eventItems.length > 0) {
|
228
|
-
console.warn('[UIStateManager] Found event items in File Tree container, clearing!');
|
229
|
-
eventItems.forEach(item => item.remove());
|
230
|
-
}
|
231
|
-
}
|
388
|
+
this._prepareFileTreeContent(targetContent);
|
232
389
|
}
|
390
|
+
} else {
|
391
|
+
console.error(`[UIStateManager] Could not find content for: ${tabName}`);
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* Clean any leaked event content from non-event tabs
|
397
|
+
*/
|
398
|
+
_cleanLeakedEventContent(contentElement) {
|
399
|
+
// Remove any event items that may have leaked
|
400
|
+
const leakedEventItems = contentElement.querySelectorAll('.event-item');
|
401
|
+
if (leakedEventItems.length > 0) {
|
402
|
+
console.warn(`[UIStateManager] Found ${leakedEventItems.length} leaked event items in ${contentElement.id}, removing...`);
|
403
|
+
leakedEventItems.forEach(item => item.remove());
|
233
404
|
}
|
234
405
|
|
235
|
-
//
|
236
|
-
|
406
|
+
// Remove any events-list elements
|
407
|
+
const leakedEventsList = contentElement.querySelectorAll('#events-list, .events-list');
|
408
|
+
if (leakedEventsList.length > 0) {
|
409
|
+
console.warn(`[UIStateManager] Found leaked events-list in ${contentElement.id}, removing...`);
|
410
|
+
leakedEventsList.forEach(list => list.remove());
|
411
|
+
}
|
412
|
+
}
|
237
413
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
414
|
+
/**
|
415
|
+
* Prepare File Tree content area
|
416
|
+
*/
|
417
|
+
_prepareFileTreeContent(fileTreeContent) {
|
418
|
+
const claudeTreeContainer = document.getElementById('claude-tree-container');
|
419
|
+
if (claudeTreeContainer) {
|
420
|
+
// Final cleanup check
|
421
|
+
this._cleanLeakedEventContent(claudeTreeContainer);
|
422
|
+
|
423
|
+
// Ensure container is properly marked for CodeViewer
|
424
|
+
claudeTreeContainer.setAttribute('data-owner', 'code-viewer');
|
425
|
+
claudeTreeContainer.setAttribute('data-component', 'CodeViewer');
|
426
|
+
|
427
|
+
console.log('[UIStateManager] File Tree container prepared');
|
428
|
+
}
|
429
|
+
}
|
245
430
|
|
246
|
-
|
431
|
+
/**
|
432
|
+
* Validate that tab state is correct after switching
|
433
|
+
*/
|
434
|
+
_validateTabState(expectedTab) {
|
247
435
|
setTimeout(() => {
|
248
|
-
|
249
|
-
|
436
|
+
const activeTabs = document.querySelectorAll('.tab-button.active');
|
437
|
+
const activeContents = document.querySelectorAll('.tab-content.active');
|
438
|
+
|
439
|
+
if (activeTabs.length !== 1) {
|
440
|
+
console.error(`[UIStateManager] VALIDATION FAILED: Expected 1 active tab, found ${activeTabs.length}`);
|
441
|
+
activeTabs.forEach((tab, idx) => {
|
442
|
+
console.error(` - Active tab ${idx + 1}: ${tab.textContent.trim()} (${tab.getAttribute('data-tab')})`);
|
443
|
+
});
|
444
|
+
// Force fix
|
445
|
+
this._removeAllActiveStates();
|
446
|
+
this._setActiveTab(expectedTab);
|
250
447
|
}
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
window.CodeViewer.show();
|
261
|
-
}
|
448
|
+
|
449
|
+
if (activeContents.length !== 1) {
|
450
|
+
console.error(`[UIStateManager] VALIDATION FAILED: Expected 1 active content, found ${activeContents.length}`);
|
451
|
+
activeContents.forEach((content, idx) => {
|
452
|
+
console.error(` - Active content ${idx + 1}: ${content.id}`);
|
453
|
+
});
|
454
|
+
// Force fix
|
455
|
+
this._removeAllActiveStates();
|
456
|
+
this._showTabContent(expectedTab);
|
262
457
|
}
|
263
|
-
|
458
|
+
|
459
|
+
console.log(`[UIStateManager] Tab state validated for: ${expectedTab}`);
|
460
|
+
}, 50);
|
264
461
|
}
|
265
462
|
|
266
463
|
/**
|
@@ -547,3 +744,6 @@ class UIStateManager {
|
|
547
744
|
// ES6 Module export
|
548
745
|
export { UIStateManager };
|
549
746
|
export default UIStateManager;
|
747
|
+
|
748
|
+
// Make UIStateManager globally available for dist/dashboard.js
|
749
|
+
window.UIStateManager = UIStateManager;
|
@@ -915,3 +915,6 @@ class WorkingDirectoryManager {
|
|
915
915
|
// ES6 Module export
|
916
916
|
export { WorkingDirectoryManager };
|
917
917
|
export default WorkingDirectoryManager;
|
918
|
+
|
919
|
+
// Make WorkingDirectoryManager globally available for dist/dashboard.js
|
920
|
+
window.WorkingDirectoryManager = WorkingDirectoryManager;
|