@dynamicu/chromedebug-mcp 2.6.6 → 2.6.7

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.
@@ -46,6 +46,69 @@ let workflowStartTime = null;
46
46
  // Debounce timer for screenshot quality slider
47
47
  let screenshotQualityDebounceTimer = null;
48
48
 
49
+ // Toast notification function
50
+ function showToast(message, duration = 2000, type = 'info') {
51
+ // Create toast element
52
+ const toast = document.createElement('div');
53
+ toast.className = `toast toast-${type}`;
54
+ toast.textContent = message;
55
+ toast.style.cssText = `
56
+ position: fixed;
57
+ top: 20px;
58
+ right: 20px;
59
+ padding: 12px 20px;
60
+ background: ${type === 'error' ? '#f44336' : type === 'success' ? '#4CAF50' : '#2196F3'};
61
+ color: white;
62
+ border-radius: 4px;
63
+ box-shadow: 0 2px 5px rgba(0,0,0,0.3);
64
+ z-index: 10000;
65
+ font-size: 14px;
66
+ animation: slideInRight 0.3s ease-out;
67
+ `;
68
+
69
+ document.body.appendChild(toast);
70
+
71
+ // Auto remove after duration
72
+ setTimeout(() => {
73
+ toast.style.animation = 'slideOutRight 0.3s ease-in';
74
+ setTimeout(() => toast.remove(), 300);
75
+ }, duration);
76
+ }
77
+
78
+ // Loading overlay helper functions
79
+ function showSectionLoading(sectionId, message = 'Loading...') {
80
+ console.log(`showSectionLoading called for: ${sectionId} with message: ${message}`);
81
+ const section = document.getElementById(sectionId);
82
+ if (!section) {
83
+ console.error(`Section not found: ${sectionId}`);
84
+ return;
85
+ }
86
+ console.log(`Section found:`, section);
87
+
88
+ // Remove any existing overlay first
89
+ hideSectionLoading(sectionId);
90
+
91
+ const overlay = document.createElement('div');
92
+ overlay.className = 'recording-section-overlay';
93
+ overlay.innerHTML = `
94
+ <div class="spinner"></div>
95
+ <div class="loading-text">${message}</div>
96
+ `;
97
+ overlay.setAttribute('data-loading-overlay', 'true');
98
+ section.appendChild(overlay);
99
+ console.log(`Overlay appended to section. Overlay:`, overlay);
100
+ }
101
+
102
+ function hideSectionLoading(sectionId) {
103
+ const section = document.getElementById(sectionId);
104
+ if (!section) return;
105
+
106
+ const overlay = section.querySelector('[data-loading-overlay="true"]');
107
+ if (overlay) {
108
+ overlay.remove();
109
+ }
110
+ }
111
+
49
112
  // Workflow recording functions
50
113
  function updateWorkflowRecordingUI() {
51
114
  const workflowBtn = document.getElementById('workflowRecordBtn');
@@ -319,10 +382,13 @@ async function checkServerStatus() {
319
382
  });
320
383
 
321
384
  clearTimeout(timeoutId);
322
- console.log(`[popup.js] Port ${port} responded: ${response.ok}`);
385
+ // Only log successful connections to reduce console noise
386
+ if (response.ok) {
387
+ console.log(`[popup.js] ✓ Connected to server on port ${port}`);
388
+ }
323
389
  return response.ok;
324
390
  } catch (error) {
325
- console.log(`[popup.js] Port ${port} failed:`, error.message);
391
+ // Silent failure - port discovery is expected to fail for most ports
326
392
  return false;
327
393
  }
328
394
  };
@@ -333,7 +399,6 @@ async function checkServerStatus() {
333
399
  if (await tryPort(cachedPort.lastSuccessfulPort, 500)) {
334
400
  connected = true;
335
401
  connectedPort = cachedPort.lastSuccessfulPort;
336
- console.log(`[popup.js] Connected to cached port ${connectedPort}`);
337
402
  }
338
403
  }
339
404
 
@@ -344,7 +409,6 @@ async function checkServerStatus() {
344
409
  if (await tryPort(port, 500)) {
345
410
  connected = true;
346
411
  connectedPort = port;
347
- console.log(`[popup.js] Connected to common port ${connectedPort}`);
348
412
  break;
349
413
  }
350
414
  }
@@ -356,7 +420,6 @@ async function checkServerStatus() {
356
420
  if (await tryPort(port, 1000)) {
357
421
  connected = true;
358
422
  connectedPort = port;
359
- console.log(`[popup.js] Connected to extended port ${connectedPort}`);
360
423
  break;
361
424
  }
362
425
  }
@@ -368,7 +431,6 @@ async function checkServerStatus() {
368
431
  if (await tryPort(port, 1000)) {
369
432
  connected = true;
370
433
  connectedPort = port;
371
- console.log(`[popup.js] Connected to alternative port ${connectedPort}`);
372
434
  break;
373
435
  }
374
436
  }
