diff --git a/content.js b/content.js index 338a5f2..825c681 100644 --- a/content.js +++ b/content.js @@ -66,6 +66,12 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { openaiApiKey = request.apiKey; console.log('✓ OpenAI API key updated'); processedQuestions.clear(); + } else if (request.action === 'retryScan') { + console.log('🔄 Manual retry initiated from popup'); + processedQuestions.clear(); + isProcessing = false; + removeHighlights(); + waitForContentAndScan(); } }); @@ -168,7 +174,7 @@ function getFullQuestionContext(questionElement) { return questionText; } -// Enhanced question finding for Microsoft Learn +// Enhanced question finding for Microsoft Learn - Groups multi-line questions function findQuestions() { const questions = []; const seenTexts = new Set(); @@ -216,6 +222,8 @@ function findQuestions() { if (questionText.length < 15 || seenTexts.has(questionText)) return; if (/^(next|previous|submit|skip)$/i.test(questionText)) return; + // Skip page metadata, scripts, and non-question content + if (/var\s+\w+\s*=\s*\{|function\s*\(|microsoft learn|practice assessment/i.test(questionText)) return; seenTexts.add(questionText); questions.push({ @@ -718,7 +726,7 @@ async function scanAndHighlight() { } } -// Mutation observer for dynamic content +// Mutation observer for dynamic content - ignores self-triggered changes let mutationTimeout; const observer = new MutationObserver((mutations) => { if (!extensionEnabled || isProcessing) return; @@ -726,7 +734,10 @@ const observer = new MutationObserver((mutations) => { const hasNewContent = mutations.some(mutation => { return mutation.addedNodes.length > 0 && Array.from(mutation.addedNodes).some(node => - node.nodeType === 1 && node.textContent.length > 50 + node.nodeType === 1 && + node.textContent.length > 50 && + !node.classList?.contains('quiz-helper-correct') && + !node.hasAttribute?.('data-quiz-helper-highlight') ); }); @@ -743,3 +754,39 @@ observer.observe(document.body, { childList: true, subtree: true }); + +// Listen for page navigation (Next button clicks) +let lastUrl = location.href; +const urlObserver = new MutationObserver(() => { + if (location.href !== lastUrl) { + lastUrl = location.href; + console.log('🔄 Page navigation detected, resetting and rescanning...'); + processedQuestions.clear(); + isProcessing = false; + setTimeout(() => waitForContentAndScan(), 1000); + } +}); + +urlObserver.observe(document.body, { childList: true, subtree: true }); + +// Also listen for popstate (browser back/forward) +window.addEventListener('popstate', () => { + console.log('🔄 Browser navigation detected, resetting and rescanning...'); + processedQuestions.clear(); + isProcessing = false; + setTimeout(() => waitForContentAndScan(), 1000); +}); + +// Listen for Next button clicks +document.addEventListener('click', (e) => { + const target = e.target; + if (target.tagName === 'SPAN' && target.textContent.trim() === 'Next') { + console.log('🔄 Next button clicked, scanning in 1 second...'); + setTimeout(() => { + processedQuestions.clear(); + isProcessing = false; + removeHighlights(); + waitForContentAndScan(); + }, 1000); + } +}); diff --git a/popup.html b/popup.html index b4ffb59..de6aac5 100644 --- a/popup.html +++ b/popup.html @@ -127,6 +127,22 @@ display: block; margin-bottom: 5px; } + .retry-btn { + padding: 6px 12px; + font-size: 11px; + background: #28a745; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + } + .retry-btn:hover { + background: #218838; + } + .retry-btn:disabled { + background: #ccc; + cursor: not-allowed; + } @@ -154,6 +170,7 @@ Off +
diff --git a/popup.js b/popup.js index 80912e1..ee53dd6 100644 --- a/popup.js +++ b/popup.js @@ -60,6 +60,47 @@ document.getElementById('toggleSwitch').addEventListener('change', function(e) { }, 2000); }); +// Handle Retry button click +document.getElementById('retryBtn').addEventListener('click', function() { + const btn = document.getElementById('retryBtn'); + btn.disabled = true; + btn.textContent = 'Scanning...'; + + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + if (tabs[0]) { + const url = tabs[0].url; + + if (url.startsWith('chrome://') || url.startsWith('chrome-extension://') || url.startsWith('edge://')) { + document.getElementById('status').textContent = 'Extension cannot run on this page'; + document.getElementById('status').style.color = '#d13438'; + btn.disabled = false; + btn.textContent = 'Retry Scan'; + setTimeout(() => { + document.getElementById('status').textContent = ''; + document.getElementById('status').style.color = ''; + }, 3000); + return; + } + + chrome.tabs.sendMessage(tabs[0].id, { + action: 'retryScan' + }).then(() => { + window.close(); + }).catch(error => { + console.log('Could not send retry message:', error); + document.getElementById('status').textContent = 'Could not initiate scan - refresh the page'; + document.getElementById('status').style.color = '#d13438'; + btn.disabled = false; + btn.textContent = 'Retry Scan'; + setTimeout(() => { + document.getElementById('status').textContent = ''; + document.getElementById('status').style.color = ''; + }, 3000); + }); + } + }); +}); + // Handle API key save document.getElementById('saveApiKey').addEventListener('click', function() { const apiKey = document.getElementById('apiKey').value.trim();