From 74b5fbd0cf6d17656e71c5b0a740632bd24a7d5c Mon Sep 17 00:00:00 2001 From: Lucie Bader <167515375+Lucie-Bdr@users.noreply.github.com> Date: Sat, 15 Feb 2025 08:35:39 +0100 Subject: [PATCH] =?UTF-8?q?Popup=20de=20confirmation=20activation/d=C3=A9s?= =?UTF-8?q?activation=20extension?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/background/background.js | 68 +++++++++ src/popup/popup.html | 196 ++++++++++++++++++++++-- src/popup/popup.js | 278 ++++++++++++++++++++++++++++++++-- src/sidebar/sidebar.js | 31 ++-- src/utils/stats.js | 157 +++++++++++++++++++ src/workers/pyodide_worker.js | 85 +++++++++++ 6 files changed, 775 insertions(+), 40 deletions(-) create mode 100644 src/utils/stats.js create mode 100644 src/workers/pyodide_worker.js diff --git a/src/background/background.js b/src/background/background.js index 6203313..241f6e0 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -295,3 +295,71 @@ function showInstructionPopup(details) { ` }); } + +// ───────────────────────────────────────────────────────────────────────────── +// Gestion de l'activation/désactivation de l'extension +// ───────────────────────────────────────────────────────────────────────────── +// === 1. Initialisation de l'état de l'extension === +async function initializeExtensionState() { + const { extensionActive } = await browser.storage.local.get("extensionActive"); + + if (extensionActive === undefined) { + await browser.storage.local.set({ extensionActive: true }); // Activation par défaut + console.log("🔄 Initialisation : extension activée par défaut."); + enableExtensionFeatures(); + } else { + console.log(`🔄 État actuel de l'extension : ${extensionActive ? "activée" : "désactivée"}`); + if (extensionActive) { + enableExtensionFeatures(); + } else { + disableExtensionFeatures(); + } +} +} + +// === 2. Gestion de l'activation/désactivation de l'extension === +browser.runtime.onMessage.addListener(async (message) => { + if (message.action === "toggleExtension") { + console.log(`🔄 Changement d'état de l'extension : ${message.isActive ? "activée" : "désactivée"}`); + await browser.storage.local.set({ extensionActive: message.isActive }); + + if (!message.isActive) { + disableExtensionFeatures(); + } else { + enableExtensionFeatures(); + } + browser.runtime.sendMessage({ action: "updateUI" }); + } +}); + + +// === 3. Fonction pour désactiver les fonctionnalités de l'extension === +async function disableExtensionFeatures() { + console.log("Désactivation des fonctionnalités de l'extension."); + browser.runtime.sendMessage({ + action: "updateUI", + autoAdd: false, + isTrackingActive: false + }); + + const { extensionActive } = await browser.storage.local.get("extensionActive"); + if (extensionActive) { + console.log("🛑 Fermeture de la barre latérale..."); + browser.sidebarAction.close(); + } +} + +// === 4. Fonction pour activer les fonctionnalités de l'extension === +async function enableExtensionFeatures() { + console.log("Réactivation des fonctionnalités de l'extension."); + browser.runtime.sendMessage({ + action: "updateUI", + autoAdd: true, + isTrackingActive: true + }); + + +} + + +initializeExtensionState(); diff --git a/src/popup/popup.html b/src/popup/popup.html index dbc12ff..5f28e19 100644 --- a/src/popup/popup.html +++ b/src/popup/popup.html @@ -5,35 +5,205 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Extension BaLex</title> <style> - body { - font-family: Arial, sans-serif; - margin: 20px; - width: 200px; - background-color: #323046; + #extension-notification { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.85); color: white; + padding: 15px; + border-radius: 8px; text-align: center; + width: 80%; + max-width: 250px; + z-index: 1000; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); + transition: opacity 0.3s ease, visibility 0.3s ease; + } + #extension-notification.hidden { + opacity: 0; + visibility: hidden; + } + + #close-notification { + margin-top: 10px; + padding: 5px 10px; + background-color: #8d5c70; + color: white; + border: none; + cursor: pointer; + border-radius: 5px; + font-weight: bold; + } + + #close-notification:hover { + background-color: #dddedd; + color: #8d5c70; + } + + body { + font-family: Helvetica, sans-serif; + font-size: medium; + margin: 0; + padding: 8px; + background-color: #525877; + color: #323046; + border-radius: 10px; + width: 200px; } button { width: 100%; padding: 10px; - margin-bottom: 10px; - font-size: 15px; - color: #94608a; + margin-bottom: 8px; + font-size: 14px; font-weight: bold; border: none; cursor: pointer; + border-radius: 6px; + transition: background 0.3s; + background-color: #8d5c70; + color: white; } button:hover { - background-color: #94608a; + background-color: #dddedd; + color: #8d5c70; + } + #auth-button { + width: auto; + display: inline-flex; + padding: 6px 12px; + font-size: 18px; + font-family: Bradley Hand, cursive; + background: none; + border: none; + color: white; + align-items: center; + gap: 6px; + border-radius: 20px; + cursor: pointer; + transition: background 0.3s; + text-align: center; + } + #auth-button:hover { + background: rgba(255,255,255,0.2); + } + #auth-button svg { + width: 18px; + height: 18px; + fill: white; + transition: transform 0.3s ease-in-out; + } + #auth-button:hover svg { + transform: scale(1.1); + } + #auth-section { + display: flex; + justify-content: center; + margin-bottom: 10px; + } + #extension-name { + font-family: Helvetica, sans-serif; + text-align: center; + font-size: 23px; + font-weight: bold; + padding: 5px; + color: #8d5c70; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: white; + } + .option-container { + background: #a08e9f; + padding: 8px; + border-radius: 8px; + margin-top: 10px; + box-shadow: 0 2px 5px rgba(0,0,0,0.2); + } + .option-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; + } + .threshold-container input { + width: 50px; + text-align: center; + padding: 4px; + border-radius: 4px; + border: 1px solid #8d5c70; + background: #525877; + color: white; + } + .language-selection { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 6px; + margin-top: 4px; + } + .lang-option { + padding: 5px 8px; + background-color: #dcdde1; + border: 2px solid #8d5c70; + border-radius: 6px; + cursor: pointer; + transition: all 0.3s ease; + } + .lang-option.selected { + background-color: #8d5c70; color: white; } </style> </head> <body> - <h3>Extension BaLex</h3> - <button id="auth-button">Se connecter</button> - <button id="toggleExtensionBtn">Activer/Désactiver l'extension</button> - <button id="toggleStatsBtn">Activer les statistiques</button> + <div id="extension-name">Extension BaLex</div> + <div id="auth-section"> + <button id="auth-button"> + <span id="auth-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <path d="M10 2a1 1 0 0 1 1 1v2h2V3a1 1 0 0 1 2 0v2h2a2 2 0 0 1 2 2v3h-2V7h-2v3a1 1 0 1 1-2 0V7h-2v3a1 1 0 1 1-2 0V7H6v3H4V7a2 2 0 0 1 2-2h2V3a1 1 0 0 1 1-1Z"/> + </svg> + </span> + <span id="auth-text">Se connecter</span> + </button> + </div> + <button id="toggleExtensionBtn">Activer/Désactiver</button> + <button id="toggleStatsBtn">Statistiques</button> + + <div id="stats-options" class="option-container hidden"> + <div class="option-row"> + <label for="auto-add">Ajout auto</label> + <input type="checkbox" id="auto-add" /> + </div> + + <div id="auto-add-options" class="hidden"> + <div class="option-row"> + <label for="include-stopwords">Inclure mots outils</label> + <input type="checkbox" id="include-stopwords" /> + </div> + + <div class="option-row threshold-container"> + <label for="threshold">Seuil</label> + <input type="number" id="threshold" value="10" min="1" /> + </div> + + <div class="option-row"> + <label>Langues suivies</label> + <div id="language-selection" class="language-selection"> + <p id="loading-languages" style="color: gray;">Chargement...</p> + </div> + </div> + + <button id="save-options" class="hidden">Valider</button> + </div> + </div> + + <div id="extension-notification" class="hidden"> + <p id="notification-text"></p> + <button id="close-notification">OK</button> + </div> + + <script src="../utils/api.js"></script> <script src="popup.js"></script> </body> </html> diff --git a/src/popup/popup.js b/src/popup/popup.js index 8f8dd7e..177ca54 100644 --- a/src/popup/popup.js +++ b/src/popup/popup.js @@ -16,31 +16,281 @@ async function updateConnectionButton() { } // === 2. Écoute des messages depuis background.js === -browser.runtime.onMessage.addListener((message) => { +browser.runtime.onMessage.addListener(async (message) => { if (message.action === "updateUI") { console.log("📩 Mise à jour reçue : État connecté :", message.isLoggedIn); console.log("🔄 Actualisation du menu de l'extension déclenchée."); updateConnectionButton(); + updateOptionsUI(); + updateLanguageSelection(); + updateSidebarUI(); + } + + if (message.action === "collapseSidebar") { + collapseSidebar(); + } else if (message.action === "expandSidebar") { + expandSidebar(); + } +}); + +// === 3. Mise à jour dynamique de la sélection des langues === +async function updateLanguageSelection() { + const languageSelection = document.getElementById("language-selection"); + languageSelection.innerHTML = "<p id='loading-languages' style='color: gray;'>Chargement...</p>"; + + const { accessToken } = await browser.storage.local.get("accessToken"); + if (!accessToken) { + languageSelection.innerHTML = "<p style='color: red;'>Veuillez vous connecter.</p>"; + return; + } + + const lexicons = await getLexicons(accessToken); + const userLanguages = [...new Set(lexicons.map(lex => lex.language))]; + + // Récupérer les langues actuellement suivies depuis le local storage + const { trackedLanguages } = await browser.storage.local.get("trackedLanguages") || { trackedLanguages: [] }; + + languageSelection.innerHTML = ""; // Suppression du message de chargement + + if (userLanguages.length === 0) { + languageSelection.innerHTML = "<p style='color: red;'>Aucun lexique personnel trouvé.</p>"; + return; + } + + userLanguages.forEach(lang => { + const langButton = document.createElement("div"); + langButton.classList.add("lang-option"); + langButton.textContent = lang.toUpperCase(); + langButton.dataset.value = lang; + + // Vérifier si la langue est suivie et ajouter la classe `selected` + if (trackedLanguages && trackedLanguages.includes(lang)) { + langButton.classList.add("selected"); + } + + langButton.addEventListener("click", () => { + langButton.classList.toggle("selected"); + }); + + languageSelection.appendChild(langButton); + }); + + console.log("✅ Sélection des langues mise à jour avec :", userLanguages); +} + +// === 4. Gestion de l'affichage des boutons des statistiques et des options utilisateur === +async function updateOptionsUI() { + const { accessToken } = await browser.storage.local.get("accessToken"); + const isLoggedIn = !!accessToken; + + const toggleStatsBtn = document.getElementById("toggleStatsBtn"); + const statsOptions = document.getElementById("stats-options"); + const autoAddContainer = document.getElementById("auto-add")?.parentElement; + const autoAddCheckbox = document.getElementById("auto-add"); + const autoAddOptions = document.getElementById("auto-add-options"); + const thresholdInput = document.getElementById("threshold"); + const saveOptionsBtn = document.getElementById("save-options"); + + // Masquer l'option "Ajout Automatique" si l'utilisateur n'est pas connecté + if (autoAddContainer) { + autoAddContainer.style.display = isLoggedIn ? "block" : "none"; + } + + // Charger les préférences utilisateur + const { isTrackingActive, autoAdd, trackedLanguages, threshold } = await browser.storage.local.get([ + "isTrackingActive", + "autoAdd", + "trackedLanguages", + "threshold" + ]) || { isTrackingActive: false }; + + // Mettre à jour le bouton des statistiques + if (toggleStatsBtn) { + toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques"; + } + if (statsOptions) { + statsOptions.classList.toggle("hidden", !isTrackingActive); + } + + if (isLoggedIn) { + if (autoAddCheckbox) { + autoAddCheckbox.checked = autoAdd || false; + } + if (thresholdInput && threshold !== undefined) { + thresholdInput.value = threshold; + } + + if (autoAddOptions) { + autoAddOptions.classList.toggle("hidden", !autoAdd); + } + if (saveOptionsBtn) { + saveOptionsBtn.classList.toggle("hidden", !autoAdd); + } + } else { + if (autoAddCheckbox) { + autoAddCheckbox.checked = false; + } + if (autoAddOptions) { + autoAddOptions.classList.add("hidden"); + } + if (saveOptionsBtn) { + saveOptionsBtn.classList.add("hidden"); + } + } +} + + +// === 5. Gestion des événements utilisateur === +document.getElementById("toggleStatsBtn").addEventListener("click", async () => { + const newState = !(await browser.storage.local.get("isTrackingActive")).isTrackingActive; + + await browser.storage.local.set({ isTrackingActive: newState }); + + if (!newState) { + await browser.storage.local.set({ autoAdd: false }); + document.getElementById("auto-add").checked = false; + document.getElementById("auto-add-options").classList.add("hidden"); + document.getElementById("save-options").classList.add("hidden"); + } + + document.getElementById("toggleStatsBtn").textContent = newState ? "Désactiver les statistiques" : "Activer les statistiques"; + document.getElementById("stats-options").classList.toggle("hidden", !newState); + + browser.runtime.sendMessage({ command: "toggle-stats", isActive: newState }); + + if (newState) { + console.log("[Popup] Demande d'initialisation de Pyodide..."); + browser.runtime.sendMessage({ command: "init-pyodide" }); } }); -// === 3. Gestion des actions des boutons === -document.getElementById("toggleExtensionBtn").addEventListener("click", async () => { +document.getElementById("auto-add").addEventListener("change", async () => { + const isChecked = document.getElementById("auto-add").checked; + document.getElementById("auto-add-options").classList.toggle("hidden", !isChecked); + document.getElementById("save-options").classList.toggle("hidden", !isChecked); + + if (!isChecked) { + await browser.storage.local.set({ autoAdd: false }); + } +}); + +document.getElementById("save-options").addEventListener("click", async () => { + const autoAdd = document.getElementById("auto-add").checked; + const threshold = parseInt(document.getElementById("threshold").value, 10); + const selectedLanguages = Array.from(document.querySelectorAll("#language-selection .lang-option.selected")) + .map(option => option.dataset.value); + + await browser.storage.local.set({ + autoAdd, + threshold, + trackedLanguages: selectedLanguages + }); + + console.log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages }); +}); + +// === 6. Gestion de l'activation/désactivation de l'extension === +async function updateExtensionToggleButton() { + const { extensionActive } = await browser.storage.local.get("extensionActive") || { extensionActive: true }; + const toggleButton = document.getElementById("toggleExtensionBtn"); + + if (toggleButton) { + toggleButton.textContent = extensionActive ? "Désactiver l'extension" : "Activer l'extension"; + + if (!toggleButton.dataset.listenerAdded) { + toggleButton.addEventListener("click", handleToggleExtension); + toggleButton.dataset.listenerAdded = "true"; + } + } else { + console.error("Le bouton d'activation de l'extension n'a pas été trouvé."); + } +} + +// Fonction de gestion du clic sur le bouton +async function handleToggleExtension() { const { extensionActive } = await browser.storage.local.get("extensionActive"); const newState = !extensionActive; + await browser.storage.local.set({ extensionActive: newState }); - alert(`Extension ${newState ? "activée" : "désactivée"}.`); -}); + document.getElementById("toggleExtensionBtn").textContent = newState ? "Désactiver l'extension" : "Activer l'extension"; + browser.runtime.sendMessage({ action: "toggleExtension", isActive: newState }); -document.getElementById("toggleStatsBtn").addEventListener("click", async () => { - const { statsActive } = await browser.storage.local.get("statsActive"); - const newState = !statsActive; - await browser.storage.local.set({ statsActive: newState }); - alert(`Statistiques ${newState ? "activées" : "désactivées"}.`); + if (newState) { + browser.sidebarAction.open(); + } else { + browser.sidebarAction.close(); + } + + showNotification(`Extension ${newState ? "activée" : "désactivée"}.`); +} + +// === 7. Initialisation === +document.addEventListener("DOMContentLoaded", async () => { + await updateConnectionButton(); + await updateOptionsUI(); + await updateLanguageSelection(); + await updateExtensionToggleButton(); }); -// === 4. Initialisation === -document.addEventListener("DOMContentLoaded", () => { - console.log("🌠Initialisation du menu de l'extension..."); - updateConnectionButton(); +// === Fonction pour mettre à jour l'UI du popup === +async function updatePopupUI(message) { + console.log("🔄 Mise à jour du popup avec :", message); + + const statsOptions = document.getElementById("stats-options"); + const autoAddCheckbox = document.getElementById("auto-add"); + const autoAddOptions = document.getElementById("auto-add-options"); + + if (message.isTrackingActive !== undefined) { + if (statsOptions) { + statsOptions.classList.toggle("hidden", !message.isTrackingActive); + } else { + console.warn("âš ï¸ Ã‰lément #stats-options introuvable."); + } + } + + if (message.autoAdd !== undefined) { + if (autoAddCheckbox) { + autoAddCheckbox.checked = message.autoAdd; + } else { + console.warn("âš ï¸ Ã‰lément #auto-add introuvable."); + } + + if (autoAddOptions) { + autoAddOptions.classList.toggle("hidden", !message.autoAdd); + } else { + console.warn("âš ï¸ Ã‰lément #auto-add-options introuvable."); + } + } + + const toggleButton = document.getElementById("toggleExtensionBtn"); + if (toggleButton) { + toggleButton.textContent = message.isTrackingActive ? "Désactiver l'extension" : "Activer l'extension"; + } +} + +browser.runtime.onMessage.addListener((message) => { + console.log("📩 Message reçu dans popup.js :", message); + + if (message.action === "updateUI") { + console.log("🔄 Mise à jour de l'UI du popup..."); + updatePopupUI(message); + } }); + +// === Fonction pour gérer la notification d'activation/désactivation === +function showNotification(message) { + const notificationBox = document.getElementById("extension-notification"); + const notificationText = document.getElementById("notification-text"); + const closeButton = document.getElementById("close-notification"); + + if (notificationBox && notificationText && closeButton) { + notificationText.textContent = message; + notificationBox.classList.remove("hidden"); + + closeButton.addEventListener("click", () => { + notificationBox.classList.add("hidden"); + }, { once: true }); + } else { + console.error("⌠Impossible d'afficher la notification : élément manquant."); + } +} diff --git a/src/sidebar/sidebar.js b/src/sidebar/sidebar.js index 8b80001..c8857fa 100644 --- a/src/sidebar/sidebar.js +++ b/src/sidebar/sidebar.js @@ -28,8 +28,6 @@ async function getAuthTokenFromStorage() { } catch (error) { console.error("⌠Erreur lors de la récupération du token :", error); } - authToken = null; - window.authToken = null; return null; } @@ -511,6 +509,10 @@ browser.runtime.onMessage.addListener(async (message) => { case "authStatusChanged": break; + case "updateUI": + await refreshSidebarState(); + break; + default: console.warn("âš ï¸ Action inconnue reçue :", message.action); } @@ -521,6 +523,9 @@ browser.runtime.onMessage.addListener(async (message) => { // ───────────────────────────────────────────────────────────────────────────── document.addEventListener("DOMContentLoaded", async () => { console.log("📦 DOM entièrement chargé. Initialisation de la sidebar."); + + authToken = await getAuthTokenFromStorage(); + window.authToken = authToken; await refreshSidebarState(); const authButton = document.getElementById("auth-button"); @@ -611,15 +616,15 @@ document.addEventListener("DOMContentLoaded", async () => { }); }); -document.querySelectorAll('.toggle-btn').forEach(btn => { - btn.addEventListener('click', function() { - const blockContent = this.parentElement.nextElementSibling; - if (blockContent.classList.contains('hidden')) { - blockContent.classList.remove('hidden'); - this.textContent = '–'; - } else { - blockContent.classList.add('hidden'); - this.textContent = '+'; - } + document.querySelectorAll('.toggle-btn').forEach(btn => { + btn.addEventListener('click', function() { + const blockContent = this.parentElement.nextElementSibling; + if (blockContent.classList.contains('hidden')) { + blockContent.classList.remove('hidden'); + this.textContent = '–'; + } else { + blockContent.classList.add('hidden'); + this.textContent = '+'; + } + }); }); -}); diff --git a/src/utils/stats.js b/src/utils/stats.js new file mode 100644 index 0000000..49a6171 --- /dev/null +++ b/src/utils/stats.js @@ -0,0 +1,157 @@ +(function () { + if (window.hasRun) { + return; + } + window.hasRun = true; + + let isTrackingActive = false; + let scrollListenerAttached = false; + const seenContent = new Set(); + let simplemmaReady = false; + + /** + * Fonction pour extraire le texte visible et l'envoyer au background script. + */ + function trackVisibleContent() { + // pas de tracking tant que Pyodide n’est pas prêt + if (!simplemmaReady) { + console.log("[Stats] Pyodide n'est pas encore prêt, attente..."); + return; + } + + let selectors = "p, h1, h2, h3, h4, h5, h6, ul, ol, li, table, tr, td, th, blockquote"; + + // Sélecteurs spécifiques à exclure sur Wikipedia et d'autres sites + let excludeSelectors = [ + "#p-lang", // Liste des langues sur Wikipedia + "#footer", // Pied de page + ".navbox", // Boîtes de navigation + ".infobox", // Infobox + ".sidebar", // Sidebars en général + "script", // Scripts JS + "style" // Styles CSS + ]; + + document.querySelectorAll(selectors).forEach((element) => { + if (excludeSelectors.some(sel => element.closest(sel))) { + return; // On ignore cet élément + } + + const rect = element.getBoundingClientRect(); + if (rect.top >= 0 && rect.bottom <= window.innerHeight) { + let text = element.innerText + text = cleanText(text); // nettoyage du texte avec cleanText + if (text.length > 10 && !seenContent.has(text)) { + seenContent.add(text); + console.log("[Stats] Envoi du texte filtré au background.js :", text); + + browser.runtime.sendMessage({ command: "process-text", text: text }); + } + } + }); + } + function cleanText(text) { + // Supprimer les puces et symboles inutiles + text = text.replace(/[\u2022\u00b7•·■◆▪▸▹▶►▻⇨]/g, " "); // Supprime puces et flèches + text = text.replace(/[\t\n\r]+/g, " "); // Supprime les sauts de ligne inutiles + text = text.replace(/\s{2,}/g, " "); // Remplace plusieurs espaces par un seul + + // Essayer d'extraire le contenu utile des tableaux (ex: "1 | Chat | Animal" → "1 Chat Animal") + text = text.replace(/(\||\t)+/g, " "); // Remplace les séparateurs de tableau par un espace + text = text.replace(/(\s*-+\s*)+/g, " "); // Supprime les lignes de séparation des tableaux + + // Supprimer les espaces en début et fin de texte + text = text.trim(); + + return text; + } + + + + // STATISTIQUES ------------------------------------------------------------------------------------------ + async function initializeTrackingState() { + const { isTrackingActive: storedState } = await browser.storage.local.get("isTrackingActive"); + isTrackingActive = storedState ?? false; + + console.log("[Stats] État initial récupéré :", isTrackingActive); + + if (isTrackingActive) { + startTracking(); + } else { + stopTracking(); + } + } + + function startTracking() { + console.log("[Stats] Suivi des statistiques activé."); + addViewportBorder(); + attachScrollListener(); + } + + function stopTracking() { + console.log("[Stats] Suivi des statistiques désactivé."); + removeViewportBorder(); + detachScrollListener(); + } + + function attachScrollListener() { + if (!scrollListenerAttached) { + window.addEventListener("scroll", trackVisibleContent); + scrollListenerAttached = true; + console.log("[Stats] Écouteur de défilement attaché."); + } + } + + function detachScrollListener() { + if (scrollListenerAttached) { + window.removeEventListener("scroll", trackVisibleContent); + scrollListenerAttached = false; + console.log("[Stats] Écouteur de défilement détaché."); + } + } + + // BORDURE ------------------------------------------------------------------------------------------ + function addViewportBorder() { + const existingBorder = document.getElementById("viewport-border"); + if (existingBorder) { + existingBorder.remove(); + } + const border = document.createElement("div"); + border.id = "viewport-border"; + border.style.position = "fixed"; + border.style.top = "0"; + border.style.left = "0"; + border.style.width = "100vw"; + border.style.height = "100vh"; + border.style.boxSizing = "border-box"; + border.style.border = "8px solid red"; + border.style.pointerEvents = "none"; + border.style.zIndex = "999999"; + document.body.appendChild(border); + } + + function removeViewportBorder() { + const border = document.getElementById("viewport-border"); + if (border) { + border.remove(); + } + } + + // GESTION DES MESSAGES ------------------------------------------------------------------------------------------ + browser.runtime.onMessage.addListener((message) => { + if (message.command === "pyodide-simplemma-ready") { + console.log("[Stats] Pyodide et Simplemma prêt, démarrage du scraping : "); + simplemmaReady = true; + initializeTrackingState(); // démarre le tracking une fois Pyodide prêt + } + + if (message.command === "update-stats") { + if (message.isActive && simplemmaReady) { + startTracking(); + } else { + stopTracking(); + } + } + }); + +})(); diff --git a/src/workers/pyodide_worker.js b/src/workers/pyodide_worker.js new file mode 100644 index 0000000..b76ab3a --- /dev/null +++ b/src/workers/pyodide_worker.js @@ -0,0 +1,85 @@ +// URL de la version Pyodide la plus récente +const LATEST_BASE_URL = "https://cdn.jsdelivr.net/pyodide/v0.27.2/full/"; + +let pyodide = null; +let pyodideLoaded = false; // Variable indiquant si Pyodide est en mémoire +let simplemmaLoaded = false; // Variable indiquant si simplemma est déjà installé + +// Écouteur des messages reçus du background script +self.onmessage = async (event) => { + const data = event.data; + + console.log("[WebWorker] Message reçu du Background:", data); + + // Initialisation unique de Pyodide puis de Simplemma + if (data.command === "pyodide-simplemma") { + if (pyodideLoaded && simplemmaLoaded) { + console.log("[Worker] Pyodide et Simplemma déjà chargés."); + self.postMessage({ type: "pyodide-simplemma", status: "already_loaded", message: "Pyodide et Simplemma déjà en mémoire" }); + return; + } + + try { + if (!pyodideLoaded) { + console.log("[Worker] Chargement de Pyodide..."); + importScripts(`${LATEST_BASE_URL}pyodide.js`); + pyodide = await loadPyodide({ indexURL: LATEST_BASE_URL }); + await pyodide.loadPackage("lzma"); + await pyodide.loadPackage("micropip"); + pyodideLoaded = true; + console.log("[Worker] Pyodide chargé avec succès !"); + } + + if (!simplemmaLoaded) { + console.log("[Worker] Installation de simplemma..."); + await pyodide.runPythonAsync(` + import micropip + import re + + try: + print("Installation de simplemma...") + await micropip.install("simplemma") + print("Installation réussie.") + + import simplemma + print("simplemma importé avec succès.") + + def tokenize(text): + return re.findall(r"\\b\\w+\\b", text.lower()) + + #Phrase de test pour vérifier l'installation de simplemma + print("Phrase de test:") + phrase = "Le chat mange la pomme" + + tokens = tokenize(phrase) + print("Tokens extraits :", tokens) + + lemmatized_tokens = [simplemma.lemmatize(token, lang="fr") for token in tokens] + print("Tokens lemmatisés :", lemmatized_tokens) + + lemmatized_tokens + + except Exception as e: + print("Erreur lors de l'installation ou de l'importation :", e) + e + + `); + simplemmaLoaded = true; + console.log("[Worker] Simplemma installé avec succès !"); + } + + // Envoyer confirmation au background script + self.postMessage({ type: "pyodide-simplemma", status: "success", message: "Pyodide et Simplemma chargés" }); + + } catch (error) { + console.error("[Worker] Erreur lors du chargement de Pyodide ou Simplemma :", error); + self.postMessage({ type: "pyodide-simplemma", status: "error", message: error.toString() }); + } + } + + // Activer/désactiver les statistiques (stats.js) + if (data.command === "toggle-stats") { + console.log(`[WebWorker] Statistiques ${data.isActive ? "activées" : "désactivées"}`); + return; + } +}; -- GitLab