@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
|
@@ -297,7 +297,7 @@ function getSelector(element) {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
if (element.className && typeof element.className === 'string') {
|
|
300
|
-
const classes = element.className.trim().split(/\s+/).filter(c => !c.startsWith('chrome-
|
|
300
|
+
const classes = element.className.trim().split(/\s+/).filter(c => !c.startsWith('chrome-debug'));
|
|
301
301
|
if (classes.length > 0) {
|
|
302
302
|
return `${element.tagName.toLowerCase()}.${classes.join('.')}`;
|
|
303
303
|
}
|
|
@@ -398,25 +398,25 @@ function getComponentInfo(element) {
|
|
|
398
398
|
// Idempotent protection for cleanup function
|
|
399
399
|
let cleanupExecuted = false;
|
|
400
400
|
|
|
401
|
-
function cleanup() {
|
|
401
|
+
async function cleanup() {
|
|
402
402
|
// Idempotent protection - only run cleanup once
|
|
403
403
|
if (cleanupExecuted) {
|
|
404
404
|
console.log('[ChromeDebug MCP] Cleanup already executed, skipping');
|
|
405
405
|
return;
|
|
406
406
|
}
|
|
407
|
-
|
|
407
|
+
|
|
408
408
|
cleanupExecuted = true;
|
|
409
409
|
console.log('[ChromeDebug MCP] Executing cleanup...');
|
|
410
|
-
|
|
410
|
+
|
|
411
411
|
// Clean up recording indicators and event listeners
|
|
412
412
|
try {
|
|
413
413
|
// Legacy recording indicator cleanup removed - now handled by ScreenCaptureVisualFeedback
|
|
414
|
-
|
|
414
|
+
|
|
415
415
|
// Stop workflow recording if active
|
|
416
416
|
if (typeof isWorkflowRecording !== 'undefined' && isWorkflowRecording) {
|
|
417
417
|
console.log('[ChromeDebug MCP] Stopping workflow recording during cleanup');
|
|
418
418
|
if (typeof stopWorkflowRecording === 'function') {
|
|
419
|
-
stopWorkflowRecording();
|
|
419
|
+
await stopWorkflowRecording();
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
|
|
@@ -460,7 +460,7 @@ if (isExtensionValid()) {
|
|
|
460
460
|
// Show the recording ID in the floating UI
|
|
461
461
|
console.log('Recording complete with ID:', request.recordingId);
|
|
462
462
|
if (recordingOverlay) {
|
|
463
|
-
const statusDiv = document.getElementById('chrome-
|
|
463
|
+
const statusDiv = document.getElementById('chrome-debug-recording-status');
|
|
464
464
|
if (statusDiv && request.recordingId) {
|
|
465
465
|
statusDiv.innerHTML = `<strong style="color: #4CAF50;">Recording saved!</strong><br>ID: <code style="background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-family: monospace;">${request.recordingId}</code>`;
|
|
466
466
|
|
|
@@ -494,12 +494,34 @@ if (isExtensionValid()) {
|
|
|
494
494
|
sendResponse({ success: true });
|
|
495
495
|
} else if (request.action === 'startWorkflowRecording') {
|
|
496
496
|
console.log('[ChromeDebug MCP] Starting workflow recording with settings:', request.screenshotSettings);
|
|
497
|
+
// Store tabId for use when stopping recording from mini-menu
|
|
498
|
+
workflowTabId = request.tabId;
|
|
497
499
|
startWorkflowRecording(request.screenshotSettings);
|
|
498
500
|
sendResponse({ success: true });
|
|
499
|
-
} else if (request.action === '
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
} else if (request.action === 'getWorkflowData') {
|
|
502
|
+
// Called by background script to retrieve workflow data
|
|
503
|
+
// This happens when popup stops the recording (not mini-menu)
|
|
504
|
+
console.log('[ChromeDebug MCP] Getting workflow data');
|
|
505
|
+
stopWorkflowRecording().then(async workflow => {
|
|
506
|
+
sendResponse({ success: true, workflow: workflow });
|
|
507
|
+
|
|
508
|
+
// CRITICAL FIX: Show completion UI in mini-menu after data is sent
|
|
509
|
+
// When popup stops recording, the mini-menu needs to show the completion state
|
|
510
|
+
// Get the workflowId from storage (set by background.js when recording started)
|
|
511
|
+
try {
|
|
512
|
+
const storedData = await chrome.storage.local.get(['currentWorkflowId']);
|
|
513
|
+
const workflowId = storedData.currentWorkflowId || 'Recording saved';
|
|
514
|
+
const actionCount = workflow.actions?.length || 0;
|
|
515
|
+
console.log('[ChromeDebug MCP] Showing completion UI with workflowId:', workflowId, 'actionCount:', actionCount);
|
|
516
|
+
showWorkflowRecordingComplete(workflowId, actionCount);
|
|
517
|
+
} catch (e) {
|
|
518
|
+
console.log('[ChromeDebug MCP] Could not show completion UI:', e);
|
|
519
|
+
}
|
|
520
|
+
}).catch(error => {
|
|
521
|
+
console.error('[ChromeDebug MCP] Error getting workflow data:', error);
|
|
522
|
+
sendResponse({ success: false, error: error.message });
|
|
523
|
+
});
|
|
524
|
+
return true; // Keep channel open for async response
|
|
503
525
|
} else if (request.action === 'createRestorePoint') {
|
|
504
526
|
// Create restore point for current workflow
|
|
505
527
|
createRestorePoint(request.actionIndex || workflowActions.length).then(restorePointId => {
|
|
@@ -629,7 +651,7 @@ async function playWorkflowRecording(workflow, actions) {
|
|
|
629
651
|
try {
|
|
630
652
|
// Show playback indicator
|
|
631
653
|
const playbackIndicator = document.createElement('div');
|
|
632
|
-
playbackIndicator.id = 'chrome-
|
|
654
|
+
playbackIndicator.id = 'chrome-debug-playback-indicator';
|
|
633
655
|
playbackIndicator.style.cssText = `
|
|
634
656
|
position: fixed;
|
|
635
657
|
top: 20px;
|
|
@@ -852,27 +874,73 @@ let isWorkflowRecording = false;
|
|
|
852
874
|
let workflowActions = [];
|
|
853
875
|
let workflowRecordingIndicator = null;
|
|
854
876
|
let workflowScreenshotSettings = null;
|
|
877
|
+
let workflowTabId = null; // Store tabId for use when stopping recording
|
|
878
|
+
let lastScreenshotTime = 0;
|
|
879
|
+
let pendingScreenshots = []; // Track pending screenshot captures
|
|
880
|
+
|
|
881
|
+
// Async screenshot capture without blocking click recording
|
|
882
|
+
function captureScreenshotAsync(action, actionIndex) {
|
|
883
|
+
// Create the async capture promise
|
|
884
|
+
const capturePromise = (async () => {
|
|
885
|
+
try {
|
|
886
|
+
// Chrome API rate limit: ~1-2 screenshots per second
|
|
887
|
+
// We enforce 600ms minimum between screenshots to stay under the limit
|
|
888
|
+
const MIN_SCREENSHOT_INTERVAL = 600; // milliseconds
|
|
889
|
+
const now = Date.now();
|
|
890
|
+
const timeSinceLastScreenshot = now - lastScreenshotTime;
|
|
891
|
+
|
|
892
|
+
if (timeSinceLastScreenshot < MIN_SCREENSHOT_INTERVAL) {
|
|
893
|
+
// Wait until we can safely capture
|
|
894
|
+
const waitTime = MIN_SCREENSHOT_INTERVAL - timeSinceLastScreenshot;
|
|
895
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const screenshotData = await captureWorkflowScreenshot();
|
|
899
|
+
|
|
900
|
+
if (screenshotData) {
|
|
901
|
+
// Add screenshot to the action object (it's already in the array)
|
|
902
|
+
action.screenshot_data = screenshotData;
|
|
903
|
+
lastScreenshotTime = Date.now(); // Update timestamp after successful capture
|
|
904
|
+
}
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error(`Error capturing screenshot for action ${actionIndex}:`, error);
|
|
907
|
+
}
|
|
908
|
+
})();
|
|
909
|
+
|
|
910
|
+
// Track this promise in pendingScreenshots
|
|
911
|
+
pendingScreenshots.push(capturePromise);
|
|
912
|
+
|
|
913
|
+
// Remove from pending when done (success or failure)
|
|
914
|
+
capturePromise.finally(() => {
|
|
915
|
+
const index = pendingScreenshots.indexOf(capturePromise);
|
|
916
|
+
if (index > -1) {
|
|
917
|
+
pendingScreenshots.splice(index, 1);
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
return capturePromise;
|
|
922
|
+
}
|
|
855
923
|
|
|
856
924
|
// Helper function to capture screenshot
|
|
857
925
|
async function captureWorkflowScreenshot() {
|
|
858
926
|
if (!workflowScreenshotSettings || !workflowScreenshotSettings.enabled) {
|
|
859
927
|
return null;
|
|
860
928
|
}
|
|
861
|
-
|
|
929
|
+
|
|
862
930
|
try {
|
|
863
931
|
// Send message to background script to capture screenshot
|
|
864
932
|
const response = await chrome.runtime.sendMessage({
|
|
865
933
|
action: 'captureWorkflowScreenshot',
|
|
866
934
|
settings: workflowScreenshotSettings
|
|
867
935
|
});
|
|
868
|
-
|
|
936
|
+
|
|
869
937
|
if (response && response.success) {
|
|
870
938
|
return response.screenshotData;
|
|
871
939
|
}
|
|
872
940
|
} catch (error) {
|
|
873
|
-
console.error('
|
|
941
|
+
console.error('Error in captureWorkflowScreenshot:', error);
|
|
874
942
|
}
|
|
875
|
-
|
|
943
|
+
|
|
876
944
|
return null;
|
|
877
945
|
}
|
|
878
946
|
|
|
@@ -886,7 +954,7 @@ function getUniqueSelector(element) {
|
|
|
886
954
|
// Try unique class combination
|
|
887
955
|
if (element.className && typeof element.className === 'string') {
|
|
888
956
|
const classes = element.className.trim().split(/\s+/)
|
|
889
|
-
.filter(c => !c.startsWith('chrome-
|
|
957
|
+
.filter(c => !c.startsWith('chrome-debug'))
|
|
890
958
|
.filter(c => !c.includes(':')) // Filter out pseudo-class artifacts
|
|
891
959
|
.map(c => {
|
|
892
960
|
// Escape special characters in class names
|
|
@@ -935,18 +1003,17 @@ function getXPath(element) {
|
|
|
935
1003
|
|
|
936
1004
|
// Create workflow recording indicator
|
|
937
1005
|
function createWorkflowRecordingIndicator() {
|
|
938
|
-
|
|
939
|
-
|
|
1006
|
+
// CRITICAL FIX: If old indicator exists (showing completion), remove it completely
|
|
1007
|
+
// This ensures the old popup closes immediately before showing the new recording indicator
|
|
1008
|
+
if (workflowRecordingIndicator && workflowRecordingIndicator.parentNode) {
|
|
1009
|
+
workflowRecordingIndicator.parentNode.removeChild(workflowRecordingIndicator);
|
|
1010
|
+
workflowRecordingIndicator = null;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// Always create fresh element for new recording
|
|
940
1014
|
workflowRecordingIndicator = document.createElement('div');
|
|
941
|
-
workflowRecordingIndicator.id = 'chrome-
|
|
942
|
-
|
|
943
|
-
<div style="display: flex; align-items: center; gap: 8px;">
|
|
944
|
-
<span style="color: #9c27b0; font-size: 20px; animation: pulse 1.5s infinite;">●</span>
|
|
945
|
-
<span>Recording Workflow...</span>
|
|
946
|
-
<span id="workflow-action-count" style="background: rgba(255,255,255,0.2); padding: 2px 8px; border-radius: 12px; font-size: 12px;">0 actions</span>
|
|
947
|
-
</div>
|
|
948
|
-
`;
|
|
949
|
-
|
|
1015
|
+
workflowRecordingIndicator.id = 'chrome-debug-workflow-indicator';
|
|
1016
|
+
|
|
950
1017
|
Object.assign(workflowRecordingIndicator.style, {
|
|
951
1018
|
position: 'fixed',
|
|
952
1019
|
bottom: '20px',
|
|
@@ -962,11 +1029,77 @@ function createWorkflowRecordingIndicator() {
|
|
|
962
1029
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
|
|
963
1030
|
backdropFilter: 'blur(10px)',
|
|
964
1031
|
WebkitBackdropFilter: 'blur(10px)',
|
|
965
|
-
pointerEvents: '
|
|
1032
|
+
pointerEvents: 'auto',
|
|
966
1033
|
userSelect: 'none'
|
|
967
1034
|
});
|
|
968
|
-
|
|
1035
|
+
|
|
1036
|
+
workflowRecordingIndicator.innerHTML = `
|
|
1037
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
1038
|
+
<div style="display: flex; align-items: center; gap: 8px; flex: 1;">
|
|
1039
|
+
<span style="color: #9c27b0; font-size: 20px; animation: pulse 1.5s infinite;">●</span>
|
|
1040
|
+
<span>Recording Workflow...</span>
|
|
1041
|
+
<span id="workflow-action-count" style="background: rgba(255,255,255,0.2); padding: 2px 8px; border-radius: 12px; font-size: 12px;">0 actions</span>
|
|
1042
|
+
</div>
|
|
1043
|
+
<button id="workflow-stop-btn" style="background: #f44336; color: white; border: none; padding: 6px 16px; border-radius: 15px; font-size: 12px; font-weight: 500; cursor: pointer; font-family: inherit;">Stop</button>
|
|
1044
|
+
<button id="workflow-close-btn" style="background: transparent; color: white; border: none; padding: 4px 8px; font-size: 16px; cursor: pointer; font-family: inherit; opacity: 0.7;" title="Close notification">×</button>
|
|
1045
|
+
</div>
|
|
1046
|
+
`;
|
|
1047
|
+
|
|
969
1048
|
document.body.appendChild(workflowRecordingIndicator);
|
|
1049
|
+
|
|
1050
|
+
// Add stop button click handler
|
|
1051
|
+
const stopBtn = document.getElementById('workflow-stop-btn');
|
|
1052
|
+
const closeBtn = document.getElementById('workflow-close-btn');
|
|
1053
|
+
|
|
1054
|
+
if (stopBtn) {
|
|
1055
|
+
stopBtn.addEventListener('click', async (e) => {
|
|
1056
|
+
e.stopPropagation();
|
|
1057
|
+
stopBtn.disabled = true;
|
|
1058
|
+
stopBtn.textContent = 'Stopping...';
|
|
1059
|
+
|
|
1060
|
+
try {
|
|
1061
|
+
// Stop workflow recording via background script (which handles saving automatically)
|
|
1062
|
+
// Include tabId explicitly - sender.tab.id can be undefined in some edge cases
|
|
1063
|
+
chrome.runtime.sendMessage({
|
|
1064
|
+
action: 'stopWorkflowRecording',
|
|
1065
|
+
tabId: workflowTabId
|
|
1066
|
+
}, (response) => {
|
|
1067
|
+
if (chrome.runtime.lastError) {
|
|
1068
|
+
console.error('[WorkflowRecording] Error:', chrome.runtime.lastError.message);
|
|
1069
|
+
stopBtn.textContent = 'Error';
|
|
1070
|
+
stopBtn.style.background = '#666';
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
if (response && response.success) {
|
|
1075
|
+
console.log('[WorkflowRecording] Stop response:', response);
|
|
1076
|
+
const workflowId = response.workflow?.sessionId || 'unknown';
|
|
1077
|
+
const actionCount = response.workflow?.actions?.length || 0;
|
|
1078
|
+
console.log('[WorkflowRecording] Extracted workflowId:', workflowId, 'actionCount:', actionCount);
|
|
1079
|
+
// Show completion with action buttons
|
|
1080
|
+
showWorkflowRecordingComplete(workflowId, actionCount);
|
|
1081
|
+
} else {
|
|
1082
|
+
console.error('[WorkflowRecording] Failed to save:', response?.error);
|
|
1083
|
+
// Don't remove indicator, just show error
|
|
1084
|
+
stopBtn.textContent = 'Error';
|
|
1085
|
+
stopBtn.style.background = '#666';
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
} catch (error) {
|
|
1089
|
+
console.error('[WorkflowRecording] Error stopping:', error);
|
|
1090
|
+
// Don't remove indicator, just show error
|
|
1091
|
+
stopBtn.textContent = 'Error';
|
|
1092
|
+
stopBtn.style.background = '#666';
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (closeBtn) {
|
|
1098
|
+
closeBtn.addEventListener('click', (e) => {
|
|
1099
|
+
e.stopPropagation();
|
|
1100
|
+
removeWorkflowRecordingIndicator();
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
970
1103
|
}
|
|
971
1104
|
|
|
972
1105
|
function removeWorkflowRecordingIndicator() {
|
|
@@ -983,6 +1116,97 @@ function updateWorkflowActionCount() {
|
|
|
983
1116
|
}
|
|
984
1117
|
}
|
|
985
1118
|
|
|
1119
|
+
// Show workflow recording completion with action buttons
|
|
1120
|
+
function showWorkflowRecordingComplete(workflowId, actionCount) {
|
|
1121
|
+
if (!workflowRecordingIndicator) return;
|
|
1122
|
+
|
|
1123
|
+
workflowRecordingIndicator.innerHTML = `
|
|
1124
|
+
<div style="display: flex; flex-direction: column; gap: 10px; position: relative; padding-right: 30px;">
|
|
1125
|
+
<button class="wf-close-complete" style="position: absolute; top: -8px; right: -8px; background: transparent; color: white; border: none; padding: 4px 8px; font-size: 20px; cursor: pointer; font-family: inherit; opacity: 0.7; line-height: 1;" title="Close notification">×</button>
|
|
1126
|
+
|
|
1127
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1128
|
+
<span style="color: #4CAF50; font-size: 16px;">✓</span>
|
|
1129
|
+
<span style="font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 400px;" title="${workflowId}">${workflowId}</span>
|
|
1130
|
+
</div>
|
|
1131
|
+
|
|
1132
|
+
<div style="font-size: 11px; color: rgba(255,255,255,0.7); margin-top: -5px;">
|
|
1133
|
+
${actionCount} actions • ${new Date().toLocaleString()}
|
|
1134
|
+
</div>
|
|
1135
|
+
|
|
1136
|
+
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
|
|
1137
|
+
<button class="wf-copy-id" data-id="${workflowId}" style="background: #2196F3; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Copy ID</button>
|
|
1138
|
+
<button class="wf-copy-prompt" data-id="${workflowId}" style="background: #4CAF50; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Copy Prompt</button>
|
|
1139
|
+
<button class="wf-view" data-id="${workflowId}" style="background: #9C27B0; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">View</button>
|
|
1140
|
+
<button class="wf-delete" data-id="${workflowId}" style="background: #f44336; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Delete</button>
|
|
1141
|
+
</div>
|
|
1142
|
+
</div>
|
|
1143
|
+
`;
|
|
1144
|
+
|
|
1145
|
+
// Add event listeners for action buttons
|
|
1146
|
+
const copyIdBtn = workflowRecordingIndicator.querySelector('.wf-copy-id');
|
|
1147
|
+
const copyPromptBtn = workflowRecordingIndicator.querySelector('.wf-copy-prompt');
|
|
1148
|
+
const viewBtn = workflowRecordingIndicator.querySelector('.wf-view');
|
|
1149
|
+
const deleteBtn = workflowRecordingIndicator.querySelector('.wf-delete');
|
|
1150
|
+
const closeBtn = workflowRecordingIndicator.querySelector('.wf-close-complete');
|
|
1151
|
+
|
|
1152
|
+
if (copyIdBtn) {
|
|
1153
|
+
copyIdBtn.addEventListener('click', async () => {
|
|
1154
|
+
try {
|
|
1155
|
+
await navigator.clipboard.writeText(workflowId);
|
|
1156
|
+
copyIdBtn.textContent = 'Copied!';
|
|
1157
|
+
setTimeout(() => copyIdBtn.textContent = 'Copy ID', 2000);
|
|
1158
|
+
} catch (error) {
|
|
1159
|
+
console.error('[WorkflowRecording] Failed to copy ID:', error);
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (copyPromptBtn) {
|
|
1165
|
+
copyPromptBtn.addEventListener('click', async () => {
|
|
1166
|
+
const prompt = `Please use the get_workflow_recording function in Chrome Debug to load the workflow recording "${workflowId}".`;
|
|
1167
|
+
try {
|
|
1168
|
+
await navigator.clipboard.writeText(prompt);
|
|
1169
|
+
copyPromptBtn.textContent = 'Copied!';
|
|
1170
|
+
setTimeout(() => copyPromptBtn.textContent = 'Copy Prompt', 2000);
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
console.error('[WorkflowRecording] Failed to copy prompt:', error);
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if (viewBtn) {
|
|
1178
|
+
viewBtn.addEventListener('click', () => {
|
|
1179
|
+
// Ask background to open frame editor (content scripts can't create tabs)
|
|
1180
|
+
chrome.runtime.sendMessage({
|
|
1181
|
+
action: 'openFrameEditor',
|
|
1182
|
+
sessionId: workflowId,
|
|
1183
|
+
type: 'workflow'
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
if (deleteBtn) {
|
|
1189
|
+
deleteBtn.addEventListener('click', async () => {
|
|
1190
|
+
if (confirm('Delete this workflow recording?')) {
|
|
1191
|
+
chrome.runtime.sendMessage({
|
|
1192
|
+
action: 'deleteWorkflowRecording',
|
|
1193
|
+
workflowId: workflowId
|
|
1194
|
+
}, (response) => {
|
|
1195
|
+
if (response && response.success) {
|
|
1196
|
+
removeWorkflowRecordingIndicator();
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (closeBtn) {
|
|
1204
|
+
closeBtn.addEventListener('click', () => {
|
|
1205
|
+
removeWorkflowRecordingIndicator();
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
986
1210
|
// Enhanced click tracking helper functions - Pro Feature
|
|
987
1211
|
// Try to import enhanced capture module (only exists in pro build)
|
|
988
1212
|
let enhancedCaptureModule = null;
|
|
@@ -1024,16 +1248,13 @@ async function shouldEnhanceCapture() {
|
|
|
1024
1248
|
}
|
|
1025
1249
|
|
|
1026
1250
|
// Record click action
|
|
1027
|
-
|
|
1251
|
+
function recordClick(event) {
|
|
1028
1252
|
if (!isWorkflowRecording) {
|
|
1029
|
-
console.log('[ChromePilot] Click ignored - not recording');
|
|
1030
1253
|
return;
|
|
1031
1254
|
}
|
|
1032
|
-
|
|
1033
|
-
console.log('[ChromePilot] Recording click event');
|
|
1034
|
-
|
|
1255
|
+
|
|
1035
1256
|
let element = event.target;
|
|
1036
|
-
|
|
1257
|
+
|
|
1037
1258
|
// If clicking on an SVG element or its children, find the parent button/link
|
|
1038
1259
|
if (element.tagName === 'svg' || element.tagName === 'path' || element.tagName === 'g' || element.tagName === 'circle' || element.tagName === 'rect') {
|
|
1039
1260
|
const clickableParent = element.closest('button, a, [role="button"], [onclick]');
|
|
@@ -1042,7 +1263,7 @@ async function recordClick(event) {
|
|
|
1042
1263
|
element = clickableParent;
|
|
1043
1264
|
}
|
|
1044
1265
|
}
|
|
1045
|
-
|
|
1266
|
+
|
|
1046
1267
|
const action = {
|
|
1047
1268
|
type: 'click',
|
|
1048
1269
|
selector: getUniqueSelector(element),
|
|
@@ -1057,92 +1278,89 @@ async function recordClick(event) {
|
|
|
1057
1278
|
action.text = element.textContent.trim();
|
|
1058
1279
|
}
|
|
1059
1280
|
|
|
1060
|
-
//
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
try {
|
|
1064
|
-
console.log('[ChromePilot] Capturing enhanced click data...');
|
|
1281
|
+
// Record the action immediately (synchronously) - don't wait for anything
|
|
1282
|
+
workflowActions.push(action);
|
|
1283
|
+
const actionIndex = workflowActions.length - 1;
|
|
1065
1284
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1285
|
+
// Enhanced click tracking data (async, non-blocking)
|
|
1286
|
+
// Check setting and apply enhanced data asynchronously without blocking
|
|
1287
|
+
shouldEnhanceCapture().then(enhanceCapture => {
|
|
1288
|
+
if (enhanceCapture) {
|
|
1289
|
+
try {
|
|
1290
|
+
// Capture enhanced element data (only include fields with meaningful values)
|
|
1291
|
+
const capturedHTML = captureElementHTML(element);
|
|
1292
|
+
if (capturedHTML) {
|
|
1293
|
+
action.element_html = capturedHTML;
|
|
1294
|
+
}
|
|
1071
1295
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1296
|
+
const componentData = getEnhancedComponentInfo(element);
|
|
1297
|
+
if (componentData) {
|
|
1298
|
+
try {
|
|
1299
|
+
JSON.stringify(componentData); // Test if serializable
|
|
1300
|
+
action.component_data = componentData;
|
|
1301
|
+
} catch (e) {
|
|
1302
|
+
console.error('[ChromePilot] Component data cannot be serialized:', e.message);
|
|
1303
|
+
action.component_data = { error: 'Serialization failed', type: typeof componentData };
|
|
1304
|
+
}
|
|
1080
1305
|
}
|
|
1081
|
-
}
|
|
1082
1306
|
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1307
|
+
const eventHandlers = extractEventHandlers(element);
|
|
1308
|
+
if (eventHandlers) {
|
|
1309
|
+
try {
|
|
1310
|
+
JSON.stringify(eventHandlers); // Test if serializable
|
|
1311
|
+
action.event_handlers = eventHandlers;
|
|
1312
|
+
} catch (e) {
|
|
1313
|
+
console.error('[ChromePilot] Event handlers cannot be serialized:', e.message);
|
|
1314
|
+
action.event_handlers = { error: 'Serialization failed', type: typeof eventHandlers };
|
|
1315
|
+
}
|
|
1091
1316
|
}
|
|
1092
|
-
}
|
|
1093
1317
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1318
|
+
const elementState = captureElementState(element);
|
|
1319
|
+
if (elementState) {
|
|
1320
|
+
try {
|
|
1321
|
+
JSON.stringify(elementState); // Test if serializable
|
|
1322
|
+
action.element_state = elementState;
|
|
1323
|
+
} catch (e) {
|
|
1324
|
+
console.error('[ChromePilot] Element state cannot be serialized:', e.message);
|
|
1325
|
+
action.element_state = { error: 'Serialization failed', type: typeof elementState };
|
|
1326
|
+
}
|
|
1102
1327
|
}
|
|
1103
|
-
}
|
|
1104
1328
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1329
|
+
const perfMetrics = getPerformanceMetrics();
|
|
1330
|
+
if (perfMetrics) {
|
|
1331
|
+
try {
|
|
1332
|
+
JSON.stringify(perfMetrics); // Test if serializable
|
|
1333
|
+
action.performance_metrics = perfMetrics;
|
|
1334
|
+
} catch (e) {
|
|
1335
|
+
console.error('[ChromePilot] Performance metrics cannot be serialized:', e.message);
|
|
1336
|
+
action.performance_metrics = { error: 'Serialization failed', type: typeof perfMetrics };
|
|
1337
|
+
}
|
|
1113
1338
|
}
|
|
1114
|
-
}
|
|
1115
1339
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1340
|
+
// Enhanced click data captured successfully - no need to log
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
console.warn('[ChromePilot] Error capturing enhanced click data:', error);
|
|
1343
|
+
// Continue with basic click recording if enhanced capture fails
|
|
1344
|
+
}
|
|
1120
1345
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1346
|
+
// Enhanced capture setting checked - no need to log state
|
|
1347
|
+
}).catch(error => {
|
|
1348
|
+
console.error('[ChromePilot] Error checking enhanced capture setting:', error);
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
// Capture screenshot asynchronously without blocking
|
|
1126
1352
|
if (workflowScreenshotSettings && workflowScreenshotSettings.enabled) {
|
|
1127
|
-
//
|
|
1128
|
-
|
|
1129
|
-
const screenshotData = await captureWorkflowScreenshot();
|
|
1130
|
-
if (screenshotData) {
|
|
1131
|
-
action.screenshot_data = screenshotData;
|
|
1132
|
-
}
|
|
1133
|
-
}, 100);
|
|
1353
|
+
// Don't await - let screenshot capture happen in background
|
|
1354
|
+
captureScreenshotAsync(action, actionIndex);
|
|
1134
1355
|
}
|
|
1135
|
-
|
|
1136
|
-
workflowActions.push(action);
|
|
1137
|
-
console.log('[ChromePilot] Action recorded, total actions:', workflowActions.length);
|
|
1138
1356
|
updateWorkflowActionCount();
|
|
1139
|
-
|
|
1357
|
+
|
|
1140
1358
|
// Visual feedback
|
|
1141
1359
|
flashElement(element);
|
|
1142
1360
|
}
|
|
1143
1361
|
|
|
1144
1362
|
// Record input changes
|
|
1145
|
-
|
|
1363
|
+
function recordInput(event) {
|
|
1146
1364
|
if (!isWorkflowRecording) return;
|
|
1147
1365
|
|
|
1148
1366
|
const element = event.target;
|
|
@@ -1163,20 +1381,13 @@ async function recordInput(event) {
|
|
|
1163
1381
|
if (element.placeholder) {
|
|
1164
1382
|
action.placeholder = element.placeholder;
|
|
1165
1383
|
}
|
|
1166
|
-
|
|
1167
|
-
//
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
const screenshotData = await captureWorkflowScreenshot();
|
|
1171
|
-
if (screenshotData) {
|
|
1172
|
-
action.screenshot_data = screenshotData;
|
|
1173
|
-
}
|
|
1174
|
-
}, 100);
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1384
|
+
|
|
1385
|
+
// NOTE: Screenshots are only captured for clicks, not input events
|
|
1386
|
+
// This prevents multiple screenshot attempts for a single user action
|
|
1387
|
+
|
|
1177
1388
|
workflowActions.push(action);
|
|
1178
1389
|
updateWorkflowActionCount();
|
|
1179
|
-
|
|
1390
|
+
|
|
1180
1391
|
// Visual feedback
|
|
1181
1392
|
flashElement(element);
|
|
1182
1393
|
}
|
|
@@ -1256,6 +1467,8 @@ function startWorkflowRecording(screenshotSettings) {
|
|
|
1256
1467
|
isWorkflowRecording = true;
|
|
1257
1468
|
workflowActions = [];
|
|
1258
1469
|
workflowScreenshotSettings = screenshotSettings;
|
|
1470
|
+
lastScreenshotTime = 0; // Reset rate limit timer
|
|
1471
|
+
pendingScreenshots = []; // Clear any pending screenshots from previous recording
|
|
1259
1472
|
createWorkflowRecordingIndicator();
|
|
1260
1473
|
|
|
1261
1474
|
console.log('[ChromePilot] Workflow recording started with settings:', screenshotSettings);
|
|
@@ -1289,8 +1502,9 @@ function startWorkflowRecording(screenshotSettings) {
|
|
|
1289
1502
|
timestamp: event.timestamp,
|
|
1290
1503
|
stack: event.call_stack || null
|
|
1291
1504
|
});
|
|
1292
|
-
|
|
1293
|
-
console.log
|
|
1505
|
+
|
|
1506
|
+
// REMOVED: console.log that logged EVERY function execution (thousands per second)
|
|
1507
|
+
// Function traces are already stored in workflowActions array - no need to log each one
|
|
1294
1508
|
}
|
|
1295
1509
|
});
|
|
1296
1510
|
};
|
|
@@ -1299,7 +1513,8 @@ function startWorkflowRecording(screenshotSettings) {
|
|
|
1299
1513
|
window.ChromePilotTracker._setRecordingStatus(true);
|
|
1300
1514
|
console.log('[ChromePilot] Connected workflow recording to FunctionTracker');
|
|
1301
1515
|
} else {
|
|
1302
|
-
|
|
1516
|
+
// FunctionTracker is a PRO feature - use debug level to avoid confusing FREE users
|
|
1517
|
+
console.debug('[ChromePilot] FunctionTracker not available (PRO feature)');
|
|
1303
1518
|
}
|
|
1304
1519
|
|
|
1305
1520
|
// Add event listeners for recording
|
|
@@ -1326,11 +1541,22 @@ function startWorkflowRecording(screenshotSettings) {
|
|
|
1326
1541
|
}
|
|
1327
1542
|
|
|
1328
1543
|
// Stop workflow recording
|
|
1329
|
-
function stopWorkflowRecording() {
|
|
1544
|
+
async function stopWorkflowRecording() {
|
|
1330
1545
|
isWorkflowRecording = false;
|
|
1331
|
-
|
|
1332
|
-
|
|
1546
|
+
workflowTabId = null; // Reset tabId when recording stops
|
|
1547
|
+
// DON'T remove indicator here - let the completion screen handle it
|
|
1548
|
+
// removeWorkflowRecordingIndicator();
|
|
1549
|
+
|
|
1333
1550
|
console.log(`[ChromePilot] Stopping recording. Captured ${workflowActions.length} actions`);
|
|
1551
|
+
|
|
1552
|
+
// Wait for all pending screenshots to complete
|
|
1553
|
+
if (pendingScreenshots.length > 0) {
|
|
1554
|
+
console.log(`[SCREENSHOT-DEBUG] Waiting for ${pendingScreenshots.length} pending screenshots to complete...`);
|
|
1555
|
+
await Promise.all(pendingScreenshots);
|
|
1556
|
+
console.log('[SCREENSHOT-DEBUG] All pending screenshots completed!');
|
|
1557
|
+
} else {
|
|
1558
|
+
console.log('[SCREENSHOT-DEBUG] No pending screenshots to wait for');
|
|
1559
|
+
}
|
|
1334
1560
|
|
|
1335
1561
|
// Disconnect from FunctionTracker and disable recording
|
|
1336
1562
|
if (window.ChromePilotTracker && window.ChromePilotTracker._functionTracker) {
|
|
@@ -1387,6 +1613,22 @@ function stopWorkflowRecording() {
|
|
|
1387
1613
|
functionTraces = [...functionTraces, ...embeddedTraces];
|
|
1388
1614
|
}
|
|
1389
1615
|
|
|
1616
|
+
// Log screenshot data before returning
|
|
1617
|
+
console.log('[SCREENSHOT-DEBUG] stopWorkflowRecording - Preparing to return workflow');
|
|
1618
|
+
console.log('[SCREENSHOT-DEBUG] stopWorkflowRecording - Total actions:', workflowActions.length);
|
|
1619
|
+
|
|
1620
|
+
const actionsWithScreenshots = workflowActions.filter(a => a.screenshot_data);
|
|
1621
|
+
console.log('[SCREENSHOT-DEBUG] stopWorkflowRecording - Actions WITH screenshot_data:', actionsWithScreenshots.length);
|
|
1622
|
+
|
|
1623
|
+
workflowActions.forEach((action, index) => {
|
|
1624
|
+
console.log(`[SCREENSHOT-DEBUG] stopWorkflowRecording - Action ${index}:`, {
|
|
1625
|
+
type: action.type,
|
|
1626
|
+
hasScreenshotData: !!action.screenshot_data,
|
|
1627
|
+
screenshotDataLength: action.screenshot_data?.length,
|
|
1628
|
+
screenshotPreview: action.screenshot_data?.substring(0, 50)
|
|
1629
|
+
});
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1390
1632
|
// Return the recorded workflow with proper functionTraces field
|
|
1391
1633
|
return {
|
|
1392
1634
|
actions: workflowActions,
|
|
@@ -1732,8 +1974,6 @@ async function recordScreenClick(event) {
|
|
|
1732
1974
|
const enhanceCapture = await shouldEnhanceCapture();
|
|
1733
1975
|
if (enhanceCapture) {
|
|
1734
1976
|
try {
|
|
1735
|
-
console.log('[ChromePilot] Capturing enhanced screen click data...');
|
|
1736
|
-
|
|
1737
1977
|
// Capture enhanced element data (only include fields with meaningful values)
|
|
1738
1978
|
const capturedHTML = captureElementHTML(element);
|
|
1739
1979
|
if (capturedHTML) {
|
|
@@ -1784,14 +2024,13 @@ async function recordScreenClick(event) {
|
|
|
1784
2024
|
}
|
|
1785
2025
|
}
|
|
1786
2026
|
|
|
1787
|
-
|
|
2027
|
+
// Enhanced screen click data captured successfully - no need to log
|
|
1788
2028
|
} catch (error) {
|
|
1789
2029
|
console.warn('[ChromePilot] Error capturing enhanced screen click data:', error);
|
|
1790
2030
|
// Continue with basic click recording if enhanced capture fails
|
|
1791
2031
|
}
|
|
1792
|
-
} else {
|
|
1793
|
-
console.log('[ChromePilot] Enhanced screen click capture disabled - using basic capture only');
|
|
1794
2032
|
}
|
|
2033
|
+
// Enhanced capture setting checked - no need to log state
|
|
1795
2034
|
|
|
1796
2035
|
sendScreenInteraction(interaction);
|
|
1797
2036
|
|
|
@@ -2087,7 +2326,7 @@ function showRecordingInterface() {
|
|
|
2087
2326
|
|
|
2088
2327
|
// Create recording overlay
|
|
2089
2328
|
recordingOverlay = document.createElement('div');
|
|
2090
|
-
recordingOverlay.id = 'chrome-
|
|
2329
|
+
recordingOverlay.id = 'chrome-debug-recording-overlay';
|
|
2091
2330
|
recordingOverlay.style.cssText = `
|
|
2092
2331
|
position: fixed;
|
|
2093
2332
|
bottom: 20px;
|
|
@@ -2105,13 +2344,13 @@ function showRecordingInterface() {
|
|
|
2105
2344
|
recordingOverlay.innerHTML = `
|
|
2106
2345
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
|
|
2107
2346
|
<h3 style="margin: 0; font-size: 16px; color: #333;">Screen Recording</h3>
|
|
2108
|
-
<button id="chrome-
|
|
2347
|
+
<button id="chrome-debug-close-recording" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #666;">×</button>
|
|
2109
2348
|
</div>
|
|
2110
2349
|
<p style="margin: 0 0 15px 0; font-size: 13px; color: #666;">
|
|
2111
2350
|
Recording this tab with console logs
|
|
2112
2351
|
</p>
|
|
2113
|
-
<div id="chrome-
|
|
2114
|
-
<button id="chrome-
|
|
2352
|
+
<div id="chrome-debug-recording-status" style="text-align: center; margin-bottom: 15px; font-size: 14px; color: #666;"></div>
|
|
2353
|
+
<button id="chrome-debug-record-btn" style="
|
|
2115
2354
|
width: 100%;
|
|
2116
2355
|
padding: 10px;
|
|
2117
2356
|
background: #f44336;
|
|
@@ -2122,13 +2361,13 @@ function showRecordingInterface() {
|
|
|
2122
2361
|
font-size: 14px;
|
|
2123
2362
|
font-weight: 500;
|
|
2124
2363
|
">Start Recording</button>
|
|
2125
|
-
<div id="chrome-
|
|
2364
|
+
<div id="chrome-debug-recording-result" style="margin-top: 15px; display: none;"></div>
|
|
2126
2365
|
`;
|
|
2127
2366
|
|
|
2128
2367
|
document.body.appendChild(recordingOverlay);
|
|
2129
2368
|
|
|
2130
2369
|
// Add event listeners
|
|
2131
|
-
document.getElementById('chrome-
|
|
2370
|
+
document.getElementById('chrome-debug-close-recording').addEventListener('click', () => {
|
|
2132
2371
|
if (isRecording) {
|
|
2133
2372
|
if (confirm('Stop recording and close?')) {
|
|
2134
2373
|
stopRecording();
|
|
@@ -2141,7 +2380,7 @@ function showRecordingInterface() {
|
|
|
2141
2380
|
}
|
|
2142
2381
|
});
|
|
2143
2382
|
|
|
2144
|
-
document.getElementById('chrome-
|
|
2383
|
+
document.getElementById('chrome-debug-record-btn').addEventListener('click', () => {
|
|
2145
2384
|
if (!isRecording) {
|
|
2146
2385
|
startRecording();
|
|
2147
2386
|
} else {
|
|
@@ -2152,8 +2391,8 @@ function showRecordingInterface() {
|
|
|
2152
2391
|
|
|
2153
2392
|
// Start recording from content script
|
|
2154
2393
|
function startRecording() {
|
|
2155
|
-
const recordBtn = document.getElementById('chrome-
|
|
2156
|
-
const statusDiv = document.getElementById('chrome-
|
|
2394
|
+
const recordBtn = document.getElementById('chrome-debug-record-btn');
|
|
2395
|
+
const statusDiv = document.getElementById('chrome-debug-recording-status');
|
|
2157
2396
|
|
|
2158
2397
|
recordBtn.disabled = true;
|
|
2159
2398
|
recordBtn.textContent = 'Starting...';
|
|
@@ -2200,9 +2439,9 @@ function startRecording() {
|
|
|
2200
2439
|
|
|
2201
2440
|
// Stop recording
|
|
2202
2441
|
function stopRecording() {
|
|
2203
|
-
const recordBtn = document.getElementById('chrome-
|
|
2204
|
-
const statusDiv = document.getElementById('chrome-
|
|
2205
|
-
const resultDiv = document.getElementById('chrome-
|
|
2442
|
+
const recordBtn = document.getElementById('chrome-debug-record-btn');
|
|
2443
|
+
const statusDiv = document.getElementById('chrome-debug-recording-status');
|
|
2444
|
+
const resultDiv = document.getElementById('chrome-debug-recording-result');
|
|
2206
2445
|
|
|
2207
2446
|
recordBtn.disabled = true;
|
|
2208
2447
|
recordBtn.textContent = 'Stopping...';
|
|
@@ -2664,6 +2903,7 @@ class ScreenCaptureVisualFeedback {
|
|
|
2664
2903
|
init() {
|
|
2665
2904
|
// Listen for screen capture events
|
|
2666
2905
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
2906
|
+
console.log('[ScreenCapture] Received message:', message.type, message);
|
|
2667
2907
|
switch (message.type) {
|
|
2668
2908
|
case 'start-screen-capture-tracking':
|
|
2669
2909
|
this.handleStartScreenCapture(message);
|
|
@@ -2677,11 +2917,27 @@ class ScreenCaptureVisualFeedback {
|
|
|
2677
2917
|
case 'stop-screen-capture-tracking':
|
|
2678
2918
|
this.handleStopScreenCapture(message);
|
|
2679
2919
|
break;
|
|
2920
|
+
case 'recording-complete-show-ui':
|
|
2921
|
+
// Show completion UI when stopped from popup (not from mini menu button)
|
|
2922
|
+
console.log('[ScreenCapture] Processing recording-complete-show-ui, sessionId:', message.sessionId, 'actionCount:', this.actionCount);
|
|
2923
|
+
if (message.sessionId) {
|
|
2924
|
+
console.log('[ScreenCapture] Calling showScreenRecordingComplete with sessionId:', message.sessionId);
|
|
2925
|
+
this.showScreenRecordingComplete(message.sessionId, this.actionCount);
|
|
2926
|
+
} else {
|
|
2927
|
+
console.log('[ScreenCapture] ERROR: No sessionId in recording-complete-show-ui message!');
|
|
2928
|
+
}
|
|
2929
|
+
break;
|
|
2930
|
+
default:
|
|
2931
|
+
// Log unknown message types for debugging
|
|
2932
|
+
if (message.type && message.type.includes('screen-capture') || message.type && message.type.includes('recording')) {
|
|
2933
|
+
console.log('[ScreenCapture] Unknown screen capture message type:', message.type);
|
|
2934
|
+
}
|
|
2680
2935
|
}
|
|
2681
2936
|
});
|
|
2682
2937
|
}
|
|
2683
2938
|
|
|
2684
2939
|
handleStartScreenCapture(message) {
|
|
2940
|
+
console.log('[ScreenCapture] Starting new recording, current indicator exists:', !!this.recordingIndicatorElement);
|
|
2685
2941
|
this.isRecording = true;
|
|
2686
2942
|
this.actionCount = 0;
|
|
2687
2943
|
this.showRecordingIndicator();
|
|
@@ -2728,9 +2984,11 @@ class ScreenCaptureVisualFeedback {
|
|
|
2728
2984
|
|
|
2729
2985
|
handleStopScreenCapture(message) {
|
|
2730
2986
|
this.isRecording = false;
|
|
2731
|
-
|
|
2987
|
+
// DON'T hide indicator here - let the completion screen handle it
|
|
2988
|
+
// this.hideRecordingIndicator();
|
|
2732
2989
|
this.hideActionCounter();
|
|
2733
|
-
|
|
2990
|
+
// Don't cleanup yet - we want to keep the indicator visible
|
|
2991
|
+
// this.cleanup();
|
|
2734
2992
|
}
|
|
2735
2993
|
|
|
2736
2994
|
showClickHighlight(x, y) {
|
|
@@ -2766,80 +3024,96 @@ class ScreenCaptureVisualFeedback {
|
|
|
2766
3024
|
}
|
|
2767
3025
|
|
|
2768
3026
|
showRecordingIndicator() {
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
3027
|
+
// CRITICAL FIX: Clear any existing countdown interval FIRST
|
|
3028
|
+
if (this.countdownInterval) {
|
|
3029
|
+
console.log('[ScreenCapture] Clearing existing countdown interval');
|
|
3030
|
+
clearInterval(this.countdownInterval);
|
|
3031
|
+
this.countdownInterval = null;
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// CRITICAL FIX: If old indicator exists (showing completion), remove it completely
|
|
3035
|
+
// This ensures the old popup closes immediately before showing the new countdown
|
|
3036
|
+
if (this.recordingIndicatorElement && this.recordingIndicatorElement.parentNode) {
|
|
3037
|
+
console.log('[ScreenCapture] Removing old indicator element from DOM');
|
|
3038
|
+
this.recordingIndicatorElement.parentNode.removeChild(this.recordingIndicatorElement);
|
|
3039
|
+
this.recordingIndicatorElement = null;
|
|
3040
|
+
console.log('[ScreenCapture] Old indicator removed successfully');
|
|
3041
|
+
} else {
|
|
3042
|
+
console.log('[ScreenCapture] No old indicator to remove, creating fresh');
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
// Add required CSS styles if not already added
|
|
3046
|
+
if (!document.getElementById('screen-capture-styles')) {
|
|
3047
|
+
const style = document.createElement('style');
|
|
3048
|
+
style.id = 'screen-capture-styles';
|
|
3049
|
+
style.textContent = `
|
|
3050
|
+
@keyframes pulse {
|
|
3051
|
+
0% { opacity: 1; }
|
|
3052
|
+
50% { opacity: 0.5; }
|
|
3053
|
+
100% { opacity: 1; }
|
|
3054
|
+
}
|
|
3055
|
+
@keyframes screen-capture-pulse {
|
|
3056
|
+
0% { opacity: 1; transform: scale(1); }
|
|
3057
|
+
50% { opacity: 0.7; transform: scale(1.1); }
|
|
3058
|
+
100% { opacity: 1; transform: scale(1); }
|
|
3059
|
+
}
|
|
3060
|
+
.screen-capture-click-highlight {
|
|
3061
|
+
position: fixed;
|
|
3062
|
+
width: 40px;
|
|
3063
|
+
height: 40px;
|
|
3064
|
+
border: 3px solid #4CAF50;
|
|
3065
|
+
border-radius: 50%;
|
|
3066
|
+
background: rgba(76, 175, 80, 0.3);
|
|
3067
|
+
transform: translate(-50%, -50%);
|
|
3068
|
+
pointer-events: none;
|
|
3069
|
+
z-index: 2147483646;
|
|
3070
|
+
animation: screen-capture-ripple 0.5s ease-out;
|
|
3071
|
+
}
|
|
3072
|
+
@keyframes screen-capture-ripple {
|
|
3073
|
+
0% {
|
|
3074
|
+
transform: translate(-50%, -50%) scale(0);
|
|
3075
|
+
opacity: 1;
|
|
2796
3076
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
opacity: 1;
|
|
2801
|
-
}
|
|
2802
|
-
100% {
|
|
2803
|
-
transform: translate(-50%, -50%) scale(1);
|
|
2804
|
-
opacity: 0;
|
|
2805
|
-
}
|
|
3077
|
+
100% {
|
|
3078
|
+
transform: translate(-50%, -50%) scale(1);
|
|
3079
|
+
opacity: 0;
|
|
2806
3080
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
3081
|
+
}
|
|
3082
|
+
`;
|
|
3083
|
+
document.head.appendChild(style);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
// Always create fresh element for new recording
|
|
3087
|
+
this.recordingIndicatorElement = document.createElement('div');
|
|
3088
|
+
this.recordingIndicatorElement.className = 'screen-capture-recording-indicator-unified';
|
|
3089
|
+
|
|
3090
|
+
// Use inline styles to match workflow recording exactly
|
|
3091
|
+
Object.assign(this.recordingIndicatorElement.style, {
|
|
3092
|
+
position: 'fixed',
|
|
3093
|
+
bottom: '80px', // Keep different from workflow to avoid collision
|
|
3094
|
+
left: '20px',
|
|
3095
|
+
background: 'rgba(0, 0, 0, 0.9)',
|
|
3096
|
+
color: 'white',
|
|
3097
|
+
padding: '12px 20px',
|
|
3098
|
+
borderRadius: '25px',
|
|
3099
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif',
|
|
3100
|
+
fontSize: '14px',
|
|
3101
|
+
fontWeight: '500',
|
|
3102
|
+
zIndex: '2147483647',
|
|
3103
|
+
display: 'flex',
|
|
3104
|
+
alignItems: 'center',
|
|
3105
|
+
gap: '8px',
|
|
3106
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
|
|
3107
|
+
backdropFilter: 'blur(10px)',
|
|
3108
|
+
WebkitBackdropFilter: 'blur(10px)',
|
|
3109
|
+
pointerEvents: 'none',
|
|
3110
|
+
userSelect: 'none'
|
|
3111
|
+
});
|
|
2836
3112
|
|
|
2837
|
-
|
|
2838
|
-
this.startCountdownSequence();
|
|
2839
|
-
document.body.appendChild(this.recordingIndicatorElement);
|
|
2840
|
-
}
|
|
3113
|
+
document.body.appendChild(this.recordingIndicatorElement);
|
|
2841
3114
|
|
|
2842
|
-
|
|
3115
|
+
// Start countdown sequence
|
|
3116
|
+
this.startCountdownSequence();
|
|
2843
3117
|
}
|
|
2844
3118
|
|
|
2845
3119
|
startCountdownSequence() {
|
|
@@ -2862,18 +3136,146 @@ class ScreenCaptureVisualFeedback {
|
|
|
2862
3136
|
<span class="action-count-badge" style="background: rgba(255,255,255,0.2); padding: 2px 8px; border-radius: 12px; font-size: 12px;">0 actions</span>
|
|
2863
3137
|
`;
|
|
2864
3138
|
} else {
|
|
2865
|
-
// Switch to recording display
|
|
3139
|
+
// Switch to recording display with stop button
|
|
3140
|
+
this.recordingIndicatorElement.style.pointerEvents = 'auto'; // Enable clicking
|
|
2866
3141
|
this.recordingIndicatorElement.innerHTML = `
|
|
2867
|
-
<
|
|
2868
|
-
|
|
2869
|
-
|
|
3142
|
+
<div style="display: flex; align-items: center; gap: 12px;">
|
|
3143
|
+
<div style="display: flex; align-items: center; gap: 8px; flex: 1;">
|
|
3144
|
+
<span style="color: #4CAF50; font-size: 20px; animation: pulse 1.5s infinite;">●</span>
|
|
3145
|
+
<span>Recording Screen Capture...</span>
|
|
3146
|
+
<span class="action-count-badge" style="background: rgba(255,255,255,0.2); padding: 2px 8px; border-radius: 12px; font-size: 12px;">0 actions</span>
|
|
3147
|
+
</div>
|
|
3148
|
+
<button id="screen-stop-btn" style="background: #f44336; color: white; border: none; padding: 6px 16px; border-radius: 15px; font-size: 12px; font-weight: 500; cursor: pointer; font-family: inherit;">Stop</button>
|
|
3149
|
+
<button id="screen-close-btn" style="background: transparent; color: white; border: none; padding: 4px 8px; font-size: 16px; cursor: pointer; font-family: inherit; opacity: 0.7;" title="Close notification">×</button>
|
|
3150
|
+
</div>
|
|
2870
3151
|
`;
|
|
2871
3152
|
clearInterval(this.countdownInterval);
|
|
2872
3153
|
this.countdownInterval = null;
|
|
3154
|
+
|
|
3155
|
+
// Add stop button click handler
|
|
3156
|
+
const stopBtn = document.getElementById('screen-stop-btn');
|
|
3157
|
+
const closeBtn = document.getElementById('screen-close-btn');
|
|
3158
|
+
|
|
3159
|
+
if (stopBtn) {
|
|
3160
|
+
stopBtn.addEventListener('click', async (e) => {
|
|
3161
|
+
e.stopPropagation();
|
|
3162
|
+
stopBtn.disabled = true;
|
|
3163
|
+
stopBtn.textContent = 'Stopping...';
|
|
3164
|
+
|
|
3165
|
+
// Send message to background to stop recording (correct action name)
|
|
3166
|
+
chrome.runtime.sendMessage({ action: 'stopRecording' }, (response) => {
|
|
3167
|
+
if (response && response.success && response.sessionId) {
|
|
3168
|
+
// Show completion with action buttons
|
|
3169
|
+
this.showScreenRecordingComplete(response.sessionId, this.actionCount);
|
|
3170
|
+
} else {
|
|
3171
|
+
console.error('[ScreenRecording] Failed to stop:', response?.error);
|
|
3172
|
+
// Don't hide indicator, just show error
|
|
3173
|
+
stopBtn.textContent = 'Error';
|
|
3174
|
+
stopBtn.style.background = '#666';
|
|
3175
|
+
}
|
|
3176
|
+
});
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
if (closeBtn) {
|
|
3181
|
+
closeBtn.addEventListener('click', (e) => {
|
|
3182
|
+
e.stopPropagation();
|
|
3183
|
+
this.hideRecordingIndicator();
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
2873
3186
|
}
|
|
2874
3187
|
}, 1000);
|
|
2875
3188
|
}
|
|
2876
3189
|
|
|
3190
|
+
showScreenRecordingComplete(sessionId, actionCount) {
|
|
3191
|
+
if (!this.recordingIndicatorElement) return;
|
|
3192
|
+
|
|
3193
|
+
this.recordingIndicatorElement.innerHTML = `
|
|
3194
|
+
<div style="display: flex; flex-direction: column; gap: 10px; position: relative; padding-right: 30px;">
|
|
3195
|
+
<button class="sc-close-complete" style="position: absolute; top: -8px; right: -8px; background: transparent; color: white; border: none; padding: 4px 8px; font-size: 20px; cursor: pointer; font-family: inherit; opacity: 0.7; line-height: 1;" title="Close notification">×</button>
|
|
3196
|
+
|
|
3197
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
3198
|
+
<span style="color: #4CAF50; font-size: 16px;">🎬</span>
|
|
3199
|
+
<span style="font-weight: 600; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 400px;" title="${sessionId}">${sessionId} (frames)</span>
|
|
3200
|
+
</div>
|
|
3201
|
+
|
|
3202
|
+
<div style="font-size: 11px; color: rgba(255,255,255,0.7); margin-top: -5px;">
|
|
3203
|
+
${actionCount} actions • ${new Date().toLocaleString()}
|
|
3204
|
+
</div>
|
|
3205
|
+
|
|
3206
|
+
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
|
|
3207
|
+
<button class="sc-copy-id" data-id="${sessionId}" style="background: #2196F3; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Copy ID</button>
|
|
3208
|
+
<button class="sc-copy-prompt" data-id="${sessionId}" style="background: #4CAF50; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Copy Prompt</button>
|
|
3209
|
+
<button class="sc-view" data-id="${sessionId}" style="background: #9C27B0; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">View</button>
|
|
3210
|
+
<button class="sc-delete" data-id="${sessionId}" style="background: #f44336; color: white; border: none; padding: 6px 12px; border-radius: 12px; font-size: 11px; cursor: pointer; font-family: inherit; font-weight: 500;">Delete</button>
|
|
3211
|
+
</div>
|
|
3212
|
+
</div>
|
|
3213
|
+
`;
|
|
3214
|
+
|
|
3215
|
+
// Add event listeners for action buttons
|
|
3216
|
+
const copyIdBtn = this.recordingIndicatorElement.querySelector('.sc-copy-id');
|
|
3217
|
+
const copyPromptBtn = this.recordingIndicatorElement.querySelector('.sc-copy-prompt');
|
|
3218
|
+
const viewBtn = this.recordingIndicatorElement.querySelector('.sc-view');
|
|
3219
|
+
const deleteBtn = this.recordingIndicatorElement.querySelector('.sc-delete');
|
|
3220
|
+
const closeBtn = this.recordingIndicatorElement.querySelector('.sc-close-complete');
|
|
3221
|
+
|
|
3222
|
+
if (copyIdBtn) {
|
|
3223
|
+
copyIdBtn.addEventListener('click', async () => {
|
|
3224
|
+
try {
|
|
3225
|
+
await navigator.clipboard.writeText(sessionId);
|
|
3226
|
+
copyIdBtn.textContent = 'Copied!';
|
|
3227
|
+
setTimeout(() => copyIdBtn.textContent = 'Copy ID', 2000);
|
|
3228
|
+
} catch (error) {
|
|
3229
|
+
console.error('[ScreenRecording] Failed to copy ID:', error);
|
|
3230
|
+
}
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
if (copyPromptBtn) {
|
|
3235
|
+
copyPromptBtn.addEventListener('click', async () => {
|
|
3236
|
+
const prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to display the screen recording "${sessionId}".`;
|
|
3237
|
+
try {
|
|
3238
|
+
await navigator.clipboard.writeText(prompt);
|
|
3239
|
+
copyPromptBtn.textContent = 'Copied!';
|
|
3240
|
+
setTimeout(() => copyPromptBtn.textContent = 'Copy Prompt', 2000);
|
|
3241
|
+
} catch (error) {
|
|
3242
|
+
console.error('[ScreenRecording] Failed to copy prompt:', error);
|
|
3243
|
+
}
|
|
3244
|
+
});
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
if (viewBtn) {
|
|
3248
|
+
viewBtn.addEventListener('click', () => {
|
|
3249
|
+
// Ask background to open frame editor (content scripts can't create tabs)
|
|
3250
|
+
chrome.runtime.sendMessage({
|
|
3251
|
+
action: 'openFrameEditor',
|
|
3252
|
+
sessionId: sessionId
|
|
3253
|
+
});
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
if (deleteBtn) {
|
|
3258
|
+
deleteBtn.addEventListener('click', async () => {
|
|
3259
|
+
if (confirm('Delete this screen recording?')) {
|
|
3260
|
+
chrome.runtime.sendMessage({
|
|
3261
|
+
action: 'deleteScreenRecording',
|
|
3262
|
+
sessionId: sessionId
|
|
3263
|
+
}, (response) => {
|
|
3264
|
+
if (response && response.success) {
|
|
3265
|
+
this.hideRecordingIndicator();
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
});
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
if (closeBtn) {
|
|
3273
|
+
closeBtn.addEventListener('click', () => {
|
|
3274
|
+
this.hideRecordingIndicator();
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
|
|
2877
3279
|
hideRecordingIndicator() {
|
|
2878
3280
|
// Clear countdown interval if running
|
|
2879
3281
|
if (this.countdownInterval) {
|