@@ -377,6 +439,8 @@ async function checkServerStatus() {
377
439
  // Cache the successful port for faster connection next time
378
440
  if (connected && connectedPort) {
379
441
  await chrome.storage.local.set({ lastSuccessfulPort: connectedPort });
442
+ } else {
443
+ console.log('[popup.js] No server found - browser-only mode active');
380
444
  }
381
445
 
382
446
  if (connected) {
@@ -902,7 +966,16 @@ function updateRecordingsDisplay(recordings) {
902
966
  const recordingId = e.target.getAttribute('data-id');
903
967
  const type = e.target.getAttribute('data-type');
904
968
 
905
- // Check if pro version (frame editor) is available
969
+ // First check if server is available (for server recordings)
970
+ const serverStatus = await checkServerStatus();
971
+
972
+ if (!serverStatus.connected) {
973
+ // No server - this is a server recording that requires server to view
974
+ alert('This recording requires Chrome Debug server to view.\n\nStart the server with: chromedebug-mcp-server\n\nOr delete this old recording and create new browser-only recordings with Export functionality.');
975
+ return;
976
+ }
977
+
978
+ // Server available - check if pro version (frame editor) is available
906
979
  try {
907
980
  const frameEditorUrl = type === 'snapshot'
908
981
  ? chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${recordingId}&type=snapshot`)
@@ -1016,8 +1089,9 @@ async function showInlineActivationManager(licenseKey) {
1016
1089
  activationsList.innerHTML = '<div style="padding: 15px; text-align: center; color: #666;">Loading activations...</div>';
1017
1090
 
1018
1091
  // Get current instance ID
1019
- const stored = await chrome.storage.local.get(['chromedebug_instance_id']);
1020
- const currentInstanceId = stored.chromedebug_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;
1021
1095
 
1022
1096
  // Fetch activations from Firebase
1023
1097
  const data = await licenseClient.listActivations(licenseKey);
@@ -1063,7 +1137,8 @@ function renderInlineActivations(activations, currentInstanceId, licenseKey) {
1063
1137
  item.onmouseleave = () => item.style.background = isCurrentDevice ? '#e8f5e9' : 'white';
1064
1138
 
1065
1139
  // Check if this is the current device
1066
- const isCurrentDevice = activation.name === currentInstanceId || activation.identifier === currentInstanceId;
1140
+ // Compare against identifier (unique per activation), not name (same for all activations from this device)
1141
+ const isCurrentDevice = currentInstanceId && activation.identifier === currentInstanceId;
1067
1142
 
1068
1143
  if (isCurrentDevice) {
1069
1144
  item.style.background = '#e8f5e9';
@@ -1282,18 +1357,37 @@ document.addEventListener('DOMContentLoaded', () => {
1282
1357
  const serverStatus = await checkServerStatus();
1283
1358
  const recordBtn = document.getElementById('recordBtn');
1284
1359
  const workflowRecordBtn = document.getElementById('workflowRecordBtn');
1285
-
1360
+ const browserOnlyBanner = document.getElementById('browserOnlyBanner');
1361
+ const quotaMeter = document.getElementById('quotaMeter');
1362
+
1286
1363
  if (!serverStatus.connected) {
1287
- // Disable recording buttons when server is not running
1288
- if (recordBtn && !isRecording) {
1289
- recordBtn.style.opacity = '0.6';
1290
- recordBtn.title = 'Server not running - please start Chrome Debug server';
1364
+ // Show browser-only mode UI
1365
+ if (browserOnlyBanner) {
1366
+ browserOnlyBanner.style.display = 'block';
1367
+ }
1368
+ if (quotaMeter) {
1369
+ quotaMeter.style.display = 'block';
1370
+ updateQuotaUI(); // Update quota display
1371
+ }
1372
+
1373
+ // Enable recording buttons in browser-only mode
1374
+ if (recordBtn) {
1375
+ recordBtn.style.opacity = '1';
1376
+ recordBtn.title = 'Recording will be stored in browser storage';
1291
1377
  }
1292
- if (workflowRecordBtn && !isWorkflowRecording) {
1293
- workflowRecordBtn.style.opacity = '0.6';
1294
- workflowRecordBtn.title = 'Server not running - please start Chrome Debug server';
1378
+ if (workflowRecordBtn) {
1379
+ workflowRecordBtn.style.opacity = '1';
1380
+ workflowRecordBtn.title = 'Recording will be stored in browser storage';
1295
1381
  }
1296
1382
  } else {
1383
+ // Hide browser-only mode UI when server is connected
1384
+ if (browserOnlyBanner) {
1385
+ browserOnlyBanner.style.display = 'none';
1386
+ }
1387
+ if (quotaMeter) {
1388
+ quotaMeter.style.display = 'none';
1389
+ }
1390
+
1297
1391
  // Enable recording buttons when server is running
1298
1392
  if (recordBtn) {
1299
1393
  recordBtn.style.opacity = '1';
@@ -1305,11 +1399,127 @@ document.addEventListener('DOMContentLoaded', () => {
1305
1399
  }
1306
1400
  }
1307
1401
  }
1402
+
1403
+ // Update quota UI for browser-only mode
1404
+ async function updateQuotaUI() {
1405
+ try {
1406
+ // Request quota info from background script
1407
+ const response = await chrome.runtime.sendMessage({
1408
+ action: 'getQuotaInfo'
1409
+ });
1410
+
1411
+ if (response && response.quotaInfo) {
1412
+ const { percentage, usageMB, quotaMB, availableMB } = response.quotaInfo;
1413
+
1414
+ const quotaBar = document.getElementById('quotaBar');
1415
+ const quotaText = document.getElementById('quotaText');
1416
+ const quotaAvailable = document.getElementById('quotaAvailable');
1417
+ const quotaUsage = document.getElementById('quotaUsage');
1418
+
1419
+ if (quotaBar && quotaText && quotaAvailable) {
1420
+ const percentageValue = Math.round(percentage * 100);
1421
+ quotaBar.style.width = `${percentageValue}%`;
1422
+
1423
+ // Change color based on usage
1424
+ if (percentage > 0.8) {
1425
+ quotaBar.style.background = '#f44336'; // Red
1426
+ } else if (percentage > 0.6) {
1427
+ quotaBar.style.background = '#ff9800'; // Orange
1428
+ } else {
1429
+ quotaBar.style.background = '#4CAF50'; // Green
1430
+ }
1431
+
1432
+ quotaText.textContent = `${percentageValue}% used`;
1433
+ quotaAvailable.textContent = `${availableMB || 0} MB available`;
1434
+ }
1435
+
1436
+ if (quotaUsage) {
1437
+ quotaUsage.textContent = `${usageMB || 0} MB / ${quotaMB || 0} MB`;
1438
+ }
1439
+ }
1440
+ } catch (error) {
1441
+ console.error('Failed to update quota UI:', error);
1442
+ }
1443
+ }
1308
1444
 
1309
- // Initialize server status check
1310
- updateButtonStatesForServerStatus();
1311
- setInterval(updateButtonStatesForServerStatus, 3000);
1312
-
1445
+ // Server polling control
1446
+ let serverPollingInterval = null;
1447
+
1448
+ function startPolling() {
1449
+ if (serverPollingInterval) {
1450
+ clearInterval(serverPollingInterval);
1451
+ }
1452
+ serverPollingInterval = setInterval(updateButtonStatesForServerStatus, 3000);
1453
+ console.log('Server polling started (every 3 seconds)');
1454
+ }
1455
+
1456
+ function stopPolling() {
1457
+ if (serverPollingInterval) {
1458
+ clearInterval(serverPollingInterval);
1459
+ serverPollingInterval = null;
1460
+ console.log('Server polling stopped');
1461
+ }
1462
+ }
1463
+
1464
+ async function checkServerOnce() {
1465
+ console.log('Running single server check...');
1466
+ await updateButtonStatesForServerStatus();
1467
+ }
1468
+
1469
+ // Initialize polling controls
1470
+ const pollContinuouslyCheckbox = document.getElementById('pollContinuouslyCheckbox');
1471
+ const retryConnectionBtn = document.getElementById('retryConnectionBtn');
1472
+
1473
+ if (pollContinuouslyCheckbox && retryConnectionBtn) {
1474
+ // Load saved polling preference (default: true)
1475
+ chrome.storage.local.get(['pollContinuously'], (result) => {
1476
+ const shouldPoll = result.pollContinuously !== false; // Default to true
1477
+ pollContinuouslyCheckbox.checked = shouldPoll;
1478
+
1479
+ // Show/hide retry button based on preference
1480
+ retryConnectionBtn.style.display = shouldPoll ? 'none' : 'inline-block';
1481
+
1482
+ // Initial server check
1483
+ updateButtonStatesForServerStatus();
1484
+
1485
+ // Start polling if enabled
1486
+ if (shouldPoll) {
1487
+ startPolling();
1488
+ }
1489
+ });
1490
+
1491
+ // Checkbox change handler
1492
+ pollContinuouslyCheckbox.addEventListener('change', () => {
1493
+ const isChecked = pollContinuouslyCheckbox.checked;
1494
+
1495
+ // Save preference
1496
+ chrome.storage.local.set({ pollContinuously: isChecked });
1497
+
1498
+ // Show/hide retry button
1499
+ retryConnectionBtn.style.display = isChecked ? 'none' : 'inline-block';
1500
+
1501
+ // Start or stop polling
1502
+ if (isChecked) {
1503
+ startPolling();
1504
+ } else {
1505
+ stopPolling();
1506
+ }
1507
+
1508
+ console.log('Continuous polling:', isChecked ? 'enabled' : 'disabled');
1509
+ });
1510
+
1511
+ // Retry button click handler
1512
+ retryConnectionBtn.addEventListener('click', () => {
1513
+ showToast('Retrying connection...', 2000, 'info');
1514
+ checkServerOnce();
1515
+ });
1516
+ } else {
1517
+ // Fallback if polling controls not found - use default behavior
1518
+ console.warn('Polling controls not found, using default polling behavior');
1519
+ updateButtonStatesForServerStatus();
1520
+ setInterval(updateButtonStatesForServerStatus, 3000);
1521
+ }
1522
+
1313
1523
  // Initialize site management functionality
1314
1524
  initializeSiteManagement();
1315
1525
 
@@ -1414,9 +1624,13 @@ document.addEventListener('DOMContentLoaded', () => {
1414
1624
  }
1415
1625
 
1416
1626
  });
1417
-
1418
- // Load saved recordings
1627
+
1628
+ // Load saved recordings (legacy)
1419
1629
  loadRecordings();
1630
+
1631
+ // Load screen and workflow recordings with saved preferences
1632
+ loadScreenRecordings();
1633
+ loadWorkflowRecordings();
1420
1634
 
1421
1635
  // Setup recording settings event listeners
1422
1636
  const frameRateSelect = document.getElementById('frameRate');
@@ -1488,19 +1702,15 @@ document.addEventListener('DOMContentLoaded', () => {
1488
1702
  try {
1489
1703
  // Check if server is running before starting recording
1490
1704
  const serverStatus = await checkServerStatus();
1491
- if (!serverStatus.connected) {
1492
- console.error('Server not running');
1493
- if (recordingStatus) {
1494
- recordingStatus.innerHTML = '<strong style="color: #f44336;">Error: Server not running</strong><br>' +
1495
- '<small>Please start the Chrome Debug server first</small>';
1496
- }
1497
- // Clear error message after 5 seconds
1498
- setTimeout(() => {
1499
- if (recordingStatus) recordingStatus.textContent = '';
1500
- }, 5000);
1501
- return;
1705
+ const isBrowserOnlyMode = !serverStatus.connected;
1706
+
1707
+ if (isBrowserOnlyMode) {
1708
+ console.log('Starting recording in browser-only mode');
1709
+ // Browser-only mode - proceed with local storage
1710
+ } else {
1711
+ console.log('Starting recording with server');
1502
1712
  }
1503
-
1713
+
1504
1714
  // Get the current active tab
1505
1715
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
1506
1716
  console.log('Current tab:', tab);
@@ -1531,10 +1741,11 @@ document.addEventListener('DOMContentLoaded', () => {
1531
1741
  };
1532
1742
 
1533
1743
  // Send message to background to start recording
1534
- chrome.runtime.sendMessage({
1535
- action: 'startRecording',
1744
+ chrome.runtime.sendMessage({
1745
+ action: 'startRecording',
1536
1746
  tabId: tab.id,
1537
- settings: settings
1747
+ settings: settings,
1748
+ mode: isBrowserOnlyMode ? 'browser-only' : 'server'
1538
1749
  }, (response) => {
1539
1750
  recordBtn.disabled = false;
1540
1751
 
@@ -1574,23 +1785,47 @@ document.addEventListener('DOMContentLoaded', () => {
1574
1785
  recordBtn.textContent = 'Stopping...';
1575
1786
  recordBtn.disabled = true;
1576
1787
 
1577
- chrome.runtime.sendMessage({
1788
+ chrome.runtime.sendMessage({
1578
1789
  action: 'stopRecording'
1579
- }, (response) => {
1790
+ }, async (response) => {
1580
1791
  isStoppingRecording = false;
1581
1792
  recordBtn.disabled = false;
1582
-
1793
+
1583
1794
  if (chrome.runtime.lastError) {
1584
1795
  console.error('Error stopping recording:', chrome.runtime.lastError);
1585
1796
  recordingStatus.textContent = 'Error: ' + chrome.runtime.lastError.message;
1586
1797
  recordBtn.textContent = 'Stop Recording';
1587
1798
  return;
1588
1799
  }
1589
-
1800
+
1590
1801
  if (response && response.success) {
1591
- console.log('Recording stopped successfully');
1802
+ console.log('Recording stopped successfully', response);
1592
1803
  isRecording = false;
1593
1804
  updateRecordingUI();
1805
+
1806
+ console.log('About to show loading overlay for screen-recording-section');
1807
+ // Show loading overlay over screen recording section
1808
+ showSectionLoading('screen-recording-section', 'Saving screen recording...');
1809
+ console.log('Loading overlay should be visible now');
1810
+
1811
+ // Use then/catch instead of async/await since we're in a callback
1812
+ loadScreenRecordings(true)
1813
+ .then(() => {
1814
+ console.log('Screen recordings loaded successfully');
1815
+ // Show success toast
1816
+ const frameCount = response.frameCount || 0;
1817
+ const duration = response.duration || '0s';
1818
+ showToast(`Screen recording saved! ${frameCount} frames (${duration})`, 3000, 'success');
1819
+ })
1820
+ .catch(error => {
1821
+ console.error('Error loading screen recordings:', error);
1822
+ showToast('Error loading recordings. Please refresh.', 3000, 'error');
1823
+ })
1824
+ .finally(() => {
1825
+ console.log('Hiding loading overlay');
1826
+ // Always hide loading overlay
1827
+ hideSectionLoading('screen-recording-section');
1828
+ });
1594
1829
  } else {
1595
1830
  recordingStatus.textContent = 'Error: ' + (response?.error || 'Failed to stop recording');
1596
1831
  recordBtn.textContent = 'Stop Recording';
@@ -1822,86 +2057,29 @@ document.addEventListener('DOMContentLoaded', () => {
1822
2057
  if (response && response.success && response.workflow) {
1823
2058
  isWorkflowRecording = false;
1824
2059
  // Clear state from storage
1825
- chrome.storage.local.set({
2060
+ chrome.storage.local.set({
1826
2061
  workflowRecording: false,
1827
2062
  workflowStartTime: null
1828
2063
  });
1829
2064
  updateWorkflowRecordingUI();
1830
-
1831
- // Display the workflow results
1832
- if (workflowRecordingsContainer) {
1833
- let workflowItem;
1834
-
1835
- // Get current server port for prompt generation
1836
- const serverConnection = await checkServerStatus();
1837
- const currentServerPort = serverConnection.connectedPort;
1838
-
1839
- // The response.workflow contains the full response from stopWorkflowRecording
2065
+
2066
+ // Show loading overlay over workflow recording section
2067
+ showSectionLoading('workflow-recording-section', 'Saving workflow recording...');
2068
+
2069
+ try {
2070
+ // Single source of truth - load from server
2071
+ await loadWorkflowRecordings(true);
2072
+
2073
+ // Show success toast
1840
2074
  const workflowData = response.workflow;
1841
-
1842
- if (workflowData.savedToServer) {
1843
- // Create a saved recording entry like screen recordings
1844
- workflowItem = document.createElement('div');
1845
- workflowItem.className = 'recording-item';
1846
- workflowItem.innerHTML = `
1847
- <div class="recording-info">
1848
- <div style="font-weight: bold; margin-bottom: 4px;">
1849
- ${workflowData.workflow.sessionId} (workflow)
1850
- </div>
1851
- <div style="color: #666; font-size: 11px;">
1852
- ${workflowData.workflow.actions.length} actions • ${workflowData.workflow.logs.length} logs
1853
- </div>
1854
- </div>
1855
- <div style="display: flex; gap: 4px; margin-top: 8px;">
1856
- <button class="copy-id-btn" style="padding: 4px 8px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
1857
- Copy ID
1858
- </button>
1859
- <button class="copy-prompt-btn" data-id="${workflowData.workflow.sessionId}" data-port="${currentServerPort || ''}" style="padding: 4px 8px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
1860
- Copy Prompt
1861
- </button>
1862
- </div>
1863
- `;
1864
-
1865
- // Add event listeners for the buttons
1866
- const copyIdBtn = workflowItem.querySelector('.copy-id-btn');
1867
- const copyPromptBtn = workflowItem.querySelector('.copy-prompt-btn');
1868
-
1869
- copyIdBtn.addEventListener('click', () => {
1870
- navigator.clipboard.writeText(workflowData.workflow.sessionId).then(() => {
1871
- copyIdBtn.textContent = 'Copied!';
1872
- copyIdBtn.style.background = '#4CAF50';
1873
- setTimeout(() => {
1874
- copyIdBtn.textContent = 'Copy ID';
1875
- copyIdBtn.style.background = '#2196F3';
1876
- }, 2000);
1877
- });
1878
- });
1879
-
1880
- // copyPromptBtn is now handled by the shared event listener system with data-id and data-port attributes
1881
- } else {
1882
- // Fallback when server not available
1883
- workflowItem = document.createElement('div');
1884
- workflowItem.className = 'recording-item';
1885
- workflowItem.innerHTML = `
1886
- <div class="recording-info">
1887
- <div style="font-weight: bold; margin-bottom: 4px; color: #ff6b6b;">
1888
- Server not available - JSON downloaded
1889
- </div>
1890
- <div style="color: #666; font-size: 11px;">
1891
- ${workflowData.workflow.length} actions recorded
1892
- </div>
1893
- </div>
1894
- `;
1895
- }
1896
-
1897
- // Add the item to the container
1898
- workflowRecordingsContainer.appendChild(workflowItem);
1899
- workflowRecordingsList.style.display = 'block';
1900
-
1901
- // Reload the list to show all recordings
1902
- setTimeout(() => {
1903
- loadWorkflowRecordings();
1904
- }, 500);
2075
+ const actionCount = workflowData?.workflow?.actions?.length || workflowData?.workflow?.length || 0;
2076
+ showToast(`Workflow recording saved! ${actionCount} actions recorded`, 3000, 'success');
2077
+ } catch (error) {
2078
+ console.error('Error loading workflow recordings:', error);
2079
+ showToast('Error loading recordings. Please refresh.', 3000, 'error');
2080
+ } finally {
2081
+ // Always hide loading overlay
2082
+ hideSectionLoading('workflow-recording-section');
1905
2083
  }
1906
2084
  }
1907
2085
  });
@@ -2193,34 +2371,46 @@ document.addEventListener('DOMContentLoaded', () => {
2193
2371
  const viewScreenRecordingsLink = document.getElementById('viewScreenRecordings');
2194
2372
 
2195
2373
  if (viewWorkflowRecordingsLink) {
2196
- viewWorkflowRecordingsLink.addEventListener('click', (e) => {
2374
+ viewWorkflowRecordingsLink.addEventListener('click', async (e) => {
2197
2375
  e.preventDefault();
2198
2376
  const workflowRecordingsList = document.getElementById('workflowRecordingsList');
2199
2377
  if (workflowRecordingsList) {
2200
2378
  if (workflowRecordingsList.style.display === 'none' || workflowRecordingsList.style.display === '') {
2379
+ // Show recordings
2201
2380
  workflowRecordingsList.style.display = 'block';
2202
- loadWorkflowRecordings();
2381
+ await loadWorkflowRecordings();
2203
2382
  viewWorkflowRecordingsLink.textContent = 'Hide Recordings';
2383
+ // Save preference
2384
+ await chrome.storage.local.set({ workflowRecordingsVisible: true });
2204
2385
  } else {
2386
+ // Hide recordings
2205
2387
  workflowRecordingsList.style.display = 'none';
2206
2388
  viewWorkflowRecordingsLink.textContent = 'View Past Recordings';
2389
+ // Save preference
2390
+ await chrome.storage.local.set({ workflowRecordingsVisible: false });
2207
2391
  }
2208
2392
  }
2209
2393
  });
2210
2394
  }
2211
2395
 
2212
2396
  if (viewScreenRecordingsLink) {
2213
- viewScreenRecordingsLink.addEventListener('click', (e) => {
2397
+ viewScreenRecordingsLink.addEventListener('click', async (e) => {
2214
2398
  e.preventDefault();
2215
2399
  const recordingsList = document.getElementById('recordingsList');
2216
2400
  if (recordingsList) {
2217
2401
  if (recordingsList.style.display === 'none') {
2218
- loadScreenRecordings();
2402
+ // Show recordings
2403
+ await loadScreenRecordings();
2219
2404
  recordingsList.style.display = 'block';
2220
2405
  viewScreenRecordingsLink.textContent = 'Hide Recordings';
2406
+ // Save preference
2407
+ await chrome.storage.local.set({ screenRecordingsVisible: true });
2221
2408
  } else {
2409
+ // Hide recordings
2222
2410
  recordingsList.style.display = 'none';
2223
2411
  viewScreenRecordingsLink.textContent = 'View Past Recordings';
2412
+ // Save preference
2413
+ await chrome.storage.local.set({ screenRecordingsVisible: false });
2224
2414
  }
2225
2415
  }
2226
2416
  });
@@ -2261,16 +2451,19 @@ document.addEventListener('DOMContentLoaded', () => {
2261
2451
  });
2262
2452
 
2263
2453
  // Function to load and display workflow recordings
2264
- async function loadWorkflowRecordings() {
2454
+ async function loadWorkflowRecordings(autoShow = false) {
2265
2455
  const workflowRecordingsList = document.getElementById('workflowRecordingsList');
2266
2456
  const workflowRecordingsContainer = document.getElementById('workflowRecordingsContainer');
2267
-
2457
+ const viewWorkflowRecordingsLink = document.getElementById('viewWorkflowRecordings');
2458
+
2268
2459
  if (!workflowRecordingsList || !workflowRecordingsContainer) {
2269
2460
  return;
2270
2461
  }
2271
-
2462
+
2272
2463
  // Clear existing items
2273
2464
  workflowRecordingsContainer.innerHTML = '';
2465
+
2466
+ let hasRecordings = false;
2274
2467
 
2275
2468
  // Check server for workflow recordings
2276
2469
  const ports = CONFIG_PORTS;
@@ -2283,8 +2476,9 @@ async function loadWorkflowRecordings() {
2283
2476
 
2284
2477
  // Handle both data.recordings and direct array response
2285
2478
  const recordings = data.recordings || data;
2286
-
2479
+
2287
2480
  if (Array.isArray(recordings) && recordings.length > 0) {
2481
+ hasRecordings = true;
2288
2482
  // Display each workflow recording
2289
2483
  recordings.forEach(recording => {
2290
2484
  const workflowItem = document.createElement('div');
@@ -2303,12 +2497,18 @@ async function loadWorkflowRecordings() {
2303
2497
  ${recording.screenshot_settings ? '<div style="color: #2196F3; font-size: 10px;">&#128248; Screenshots included</div>' : ''}
2304
2498
  </div>
2305
2499
  <div class="recording-buttons">
2500
+ <button class="copy-id-btn" data-session-id="${recording.session_id}" style="padding: 4px 8px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2501
+ Copy ID
2502
+ </button>
2503
+ <button class="copy-prompt-btn" data-session-id="${recording.session_id}" data-name="${recording.name || ''}" style="padding: 4px 8px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2504
+ Copy Prompt
2505
+ </button>
2506
+ <button class="view-btn" data-session-id="${recording.session_id}" style="padding: 4px 8px; background: #9C27B0; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2507
+ View
2508
+ </button>
2306
2509
  <button class="play-btn" data-session-id="${recording.session_id}" style="padding: 4px 8px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2307
2510
  &#9654; Play
2308
2511
  </button>
2309
- <button class="get-prompt-btn" data-session-id="${recording.session_id}" data-name="${recording.name || ''}" style="padding: 4px 8px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2310
- Get Prompt
2311
- </button>
2312
2512
  <button class="delete-btn" data-recording-id="${recording.id || recording.session_id}" style="padding: 4px 8px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2313
2513
  Delete
2314
2514
  </button>
@@ -2316,17 +2516,29 @@ async function loadWorkflowRecordings() {
2316
2516
  `;
2317
2517
 
2318
2518
  // Add event listeners
2519
+ const copyIdBtn = workflowItem.querySelector('.copy-id-btn');
2520
+ const copyPromptBtn = workflowItem.querySelector('.copy-prompt-btn');
2521
+ const viewBtn = workflowItem.querySelector('.view-btn');
2319
2522
  const playBtn = workflowItem.querySelector('.play-btn');
2320
- const getPromptBtn = workflowItem.querySelector('.get-prompt-btn');
2321
2523
  const deleteBtn = workflowItem.querySelector('.delete-btn');
2322
-
2323
- playBtn.addEventListener('click', () => playWorkflow(recording.session_id));
2324
- getPromptBtn.addEventListener('click', () => {
2524
+
2525
+ copyIdBtn.addEventListener('click', () => {
2526
+ navigator.clipboard.writeText(recording.session_id).then(() => {
2527
+ copyIdBtn.textContent = 'Copied!';
2528
+ copyIdBtn.style.background = '#4CAF50';
2529
+ setTimeout(() => {
2530
+ copyIdBtn.textContent = 'Copy ID';
2531
+ copyIdBtn.style.background = '#2196F3';
2532
+ }, 2000);
2533
+ });
2534
+ });
2535
+
2536
+ copyPromptBtn.addEventListener('click', () => {
2325
2537
  const sessionId = recording.session_id;
2326
2538
  const name = recording.name;
2327
2539
  const port = window.CHROMEDEBUG_CONFIG?.ports?.[0] || '3001';
2328
2540
  let prompt;
2329
-
2541
+
2330
2542
  if (name) {
2331
2543
  // Use name-based prompt similar to screen recording format
2332
2544
  prompt = `Please use the get_workflow_recording function in Chrome Debug to load the workflow recording "${name}" (session: ${sessionId}) that was recorded on port ${port}.`;
@@ -2334,16 +2546,26 @@ async function loadWorkflowRecordings() {
2334
2546
  // Use session ID based prompt
2335
2547
  prompt = `Please use the get_workflow_recording function in Chrome Debug to load the workflow recording "${sessionId}" that was recorded on port ${port}.`;
2336
2548
  }
2337
-
2549
+
2338
2550
  navigator.clipboard.writeText(prompt).then(() => {
2339
- getPromptBtn.textContent = 'Copied!';
2340
- getPromptBtn.style.background = '#4CAF50';
2551
+ copyPromptBtn.textContent = 'Copied!';
2552
+ const originalBg = copyPromptBtn.style.background;
2553
+ copyPromptBtn.style.background = '#4CAF50';
2341
2554
  setTimeout(() => {
2342
- getPromptBtn.textContent = 'Get Prompt';
2343
- getPromptBtn.style.background = '#2196F3';
2555
+ copyPromptBtn.textContent = 'Copy Prompt';
2556
+ copyPromptBtn.style.background = originalBg;
2344
2557
  }, 2000);
2345
2558
  });
2346
2559
  });
2560
+
2561
+ viewBtn.addEventListener('click', async () => {
2562
+ // Open frame editor in workflow mode
2563
+ const frameEditorUrl = chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${recording.session_id}&type=workflow`);
2564
+ chrome.tabs.create({ url: frameEditorUrl });
2565
+ });
2566
+
2567
+ playBtn.addEventListener('click', () => playWorkflow(recording.session_id));
2568
+
2347
2569
  deleteBtn.addEventListener('click', () => {
2348
2570
  console.log('Delete clicked for recording:', recording);
2349
2571
  const recordingId = recording.id || recording.session_id;
@@ -2378,6 +2600,27 @@ async function loadWorkflowRecordings() {
2378
2600
  errorMsg.textContent = 'Could not connect to Chrome Debug server';
2379
2601
  workflowRecordingsContainer.appendChild(errorMsg);
2380
2602
  }
2603
+
2604
+ // Handle visibility based on recordings and user preference
2605
+ if (!hasRecordings) {
2606
+ workflowRecordingsList.style.display = 'none';
2607
+ } else {
2608
+ // Get user's saved preference
2609
+ const prefs = await chrome.storage.local.get(['workflowRecordingsVisible']);
2610
+ const shouldShow = autoShow || (prefs.workflowRecordingsVisible !== false); // Default to true
2611
+
2612
+ if (shouldShow) {
2613
+ workflowRecordingsList.style.display = 'block';
2614
+ if (viewWorkflowRecordingsLink) {
2615
+ viewWorkflowRecordingsLink.textContent = 'Hide Recordings';
2616
+ }
2617
+ } else {
2618
+ workflowRecordingsList.style.display = 'none';
2619
+ if (viewWorkflowRecordingsLink) {
2620
+ viewWorkflowRecordingsLink.textContent = 'View Past Recordings';
2621
+ }
2622
+ }
2623
+ }
2381
2624
  }
2382
2625
 
2383
2626
  // Function to play a workflow
@@ -2526,18 +2769,147 @@ async function deleteWorkflowRecording(recordingId) {
2526
2769
  }
2527
2770
 
2528
2771
  // Function to load and display screen recordings
2529
- async function loadScreenRecordings() {
2772
+ async function loadScreenRecordings(autoShow = false) {
2530
2773
  const recordingsList = document.getElementById('recordingsList');
2531
2774
  const recordingsContainer = document.getElementById('recordingsContainer');
2532
-
2775
+ const viewScreenRecordingsLink = document.getElementById('viewScreenRecordings');
2776
+
2533
2777
  if (!recordingsList || !recordingsContainer) {
2534
2778
  return;
2535
2779
  }
2536
-
2780
+
2537
2781
  // Clear existing items
2538
2782
  recordingsContainer.innerHTML = '';
2539
-
2540
- // Check server for screen recordings
2783
+
2784
+ let hasRecordings = false;
2785
+
2786
+ // First, load browser-only recordings from IndexedDB
2787
+ try {
2788
+ console.log('[Popup] Requesting browser-only recordings...');
2789
+ const browserRecordings = await chrome.runtime.sendMessage({
2790
+ action: 'getBrowserRecordings'
2791
+ });
2792
+ console.log('[Popup] Browser recordings received:', browserRecordings);
2793
+
2794
+ if (browserRecordings && browserRecordings.length > 0) {
2795
+ hasRecordings = true;
2796
+ recordingsList.style.display = 'block';
2797
+
2798
+ // Display each browser-only recording
2799
+ browserRecordings.forEach(recording => {
2800
+ const recordingItem = document.createElement('div');
2801
+ recordingItem.className = 'recording-item';
2802
+
2803
+ const date = new Date(recording.startTime);
2804
+ const formattedDate = date.toLocaleString();
2805
+ const displayName = recording.title || `Recording ${recording.sessionId}`;
2806
+
2807
+ recordingItem.innerHTML = `
2808
+ <div class="recording-info">
2809
+ <div class="recording-id" title="${recording.sessionId}">
2810
+ <span class="recording-id-text">🌐 ${displayName}</span>
2811
+ <span style="font-size: 10px; background: #ff9800; color: white; padding: 2px 6px; border-radius: 3px; margin-left: 4px;">BROWSER-ONLY</span>
2812
+ <span style="color: #666; font-size: 11px; margin-left: 8px;">${recording.frameCount || 0} frames • ${formattedDate}</span>
2813
+ </div>
2814
+ </div>
2815
+ <div class="recording-buttons">
2816
+ <button class="copy-id-btn" data-session-id="${recording.sessionId}" title="Copy ID">Copy ID</button>
2817
+ <button class="copy-prompt-btn" data-session-id="${recording.sessionId}" data-name="${recording.title || ''}" title="Copy Claude Prompt">Copy Prompt</button>
2818
+ <button class="view-browser-btn" data-session-id="${recording.sessionId}" title="View Screenshots">View</button>
2819
+ <button class="export-btn" data-session-id="${recording.sessionId}" title="Export Recording">Export</button>
2820
+ <button class="delete-browser-btn" data-session-id="${recording.sessionId}" title="Delete">Delete</button>
2821
+ </div>
2822
+ `;
2823
+
2824
+ // Add event listeners
2825
+ const copyIdBtn = recordingItem.querySelector('.copy-id-btn');
2826
+ const copyPromptBtn = recordingItem.querySelector('.copy-prompt-btn');
2827
+ const viewBtn = recordingItem.querySelector('.view-browser-btn');
2828
+ const exportBtn = recordingItem.querySelector('.export-btn');
2829
+ const deleteBtn = recordingItem.querySelector('.delete-browser-btn');
2830
+
2831
+ copyIdBtn.addEventListener('click', () => {
2832
+ navigator.clipboard.writeText(recording.sessionId).then(() => {
2833
+ copyIdBtn.textContent = 'Copied!';
2834
+ copyIdBtn.style.background = '#4CAF50';
2835
+ setTimeout(() => {
2836
+ copyIdBtn.textContent = 'Copy ID';
2837
+ copyIdBtn.style.background = '#2196F3';
2838
+ }, 2000);
2839
+ });
2840
+ });
2841
+
2842
+ copyPromptBtn.addEventListener('click', () => {
2843
+ const sessionId = recording.sessionId;
2844
+ const title = recording.title;
2845
+ let prompt;
2846
+
2847
+ if (title) {
2848
+ prompt = `Please analyze the browser-only screen recording "${title}" (session: ${sessionId}). This recording is stored locally in the browser.`;
2849
+ } else {
2850
+ prompt = `Please analyze the browser-only screen recording "${sessionId}". This recording is stored locally in the browser.`;
2851
+ }
2852
+
2853
+ navigator.clipboard.writeText(prompt).then(() => {
2854
+ copyPromptBtn.textContent = 'Copied!';
2855
+ const originalBg = copyPromptBtn.style.background;
2856
+ copyPromptBtn.style.background = '#4CAF50';
2857
+ setTimeout(() => {
2858
+ copyPromptBtn.textContent = 'Copy Prompt';
2859
+ copyPromptBtn.style.background = originalBg;
2860
+ }, 2000);
2861
+ });
2862
+ });
2863
+
2864
+ viewBtn.addEventListener('click', () => {
2865
+ // Open frame editor in browser-only mode
2866
+ const frameEditorUrl = chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${recording.sessionId}&browserOnly=true`);
2867
+ chrome.tabs.create({ url: frameEditorUrl });
2868
+ });
2869
+
2870
+ exportBtn.addEventListener('click', async () => {
2871
+ try {
2872
+ const result = await chrome.runtime.sendMessage({
2873
+ action: 'exportBrowserRecording',
2874
+ sessionId: recording.sessionId
2875
+ });
2876
+
2877
+ if (result && result.success) {
2878
+ exportBtn.textContent = 'Exported!';
2879
+ exportBtn.style.background = '#4CAF50';
2880
+ setTimeout(() => {
2881
+ exportBtn.textContent = 'Export';
2882
+ exportBtn.style.background = '#FF9800';
2883
+ }, 2000);
2884
+ }
2885
+ } catch (error) {
2886
+ console.error('Failed to export recording:', error);
2887
+ alert('Failed to export recording');
2888
+ }
2889
+ });
2890
+
2891
+ deleteBtn.addEventListener('click', async () => {
2892
+ if (confirm('Are you sure you want to delete this browser recording?')) {
2893
+ try {
2894
+ await chrome.runtime.sendMessage({
2895
+ action: 'deleteBrowserRecording',
2896
+ sessionId: recording.sessionId
2897
+ });
2898
+ loadScreenRecordings(); // Reload list
2899
+ } catch (error) {
2900
+ console.error('Failed to delete recording:', error);
2901
+ }
2902
+ }
2903
+ });
2904
+
2905
+ recordingsContainer.appendChild(recordingItem);
2906
+ });
2907
+ }
2908
+ } catch (error) {
2909
+ console.error('Failed to load browser-only recordings:', error);
2910
+ }
2911
+
2912
+ // Then, check server for screen recordings
2541
2913
  const ports = CONFIG_PORTS;
2542
2914
  for (const port of ports) {
2543
2915
  try {
@@ -2545,6 +2917,7 @@ async function loadScreenRecordings() {
2545
2917
  if (response.ok) {
2546
2918
  const data = await response.json();
2547
2919
  if (data && data.length > 0) {
2920
+ hasRecordings = true;
2548
2921
  recordingsList.style.display = 'block';
2549
2922
 
2550
2923
  // Display each screen recording
@@ -2563,25 +2936,25 @@ async function loadScreenRecordings() {
2563
2936
 
2564
2937
  recordingItem.innerHTML = `
2565
2938
  <div class="recording-info">
2566
- <div style="font-weight: bold; margin-bottom: 4px;">${displayName}</div>
2567
- <div style="color: #666; font-size: 11px;">
2568
- ${recording.totalFrames} frames • ${formattedDate}
2939
+ <div class="recording-id" title="${recording.sessionId}">
2940
+ <span class="recording-id-text">🎥 ${displayName}</span>
2941
+ <span style="color: #666; font-size: 11px; margin-left: 8px;">${recording.totalFrames} frames • ${formattedDate}</span>
2569
2942
  </div>
2570
2943
  </div>
2571
2944
  <div class="recording-buttons">
2572
- <button class="copy-id-btn" data-session-id="${recording.sessionId}" style="padding: 4px 8px; background: #2196F3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2573
- Copy ID
2574
- </button>
2575
- <button class="delete-btn" data-session-id="${recording.sessionId}" style="padding: 4px 8px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">
2576
- Delete
2577
- </button>
2945
+ <button class="copy-id-btn" data-session-id="${recording.sessionId}" title="Copy ID">Copy ID</button>
2946
+ <button class="copy-prompt-btn" data-session-id="${recording.sessionId}" data-name="${recording.name || ''}" data-port="${port}" title="Copy Claude Prompt">Copy Prompt</button>
2947
+ <button class="view-btn" data-session-id="${recording.sessionId}" title="View Screenshots">View</button>
2948
+ <button class="delete-btn" data-session-id="${recording.sessionId}" title="Delete">Delete</button>
2578
2949
  </div>
2579
2950
  `;
2580
2951
 
2581
2952
  // Add event listeners
2582
2953
  const copyIdBtn = recordingItem.querySelector('.copy-id-btn');
2954
+ const copyPromptBtn = recordingItem.querySelector('.copy-prompt-btn');
2955
+ const viewBtn = recordingItem.querySelector('.view-btn');
2583
2956
  const deleteBtn = recordingItem.querySelector('.delete-btn');
2584
-
2957
+
2585
2958
  copyIdBtn.addEventListener('click', () => {
2586
2959
  navigator.clipboard.writeText(recording.sessionId).then(() => {
2587
2960
  copyIdBtn.textContent = 'Copied!';
@@ -2592,14 +2965,60 @@ async function loadScreenRecordings() {
2592
2965
  }, 2000);
2593
2966
  });
2594
2967
  });
2595
-
2968
+
2969
+ copyPromptBtn.addEventListener('click', () => {
2970
+ const sessionId = recording.sessionId;
2971
+ const name = recording.name;
2972
+ let prompt;
2973
+
2974
+ 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}.`;
2976
+ } 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}.`;
2978
+ }
2979
+
2980
+ navigator.clipboard.writeText(prompt).then(() => {
2981
+ copyPromptBtn.textContent = 'Copied!';
2982
+ const originalBg = copyPromptBtn.style.background;
2983
+ copyPromptBtn.style.background = '#4CAF50';
2984
+ setTimeout(() => {
2985
+ copyPromptBtn.textContent = 'Copy Prompt';
2986
+ copyPromptBtn.style.background = originalBg;
2987
+ }, 2000);
2988
+ });
2989
+ });
2990
+
2991
+ viewBtn.addEventListener('click', async () => {
2992
+ try {
2993
+ // Check if frame editor is available (Pro version)
2994
+ const frameEditorUrl = chrome.runtime.getURL(`pro/frame-editor.html?sessionId=${recording.sessionId}`);
2995
+ const response = await fetch(frameEditorUrl, { method: 'HEAD' });
2996
+
2997
+ if (response.ok) {
2998
+ // Pro version - open frame editor
2999
+ chrome.tabs.create({ url: frameEditorUrl });
3000
+ } else {
3001
+ // Free version - show summary
3002
+ const framesCount = recording.totalFrames || 0;
3003
+ const noteText = recording.name ? `\nName: ${recording.name}` : '';
3004
+ alert(`Screen Recording${noteText}\nSession ID: ${recording.sessionId}\n\nFrames: ${framesCount}\n\nUse "Copy ID" to share with Claude Code for detailed analysis.\n\nUpgrade to Pro for the Frame Editor with advanced viewing and editing features.`);
3005
+ }
3006
+ } catch (error) {
3007
+ console.error('Failed to view recording:', error);
3008
+ // Fallback to alert
3009
+ const framesCount = recording.totalFrames || 0;
3010
+ const noteText = recording.name ? `\nName: ${recording.name}` : '';
3011
+ alert(`Screen Recording${noteText}\nSession ID: ${recording.sessionId}\n\nFrames: ${framesCount}\n\nUse "Copy ID" to share with Claude Code for detailed analysis.`);
3012
+ }
3013
+ });
3014
+
2596
3015
  deleteBtn.addEventListener('click', async () => {
2597
3016
  if (confirm('Are you sure you want to delete this screen recording?')) {
2598
3017
  try {
2599
3018
  const deleteResponse = await fetch(`http://localhost:${port}/chromedebug/recording/${recording.sessionId}`, {
2600
3019
  method: 'DELETE'
2601
3020
  });
2602
-
3021
+
2603
3022
  if (deleteResponse.ok) {
2604
3023
  // Reload the list
2605
3024
  loadScreenRecordings();
@@ -2612,8 +3031,6 @@ async function loadScreenRecordings() {
2612
3031
 
2613
3032
  recordingsContainer.appendChild(recordingItem);
2614
3033
  });
2615
- } else {
2616
- recordingsList.style.display = 'none';
2617
3034
  }
2618
3035
  break; // Found working server
2619
3036
  }
@@ -2621,6 +3038,27 @@ async function loadScreenRecordings() {
2621
3038
  console.error(`Failed to load screen recordings from port ${port}:`, error);
2622
3039
  }
2623
3040
  }
3041
+
3042
+ // Handle visibility based on recordings and user preference
3043
+ if (!hasRecordings) {
3044
+ recordingsList.style.display = 'none';
3045
+ } else {
3046
+ // Get user's saved preference
3047
+ const prefs = await chrome.storage.local.get(['screenRecordingsVisible']);
3048
+ const shouldShow = autoShow || (prefs.screenRecordingsVisible !== false); // Default to true
3049
+
3050
+ if (shouldShow) {
3051
+ recordingsList.style.display = 'block';
3052
+ if (viewScreenRecordingsLink) {
3053
+ viewScreenRecordingsLink.textContent = 'Hide Recordings';
3054
+ }
3055
+ } else {
3056
+ recordingsList.style.display = 'none';
3057
+ if (viewScreenRecordingsLink) {
3058
+ viewScreenRecordingsLink.textContent = 'View Past Recordings';
3059
+ }
3060
+ }
3061
+ }
2624
3062
  }
2625
3063
 
2626
3064
  /*