@dynamicu/chromedebug-mcp 2.6.7 → 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.
Files changed (39) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/background.js +611 -505
  4. package/chrome-extension/browser-recording-manager.js +1 -1
  5. package/chrome-extension/chrome-debug-logger.js +168 -0
  6. package/chrome-extension/console-interception-library.js +430 -0
  7. package/chrome-extension/content.css +16 -16
  8. package/chrome-extension/content.js +458 -126
  9. package/chrome-extension/extension-config.js +1 -1
  10. package/chrome-extension/license-helper.js +26 -0
  11. package/chrome-extension/manifest.free.json +0 -3
  12. package/chrome-extension/options.js +1 -1
  13. package/chrome-extension/popup.html +221 -191
  14. package/chrome-extension/popup.js +88 -379
  15. package/chrome-extension/pro/enhanced-capture.js +406 -0
  16. package/chrome-extension/pro/frame-editor.html +410 -0
  17. package/chrome-extension/pro/frame-editor.js +1496 -0
  18. package/chrome-extension/pro/function-tracker.js +843 -0
  19. package/chrome-extension/pro/jszip.min.js +13 -0
  20. package/dist/chromedebug-extension-free.zip +0 -0
  21. package/package.json +3 -1
  22. package/scripts/webpack.config.free.cjs +8 -8
  23. package/scripts/webpack.config.pro.cjs +2 -0
  24. package/src/cli.js +2 -2
  25. package/src/database.js +55 -7
  26. package/src/index.js +9 -6
  27. package/src/mcp/server.js +2 -2
  28. package/src/services/process-manager.js +10 -6
  29. package/src/services/process-tracker.js +10 -5
  30. package/src/services/profile-manager.js +17 -2
  31. package/src/validation/schemas.js +2 -2
  32. package/src/index-direct.js +0 -157
  33. package/src/index-modular.js +0 -219
  34. package/src/index-monolithic-backup.js +0 -2230
  35. package/src/legacy/chrome-controller-old.js +0 -1406
  36. package/src/legacy/index-express.js +0 -625
  37. package/src/legacy/index-old.js +0 -977
  38. package/src/legacy/routes.js +0 -260
  39. 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-pilot'));
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
  }
