@dynamicu/chromedebug-mcp 2.6.6 → 2.7.0
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.
- package/CLAUDE.md +1 -1
- package/README.md +1 -1
- package/chrome-extension/activation-manager.js +18 -4
- package/chrome-extension/background.js +1044 -552
- package/chrome-extension/browser-recording-manager.js +256 -0
- package/chrome-extension/chrome-debug-logger.js +168 -0
- package/chrome-extension/console-interception-library.js +430 -0
- package/chrome-extension/content.css +16 -16
- package/chrome-extension/content.js +617 -215
- package/chrome-extension/data-buffer.js +206 -17
- package/chrome-extension/extension-config.js +1 -1
- package/chrome-extension/frame-capture.js +52 -15
- package/chrome-extension/license-helper.js +26 -0
- package/chrome-extension/manifest.free.json +3 -6
- package/chrome-extension/options.js +1 -1
- package/chrome-extension/popup.html +315 -181
- package/chrome-extension/popup.js +673 -526
- package/chrome-extension/pro/enhanced-capture.js +406 -0
- package/chrome-extension/pro/frame-editor.html +410 -0
- package/chrome-extension/pro/frame-editor.js +1496 -0
- package/chrome-extension/pro/function-tracker.js +843 -0
- package/chrome-extension/pro/jszip.min.js +13 -0
- package/config/chromedebug-config.json +101 -0
- package/dist/chromedebug-extension-free.zip +0 -0
- package/package.json +3 -1
- package/scripts/package-pro-extension.js +1 -1
- package/scripts/webpack.config.free.cjs +11 -8
- package/scripts/webpack.config.pro.cjs +5 -0
- package/src/chrome-controller.js +7 -7
- package/src/cli.js +2 -2
- package/src/database.js +61 -9
- package/src/http-server.js +3 -2
- package/src/index.js +9 -6
- package/src/mcp/server.js +2 -2
- package/src/services/process-manager.js +10 -6
- package/src/services/process-tracker.js +10 -5
- package/src/services/profile-manager.js +17 -2
- package/src/validation/schemas.js +36 -6
- package/src/index-direct.js +0 -157
- package/src/index-modular.js +0 -219
- package/src/index-monolithic-backup.js +0 -2230
- package/src/legacy/chrome-controller-old.js +0 -1406
- package/src/legacy/index-express.js +0 -625
- package/src/legacy/index-old.js +0 -977
- package/src/legacy/routes.js +0 -260
- 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
|
+
}
|