@dynamicu/chromedebug-mcp 2.6.7 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CLAUDE.md +17 -1
  2. package/README.md +1 -1
  3. package/chrome-extension/activation-manager.js +10 -10
  4. package/chrome-extension/background.js +1045 -736
  5. package/chrome-extension/browser-recording-manager.js +1 -1
  6. package/chrome-extension/chrome-debug-logger.js +168 -0
  7. package/chrome-extension/chrome-session-manager.js +5 -5
  8. package/chrome-extension/console-interception-library.js +430 -0
  9. package/chrome-extension/content.css +16 -16
  10. package/chrome-extension/content.js +739 -221
  11. package/chrome-extension/data-buffer.js +5 -5
  12. package/chrome-extension/dom-tracker.js +9 -9
  13. package/chrome-extension/extension-config.js +1 -1
  14. package/chrome-extension/firebase-client.js +13 -13
  15. package/chrome-extension/frame-capture.js +20 -38
  16. package/chrome-extension/license-helper.js +33 -7
  17. package/chrome-extension/manifest.free.json +3 -6
  18. package/chrome-extension/network-tracker.js +9 -9
  19. package/chrome-extension/options.html +10 -0
  20. package/chrome-extension/options.js +21 -8
  21. package/chrome-extension/performance-monitor.js +17 -17
  22. package/chrome-extension/popup.html +230 -193
  23. package/chrome-extension/popup.js +146 -458
  24. package/chrome-extension/pro/enhanced-capture.js +406 -0
  25. package/chrome-extension/pro/frame-editor.html +433 -0
  26. package/chrome-extension/pro/frame-editor.js +1567 -0
  27. package/chrome-extension/pro/function-tracker.js +843 -0
  28. package/chrome-extension/pro/jszip.min.js +13 -0
  29. package/chrome-extension/upload-manager.js +7 -7
  30. package/dist/chromedebug-extension-free.zip +0 -0
  31. package/package.json +3 -1
  32. package/scripts/webpack.config.free.cjs +8 -8
  33. package/scripts/webpack.config.pro.cjs +2 -0
  34. package/src/cli.js +2 -2
  35. package/src/database.js +55 -7
  36. package/src/index.js +9 -6
  37. package/src/mcp/server.js +2 -2
  38. package/src/services/process-manager.js +10 -6
  39. package/src/services/process-tracker.js +10 -5
  40. package/src/services/profile-manager.js +17 -2
  41. package/src/validation/schemas.js +12 -11
  42. package/src/index-direct.js +0 -157
  43. package/src/index-modular.js +0 -219
  44. package/src/index-monolithic-backup.js +0 -2230
  45. package/src/legacy/chrome-controller-old.js +0 -1406
  46. package/src/legacy/index-express.js +0 -625
  47. package/src/legacy/index-old.js +0 -977
  48. package/src/legacy/routes.js +0 -260
  49. package/src/legacy/shared-storage.js +0 -101
@@ -1,30 +1,10 @@
1
1
  // Chrome Debug Extension Popup Script v2.0
2
2
  const EXTENSION_VERSION = '2.0.4-BUILD-20250119';
3
- console.log(`[popup.js] Loaded version: ${EXTENSION_VERSION}`);
3
+ // console.log(`[popup.js] Loaded version: ${EXTENSION_VERSION}`);
4
4
 
5
- // Import Firebase license client with graceful fallback
6
- let FirebaseLicenseClient = null;
7
- let LEMONSQUEEZY_CHECKOUT_URL = 'https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967';
8
- let licenseClient = null;
9
- let currentUserId = null;
10
-
11
- // Load Firebase asynchronously to avoid blocking module execution
12
- (async () => {
13
- try {
14
- const firebaseClientModule = await import('./firebase-client.js');
15
- FirebaseLicenseClient = firebaseClientModule.FirebaseLicenseClient;
16
-
17
- const firebaseConfigModule = await import('./firebase-config.public.js');
18
- LEMONSQUEEZY_CHECKOUT_URL = firebaseConfigModule.LEMONSQUEEZY_CHECKOUT_URL;
19
-
20
- // Initialize license client if Firebase loaded successfully
21
- licenseClient = new FirebaseLicenseClient();
22
- console.log('[popup.js] Firebase license client initialized');
23
- } catch (error) {
24
- console.warn('[popup.js] Firebase not available - license features disabled:', error);
25
- // Extension will work without Firebase, just no license validation
26
- }
27
- })();
5
+ // LemonSqueezy purchase URL for PRO upgrade
6
+ const PRO_PURCHASE_URL = 'https://chromedebug.com/buy/996773cb-682b-430f-b9e3-9ce2130bd967';
7
+ const IS_PRO_VERSION = chrome.runtime.getManifest().name.includes('PRO');
28
8
 
29
9
  // Global variables for recording functionality
30
10
  let isRecording = false;
