@dynamicu/chromedebug-mcp 2.6.7 → 2.7.1

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 (49) hide show
  1. package/CLAUDE.md +17 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/activation-manager.js +10 -10
  4. package/chrome-extension/background.js +1045 -736
  5. package/chrome-extension/browser-recording-manager.js +1 -1
  6. package/chrome-extension/chrome-debug-logger.js +168 -0
  7. package/chrome-extension/chrome-session-manager.js +5 -5
  8. package/chrome-extension/console-interception-library.js +430 -0
  9. package/chrome-extension/content.css +16 -16
  10. package/chrome-extension/content.js +739 -221
  11. package/chrome-extension/data-buffer.js +5 -5
  12. package/chrome-extension/dom-tracker.js +9 -9
  13. package/chrome-extension/extension-config.js +1 -1
  14. package/chrome-extension/firebase-client.js +13 -13
  15. package/chrome-extension/frame-capture.js +20 -38
  16. package/chrome-extension/license-helper.js +33 -7
  17. package/chrome-extension/manifest.free.json +3 -6
  18. package/chrome-extension/network-tracker.js +9 -9
  19. package/chrome-extension/options.html +10 -0
  20. package/chrome-extension/options.js +21 -8
  21. package/chrome-extension/performance-monitor.js +17 -17
  22. package/chrome-extension/popup.html +230 -193
  23. package/chrome-extension/popup.js +146 -458
  24. package/chrome-extension/pro/enhanced-capture.js +406 -0
  25. package/chrome-extension/pro/frame-editor.html +433 -0
  26. package/chrome-extension/pro/frame-editor.js +1567 -0
  27. package/chrome-extension/pro/function-tracker.js +843 -0
  28. package/chrome-extension/pro/jszip.min.js +13 -0
  29. package/chrome-extension/upload-manager.js +7 -7
  30. package/dist/chromedebug-extension-free.zip +0 -0
  31. package/package.json +3 -1
  32. package/scripts/webpack.config.free.cjs +8 -8
  33. package/scripts/webpack.config.pro.cjs +2 -0
  34. package/src/cli.js +2 -2
  35. package/src/database.js +55 -7
  36. package/src/index.js +9 -6
  37. package/src/mcp/server.js +2 -2
  38. package/src/services/process-manager.js +10 -6
  39. package/src/services/process-tracker.js +10 -5
  40. package/src/services/profile-manager.js +17 -2
  41. package/src/validation/schemas.js +12 -11
  42. package/src/index-direct.js +0 -157
  43. package/src/index-modular.js +0 -219
  44. package/src/index-monolithic-backup.js +0 -2230
  45. package/src/legacy/chrome-controller-old.js +0 -1406
  46. package/src/legacy/index-express.js +0 -625
  47. package/src/legacy/index-old.js +0 -977
  48. package/src/legacy/routes.js +0 -260
  49. package/src/legacy/shared-storage.js +0 -101
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Enhanced Click Capture - Pro Feature
3
+ *
4
+ * Provides detailed element tracking including:
5
+ * - Full HTML capture
6
+ * - Event handlers detection
7
+ * - Element state and CSS
8
+ * - Framework component detection (React/Vue/Angular)
9
+ * - Performance metrics
10
+ */
11
+
12
+ /**
13
+ * Captures element HTML with size limit (10KB max)
14
+ * @param {Element} element - The DOM element to capture
15
+ * @returns {string|null} Element HTML (truncated if over limit), null if capture failed or no element
16
+ */
17
+ export function captureElementHTML(element) {
18
+ try {
19
+ if (!element) {
20
+ return null; // No element to capture
21
+ }
22
+
23
+ // Defensive check to prevent race conditions
24
+ let html;
25
+ try {
26
+ html = element.outerHTML;
27
+ } catch (e) {
28
+ // Element was likely removed from DOM during capture
29
+ console.error('[ChromePilot Pro] Element removed during capture:', e.message);
30
+ return null;
31
+ }
32
+
33
+ if (!html) {
34
+ console.error('[ChromePilot Pro] Element exists but has no outerHTML:', element.tagName || 'unknown');
35
+ return null; // Element exists but can't get HTML
36
+ }
37
+
38
+ const maxSize = 10 * 1024; // 10KB limit
39
+
40
+ if (html.length <= maxSize) {
41
+ // Return raw HTML - JSON.stringify will handle escaping properly
42
+ return html;
43
+ }
44
+
45
+ // Truncate and add indicator - JSON.stringify will handle escaping
46
+ const truncated = html.substring(0, maxSize - 20) + '...truncated';
47
+ return truncated;
48
+ } catch (error) {
49
+ console.error('[ChromePilot Pro] Error capturing element HTML:', error.message);
50
+ return null; // Capture failed with error
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Extracts event handlers from element (2KB max)
56
+ * @param {Element} element - The DOM element to analyze
57
+ * @returns {Object|null} Event handlers data, null if capture failed or no element
58
+ */
59
+ export function extractEventHandlers(element) {
60
+ try {
61
+ const handlers = {};
62
+ const maxSize = 2 * 1024; // 2KB limit
63
+
64
+ if (!element) {
65
+ return null; // No element to analyze
66
+ }
67
+
68
+ // Get inline event handlers
69
+ const inlineHandlers = {};
70
+ for (const attr of element.attributes || []) {
71
+ if (attr.name.startsWith('on')) {
72
+ inlineHandlers[attr.name] = attr.value;
73
+ }
74
+ }
75
+
76
+ if (Object.keys(inlineHandlers).length > 0) {
77
+ handlers.inline = inlineHandlers;
78
+ }
79
+
80
+ // Try to detect jQuery/library events if available
81
+ if (window.jQuery && element._jQuery) {
82
+ try {
83
+ const jQueryEvents = window.jQuery._data(element, 'events');
84
+ if (jQueryEvents) {
85
+ handlers.jquery = Object.keys(jQueryEvents);
86
+ }
87
+ } catch (e) {
88
+ // Ignore jQuery access errors
89
+ }
90
+ }
91
+
92
+ // Check for common framework event attributes
93
+ const frameworkAttrs = {};
94
+ for (const attr of element.attributes || []) {
95
+ if (attr.name.match(/^(v-on|@|ng-|data-action)/)) {
96
+ frameworkAttrs[attr.name] = attr.value;
97
+ }
98
+ }
99
+
100
+ if (Object.keys(frameworkAttrs).length > 0) {
101
+ handlers.framework = frameworkAttrs;
102
+ }
103
+
104
+ // Return null if no handlers found
105
+ if (Object.keys(handlers).length === 0) {
106
+ return null;
107
+ }
108
+
109
+ // Truncate if too large
110
+ const handlersStr = JSON.stringify(handlers);
111
+ if (handlersStr.length > maxSize) {
112
+ return { truncated: true, size: handlersStr.length };
113
+ }
114
+
115
+ return handlers;
116
+ } catch (error) {
117
+ console.error('[ChromePilot Pro] Error extracting event handlers:', error.message);
118
+ return null; // Capture failed with error
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Captures element state including CSS and visibility (2KB max)
124
+ * @param {Element} element - The DOM element to analyze
125
+ * @returns {Object|null} Element state data, null if capture failed or no element
126
+ */
127
+ export function captureElementState(element) {
128
+ try {
129
+ const state = {};
130
+ const maxSize = 2 * 1024; // 2KB limit
131
+
132
+ if (!element) {
133
+ return null; // No element to analyze
134
+ }
135
+
136
+ // Basic visibility and position
137
+ const rect = element.getBoundingClientRect();
138
+ state.boundingRect = {
139
+ width: Math.round(rect.width),
140
+ height: Math.round(rect.height),
141
+ top: Math.round(rect.top),
142
+ left: Math.round(rect.left)
143
+ };
144
+
145
+ // Visibility states
146
+ state.visibility = {
147
+ visible: element.offsetParent !== null,
148
+ inViewport: rect.top < window.innerHeight && rect.bottom > 0,
149
+ hidden: element.hidden,
150
+ display: getComputedStyle(element).display,
151
+ opacity: getComputedStyle(element).opacity
152
+ };
153
+
154
+ // Form element states
155
+ if (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName === 'TEXTAREA') {
156
+ state.formElement = {
157
+ disabled: element.disabled,
158
+ readonly: element.readOnly,
159
+ required: element.required,
160
+ value: element.value || '',
161
+ checked: element.checked
162
+ };
163
+ }
164
+
165
+ // Interactive states
166
+ state.interactive = {
167
+ tabIndex: element.tabIndex,
168
+ contentEditable: element.contentEditable,
169
+ draggable: element.draggable
170
+ };
171
+
172
+ // Key CSS properties that affect behavior
173
+ const computedStyle = getComputedStyle(element);
174
+ state.css = {
175
+ position: computedStyle.position,
176
+ zIndex: computedStyle.zIndex,
177
+ pointerEvents: computedStyle.pointerEvents,
178
+ cursor: computedStyle.cursor
179
+ };
180
+
181
+ // Truncate if too large
182
+ const stateStr = JSON.stringify(state);
183
+ if (stateStr.length > maxSize) {
184
+ return {
185
+ truncated: true,
186
+ size: stateStr.length,
187
+ boundingRect: state.boundingRect,
188
+ visibility: state.visibility
189
+ };
190
+ }
191
+
192
+ return state;
193
+ } catch (error) {
194
+ console.error('[ChromePilot Pro] Error capturing element state:', error.message);
195
+ return null; // Capture failed with error
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Extracts component information for React/Vue/Angular elements (3KB max)
201
+ * @param {Element} element - The DOM element to analyze
202
+ * @returns {Object|null} Component information, null if capture failed or no element
203
+ */
204
+ export function getEnhancedComponentInfo(element) {
205
+ try {
206
+ const componentInfo = {};
207
+ const maxSize = 3 * 1024; // 3KB limit
208
+
209
+ if (!element) {
210
+ return null; // No element to analyze
211
+ }
212
+
213
+ // React component detection
214
+ try {
215
+ const reactFiberKey = Object.keys(element).find(key =>
216
+ key.startsWith('__reactInternalInstance') || key.startsWith('_reactInternalFiber')
217
+ );
218
+
219
+ if (reactFiberKey) {
220
+ const fiber = element[reactFiberKey];
221
+ if (fiber && fiber.type) {
222
+ componentInfo.react = {
223
+ componentName: fiber.type.displayName || fiber.type.name || 'Unknown',
224
+ key: fiber.key,
225
+ props: fiber.memoizedProps ? Object.keys(fiber.memoizedProps) : []
226
+ };
227
+ }
228
+ }
229
+ } catch (e) {
230
+ // Ignore React detection errors
231
+ }
232
+
233
+ // Vue component detection
234
+ try {
235
+ if (element.__vue__) {
236
+ const vue = element.__vue__;
237
+ componentInfo.vue = {
238
+ componentName: vue.$options.name || vue.$options._componentTag || 'Unknown',
239
+ props: vue.$props ? Object.keys(vue.$props) : [],
240
+ data: vue.$data ? Object.keys(vue.$data) : []
241
+ };
242
+ }
243
+ } catch (e) {
244
+ // Ignore Vue detection errors
245
+ }
246
+
247
+ // Angular component detection
248
+ try {
249
+ if (element.getAttribute && element.getAttribute('ng-version')) {
250
+ const ngAttributes = [];
251
+ for (const attr of element.attributes) {
252
+ if (attr.name.startsWith('ng-') || attr.name.startsWith('_ng')) {
253
+ ngAttributes.push(attr.name);
254
+ }
255
+ }
256
+ if (ngAttributes.length > 0) {
257
+ componentInfo.angular = {
258
+ attributes: ngAttributes,
259
+ version: element.getAttribute('ng-version')
260
+ };
261
+ }
262
+ }
263
+ } catch (e) {
264
+ // Ignore Angular detection errors
265
+ }
266
+
267
+ // Generic component attributes
268
+ const dataAttributes = {};
269
+ const classNames = element.className ? element.className.split(' ').filter(c => c) : [];
270
+
271
+ for (const attr of element.attributes || []) {
272
+ if (attr.name.startsWith('data-') && !attr.name.startsWith('data-testid')) {
273
+ dataAttributes[attr.name] = attr.value;
274
+ }
275
+ }
276
+
277
+ if (Object.keys(dataAttributes).length > 0) {
278
+ componentInfo.dataAttributes = dataAttributes;
279
+ }
280
+
281
+ if (classNames.length > 0) {
282
+ componentInfo.classNames = classNames;
283
+ }
284
+
285
+ // Role and accessibility info
286
+ if (element.getAttribute('role')) {
287
+ componentInfo.accessibility = {
288
+ role: element.getAttribute('role'),
289
+ ariaLabel: element.getAttribute('aria-label'),
290
+ ariaDescribedBy: element.getAttribute('aria-describedby')
291
+ };
292
+ }
293
+
294
+ // Return null if no component info found
295
+ if (Object.keys(componentInfo).length === 0) {
296
+ return null;
297
+ }
298
+
299
+ // Truncate if too large
300
+ const componentStr = JSON.stringify(componentInfo);
301
+ if (componentStr.length > maxSize) {
302
+ return {
303
+ truncated: true,
304
+ size: componentStr.length,
305
+ framework: Object.keys(componentInfo).filter(k => ['react', 'vue', 'angular'].includes(k))
306
+ };
307
+ }
308
+
309
+ return componentInfo;
310
+ } catch (error) {
311
+ console.error('[ChromePilot Pro] Error capturing component info:', error.message);
312
+ return null; // Capture failed with error
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Captures performance metrics (1KB max)
318
+ * @returns {Object|null} Performance metrics, null if capture failed
319
+ */
320
+ export function getPerformanceMetrics() {
321
+ try {
322
+ const metrics = {};
323
+ const maxSize = 1024; // 1KB limit
324
+
325
+ // Timing metrics
326
+ if (performance && performance.now) {
327
+ metrics.timestamp = Math.round(performance.now());
328
+ }
329
+
330
+ // Navigation timing
331
+ if (performance && performance.timing) {
332
+ const timing = performance.timing;
333
+ metrics.navigation = {
334
+ loadTime: timing.loadEventEnd - timing.navigationStart,
335
+ domReady: timing.domContentLoadedEventEnd - timing.navigationStart,
336
+ renderTime: timing.domComplete - timing.domLoading
337
+ };
338
+ }
339
+
340
+ // Memory usage (if available)
341
+ if (performance && performance.memory) {
342
+ metrics.memory = {
343
+ used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
344
+ total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024)
345
+ };
346
+ }
347
+
348
+ // Return null if no metrics captured
349
+ if (Object.keys(metrics).length === 0) {
350
+ return null;
351
+ }
352
+
353
+ // Truncate if too large
354
+ const metricsStr = JSON.stringify(metrics);
355
+ if (metricsStr.length > maxSize) {
356
+ return {
357
+ truncated: true,
358
+ size: metricsStr.length,
359
+ timestamp: metrics.timestamp
360
+ };
361
+ }
362
+
363
+ return metrics;
364
+ } catch (error) {
365
+ console.error('[ChromePilot Pro] Error capturing performance metrics:', error.message);
366
+ return null; // Capture failed with error
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Captures all enhanced click data for an element
372
+ * @param {Event} event - The click event
373
+ * @param {Element} element - The clicked element
374
+ * @returns {Object} Enhanced click data (only includes fields with meaningful values)
375
+ */
376
+ export async function captureEnhancedClickData(event, element) {
377
+ const enhancedData = {};
378
+
379
+ // Only include fields with meaningful values (not null/undefined)
380
+ const capturedHTML = captureElementHTML(element);
381
+ if (capturedHTML) {
382
+ enhancedData.element_html = capturedHTML;
383
+ }
384
+
385
+ const componentData = getEnhancedComponentInfo(element);
386
+ if (componentData) {
387
+ enhancedData.component_data = componentData;
388
+ }
389
+
390
+ const eventHandlers = extractEventHandlers(element);
391
+ if (eventHandlers) {
392
+ enhancedData.event_handlers = eventHandlers;
393
+ }
394
+
395
+ const elementState = captureElementState(element);
396
+ if (elementState) {
397
+ enhancedData.element_state = elementState;
398
+ }
399
+
400
+ const perfMetrics = getPerformanceMetrics();
401
+ if (perfMetrics) {
402
+ enhancedData.performance_metrics = perfMetrics;
403
+ }
404
+
405
+ return enhancedData;
406
+ }