claude-mpm 4.2.13__py3-none-any.whl → 4.2.14__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.
Files changed (34) hide show
  1. claude_mpm/core/constants.py +65 -0
  2. claude_mpm/core/error_handler.py +625 -0
  3. claude_mpm/core/file_utils.py +770 -0
  4. claude_mpm/core/logging_utils.py +502 -0
  5. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  6. claude_mpm/dashboard/static/dist/components/file-viewer.js +1 -1
  7. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  8. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  9. claude_mpm/dashboard/static/js/components/code-simple.js +44 -1
  10. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +353 -0
  11. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +235 -0
  12. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +409 -0
  13. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +435 -0
  14. claude_mpm/dashboard/static/js/components/code-tree.js +29 -5
  15. claude_mpm/dashboard/static/js/components/file-viewer.js +69 -27
  16. claude_mpm/dashboard/static/js/components/session-manager.js +1 -1
  17. claude_mpm/dashboard/static/js/components/working-directory.js +18 -9
  18. claude_mpm/dashboard/static/js/dashboard.js +55 -9
  19. claude_mpm/dashboard/static/js/shared/dom-helpers.js +396 -0
  20. claude_mpm/dashboard/static/js/shared/event-bus.js +330 -0
  21. claude_mpm/dashboard/static/js/shared/logger.js +385 -0
  22. claude_mpm/dashboard/static/js/shared/tooltip-service.js +253 -0
  23. claude_mpm/dashboard/static/js/socket-client.js +18 -0
  24. claude_mpm/dashboard/templates/index.html +21 -8
  25. claude_mpm/services/monitor/handlers/__init__.py +2 -1
  26. claude_mpm/services/monitor/handlers/file.py +263 -0
  27. claude_mpm/services/monitor/server.py +81 -1
  28. claude_mpm/services/socketio/handlers/file.py +40 -5
  29. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/METADATA +1 -1
  30. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/RECORD +34 -22
  31. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/WHEEL +0 -0
  32. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/entry_points.txt +0 -0
  33. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/licenses/LICENSE +0 -0
  34. {claude_mpm-4.2.13.dist-info → claude_mpm-4.2.14.dist-info}/top_level.txt +0 -0