@@ -77,13 +57,10 @@ function showToast(message, duration = 2000, type = 'info') {
77
57
 
78
58
  // Loading overlay helper functions
79
59
  function showSectionLoading(sectionId, message = 'Loading...') {
80
- console.log(`showSectionLoading called for: ${sectionId} with message: ${message}`);
81
60
  const section = document.getElementById(sectionId);
82
61
  if (!section) {
83
- console.error(`Section not found: ${sectionId}`);
84
62
  return;
85
63
  }
86
- console.log(`Section found:`, section);
87
64
 
88
65
  // Remove any existing overlay first
89
66
  hideSectionLoading(sectionId);
@@ -96,7 +73,6 @@ function showSectionLoading(sectionId, message = 'Loading...') {
96
73
  `;
97
74
  overlay.setAttribute('data-loading-overlay', 'true');
98
75
  section.appendChild(overlay);
99
- console.log(`Overlay appended to section. Overlay:`, overlay);
100
76
  }
101
77
 
102
78
  function hideSectionLoading(sectionId) {
@@ -114,19 +90,12 @@ function updateWorkflowRecordingUI() {
114
90
  const workflowBtn = document.getElementById('workflowRecordBtn');
115
91
  const workflowStatus = document.getElementById('workflowRecordingStatus');
116
92
  const saveRestorePointBtn = document.getElementById('saveRestorePointBtn');
117
-
118
- console.log('[Popup v2.0.6] updateWorkflowRecordingUI called, isWorkflowRecording:', isWorkflowRecording);
119
- console.log('[Popup v2.0.6] workflowBtn element exists?', !!workflowBtn);
120
-
93
+
121
94
  if (workflowBtn) {
122
95
  if (isWorkflowRecording) {
123
- console.log('[Popup v2.0.6] Updating button to STOP state');
124
- console.log('[Popup v2.0.6] Current button text:', workflowBtn.textContent);
125
96
  workflowBtn.textContent = 'Stop Workflow Recording';
126
97
  workflowBtn.style.background = '#e91e63';
127
98
  workflowBtn.classList.add('recording');
128
- console.log('[Popup v2.0.6] Button updated - new text:', workflowBtn.textContent);
129
- console.log('[Popup v2.0.6] Button classes:', workflowBtn.className);
130
99
  startWorkflowTimer();
131
100
  // Show restore point button during recording
132
101
  if (saveRestorePointBtn) {
@@ -357,7 +326,7 @@ function testSiteAccess(hostname, mode, allowedSites, restrictedSites) {
357
326
 
358
327
  // Function definitions (outside DOMContentLoaded for proper scope)
359
328
  async function checkServerStatus() {
360
- console.log('[popup.js] checkServerStatus() called');
329
+ // console.log('[popup.js] checkServerStatus() called');
361
330
  const statusEl = document.getElementById('serverStatus');
362
331
  const statusTextEl = document.getElementById('statusText');
363
332
 
@@ -384,7 +353,7 @@ async function checkServerStatus() {
384
353
  clearTimeout(timeoutId);
385
354
  // Only log successful connections to reduce console noise
386
355
  if (response.ok) {
387
- console.log(`[popup.js] ✓ Connected to server on port ${port}`);
356
+ // console.log(`[popup.js] ✓ Connected to server on port ${port}`);
388
357
  }
389
358
  return response.ok;
390
359
  } catch (error) {
@@ -440,19 +409,12 @@ async function checkServerStatus() {
440
409
  if (connected && connectedPort) {
441
410
  await chrome.storage.local.set({ lastSuccessfulPort: connectedPort });
442
411
  } else {
443
- console.log('[popup.js] No server found - browser-only mode active');
412
+ // console.log('[popup.js] No server found - browser-only mode active');
444
413
  }
445
414
 
446
415
  if (connected) {
447
416
  statusEl.className = 'server-status connected';
448
- statusTextEl.innerHTML = `
449
- Server connected (port ${connectedPort})
450
- <div style="margin-top: 4px;">
451
- <a href="https://www.npmjs.com/package/@dynamicu/chromedebug-mcp" target="_blank" style="font-size: 10px; color: #666; text-decoration: none;">
452
- 📚 Documentation
453
- </a>
454
- </div>
455
- `;
417
+ statusTextEl.textContent = `Server connected (port ${connectedPort})`;
456
418
  } else {
457
419
  statusEl.className = 'server-status disconnected';
458
420
 
@@ -472,9 +434,6 @@ async function checkServerStatus() {
472
434
  <button id="copyInstallCmd" style="padding: 4px 8px; font-size: 11px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer;">
473
435
  📋 Copy Commands
474
436
  </button>
475
- <a href="https://www.npmjs.com/package/@dynamicu/chromedebug-mcp" target="_blank" style="font-size: 11px; color: #2196F3; text-decoration: none;">
476
- 📚 Full Documentation
477
- </a>
478
437
  </div>
479
438
  `;
480
439
 
@@ -637,7 +596,7 @@ function takeManualSnapshot() {
637
596
  }
638
597
 
639
598
  if (response && response.success) {
640
- console.log('Manual snapshot taken successfully');
599
+ // console.log('Manual snapshot taken successfully');
641
600
  // Show brief feedback
642
601
  const manualSnapshotBtn = document.getElementById('manualSnapshotBtn');
643
602
  if (manualSnapshotBtn) {
@@ -692,7 +651,7 @@ function takeManualSnapshot() {
692
651
  */
693
652
  /*
694
653
  async function takeSnapshot() {
695
- console.log('Taking standalone snapshot...');
654
+ // console.log('Taking standalone snapshot...');
696
655
 
697
656
  try {
698
657
  const serverStatus = await checkServerStatus();
@@ -749,7 +708,7 @@ async function takeSnapshot() {
749
708
  }
750
709
 
751
710
  if (response && response.success) {
752
- console.log('Snapshot taken successfully:', response.sessionId);
711
+ // console.log('Snapshot taken successfully:', response.sessionId);
753
712
  const recordingStatus = document.getElementById('recordingStatus');
754
713
  if (recordingStatus) {
755
714
  recordingStatus.innerHTML = `<strong style="color: #4CAF50;">Snapshot saved!</strong>`;
@@ -929,7 +888,7 @@ function updateRecordingsDisplay(recordings) {
929
888
  if (type === 'snapshot') {
930
889
  prompt = `Please use the get_frame_screenshot function in Chrome Debug to view the snapshot "${recordingId}"${portText}. This is a single-frame screenshot with console logs captured for debugging.`;
931
890
  } else {
932
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to load the recording "${recordingId}"${portText}.`;
891
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to load the recording "${recordingId}"${portText}.`;
933
892
  }
934
893
 
935
894
  navigator.clipboard.writeText(prompt).then(() => {
@@ -992,7 +951,7 @@ function updateRecordingsDisplay(recordings) {
992
951
  }
993
952
  } catch (error) {
994
953
  // Free version - show upgrade prompt
995
- console.log('[ChromePilot Free] Frame editor not available - showing upgrade prompt');
954
+ // console.log('[ChromePilot Free] Frame editor not available - showing upgrade prompt');
996
955
  alert('Frame Editor is a Pro feature. Upgrade to ChromePilot Pro to view detailed frame recordings with console logs and advanced debugging tools.');
997
956
  }
998
957
  });
@@ -1002,356 +961,90 @@ function updateRecordingsDisplay(recordings) {
1002
961
 
1003
962
 
1004
963
 
1005
- // Initialize when DOM is ready
1006
- // License UI initialization function
1007
- async function initializeLicenseUI() {
1008
- console.log('[License] Initializing license UI');
1009
-
1010
- // Get or create userId
1011
- const stored = await chrome.storage.local.get('chromedebug_user_id');
1012
- currentUserId = stored.chromedebug_user_id || crypto.randomUUID();
1013
- if (!stored.chromedebug_user_id) {
1014
- await chrome.storage.local.set({chromedebug_user_id: currentUserId});
1015
- console.log('[License] Created new user ID:', currentUserId);
1016
- }
1017
-
1018
- // Check for license activation file and pre-fill license key
1019
- try {
1020
- const activationFileUrl = chrome.runtime.getURL('license-activation.json');
1021
- const response = await fetch(activationFileUrl);
1022
- if (response.ok) {
1023
- const activationData = await response.json();
1024
- if (activationData.license_key) {
1025
- const licenseKeyInput = document.getElementById('license-key-input');
1026
- if (licenseKeyInput) {
1027
- licenseKeyInput.value = activationData.license_key;
1028
- console.log('[License UI] Pre-filled license key from activation file');
1029
- }
1030
- }
964
+ // Initialize tier status display for FREE version
965
+ function initializeTierStatus() {
966
+ if (IS_PRO_VERSION) {
967
+ // Hide license section for PRO version
968
+ const licenseSection = document.getElementById('license-section');
969
+ if (licenseSection) {
970
+ licenseSection.style.display = 'none';
971
+ // console.log('[Tier] PRO version detected - hiding license section');
1031
972
  }
1032
- } catch (error) {
1033
- // Activation file doesn't exist or is inaccessible - this is normal for FREE version
1034
- console.log('[License UI] No activation file found - normal for FREE version');
1035
- }
1036
-
1037
- // Check license status
1038
- const licenseStatus = await licenseClient.getCachedLicenseStatus();
1039
- console.log('[License] License status:', licenseStatus);
1040
-
1041
- // Update workflow recording PRO badge visibility
1042
- const workflowProBadge = document.getElementById('workflowProBadge');
1043
-
1044
- if (licenseStatus.valid && licenseStatus.tier === 'pro') {
1045
- // Show pro status
1046
- document.getElementById('pro-tier-status').style.display = 'block';
1047
- document.getElementById('free-tier-status').style.display = 'none';
1048
- // Hide license activation input/button when Pro license is active
1049
- document.getElementById('license-activation').style.display = 'none';
1050
- // Hide PRO badge for Pro users
1051
- if (workflowProBadge) workflowProBadge.style.display = 'none';
1052
- console.log('[License] Displaying Pro tier status');
1053
973
  } else {
1054
- // Show free tier + usage
1055
- const usage = await licenseClient.checkUsageLimit(currentUserId);
1056
- console.log('[License] Usage check result:', usage);
1057
-
1058
- // Handle both API response format (currentUsage, dailyLimit) and offline format (count, limit)
1059
- document.getElementById('usage-count').textContent = usage.currentUsage ?? usage.count ?? 0;
1060
- document.getElementById('usage-limit').textContent = usage.dailyLimit ?? usage.limit ?? 50;
1061
- document.getElementById('free-tier-status').style.display = 'block';
1062
- document.getElementById('pro-tier-status').style.display = 'none';
1063
- // Show license activation input/button for free tier
1064
- document.getElementById('license-activation').style.display = 'block';
1065
- // Show PRO badge for free users
1066
- if (workflowProBadge) workflowProBadge.style.display = 'block';
1067
- console.log('[License] Displaying Free tier status');
1068
- }
1069
- }
1070
-
1071
- // Inline Activation Manager Functions
1072
- /**
1073
- * Show the inline activation manager with slide-down animation
1074
- */
1075
- async function showInlineActivationManager(licenseKey) {
1076
- const inlineManager = document.getElementById('inline-activation-manager');
1077
- const activationsList = document.getElementById('activations-list-inline');
1078
-
1079
- // Show the container
1080
- inlineManager.style.display = 'block';
974
+ // Show FREE tier status (no license validation)
975
+ const freeTierStatus = document.getElementById('free-tier-status');
976
+ const proTierStatus = document.getElementById('pro-tier-status');
977
+ const licenseActivation = document.getElementById('license-activation');
1081
978
 
1082
- // Trigger animation
1083
- setTimeout(() => {
1084
- inlineManager.style.maxHeight = '400px';
1085
- }, 10);
1086
-
1087
- // Load activations
1088
- try {
1089
- activationsList.innerHTML = '<div style="padding: 15px; text-align: center; color: #666;">Loading activations...</div>';
1090
-
1091
- // Get current instance ID
1092
- // Note: ls_instance_id is the LemonSqueezy instance ID set during activation
1093
- const stored = await chrome.storage.local.get(['ls_instance_id']);
1094
- const currentInstanceId = stored.ls_instance_id;
979
+ if (freeTierStatus) freeTierStatus.style.display = 'block';
980
+ if (proTierStatus) proTierStatus.style.display = 'none';
981
+ if (licenseActivation) licenseActivation.style.display = 'none';
1095
982
 
1096
- // Fetch activations from Firebase
1097
- const data = await licenseClient.listActivations(licenseKey);
1098
- const activations = data.activations || [];
983
+ // Fetch and display current usage count
984
+ updateUsageDisplay();
1099
985
 
1100
- // Render activations
1101
- renderInlineActivations(activations, currentInstanceId, licenseKey);
1102
-
1103
- } catch (error) {
1104
- console.error('[License] Error loading activations:', error);
1105
- activationsList.innerHTML = `<div style="padding: 15px; text-align: center; color: #f44336;">Failed to load activations: ${error.message}</div>`;
986
+ // console.log('[Tier] FREE version - showing usage counter');
1106
987
  }
1107
988
  }
1108
989
 
1109
990
  /**
1110
- * Hide the inline activation manager with slide-up animation
991
+ * Fetch usage from background and update the display
1111
992
  */
1112
- function hideInlineActivationManager() {
1113
- const inlineManager = document.getElementById('inline-activation-manager');
1114
- inlineManager.style.maxHeight = '0';
1115
-
1116
- setTimeout(() => {
1117
- inlineManager.style.display = 'none';
1118
- }, 300);
1119
- }
1120
-
1121
- /**
1122
- * Render activations in the inline manager
1123
- */
1124
- function renderInlineActivations(activations, currentInstanceId, licenseKey) {
1125
- const container = document.getElementById('activations-list-inline');
1126
- container.innerHTML = '';
1127
-
1128
- if (activations.length === 0) {
1129
- container.innerHTML = '<div style="padding: 15px; text-align: center; color: #666;">No activations found.</div>';
1130
- return;
1131
- }
1132
-
1133
- activations.forEach((activation, index) => {
1134
- const item = document.createElement('div');
1135
- item.style.cssText = 'padding: 12px; border-bottom: 1px solid #e0e0e0; transition: background 0.2s;';
1136
- item.onmouseenter = () => item.style.background = '#f9f9f9';
1137
- item.onmouseleave = () => item.style.background = isCurrentDevice ? '#e8f5e9' : 'white';
1138
-
1139
- // Check if this is the current device
1140
- // Compare against identifier (unique per activation), not name (same for all activations from this device)
1141
- const isCurrentDevice = currentInstanceId && activation.identifier === currentInstanceId;
1142
-
1143
- if (isCurrentDevice) {
1144
- item.style.background = '#e8f5e9';
1145
- item.style.borderLeft = '3px solid #4caf50';
1146
- }
1147
-
1148
- // Format date
1149
- const date = new Date(activation.createdAt);
1150
- const formattedDate = date.toLocaleString('en-US', {
1151
- month: 'short',
1152
- day: 'numeric',
1153
- year: 'numeric',
1154
- hour: '2-digit',
1155
- minute: '2-digit'
1156
- });
1157
-
1158
- // Build device info
1159
- let deviceDisplay = 'Unknown Device';
1160
- if (activation.deviceInfo) {
1161
- deviceDisplay = activation.deviceInfo.deviceName ||
1162
- `${activation.deviceInfo.platform || 'Unknown'} • ${activation.deviceInfo.browser || 'Unknown'}`;
1163
- } else if (activation.name && activation.name !== currentInstanceId) {
1164
- deviceDisplay = activation.name;
1165
- }
1166
-
1167
- item.innerHTML = `
1168
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
1169
- <span style="font-weight: 500; font-size: 13px; color: #333;">Activation ${index + 1}</span>
1170
- ${isCurrentDevice ? '<span style="background: #4caf50; color: white; padding: 2px 6px; border-radius: 10px; font-size: 10px; font-weight: bold;">CURRENT</span>' : ''}
1171
- </div>
1172
- <div style="font-size: 12px; color: #666; margin-bottom: 4px;">
1173
- <strong style="color: #333;">${deviceDisplay}</strong>
1174
- </div>
1175
- <div style="font-size: 11px; color: #999; margin-bottom: 8px;">
1176
- ${formattedDate}
1177
- </div>
1178
- <button
1179
- class="deactivate-btn-inline"
1180
- data-instance-id="${activation.identifier}"
1181
- data-activation-number="${index + 1}"
1182
- data-license-key="${licenseKey}"
1183
- style="padding: 5px 10px; background: ${isCurrentDevice ? '#ccc' : '#f44336'}; color: white; border: none; border-radius: 3px; cursor: ${isCurrentDevice ? 'not-allowed' : 'pointer'}; font-size: 11px; width: 100%;"
1184
- ${isCurrentDevice ? 'disabled' : ''}
1185
- >
1186
- ${isCurrentDevice ? '✓ Current Device' : 'Deactivate'}
1187
- </button>
1188
- `;
1189
-
1190
- // Add click handler for deactivate button
1191
- if (!isCurrentDevice) {
1192
- const deactivateBtn = item.querySelector('.deactivate-btn-inline');
1193
- deactivateBtn.addEventListener('click', () => {
1194
- handleInlineDeactivate(activation.identifier, index + 1, licenseKey);
1195
- });
1196
- }
1197
-
1198
- container.appendChild(item);
1199
- });
1200
- }
1201
-
1202
- /**
1203
- * Handle deactivation of an instance (inline)
1204
- */
1205
- async function handleInlineDeactivate(instanceId, activationNumber, licenseKey) {
1206
- const confirmed = confirm(
1207
- `Are you sure you want to deactivate Activation ${activationNumber}?\n\n` +
1208
- `This will free up an activation slot and automatically activate on this device.`
1209
- );
1210
-
1211
- if (!confirmed) {
1212
- return;
1213
- }
1214
-
1215
- const messageDiv = document.getElementById('license-message');
1216
-
993
+ async function updateUsageDisplay() {
1217
994
  try {
1218
- // Disable all deactivate buttons
1219
- const buttons = document.querySelectorAll('.deactivate-btn-inline');
1220
- buttons.forEach(btn => btn.disabled = true);
1221
-
1222
- messageDiv.textContent = 'Deactivating...';
1223
- messageDiv.style.color = '#2196F3';
1224
-
1225
- // Deactivate the instance
1226
- const result = await licenseClient.deactivateInstance(licenseKey, instanceId);
1227
- console.log('[License] Deactivation result:', result);
995
+ const response = await chrome.runtime.sendMessage({ action: 'getUsage' });
1228
996
 
1229
- if (result.deactivated) {
1230
- messageDiv.textContent = 'Deactivated! Activating on this device...';
1231
- messageDiv.style.color = '#4caf50';
1232
-
1233
- // Hide inline activation manager
1234
- hideInlineActivationManager();
1235
-
1236
- // Wait a moment, then retry activation
1237
- setTimeout(async () => {
1238
- // Set the license key in the input
1239
- document.getElementById('license-key-input').value = licenseKey;
997
+ if (response && response.success) {
998
+ const usageCountEl = document.getElementById('usage-count');
999
+ const usageLimitEl = document.getElementById('usage-limit');
1240
1000
 
1241
- // Retry activation automatically
1242
- await handleLicenseActivation();
1243
- }, 1000);
1001
+ if (usageCountEl) {
1002
+ usageCountEl.textContent = response.count || 0;
1003
+ }
1004
+ if (usageLimitEl) {
1005
+ usageLimitEl.textContent = response.limit || 5;
1006
+ }
1244
1007
 
1245
- } else {
1246
- throw new Error(result.error || 'Deactivation failed');
1008
+ // console.log(`[Usage] Display updated: ${response.count}/${response.limit}`);
1247
1009
  }
1248
-
1249
1010
  } catch (error) {
1250
- console.error('[License] Deactivation error:', error);
1251
- messageDiv.textContent = `Deactivation failed: ${error.message}`;
1252
- messageDiv.style.color = '#f44336';
1253
-
1254
- // Re-enable buttons
1255
- const buttons = document.querySelectorAll('.deactivate-btn-inline:not([disabled])');
1256
- buttons.forEach(btn => btn.disabled = false);
1257
- }
1258
- }
1259
-
1260
- // License activation handler
1261
- async function handleLicenseActivation() {
1262
- const licenseKey = document.getElementById('license-key-input').value.trim();
1263
- const messageDiv = document.getElementById('license-message');
1264
-
1265
- if (!licenseKey) {
1266
- messageDiv.textContent = 'Please enter a license key';
1267
- messageDiv.style.color = '#f44336';
1268
- return;
1269
- }
1270
-
1271
- messageDiv.textContent = 'Validating...';
1272
- messageDiv.style.color = '#2196F3';
1273
- console.log('[License] Validating license key...');
1274
-
1275
- // Clear any existing instance ID to force fresh activation
1276
- // This ensures we don't try to validate with stale/deleted instance IDs
1277
- await chrome.storage.local.remove(['ls_instance_id']);
1278
- console.log('[License] Cleared existing instance ID for fresh activation');
1279
-
1280
- const result = await licenseClient.validateLicense(licenseKey);
1281
- console.log('[License] Validation result:', result);
1282
- console.log('[License] Error value:', result.error);
1283
- console.log('[License] Error type:', typeof result.error);
1284
-
1285
- if (result.valid) {
1286
- messageDiv.textContent = 'License activated successfully!';
1287
- messageDiv.style.color = '#4caf50';
1288
- document.getElementById('license-key-input').value = '';
1289
- await initializeLicenseUI(); // Refresh UI
1290
- } else {
1291
- // Check if activation limit reached
1292
- console.log('[License] Checking if activation limit reached...');
1293
- console.log('[License] result.error === "ACTIVATION_LIMIT_REACHED":', result.error === 'ACTIVATION_LIMIT_REACHED');
1294
- console.log('[License] result.error includes "activation limit":', result.error && result.error.toLowerCase().includes('activation limit'));
1295
-
1296
- if (result.error === 'ACTIVATION_LIMIT_REACHED' ||
1297
- (result.error && result.error.toLowerCase().includes('activation limit'))) {
1298
- console.log('[License] Activation limit reached, showing inline activation manager');
1299
- messageDiv.textContent = 'Please deactivate an existing activation to continue';
1300
- messageDiv.style.color = '#ff9800';
1301
-
1302
- // Show inline activation manager
1303
- await showInlineActivationManager(licenseKey.trim());
1304
- } else {
1305
- messageDiv.textContent = result.error || 'Invalid license key';
1306
- messageDiv.style.color = '#f44336';
1307
- }
1011
+ console.error('[Usage] Failed to fetch usage:', error);
1308
1012
  }
1309
1013
  }
1310
1014
 
1311
- // Upgrade button handler
1015
+ // Upgrade button handler - redirect to LemonSqueezy purchase page
1312
1016
  function handleUpgradeClick() {
1313
- console.log('[License] Opening upgrade page:', LEMONSQUEEZY_CHECKOUT_URL);
1314
- chrome.tabs.create({url: LEMONSQUEEZY_CHECKOUT_URL});
1017
+ // console.log('[Upgrade] Opening LemonSqueezy PRO purchase page');
1018
+ chrome.tabs.create({url: PRO_PURCHASE_URL});
1315
1019
  }
1316
1020
 
1317
- // Listen for messages from activation manager
1318
- chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
1319
- if (message.type === 'RETRY_ACTIVATION' && message.licenseKey) {
1320
- console.log('[License] Retrying activation after deactivation');
1321
-
1322
- // Set the license key in the input
1323
- const licenseKeyInput = document.getElementById('license-key-input');
1324
- if (licenseKeyInput) {
1325
- licenseKeyInput.value = message.licenseKey;
1326
- }
1327
-
1328
- // Retry activation
1329
- handleLicenseActivation().then(() => {
1330
- console.log('[License] Retry activation complete');
1331
- });
1332
- }
1333
- });
1334
-
1335
1021
  document.addEventListener('DOMContentLoaded', () => {
1336
- console.log('DOM loaded, initializing popup');
1022
+ // console.log('DOM loaded, initializing popup');
1337
1023
 
1338
- // Initialize license UI first
1339
- initializeLicenseUI().catch(error => {
1340
- console.error('[License] Failed to initialize license UI:', error);
1341
- });
1024
+ // Initialize tier status display
1025
+ initializeTierStatus();
1342
1026
 
1343
- // License event handlers
1344
- const activateLicenseBtn = document.getElementById('activate-license-btn');
1027
+ // Upgrade button handler
1345
1028
  const upgradeBtn = document.getElementById('upgrade-btn');
1346
-
1347
- if (activateLicenseBtn) {
1348
- activateLicenseBtn.addEventListener('click', handleLicenseActivation);
1349
- }
1350
-
1351
1029
  if (upgradeBtn) {
1352
1030
  upgradeBtn.addEventListener('click', handleUpgradeClick);
1353
1031
  }
1354
1032
 
1033
+ // Settings button handler - open options page
1034
+ const settingsBtn = document.getElementById('settings-btn');
1035
+ if (settingsBtn) {
1036
+ settingsBtn.addEventListener('click', () => {
1037
+ chrome.runtime.openOptionsPage();
1038
+ });
1039
+ // Add hover effect
1040
+ settingsBtn.addEventListener('mouseenter', () => {
1041
+ settingsBtn.style.opacity = '1';
1042
+ });
1043
+ settingsBtn.addEventListener('mouseleave', () => {
1044
+ settingsBtn.style.opacity = '0.6';
1045
+ });
1046
+ }
1047
+
1355
1048
  // Function to update button states based on server status
1356
1049
  async function updateButtonStatesForServerStatus() {
1357
1050
  const serverStatus = await checkServerStatus();
@@ -1450,19 +1143,19 @@ document.addEventListener('DOMContentLoaded', () => {
1450
1143
  clearInterval(serverPollingInterval);
1451
1144
  }
1452
1145
  serverPollingInterval = setInterval(updateButtonStatesForServerStatus, 3000);
1453
- console.log('Server polling started (every 3 seconds)');
1146
+ // console.log('Server polling started (every 3 seconds)');
1454
1147
  }
1455
1148
 
1456
1149
  function stopPolling() {
1457
1150
  if (serverPollingInterval) {
1458
1151
  clearInterval(serverPollingInterval);
1459
1152
  serverPollingInterval = null;
1460
- console.log('Server polling stopped');
1153
+ // console.log('Server polling stopped');
1461
1154
  }
1462
1155
  }
1463
1156
 
1464
1157
  async function checkServerOnce() {
1465
- console.log('Running single server check...');
1158
+ // console.log('Running single server check...');
1466
1159
  await updateButtonStatesForServerStatus();
1467
1160
  }
1468
1161
 
@@ -1505,7 +1198,7 @@ document.addEventListener('DOMContentLoaded', () => {
1505
1198
  stopPolling();
1506
1199
  }
1507
1200
 
1508
- console.log('Continuous polling:', isChecked ? 'enabled' : 'disabled');
1201
+ // console.log('Continuous polling:', isChecked ? 'enabled' : 'disabled');
1509
1202
  });
1510
1203
 
1511
1204
  // Retry button click handler
@@ -1534,7 +1227,7 @@ document.addEventListener('DOMContentLoaded', () => {
1534
1227
  // Save changes when checkbox is clicked
1535
1228
  enhancedCaptureCheckbox.addEventListener('change', () => {
1536
1229
  chrome.storage.local.set({ enhancedClickCapture: enhancedCaptureCheckbox.checked });
1537
- console.log('Enhanced click capture setting changed to:', enhancedCaptureCheckbox.checked);
1230
+ // console.log('Enhanced click capture setting changed to:', enhancedCaptureCheckbox.checked);
1538
1231
  });
1539
1232
  }
1540
1233
 
@@ -1661,10 +1354,10 @@ document.addEventListener('DOMContentLoaded', () => {
1661
1354
  isRecording = true;
1662
1355
  recordingStartTime = result.recordingStartTime || Date.now();
1663
1356
  updateRecordingUI();
1664
- console.log('Restored recording state from storage');
1357
+ // console.log('Restored recording state from storage');
1665
1358
  } else if (result.pendingRecording && result.pendingTabId) {
1666
1359
  // Auto-start recording for the pending tab
1667
- console.log('Found pending recording for tab:', result.pendingTabId);
1360
+ // console.log('Found pending recording for tab:', result.pendingTabId);
1668
1361
  const targetTabId = result.pendingTabId;
1669
1362
  chrome.storage.local.remove(['pendingRecording', 'pendingTabId']);
1670
1363
 
@@ -1693,10 +1386,10 @@ document.addEventListener('DOMContentLoaded', () => {
1693
1386
  const recordingStatus = document.getElementById('recordingStatus');
1694
1387
 
1695
1388
  if (recordBtn) {
1696
- console.log('Setting up record button handler');
1389
+ // console.log('Setting up record button handler');
1697
1390
  recordBtn.addEventListener('click', async (e) => {
1698
1391
  e.preventDefault();
1699
- console.log('Record button clicked, isRecording:', isRecording);
1392
+ // console.log('Record button clicked, isRecording:', isRecording);
1700
1393
 
1701
1394
  if (!isRecording && !isStoppingRecording) {
1702
1395
  try {
@@ -1705,15 +1398,26 @@ document.addEventListener('DOMContentLoaded', () => {
1705
1398
  const isBrowserOnlyMode = !serverStatus.connected;
1706
1399
 
1707
1400
  if (isBrowserOnlyMode) {
1708
- console.log('Starting recording in browser-only mode');
1401
+ // console.log('Starting recording in browser-only mode');
1709
1402
  // Browser-only mode - proceed with local storage
1710
1403
  } else {
1711
- console.log('Starting recording with server');
1404
+ // console.log('Starting recording with server');
1405
+ }
1406
+
1407
+ // Check FREE tier usage limit before starting
1408
+ const usageCheck = await chrome.runtime.sendMessage({ action: 'checkUsageLimit' });
1409
+ if (usageCheck && !usageCheck.allowed) {
1410
+ // console.log('[Usage] Daily limit reached:', usageCheck);
1411
+ if (recordingStatus) {
1412
+ recordingStatus.innerHTML = '<strong style="color: #f44336;">Daily limit reached</strong><br>' +
1413
+ `<small>${usageCheck.count}/${usageCheck.limit} recordings used today.<br>Upgrade to Pro for unlimited.</small>`;
1414
+ }
1415
+ return;
1712
1416
  }
1713
1417
 
1714
1418
  // Get the current active tab
1715
1419
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
1716
- console.log('Current tab:', tab);
1420
+ // console.log('Current tab:', tab);
1717
1421
 
1718
1422
  if (!tab || !tab.id) {
1719
1423
  console.error('No active tab found');
@@ -1757,7 +1461,7 @@ document.addEventListener('DOMContentLoaded', () => {
1757
1461
  }
1758
1462
 
1759
1463
  if (response && response.success) {
1760
- console.log('Recording started successfully');
1464
+ // console.log('Recording started successfully');
1761
1465
  isRecording = true;
1762
1466
  recordingStartTime = Date.now();
1763
1467
 
@@ -1780,7 +1484,7 @@ document.addEventListener('DOMContentLoaded', () => {
1780
1484
  }
1781
1485
  } else if (isRecording && !isStoppingRecording) {
1782
1486
  // Stop recording
1783
- console.log('Stopping recording...');
1487
+ // console.log('Stopping recording...');
1784
1488
  isStoppingRecording = true;
1785
1489
  recordBtn.textContent = 'Stopping...';
1786
1490
  recordBtn.disabled = true;
@@ -1799,19 +1503,16 @@ document.addEventListener('DOMContentLoaded', () => {
1799
1503
  }
1800
1504
 
1801
1505
  if (response && response.success) {
1802
- console.log('Recording stopped successfully', response);
1506
+ // console.log('Recording stopped successfully', response);
1803
1507
  isRecording = false;
1804
1508
  updateRecordingUI();
1805
1509
 
1806
- console.log('About to show loading overlay for screen-recording-section');
1807
1510
  // Show loading overlay over screen recording section
1808
1511
  showSectionLoading('screen-recording-section', 'Saving screen recording...');
1809
- console.log('Loading overlay should be visible now');
1810
1512
 
1811
1513
  // Use then/catch instead of async/await since we're in a callback
1812
1514
  loadScreenRecordings(true)
1813
1515
  .then(() => {
1814
- console.log('Screen recordings loaded successfully');
1815
1516
  // Show success toast
1816
1517
  const frameCount = response.frameCount || 0;
1817
1518
  const duration = response.duration || '0s';
@@ -1822,7 +1523,6 @@ document.addEventListener('DOMContentLoaded', () => {
1822
1523
  showToast('Error loading recordings. Please refresh.', 3000, 'error');
1823
1524
  })
1824
1525
  .finally(() => {
1825
- console.log('Hiding loading overlay');
1826
1526
  // Always hide loading overlay
1827
1527
  hideSectionLoading('screen-recording-section');
1828
1528
  });
@@ -1889,26 +1589,16 @@ document.addEventListener('DOMContentLoaded', () => {
1889
1589
  return;
1890
1590
  }
1891
1591
 
1892
- // Check license before allowing workflow recording (same pattern as strategic plan)
1893
- console.log('[License] Checking license for workflow recording...');
1894
- const licenseCheck = await chrome.runtime.sendMessage({
1895
- action: 'checkLicenseForWorkflow'
1896
- });
1897
- console.log('[License] License check result:', licenseCheck);
1898
-
1899
- // If not Pro or limit reached, show upgrade modal
1900
- if (!licenseCheck || !licenseCheck.allowed || (licenseCheck.tier && licenseCheck.tier !== 'pro')) {
1901
- const message = licenseCheck?.message || 'Workflow Recording is a Pro feature. Upgrade to Pro for unlimited workflow recordings with function tracing.';
1902
-
1903
- // Show modal
1904
- if (confirm(`${message}\n\nWould you like to upgrade now?`)) {
1905
- // Open upgrade page
1906
- chrome.tabs.create({
1907
- url: LEMONSQUEEZY_CHECKOUT_URL
1908
- });
1592
+ // Check FREE tier usage limit before starting
1593
+ const usageCheck = await chrome.runtime.sendMessage({ action: 'checkUsageLimit' });
1594
+ if (usageCheck && !usageCheck.allowed) {
1595
+ // console.log('[Usage] Daily limit reached:', usageCheck);
1596
+ const workflowStatus = document.getElementById('workflowRecordingStatus');
1597
+ if (workflowStatus) {
1598
+ workflowStatus.innerHTML = '<strong style="color: #f44336;">Daily limit reached</strong><br>' +
1599
+ `<small>${usageCheck.count}/${usageCheck.limit} recordings used today.<br>Upgrade to Pro for unlimited recordings.</small>`;
1909
1600
  }
1910
-
1911
- return; // Stop execution
1601
+ return;
1912
1602
  }
1913
1603
 
1914
1604
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
@@ -1934,23 +1624,16 @@ document.addEventListener('DOMContentLoaded', () => {
1934
1624
  }
1935
1625
 
1936
1626
  // Send message to background script to start recording
1937
- console.log('[Popup v2.0.6] Sending startWorkflowRecording message to background');
1938
- chrome.runtime.sendMessage({
1627
+ chrome.runtime.sendMessage({
1939
1628
  action: 'startWorkflowRecording',
1940
1629
  tabId: tab.id,
1941
1630
  includeLogsInExport: includeLogsInExport,
1942
1631
  sessionName: sessionName,
1943
1632
  screenshotSettings: screenshotSettings
1944
1633
  }, (response) => {
1945
- console.log('[Popup v2.0.6] Received response from background:', response);
1946
- console.log('[Popup v2.0.6] chrome.runtime.lastError:', chrome.runtime.lastError);
1947
- console.log('[Popup v2.0.6] Checking which branch will execute...');
1948
-
1949
1634
  if (chrome.runtime.lastError) {
1950
- console.error('[Popup v2.0.6] RUNTIME ERROR BRANCH:', chrome.runtime.lastError);
1951
1635
  // Check if this tab allows content script injection
1952
1636
  if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://') || tab.url.startsWith('moz-extension://')) {
1953
- console.log('Cannot inject content script into restricted URL:', tab.url);
1954
1637
  return;
1955
1638
  }
1956
1639
  // Inject content script if needed
@@ -1996,31 +1679,17 @@ document.addEventListener('DOMContentLoaded', () => {
1996
1679
  });
1997
1680
  });
1998
1681
  } else if (response && response.success) {
1999
- console.log('[Popup v2.0.6] *** SUCCESS BRANCH REACHED! ***');
2000
- console.log('[Popup v2.0.6] Workflow recording started successfully, updating UI now');
2001
- console.log('[Popup v2.0.6] Setting isWorkflowRecording = true');
2002
1682
  isWorkflowRecording = true;
2003
1683
  // Save state and start time to storage
2004
1684
  const startTime = Date.now();
2005
- chrome.storage.local.set({
1685
+ chrome.storage.local.set({
2006
1686
  workflowRecording: true,
2007
1687
  workflowStartTime: startTime
2008
1688
  });
2009
1689
  workflowStartTime = startTime;
2010
- console.log('[Popup v2.0.6] About to call updateWorkflowRecordingUI()');
2011
1690
  updateWorkflowRecordingUI();
2012
- console.log('[Popup v2.0.6] updateWorkflowRecordingUI() call completed');
2013
1691
  // Hide any previous results
2014
1692
  if (workflowRecordingsList) workflowRecordingsList.style.display = 'none';
2015
- } else {
2016
- console.error('[Popup v2.0.6] *** ELSE BRANCH - Unexpected response ***');
2017
- console.error('[Popup v2.0.6] Response details:', {
2018
- exists: !!response,
2019
- success: response?.success,
2020
- type: typeof response,
2021
- keys: response ? Object.keys(response) : 'no response',
2022
- fullResponse: response
2023
- });
2024
1693
  }
2025
1694
  });
2026
1695
  } catch (error) {
@@ -2070,9 +1739,9 @@ document.addEventListener('DOMContentLoaded', () => {
2070
1739
  // Single source of truth - load from server
2071
1740
  await loadWorkflowRecordings(true);
2072
1741
 
2073
- // Show success toast
1742
+ // Show success toast - response.workflow.actions is the array
2074
1743
  const workflowData = response.workflow;
2075
- const actionCount = workflowData?.workflow?.actions?.length || workflowData?.workflow?.length || 0;
1744
+ const actionCount = workflowData?.actions?.length || 0;
2076
1745
  showToast(`Workflow recording saved! ${actionCount} actions recorded`, 3000, 'success');
2077
1746
  } catch (error) {
2078
1747
  console.error('Error loading workflow recordings:', error);
@@ -2122,7 +1791,7 @@ document.addEventListener('DOMContentLoaded', () => {
2122
1791
  }
2123
1792
 
2124
1793
  if (response && response.success) {
2125
- console.log('Restore point created:', response.restorePointId);
1794
+ // console.log('Restore point created:', response.restorePointId);
2126
1795
  // Refresh restore points list
2127
1796
  loadRestorePoints();
2128
1797
  } else {
@@ -2256,7 +1925,7 @@ document.addEventListener('DOMContentLoaded', () => {
2256
1925
  }
2257
1926
 
2258
1927
  if (response && response.success) {
2259
- console.log('Successfully restored from point');
1928
+ // console.log('Successfully restored from point');
2260
1929
  } else {
2261
1930
  console.error('Failed to restore from point:', response);
2262
1931
  }
@@ -2278,7 +1947,7 @@ document.addEventListener('DOMContentLoaded', () => {
2278
1947
  });
2279
1948
 
2280
1949
  if (response.ok) {
2281
- console.log('Restore point deleted');
1950
+ // console.log('Restore point deleted');
2282
1951
  break;
2283
1952
  }
2284
1953
  } catch (error) {
@@ -2472,7 +2141,7 @@ async function loadWorkflowRecordings(autoShow = false) {
2472
2141
  const response = await fetch(`http://localhost:${port}/chromedebug/workflow-recordings`);
2473
2142
  if (response.ok) {
2474
2143
  const data = await response.json();
2475
- console.log('Loaded workflow recordings from port', port, ':', data);
2144
+ // console.log('Loaded workflow recordings from port', port, ':', data);
2476
2145
 
2477
2146
  // Handle both data.recordings and direct array response
2478
2147
  const recordings = data.recordings || data;
@@ -2567,7 +2236,7 @@ async function loadWorkflowRecordings(autoShow = false) {
2567
2236
  playBtn.addEventListener('click', () => playWorkflow(recording.session_id));
2568
2237
 
2569
2238
  deleteBtn.addEventListener('click', () => {
2570
- console.log('Delete clicked for recording:', recording);
2239
+ // console.log('Delete clicked for recording:', recording);
2571
2240
  const recordingId = recording.id || recording.session_id;
2572
2241
  if (!recordingId) {
2573
2242
  console.error('No ID found for recording:', recording);
@@ -2625,7 +2294,7 @@ async function loadWorkflowRecordings(autoShow = false) {
2625
2294
 
2626
2295
  // Function to play a workflow
2627
2296
  async function playWorkflow(sessionId) {
2628
- console.log('Playing workflow:', sessionId);
2297
+ // console.log('Playing workflow:', sessionId);
2629
2298
 
2630
2299
  try {
2631
2300
  // Get the current active tab
@@ -2705,7 +2374,7 @@ async function playWorkflow(sessionId) {
2705
2374
  }, 100);
2706
2375
  });
2707
2376
  } else if (response && response.success) {
2708
- console.log('Workflow playback started');
2377
+ // console.log('Workflow playback started');
2709
2378
  }
2710
2379
  });
2711
2380
  } catch (error) {
@@ -2785,11 +2454,11 @@ async function loadScreenRecordings(autoShow = false) {
2785
2454
 
2786
2455
  // First, load browser-only recordings from IndexedDB
2787
2456
  try {
2788
- console.log('[Popup] Requesting browser-only recordings...');
2457
+ // console.log('[Popup] Requesting browser-only recordings...');
2789
2458
  const browserRecordings = await chrome.runtime.sendMessage({
2790
2459
  action: 'getBrowserRecordings'
2791
2460
  });
2792
- console.log('[Popup] Browser recordings received:', browserRecordings);
2461
+ // console.log('[Popup] Browser recordings received:', browserRecordings);
2793
2462
 
2794
2463
  if (browserRecordings && browserRecordings.length > 0) {
2795
2464
  hasRecordings = true;
@@ -2929,10 +2598,10 @@ async function loadScreenRecordings(autoShow = false) {
2929
2598
  const formattedDate = date.toLocaleString();
2930
2599
 
2931
2600
  // Use session name if available, otherwise use session ID
2932
- console.log('Recording data:', recording);
2933
- console.log('Recording name:', recording.name);
2601
+ // console.log('Recording data:', recording);
2602
+ // console.log('Recording name:', recording.name);
2934
2603
  const displayName = recording.name || `Recording ${recording.sessionId}`;
2935
- console.log('Display name:', displayName);
2604
+ // console.log('Display name:', displayName);
2936
2605
 
2937
2606
  recordingItem.innerHTML = `
2938
2607
  <div class="recording-info">
@@ -2972,9 +2641,9 @@ async function loadScreenRecordings(autoShow = false) {
2972
2641
  let prompt;
2973
2642
 
2974
2643
  if (name) {
2975
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to display the screen recording "${name}" (session: ${sessionId}) that was recorded on port ${port}.`;
2644
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to display the screen recording "${name}" (session: ${sessionId}) that was recorded on port ${port}.`;
2976
2645
  } else {
2977
- prompt = `Please use the chrome_pilot_show_frames function in Chrome Debug to display the screen recording "${sessionId}" that was recorded on port ${port}.`;
2646
+ prompt = `Please use the chrome_debug_show_frames function in Chrome Debug to display the screen recording "${sessionId}" that was recorded on port ${port}.`;
2978
2647
  }
2979
2648
 
2980
2649
  navigator.clipboard.writeText(prompt).then(() => {
@@ -3228,14 +2897,25 @@ chrome.storage.onChanged.addListener((changes, namespace) => {
3228
2897
  isWorkflowRecording = changes.workflowRecording.newValue === true;
3229
2898
  updateWorkflowRecordingUI();
3230
2899
  }
2900
+ // Update usage display when usage count changes
2901
+ if (changes.chromedebug_daily_usage) {
2902
+ const usage = changes.chromedebug_daily_usage.newValue;
2903
+ if (usage) {
2904
+ const usageCountEl = document.getElementById('usage-count');
2905
+ const usageLimitEl = document.getElementById('usage-limit');
2906
+ if (usageCountEl) usageCountEl.textContent = usage.count || 0;
2907
+ if (usageLimitEl) usageLimitEl.textContent = '5'; // FREE tier limit
2908
+ // console.log(`[Usage] Storage updated: ${usage.count}/5`);
2909
+ }
2910
+ }
3231
2911
  }
3232
2912
  });
3233
2913
 
3234
2914
  // Listen for messages from background script
3235
2915
  chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3236
- console.log('Popup received message:', request);
2916
+ // console.log('Popup received message:', request);
3237
2917
  if (request.action === 'uploadComplete') {
3238
- console.log('Upload complete with ID:', request.recordingId);
2918
+ // console.log('Upload complete with ID:', request.recordingId);
3239
2919
  const recordingStatus = document.getElementById('recordingStatus');
3240
2920
  if (recordingStatus) {
3241
2921
  recordingStatus.innerHTML = `<strong style="color: #4CAF50;">Recording saved!</strong>`;
@@ -3247,7 +2927,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3247
2927
  if (recordingStatus) recordingStatus.textContent = '';
3248
2928
  }, 3000);
3249
2929
  } else if (request.action === 'sessionComplete') {
3250
- console.log('Session complete:', request.sessionId, 'with', request.totalChunks, 'chunks');
2930
+ // console.log('Session complete:', request.sessionId, 'with', request.totalChunks, 'chunks');
3251
2931
  const recordingStatus = document.getElementById('recordingStatus');
3252
2932
  if (recordingStatus) {
3253
2933
  recordingStatus.innerHTML = `<strong style="color: #4CAF50;">Recording session saved!</strong><br>` +
@@ -3260,18 +2940,26 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
3260
2940
  if (recordingStatus) recordingStatus.textContent = '';
3261
2941
  }, 5000);
3262
2942
  } else if (request.action === 'frameSessionComplete') {
3263
- console.log('Frame session complete:', request.sessionId, 'with', request.totalFrames, 'frames');
2943
+ // console.log('Frame session complete:', request.sessionId, 'with', request.totalFrames, 'frames');
3264
2944
  const recordingStatus = document.getElementById('recordingStatus');
3265
2945
  if (recordingStatus) {
3266
2946
  recordingStatus.innerHTML = `<strong style="color: #4CAF50;">Recording saved!</strong><br>` +
3267
2947
  `<small>${request.totalFrames} frames (${Math.round(request.duration / 1000)}s)</small>`;
3268
2948
  }
3269
- // Add frame session to recordings list
3270
- addRecording(request.sessionId, false, true, request.serverPort, null, request.sessionName);
2949
+ // Reload screen recordings list to show the new recording
2950
+ loadScreenRecordings(true);
3271
2951
  // Clear status after 5 seconds
3272
2952
  setTimeout(() => {
3273
2953
  if (recordingStatus) recordingStatus.textContent = '';
3274
2954
  }, 5000);
2955
+ } else if (request.action === 'workflowRecordingSaved') {
2956
+ // console.log('Workflow recording saved:', request.workflowId);
2957
+ // Reload workflow recordings list to show the new recording
2958
+ loadWorkflowRecordings(true);
2959
+ } else if (request.action === 'recordingStopped') {
2960
+ // console.log('Recording stopped:', request.sessionId);
2961
+ // Reload screen recordings list immediately
2962
+ loadScreenRecordings(true);
3275
2963
  } else if (request.action === 'uploadError') {
3276
2964
  const recordingStatus = document.getElementById('recordingStatus');
3277
2965
  if (recordingStatus) {