@@ -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-pilot-recording-status');
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,14 +494,31 @@ 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 === 'stopWorkflowRecording') {
500
- console.log('[ChromeDebug MCP] Stopping workflow recording');
501
- stopWorkflowRecording().then(workflow => {
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 => {
502
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
+ }
503
520
  }).catch(error => {
504
- console.error('[ChromeDebug MCP] Error stopping workflow recording:', error);
521
+ console.error('[ChromeDebug MCP] Error getting workflow data:', error);
505
522
  sendResponse({ success: false, error: error.message });
506
523
  });
507
524
  return true; // Keep channel open for async response
@@ -634,7 +651,7 @@ async function playWorkflowRecording(workflow, actions) {
634
651
  try {
635
652
  // Show playback indicator
636
653
  const playbackIndicator = document.createElement('div');
637
- playbackIndicator.id = 'chrome-pilot-playback-indicator';
654
+ playbackIndicator.id = 'chrome-debug-playback-indicator';
638
655
  playbackIndicator.style.cssText = `
639
656
  position: fixed;
640
657
  top: 20px;
@@ -857,6 +874,7 @@ let isWorkflowRecording = false;
857
874
  let workflowActions = [];
858
875
  let workflowRecordingIndicator = null;
859
876
  let workflowScreenshotSettings = null;
877
+ let workflowTabId = null; // Store tabId for use when stopping recording
860
878
  let lastScreenshotTime = 0;
861
879
  let pendingScreenshots = []; // Track pending screenshot captures
862
880
 
@@ -936,7 +954,7 @@ function getUniqueSelector(element) {
936
954
  // Try unique class combination
937
955
  if (element.className && typeof element.className === 'string') {
938
956
  const classes = element.className.trim().split(/\s+/)
939
- .filter(c => !c.startsWith('chrome-pilot'))
957
+ .filter(c => !c.startsWith('chrome-debug'))
940
958
  .filter(c => !c.includes(':')) // Filter out pseudo-class artifacts
941
959
  .map(c => {
942
960
  // Escape special characters in class names
@@ -985,18 +1003,17 @@ function getXPath(element) {
985
1003
 
986
1004
  // Create workflow recording indicator
987
1005
  function createWorkflowRecordingIndicator() {
988
- if (workflowRecordingIndicator) return;
989
-
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
990
1014
  workflowRecordingIndicator = document.createElement('div');
991
- workflowRecordingIndicator.id = 'chrome-pilot-workflow-indicator';
992
- workflowRecordingIndicator.innerHTML = `
993
- <div style="display: flex; align-items: center; gap: 8px;">
994
- <span style="color: #9c27b0; font-size: 20px; animation: pulse 1.5s infinite;">●</span>
995
- <span>Recording Workflow...</span>
996
- <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>
997
- </div>
998
- `;
999
-
1015
+ workflowRecordingIndicator.id = 'chrome-debug-workflow-indicator';
1016
+
1000
1017
  Object.assign(workflowRecordingIndicator.style, {
1001
1018
  position: 'fixed',
1002
1019
  bottom: '20px',
@@ -1012,11 +1029,77 @@ function createWorkflowRecordingIndicator() {
1012
1029
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
1013
1030
  backdropFilter: 'blur(10px)',
1014
1031
  WebkitBackdropFilter: 'blur(10px)',
1015
- pointerEvents: 'none',
1032
+ pointerEvents: 'auto',
1016
1033
  userSelect: 'none'
1017
1034
  });
1018
-
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
+
1019
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
+ }
1020
1103
  }
1021
1104
 
1022
1105
  function removeWorkflowRecordingIndicator() {
@@ -1033,6 +1116,97 @@ function updateWorkflowActionCount() {
1033
1116
  }
1034
1117
  }
1035
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
+
1036
1210
  // Enhanced click tracking helper functions - Pro Feature
1037
1211
  // Try to import enhanced capture module (only exists in pro build)
1038
1212
  let enhancedCaptureModule = null;
@@ -1076,12 +1250,9 @@ async function shouldEnhanceCapture() {
1076
1250
  // Record click action
1077
1251
  function recordClick(event) {
1078
1252
  if (!isWorkflowRecording) {
1079
- console.log('[ChromePilot] Click ignored - not recording');
1080
1253
  return;
1081
1254
  }
1082
1255
 
1083
- console.log('[ChromePilot] Recording click event');
1084
-
1085
1256
  let element = event.target;
1086
1257
 
1087
1258
  // If clicking on an SVG element or its children, find the parent button/link
@@ -1116,8 +1287,6 @@ function recordClick(event) {
1116
1287
  shouldEnhanceCapture().then(enhanceCapture => {
1117
1288
  if (enhanceCapture) {
1118
1289
  try {
1119
- console.log('[ChromePilot] Capturing enhanced click data asynchronously...');
1120
-
1121
1290
  // Capture enhanced element data (only include fields with meaningful values)
1122
1291
  const capturedHTML = captureElementHTML(element);
1123
1292
  if (capturedHTML) {
@@ -1168,14 +1337,13 @@ function recordClick(event) {
1168
1337
  }
1169
1338
  }
1170
1339
 
1171
- console.log('[ChromePilot] Enhanced click data captured successfully');
1340
+ // Enhanced click data captured successfully - no need to log
1172
1341
  } catch (error) {
1173
1342
  console.warn('[ChromePilot] Error capturing enhanced click data:', error);
1174
1343
  // Continue with basic click recording if enhanced capture fails
1175
1344
  }
1176
- } else {
1177
- console.log('[ChromePilot] Enhanced click capture disabled - using basic capture only');
1178
1345
  }
1346
+ // Enhanced capture setting checked - no need to log state
1179
1347
  }).catch(error => {
1180
1348
  console.error('[ChromePilot] Error checking enhanced capture setting:', error);
1181
1349
  });
@@ -1334,8 +1502,9 @@ function startWorkflowRecording(screenshotSettings) {
1334
1502
  timestamp: event.timestamp,
1335
1503
  stack: event.call_stack || null
1336
1504
  });
1337
-
1338
- console.log('[ChromePilot] Function trace captured for workflow:', event.function_name);
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
1339
1508
  }
1340
1509
  });
1341
1510
  };
@@ -1344,7 +1513,8 @@ function startWorkflowRecording(screenshotSettings) {
1344
1513
  window.ChromePilotTracker._setRecordingStatus(true);
1345
1514
  console.log('[ChromePilot] Connected workflow recording to FunctionTracker');
1346
1515
  } else {
1347
- console.warn('[ChromePilot] FunctionTracker not available for workflow recording');
1516
+ // FunctionTracker is a PRO feature - use debug level to avoid confusing FREE users
1517
+ console.debug('[ChromePilot] FunctionTracker not available (PRO feature)');
1348
1518
  }
1349
1519
 
1350
1520
  // Add event listeners for recording
@@ -1373,7 +1543,9 @@ function startWorkflowRecording(screenshotSettings) {
1373
1543
  // Stop workflow recording
1374
1544
  async function stopWorkflowRecording() {
1375
1545
  isWorkflowRecording = false;
1376
- removeWorkflowRecordingIndicator();
1546
+ workflowTabId = null; // Reset tabId when recording stops
1547
+ // DON'T remove indicator here - let the completion screen handle it
1548
+ // removeWorkflowRecordingIndicator();
1377
1549
 
1378
1550
  console.log(`[ChromePilot] Stopping recording. Captured ${workflowActions.length} actions`);
1379
1551
 
@@ -1802,8 +1974,6 @@ async function recordScreenClick(event) {
1802
1974
  const enhanceCapture = await shouldEnhanceCapture();
1803
1975
  if (enhanceCapture) {
1804
1976
  try {
1805
- console.log('[ChromePilot] Capturing enhanced screen click data...');
1806
-
1807
1977
  // Capture enhanced element data (only include fields with meaningful values)
1808
1978
  const capturedHTML = captureElementHTML(element);
1809
1979
  if (capturedHTML) {
@@ -1854,14 +2024,13 @@ async function recordScreenClick(event) {
1854
2024
  }
1855
2025
  }
1856
2026
 
1857
- console.log('[ChromePilot] Enhanced screen click data captured successfully');
2027
+ // Enhanced screen click data captured successfully - no need to log
1858
2028
  } catch (error) {
1859
2029
  console.warn('[ChromePilot] Error capturing enhanced screen click data:', error);
1860
2030
  // Continue with basic click recording if enhanced capture fails
1861
2031
  }
1862
- } else {
1863
- console.log('[ChromePilot] Enhanced screen click capture disabled - using basic capture only');
1864
2032
  }
2033
+ // Enhanced capture setting checked - no need to log state
1865
2034
 
1866
2035
  sendScreenInteraction(interaction);
1867
2036
 
@@ -2157,7 +2326,7 @@ function showRecordingInterface() {
2157
2326
 
2158
2327
  // Create recording overlay
2159
2328
  recordingOverlay = document.createElement('div');
2160
- recordingOverlay.id = 'chrome-pilot-recording-overlay';
2329
+ recordingOverlay.id = 'chrome-debug-recording-overlay';
2161
2330
  recordingOverlay.style.cssText = `
2162
2331
  position: fixed;
2163
2332
  bottom: 20px;
@@ -2175,13 +2344,13 @@ function showRecordingInterface() {
2175
2344
  recordingOverlay.innerHTML = `
2176
2345
  <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
2177
2346
  <h3 style="margin: 0; font-size: 16px; color: #333;">Screen Recording</h3>
2178
- <button id="chrome-pilot-close-recording" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #666;">&times;</button>
2347
+ <button id="chrome-debug-close-recording" style="background: none; border: none; font-size: 20px; cursor: pointer; color: #666;">&times;</button>
2179
2348
  </div>
2180
2349
  <p style="margin: 0 0 15px 0; font-size: 13px; color: #666;">
2181
2350
  Recording this tab with console logs
2182
2351
  </p>
2183
- <div id="chrome-pilot-recording-status" style="text-align: center; margin-bottom: 15px; font-size: 14px; color: #666;"></div>
2184
- <button id="chrome-pilot-record-btn" style="
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="
2185
2354
  width: 100%;
2186
2355
  padding: 10px;
2187
2356
  background: #f44336;
@@ -2192,13 +2361,13 @@ function showRecordingInterface() {
2192
2361
  font-size: 14px;
2193
2362
  font-weight: 500;
2194
2363
  ">Start Recording</button>
2195
- <div id="chrome-pilot-recording-result" style="margin-top: 15px; display: none;"></div>
2364
+ <div id="chrome-debug-recording-result" style="margin-top: 15px; display: none;"></div>
2196
2365
  `;
2197
2366
 
2198
2367
  document.body.appendChild(recordingOverlay);
2199
2368
 
2200
2369
  // Add event listeners
2201
- document.getElementById('chrome-pilot-close-recording').addEventListener('click', () => {
2370
+ document.getElementById('chrome-debug-close-recording').addEventListener('click', () => {
2202
2371
  if (isRecording) {
2203
2372
  if (confirm('Stop recording and close?')) {
2204
2373
  stopRecording();
@@ -2211,7 +2380,7 @@ function showRecordingInterface() {
2211
2380
  }
2212
2381
  });
2213
2382
 
2214
- document.getElementById('chrome-pilot-record-btn').addEventListener('click', () => {
2383
+ document.getElementById('chrome-debug-record-btn').addEventListener('click', () => {
2215
2384
  if (!isRecording) {
2216
2385
  startRecording();
2217
2386
  } else {
@@ -2222,8 +2391,8 @@ function showRecordingInterface() {
2222
2391
 
2223
2392
  // Start recording from content script
2224
2393
  function startRecording() {
2225
- const recordBtn = document.getElementById('chrome-pilot-record-btn');
2226
- const statusDiv = document.getElementById('chrome-pilot-recording-status');
2394
+ const recordBtn = document.getElementById('chrome-debug-record-btn');
2395
+ const statusDiv = document.getElementById('chrome-debug-recording-status');
2227
2396
 
2228
2397
  recordBtn.disabled = true;
2229
2398
  recordBtn.textContent = 'Starting...';
@@ -2270,9 +2439,9 @@ function startRecording() {
2270
2439
 
2271
2440
  // Stop recording
2272
2441
  function stopRecording() {
2273
- const recordBtn = document.getElementById('chrome-pilot-record-btn');
2274
- const statusDiv = document.getElementById('chrome-pilot-recording-status');
2275
- const resultDiv = document.getElementById('chrome-pilot-recording-result');
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');
2276
2445
 
2277
2446
  recordBtn.disabled = true;
2278
2447
  recordBtn.textContent = 'Stopping...';
@@ -2734,6 +2903,7 @@ class ScreenCaptureVisualFeedback {
2734
2903
  init() {
2735
2904
  // Listen for screen capture events
2736
2905
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
2906
+ console.log('[ScreenCapture] Received message:', message.type, message);
2737
2907
  switch (message.type) {
2738
2908
  case 'start-screen-capture-tracking':
2739
2909
  this.handleStartScreenCapture(message);
@@ -2747,11 +2917,27 @@ class ScreenCaptureVisualFeedback {
2747
2917
  case 'stop-screen-capture-tracking':
2748
2918
  this.handleStopScreenCapture(message);
2749
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
+ }
2750
2935
  }
2751
2936
  });
2752
2937
  }
2753
2938
 
2754
2939
  handleStartScreenCapture(message) {
2940
+ console.log('[ScreenCapture] Starting new recording, current indicator exists:', !!this.recordingIndicatorElement);
2755
2941
  this.isRecording = true;
2756
2942
  this.actionCount = 0;
2757
2943
  this.showRecordingIndicator();
@@ -2798,9 +2984,11 @@ class ScreenCaptureVisualFeedback {
2798
2984
 
2799
2985
  handleStopScreenCapture(message) {
2800
2986
  this.isRecording = false;
2801
- this.hideRecordingIndicator();
2987
+ // DON'T hide indicator here - let the completion screen handle it
2988
+ // this.hideRecordingIndicator();
2802
2989
  this.hideActionCounter();
2803
- this.cleanup();
2990
+ // Don't cleanup yet - we want to keep the indicator visible
2991
+ // this.cleanup();
2804
2992
  }
2805
2993
 
2806
2994
  showClickHighlight(x, y) {
@@ -2836,80 +3024,96 @@ class ScreenCaptureVisualFeedback {
2836
3024
  }
2837
3025
 
2838
3026
  showRecordingIndicator() {
2839
- if (!this.recordingIndicatorElement) {
2840
- // Add required CSS styles if not already added
2841
- if (!document.getElementById('screen-capture-styles')) {
2842
- const style = document.createElement('style');
2843
- style.id = 'screen-capture-styles';
2844
- style.textContent = `
2845
- @keyframes pulse {
2846
- 0% { opacity: 1; }
2847
- 50% { opacity: 0.5; }
2848
- 100% { opacity: 1; }
2849
- }
2850
- @keyframes screen-capture-pulse {
2851
- 0% { opacity: 1; transform: scale(1); }
2852
- 50% { opacity: 0.7; transform: scale(1.1); }
2853
- 100% { opacity: 1; transform: scale(1); }
2854
- }
2855
- .screen-capture-click-highlight {
2856
- position: fixed;
2857
- width: 40px;
2858
- height: 40px;
2859
- border: 3px solid #4CAF50;
2860
- border-radius: 50%;
2861
- background: rgba(76, 175, 80, 0.3);
2862
- transform: translate(-50%, -50%);
2863
- pointer-events: none;
2864
- z-index: 2147483646;
2865
- animation: screen-capture-ripple 0.5s ease-out;
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;
2866
3076
  }
2867
- @keyframes screen-capture-ripple {
2868
- 0% {
2869
- transform: translate(-50%, -50%) scale(0);
2870
- opacity: 1;
2871
- }
2872
- 100% {
2873
- transform: translate(-50%, -50%) scale(1);
2874
- opacity: 0;
2875
- }
3077
+ 100% {
3078
+ transform: translate(-50%, -50%) scale(1);
3079
+ opacity: 0;
2876
3080
  }
2877
- `;
2878
- document.head.appendChild(style);
2879
- }
2880
-
2881
- this.recordingIndicatorElement = document.createElement('div');
2882
- this.recordingIndicatorElement.className = 'screen-capture-recording-indicator-unified';
2883
-
2884
- // Use inline styles to match workflow recording exactly
2885
- Object.assign(this.recordingIndicatorElement.style, {
2886
- position: 'fixed',
2887
- bottom: '80px', // Keep different from workflow to avoid collision
2888
- left: '20px',
2889
- background: 'rgba(0, 0, 0, 0.9)',
2890
- color: 'white',
2891
- padding: '12px 20px',
2892
- borderRadius: '25px',
2893
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif',
2894
- fontSize: '14px',
2895
- fontWeight: '500',
2896
- zIndex: '2147483647',
2897
- display: 'flex',
2898
- alignItems: 'center',
2899
- gap: '8px',
2900
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
2901
- backdropFilter: 'blur(10px)',
2902
- WebkitBackdropFilter: 'blur(10px)',
2903
- pointerEvents: 'none',
2904
- userSelect: 'none'
2905
- });
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
+ });
2906
3112
 
2907
- // Start with countdown
2908
- this.startCountdownSequence();
2909
- document.body.appendChild(this.recordingIndicatorElement);
2910
- }
3113
+ document.body.appendChild(this.recordingIndicatorElement);
2911
3114
 
2912
- this.recordingIndicatorElement.style.display = 'flex';
3115
+ // Start countdown sequence
3116
+ this.startCountdownSequence();
2913
3117
  }
2914
3118
 
2915
3119
  startCountdownSequence() {
@@ -2932,18 +3136,146 @@ class ScreenCaptureVisualFeedback {
2932
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>
2933
3137
  `;
2934
3138
  } else {
2935
- // Switch to recording display
3139
+ // Switch to recording display with stop button
3140
+ this.recordingIndicatorElement.style.pointerEvents = 'auto'; // Enable clicking
2936
3141
  this.recordingIndicatorElement.innerHTML = `
2937
- <span style="color: #4CAF50; font-size: 20px; animation: pulse 1.5s infinite;">●</span>
2938
- <span>Recording Screen Capture...</span>
2939
- <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>
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>
2940
3151
  `;
2941
3152
  clearInterval(this.countdownInterval);
2942
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
+ }
2943
3186
  }
2944
3187
  }, 1000);
2945
3188
  }
2946
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
+
2947
3279
  hideRecordingIndicator() {
2948
3280
  // Clear countdown interval if running
2949
3281
  if (this.countdownInterval) {