@@ -1214,38 +1214,61 @@ async function updateFileViewerModal(modal, filePath, workingDir) {
1214
1214
 
1215
1215
  try {
1216
1216
  // Get the Socket.IO client
1217
- const socket = window.socket || window.dashboard?.socketClient?.socket;
1217
+ const socket = window.socket || window.dashboard?.socketClient?.socket || window.socketClient?.socket;
1218
+
1219
+ console.log('[FileViewer] Socket search results:', {
1220
+ 'window.socket': !!window.socket,
1221
+ 'window.socket.connected': window.socket?.connected,
1222
+ 'dashboard.socketClient.socket': !!window.dashboard?.socketClient?.socket,
1223
+ 'dashboard.socketClient.socket.connected': window.dashboard?.socketClient?.socket?.connected,
1224
+ 'window.socketClient.socket': !!window.socketClient?.socket,
1225
+ 'window.socketClient.socket.connected': window.socketClient?.socket?.connected
1226
+ });
1227
+
1218
1228
  if (!socket) {
1219
- throw new Error('No socket connection available');
1229
+ throw new Error('No socket connection available. Please ensure the dashboard is connected.');
1220
1230
  }
1231
+
1232
+ if (!socket.connected) {
1233
+ console.warn('[FileViewer] Socket found but not connected, attempting to use anyway...');
1234
+ }
1235
+
1236
+ console.log('[FileViewer] Socket found, setting up listener for file_content_response');
1221
1237
 
1222
1238
  // Set up one-time listener for file content response
1223
1239
  const responsePromise = new Promise((resolve, reject) => {
1224
1240
  const responseHandler = (data) => {
1241
+ console.log('[FileViewer] Received file_content_response:', data);
1225
1242
  if (data.file_path === filePath) {
1226
1243
  socket.off('file_content_response', responseHandler);
1227
1244
  if (data.success) {
1245
+ console.log('[FileViewer] File content loaded successfully');
1228
1246
  resolve(data);
1229
1247
  } else {
1248
+ console.error('[FileViewer] File read failed:', data.error);
1230
1249
  reject(new Error(data.error || 'Failed to read file'));
1231
1250
  }
1232
1251
  }
1233
1252
  };
1234
1253
 
1235
1254
  socket.on('file_content_response', responseHandler);
1255
+ console.log('[FileViewer] Listener registered for file_content_response');
1236
1256
 
1237
1257
  // Timeout after 10 seconds
1238
1258
  setTimeout(() => {
1239
1259
  socket.off('file_content_response', responseHandler);
1240
- reject(new Error('Request timeout'));
1260
+ console.error('[FileViewer] Request timeout after 10 seconds');
1261
+ reject(new Error('Request timeout - server did not respond'));
1241
1262
  }, 10000);
1242
1263
  });
1243
1264
 
1244
1265
  // Send file read request
1245
- socket.emit('read_file', {
1266
+ const requestData = {
1246
1267
  file_path: filePath,
1247
1268
  working_dir: workingDir
1248
- });
1269
+ };
1270
+ console.log('[FileViewer] Emitting read_file event with data:', requestData);
1271
+ socket.emit('read_file', requestData);
1249
1272
 
1250
1273
  // File viewer request sent
1251
1274
 
@@ -1505,15 +1528,19 @@ function formatFileSize(bytes) {
1505
1528
 
1506
1529
  // File Viewer Modal Functions
1507
1530
  window.showFileViewerModal = async function(filePath) {
1531
+ console.log('[FileViewer] Opening file:', filePath);
1532
+
1508
1533
  // Use the dashboard's current working directory
1509
1534
  let workingDir = '';
1510
1535
  if (window.dashboard && window.dashboard.currentWorkingDir) {
1511
1536
  workingDir = window.dashboard.currentWorkingDir;
1537
+ console.log('[FileViewer] Using working directory:', workingDir);
1512
1538
  }
1513
1539
 
1514
1540
  // Create modal if it doesn't exist
1515
1541
  let modal = document.getElementById('file-viewer-modal');
1516
1542
  if (!modal) {
1543
+ console.log('[FileViewer] Creating new modal');
1517
1544
  modal = createFileViewerModal();
1518
1545
  document.body.appendChild(modal);
1519
1546
 
@@ -1521,14 +1548,16 @@ window.showFileViewerModal = async function(filePath) {
1521
1548
  await new Promise(resolve => setTimeout(resolve, 10));
1522
1549
  }
1523
1550
 
1551
+ // Show the modal as flex container first (ensures proper rendering)
1552
+ modal.style.display = 'flex';
1553
+ document.body.style.overflow = 'hidden'; // Prevent background scrolling
1554
+
1524
1555
  // Update modal content
1525
1556
  updateFileViewerModal(modal, filePath, workingDir).catch(error => {
1526
1557
  console.error('Error updating file viewer modal:', error);
1558
+ // Show error in the modal
1559
+ displayFileContentError(modal, { error: error.message });
1527
1560
  });
1528
-
1529
- // Show the modal as flex container
1530
- modal.style.display = 'flex';
1531
- document.body.style.overflow = 'hidden'; // Prevent background scrolling
1532
1561
  };
1533
1562
 
1534
1563
  window.hideFileViewerModal = function() {
@@ -1589,6 +1618,19 @@ function displayFileContentError(modal, result) {
1589
1618
  const errorArea = modal.querySelector('.file-viewer-error');
1590
1619
  const messageElement = modal.querySelector('.error-message');
1591
1620
  const suggestionsElement = modal.querySelector('.error-suggestions');
1621
+ const loadingElement = modal.querySelector('.file-viewer-loading');
1622
+ const contentArea = modal.querySelector('.file-viewer-content-area');
1623
+
1624
+ // Hide loading and content areas, show error
1625
+ if (loadingElement) {
1626
+ loadingElement.style.display = 'none';
1627
+ }
1628
+ if (contentArea) {
1629
+ contentArea.style.display = 'none';
1630
+ }
1631
+ if (errorArea) {
1632
+ errorArea.style.display = 'flex';
1633
+ }
1592
1634
 
1593
1635
  // Create user-friendly error messages
1594
1636
  let errorMessage = result.error || 'Unknown error occurred';
@@ -1599,6 +1641,10 @@ function displayFileContentError(modal, result) {
1599
1641
  errorMessage = '🔒 Permission denied accessing this file';
1600
1642
  } else if (errorMessage.includes('too large')) {
1601
1643
  errorMessage = '📏 File is too large to display';
1644
+ } else if (errorMessage.includes('socket connection')) {
1645
+ errorMessage = '🔌 Not connected to the server. Please check your connection.';
1646
+ } else if (errorMessage.includes('timeout')) {
1647
+ errorMessage = '⏱️ Request timed out. The server may be busy or unresponsive.';
1602
1648
  } else if (!errorMessage.includes('📁') && !errorMessage.includes('🔒') && !errorMessage.includes('📏')) {
1603
1649
  errorMessage = `⚠️ ${errorMessage}`;
1604
1650
  }
@@ -0,0 +1,396 @@
1
+ /**
2
+ * DOM Helper Utilities
3
+ *
4
+ * Common DOM manipulation utilities for dashboard components.
5
+ * Provides safe, consistent methods for element creation and manipulation.
6
+ *
7
+ * @module dom-helpers
8
+ */
9
+
10
+ const domHelpers = {
11
+ /**
12
+ * Create an element with attributes and content
13
+ * @param {string} tag - Element tag name
14
+ * @param {Object} attrs - Attributes to set
15
+ * @param {string|Element|Array} content - Element content
16
+ * @returns {HTMLElement} Created element
17
+ */
18
+ createElement(tag, attrs = {}, content = null) {
19
+ const element = document.createElement(tag);
20
+
21
+ // Set attributes
22
+ for (const [key, value] of Object.entries(attrs)) {
23
+ if (key === 'className') {
24
+ element.className = value;
25
+ } else if (key === 'style' && typeof value === 'object') {
26
+ Object.assign(element.style, value);
27
+ } else if (key === 'dataset' && typeof value === 'object') {
28
+ for (const [dataKey, dataValue] of Object.entries(value)) {
29
+ element.dataset[dataKey] = dataValue;
30
+ }
31
+ } else if (key.startsWith('on') && typeof value === 'function') {
32
+ const eventName = key.slice(2).toLowerCase();
33
+ element.addEventListener(eventName, value);
34
+ } else {
35
+ element.setAttribute(key, value);
36
+ }
37
+ }
38
+
39
+ // Add content
40
+ if (content !== null) {
41
+ this.setContent(element, content);
42
+ }
43
+
44
+ return element;
45
+ },
46
+
47
+ /**
48
+ * Set element content (supports text, HTML, elements, and arrays)
49
+ * @param {HTMLElement} element - Target element
50
+ * @param {string|Element|Array} content - Content to set
51
+ */
52
+ setContent(element, content) {
53
+ if (typeof content === 'string') {
54
+ element.textContent = content;
55
+ } else if (content instanceof HTMLElement) {
56
+ element.appendChild(content);
57
+ } else if (Array.isArray(content)) {
58
+ content.forEach(item => {
59
+ if (typeof item === 'string') {
60
+ element.appendChild(document.createTextNode(item));
61
+ } else if (item instanceof HTMLElement) {
62
+ element.appendChild(item);
63
+ }
64
+ });
65
+ }
66
+ },
67
+
68
+ /**
69
+ * Safely query selector with null check
70
+ * @param {string} selector - CSS selector
71
+ * @param {Element} context - Context element (default: document)
72
+ * @returns {Element|null} Found element or null
73
+ */
74
+ query(selector, context = document) {
75
+ try {
76
+ return context.querySelector(selector);
77
+ } catch (e) {
78
+ console.error(`Invalid selector: ${selector}`, e);
79
+ return null;
80
+ }
81
+ },
82
+
83
+ /**
84
+ * Safely query all matching elements
85
+ * @param {string} selector - CSS selector
86
+ * @param {Element} context - Context element (default: document)
87
+ * @returns {Array} Array of elements
88
+ */
89
+ queryAll(selector, context = document) {
90
+ try {
91
+ return Array.from(context.querySelectorAll(selector));
92
+ } catch (e) {
93
+ console.error(`Invalid selector: ${selector}`, e);
94
+ return [];
95
+ }
96
+ },
97
+
98
+ /**
99
+ * Add classes to element
100
+ * @param {HTMLElement} element - Target element
101
+ * @param {...string} classes - Classes to add
102
+ */
103
+ addClass(element, ...classes) {
104
+ if (element && element.classList) {
105
+ element.classList.add(...classes.filter(c => c));
106
+ }
107
+ },
108
+
109
+ /**
110
+ * Remove classes from element
111
+ * @param {HTMLElement} element - Target element
112
+ * @param {...string} classes - Classes to remove
113
+ */
114
+ removeClass(element, ...classes) {
115
+ if (element && element.classList) {
116
+ element.classList.remove(...classes);
117
+ }
118
+ },
119
+
120
+ /**
121
+ * Toggle classes on element
122
+ * @param {HTMLElement} element - Target element
123
+ * @param {string} className - Class to toggle
124
+ * @param {boolean} force - Force add (true) or remove (false)
125
+ * @returns {boolean} Whether class is now present
126
+ */
127
+ toggleClass(element, className, force) {
128
+ if (element && element.classList) {
129
+ return element.classList.toggle(className, force);
130
+ }
131
+ return false;
132
+ },
133
+
134
+ /**
135
+ * Check if element has class
136
+ * @param {HTMLElement} element - Target element
137
+ * @param {string} className - Class to check
138
+ * @returns {boolean} Whether element has class
139
+ */
140
+ hasClass(element, className) {
141
+ return element && element.classList && element.classList.contains(className);
142
+ },
143
+
144
+ /**
145
+ * Set multiple styles on element
146
+ * @param {HTMLElement} element - Target element
147
+ * @param {Object} styles - Style properties and values
148
+ */
149
+ setStyles(element, styles) {
150
+ if (element && element.style && styles) {
151
+ Object.assign(element.style, styles);
152
+ }
153
+ },
154
+
155
+ /**
156
+ * Get computed style value
157
+ * @param {HTMLElement} element - Target element
158
+ * @param {string} property - CSS property name
159
+ * @returns {string} Computed style value
160
+ */
161
+ getStyle(element, property) {
162
+ if (element) {
163
+ return window.getComputedStyle(element).getPropertyValue(property);
164
+ }
165
+ return '';
166
+ },
167
+
168
+ /**
169
+ * Show element (removes display: none)
170
+ * @param {HTMLElement} element - Element to show
171
+ * @param {string} displayValue - Display value to use (default: '')
172
+ */
173
+ show(element, displayValue = '') {
174
+ if (element && element.style) {
175
+ element.style.display = displayValue;
176
+ }
177
+ },
178
+
179
+ /**
180
+ * Hide element (sets display: none)
181
+ * @param {HTMLElement} element - Element to hide
182
+ */
183
+ hide(element) {
184
+ if (element && element.style) {
185
+ element.style.display = 'none';
186
+ }
187
+ },
188
+
189
+ /**
190
+ * Toggle element visibility
191
+ * @param {HTMLElement} element - Element to toggle
192
+ * @param {boolean} show - Force show (true) or hide (false)
193
+ */
194
+ toggle(element, show) {
195
+ if (element) {
196
+ if (show === undefined) {
197
+ show = element.style.display === 'none';
198
+ }
199
+ if (show) {
200
+ this.show(element);
201
+ } else {
202
+ this.hide(element);
203
+ }
204
+ }
205
+ },
206
+
207
+ /**
208
+ * Remove element from DOM
209
+ * @param {HTMLElement} element - Element to remove
210
+ */
211
+ remove(element) {
212
+ if (element && element.parentNode) {
213
+ element.parentNode.removeChild(element);
214
+ }
215
+ },
216
+
217
+ /**
218
+ * Empty element content
219
+ * @param {HTMLElement} element - Element to empty
220
+ */
221
+ empty(element) {
222
+ if (element) {
223
+ while (element.firstChild) {
224
+ element.removeChild(element.firstChild);
225
+ }
226
+ }
227
+ },
228
+
229
+ /**
230
+ * Insert element after reference element
231
+ * @param {HTMLElement} newElement - Element to insert
232
+ * @param {HTMLElement} referenceElement - Reference element
233
+ */
234
+ insertAfter(newElement, referenceElement) {
235
+ if (referenceElement && referenceElement.parentNode) {
236
+ referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
237
+ }
238
+ },
239
+
240
+ /**
241
+ * Wrap element with wrapper element
242
+ * @param {HTMLElement} element - Element to wrap
243
+ * @param {HTMLElement} wrapper - Wrapper element
244
+ */
245
+ wrap(element, wrapper) {
246
+ if (element && element.parentNode) {
247
+ element.parentNode.insertBefore(wrapper, element);
248
+ wrapper.appendChild(element);
249
+ }
250
+ },
251
+
252
+ /**
253
+ * Get element dimensions
254
+ * @param {HTMLElement} element - Target element
255
+ * @returns {Object} Width and height
256
+ */
257
+ getDimensions(element) {
258
+ if (element) {
259
+ return {
260
+ width: element.offsetWidth,
261
+ height: element.offsetHeight,
262
+ innerWidth: element.clientWidth,
263
+ innerHeight: element.clientHeight
264
+ };
265
+ }
266
+ return { width: 0, height: 0, innerWidth: 0, innerHeight: 0 };
267
+ },
268
+
269
+ /**
270
+ * Get element position relative to viewport
271
+ * @param {HTMLElement} element - Target element
272
+ * @returns {Object} Position coordinates
273
+ */
274
+ getPosition(element) {
275
+ if (element) {
276
+ const rect = element.getBoundingClientRect();
277
+ return {
278
+ top: rect.top,
279
+ right: rect.right,
280
+ bottom: rect.bottom,
281
+ left: rect.left,
282
+ x: rect.x,
283
+ y: rect.y,
284
+ width: rect.width,
285
+ height: rect.height
286
+ };
287
+ }
288
+ return { top: 0, right: 0, bottom: 0, left: 0, x: 0, y: 0, width: 0, height: 0 };
289
+ },
290
+
291
+ /**
292
+ * Check if element is visible in viewport
293
+ * @param {HTMLElement} element - Element to check
294
+ * @param {boolean} partial - Allow partial visibility
295
+ * @returns {boolean} Whether element is visible
296
+ */
297
+ isInViewport(element, partial = false) {
298
+ if (!element) return false;
299
+
300
+ const rect = element.getBoundingClientRect();
301
+ const windowHeight = window.innerHeight || document.documentElement.clientHeight;
302
+ const windowWidth = window.innerWidth || document.documentElement.clientWidth;
303
+
304
+ const vertInView = partial
305
+ ? rect.top < windowHeight && rect.bottom > 0
306
+ : rect.top >= 0 && rect.bottom <= windowHeight;
307
+
308
+ const horInView = partial
309
+ ? rect.left < windowWidth && rect.right > 0
310
+ : rect.left >= 0 && rect.right <= windowWidth;
311
+
312
+ return vertInView && horInView;
313
+ },
314
+
315
+ /**
316
+ * Smoothly scroll element into view
317
+ * @param {HTMLElement} element - Element to scroll to
318
+ * @param {Object} options - Scroll options
319
+ */
320
+ scrollIntoView(element, options = {}) {
321
+ if (element && element.scrollIntoView) {
322
+ const defaultOptions = {
323
+ behavior: 'smooth',
324
+ block: 'nearest',
325
+ inline: 'nearest'
326
+ };
327
+ element.scrollIntoView({ ...defaultOptions, ...options });
328
+ }
329
+ },
330
+
331
+ /**
332
+ * Create DocumentFragment from HTML string
333
+ * @param {string} html - HTML string
334
+ * @returns {DocumentFragment} Document fragment
335
+ */
336
+ createFragment(html) {
337
+ const template = document.createElement('template');
338
+ template.innerHTML = html.trim();
339
+ return template.content;
340
+ },
341
+
342
+ /**
343
+ * Escape HTML special characters
344
+ * @param {string} text - Text to escape
345
+ * @returns {string} Escaped text
346
+ */
347
+ escapeHtml(text) {
348
+ const div = document.createElement('div');
349
+ div.textContent = text;
350
+ return div.innerHTML;
351
+ },
352
+
353
+ /**
354
+ * Debounce function calls
355
+ * @param {Function} func - Function to debounce
356
+ * @param {number} wait - Wait time in ms
357
+ * @returns {Function} Debounced function
358
+ */
359
+ debounce(func, wait) {
360
+ let timeout;
361
+ return function executedFunction(...args) {
362
+ const later = () => {
363
+ clearTimeout(timeout);
364
+ func(...args);
365
+ };
366
+ clearTimeout(timeout);
367
+ timeout = setTimeout(later, wait);
368
+ };
369
+ },
370
+
371
+ /**
372
+ * Throttle function calls
373
+ * @param {Function} func - Function to throttle
374
+ * @param {number} limit - Time limit in ms
375
+ * @returns {Function} Throttled function
376
+ */
377
+ throttle(func, limit) {
378
+ let inThrottle;
379
+ return function(...args) {
380
+ if (!inThrottle) {
381
+ func.apply(this, args);
382
+ inThrottle = true;
383
+ setTimeout(() => inThrottle = false, limit);
384
+ }
385
+ };
386
+ }
387
+ };
388
+
389
+ // Support both module and global usage
390
+ if (typeof module !== 'undefined' && module.exports) {
391
+ module.exports = domHelpers;
392
+ } else if (typeof define === 'function' && define.amd) {
393
+ define([], () => domHelpers);
394
+ } else {
395
+ window.domHelpers = domHelpers;
396
+ }