From 1ca02d70cf2bc9850cd26ceca633e5bfc7e78117 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pr=C3=A9nom=20Nom?= <adresse@mail.com>
Date: Sun, 16 Feb 2025 05:49:41 +0100
Subject: [PATCH] correction + affichage stats

---
 src/background/background.js  | 156 +++++++++-----
 src/popup/popup.js            |  80 ++++---
 src/utils/stats.js            | 386 ++++++++++++++++++++++++----------
 src/workers/pyodide_worker.js |  72 ++++++-
 4 files changed, 485 insertions(+), 209 deletions(-)

diff --git a/src/background/background.js b/src/background/background.js
index 178b3bf..ff331a8 100644
--- a/src/background/background.js
+++ b/src/background/background.js
@@ -296,7 +296,6 @@ function showInstructionPopup(details) {
   });
 }
 
-
 // ─────────────────────────────────────────────────────────────────────────────
 // Initialisation du WebWorker
 // ─────────────────────────────────────────────────────────────────────────────
@@ -344,7 +343,7 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
       if (!isActive) {
         await browser.storage.local.set({ autoAdd: false });
         //console.log("[Background] autoAdd désactivé car les statistiques ont été désactivées.");
-      //Vérifier et activer le tracking
+      //Vérifier et activer le tracking des stats
       checkAndUpdateTracking();
     }      
   }
@@ -354,27 +353,42 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
     worker.postMessage({ command: "pyodide-simplemma" });
   }
 
-  if (message.command === "process-text") {
-    console.log("[Background] Envoi du texte au Worker pour traitement :", message.text);
-
-    // // Récupérer les préférences utilisateur depuis le stockage local
-    // const { accessToken, trackedLanguages, threshold, autoAdd } = await browser.storage.local.get([
-    //     "accessToken", "trackedLanguages", "threshold", "autoAdd"
-    // ]) || { trackedLanguages: [], threshold: 10, autoAdd: false };
-
-    // // Envoyer les données au Worker
-    // worker.postMessage({
-    //     type: "process-text",
-    //     text: message.text,
-    //     accessToken, 
-    //     trackedLanguages, 
-    //     threshold, 
-    //     autoAdd
-    // });
-  }
+  // if (message.command === "process-text") {
+  //   console.log("[Background] Envoi du texte au Worker pour traitement :", message.text);
+  //   worker.postMessage({ command: "process-text", text: message.text });
+
+  //   // // Récupérer les préférences utilisateur depuis le stockage local
+  //   // const { accessToken, trackedLanguages, threshold, autoAdd } = await browser.storage.local.get([
+  //   //     "accessToken", "trackedLanguages", "threshold", "autoAdd"
+  //   // ]) || { trackedLanguages: [], threshold: 10, autoAdd: false };
+
+  //   // // Envoyer les données au Worker
+  //   // worker.postMessage({
+  //   //     type: "process-text",
+  //   //     text: message.text,
+  //   //     accessToken, 
+  //   //     trackedLanguages, 
+  //   //     threshold, 
+  //   //     autoAdd
+  //   // });
+  // }
   return true; 
 });
 
+// Charger les fréquences stockées au démarrage
+async function loadStoredFrequencies() {
+  const { storedFrequencies } = await browser.storage.local.get("storedFrequencies");
+  return storedFrequencies || {};
+}
+
+// Initialiser le stockage au démarrage
+let storedFrequencies = {};
+
+loadStoredFrequencies().then(frequencies => {
+    storedFrequencies = frequencies;
+    console.log("[Background] Fréquences initialisées :", storedFrequencies);
+});
+
 // ─────────────────────────────────────────────────────────────────────────────
 // Écouter les réponses du WebWorker
 // ─────────────────────────────────────────────────────────────────────────────
@@ -384,12 +398,29 @@ worker.onmessage = async (event) => {
     //Si Pyodide et Simplemma sont prêts : AJOUTER UNE INFO VISUELLE 
     if (event.data.type === "pyodide-simplemma" && event.data.status === "success") {
       console.log("[Background] Pyodide et Simplemma prêts. Mise à jour de l'état.");
-
       // Stocker l'information dans le localStorage
       await browser.storage.local.set({ pyodideSimplemmaReady: true });
-
+      //Activer le tracking des stats
       checkAndUpdateTracking();
     }
+
+    // if (event.data.type === "update-frequencies") {
+    //   console.log("test")
+    //   const { lang, frequencies } = data;
+    //   console.log(`[Background] TEST LANGUE DETECTEE : ${lang}`);
+    // }
+
+    //Stockage des fréquences 
+  //   if (event.data.type === "save-frequencies") {
+  //     cconsole.log("[Background] Sauvegarde des fréquences :", event.data.frequencies);
+  //     storedFrequencies = event.data.frequencies;
+  //     try {
+  //         await browser.storage.local.set({ storedFrequencies });
+  //         console.log("[Background] Fréquences sauvegardées avec succès.");
+  //     } catch (error) {
+  //         console.error("[Background] Erreur lors de la sauvegarde des fréquences :", error);
+  //     }
+  // }
 };
 
 // ─────────────────────────────────────────────────────────────────────────────
@@ -414,48 +445,69 @@ async function checkAndUpdateTracking() {
 async function notifyAllTabs(message) {
   browser.tabs.query({}).then((tabs) => {
       tabs.forEach((tab) => {
+        //console.log(`[Background] Tentative d'envoi à l'onglet ${tab.id}`);
           browser.tabs.sendMessage(tab.id, message)
               .catch((error) => console.warn(`[Background] Impossible d'envoyer un message à l'onglet ${tab.id} : ${error}`));
       });
   });
 }
 
-// Gestion des nouveaux onglets
-browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
-  if (changeInfo.status === "complete") {
-      browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]).then(({ isTrackingActive, pyodideSimplemmaReady }) => {
-          if (isTrackingActive && pyodideSimplemmaReady) {
-              browser.tabs.executeScript(tabId, { file: "src/utils/stats.js" })
-                  .then(() => console.log(`[Background] stats.js injecté dans le nouvel onglet ${tabId}`))
-                  .catch((error) => console.warn(`[Background] Impossible d'injecter stats.js dans ${tabId} : ${error}`));
-
-              setTimeout(() => {
-                  browser.tabs.sendMessage(tabId, { command: "activate-stats" })
-                      .catch((error) => console.warn(`[Background] Impossible d'envoyer 'activate-stats' à ${tabId} : ${error}`));
-              }, 1000);
-          }
-      });
+// ─────────────────────────────────────────────────────────────────────────────
+// Statistiques : Écoute des modifications du stockage et mise à jour du tracking
+// ─────────────────────────────────────────────────────────────────────────────
+browser.storage.onChanged.addListener(async (changes, area) => {
+  if (area === "local" && (changes.isTrackingActive || changes.pyodideSimplemmaReady)) {
+    checkAndUpdateTracking();
   }
 });
 
-// Gestion du tracking pour les nouveaux onglets
-browser.tabs.onCreated.addListener((tab) => {
-  setTimeout(() => {
-      browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]).then(({ isTrackingActive, pyodideSimplemmaReady }) => {
-          if (isTrackingActive && pyodideSimplemmaReady) {
-              console.log(`[Background] Nouveau onglet détecté (${tab.id}), activation du tracking.`);
-              browser.tabs.sendMessage(tab.id, { command: "activate-stats" })
-                  .catch((error) => console.warn(`[Background] Impossible d'activer le tracking sur ${tab.id} : ${error}`));
-          }
-      });
-  }, 1000);
+// ─────────────────────────────────────────────────────────────────────────────
+// Écoute l'inscription de stats.js
+// ─────────────────────────────────────────────────────────────────────────────
+browser.runtime.onMessage.addListener(async (message, sender) => {
+  if (message.command === "register-stats-script") {
+      console.log("[Background] stats.js s'est enregistré.");
+      const { isTrackingActive, pyodideSimplemmaReady } = await browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]);
+      if (isTrackingActive && pyodideSimplemmaReady) {
+          browser.tabs.sendMessage(sender.tab.id, { command: "activate-stats" })
+              .catch((error) => console.warn(`[Background] Impossible d'envoyer activate-stats à ${sender.tab.id} : ${error}`));
+      }
+  }
 });
 
 // ─────────────────────────────────────────────────────────────────────────────
-// Écoute des modifications du stockage et mise à jour du tracking
+// Statistiques : Créer une liaison entre stats.js et le Worker
 // ─────────────────────────────────────────────────────────────────────────────
-browser.storage.onChanged.addListener(async (changes, area) => {
-  if (area === "local" && (changes.isTrackingActive || changes.pyodideSimplemmaReady)) {
-    checkAndUpdateTracking();
+// Gestion des connexions des scripts (ex: stats.js)
+browser.runtime.onConnect.addListener((port) => {
+  console.log("[Background] Connexion établie :", port.name);
+
+  if (port.name === "stats-worker-port") {
+      // Redirige les messages de stats.js vers pyodide-worker.js
+      port.onMessage.addListener((message) => {
+          console.log("[Background] Message reçu de stats.js :", message);
+          worker.postMessage(message);
+      });
+
+      //A DEPLACER ? 
+      // Gérer les messages en retour du Worker vers stats.js
+      worker.onmessage = async (event) => {
+          console.log("[Background] Message du Worker :", event.data);
+
+          //Màj des fréquences
+          if (event.data.type === "update-frequencies") {
+              // const { lang, frequencies } = event.data;
+              const frequencies = event.data.frequencies;
+              //console.log("[Background] Mise à jour des fréquences :", frequencies);
+              //console.log(`[Background] Mise à jour des fréquences pour la langue : ${lang}`);
+              notifyAllTabs({ command: "update-frequencies", frequencies: event.data.frequencies }); 
+              port.postMessage({ command: "update-frequencies", frequencies: event.data.frequencies });
+              
+             // Enregistrer les fréquences dans le storage
+              browser.storage.local.set({ lemmaFrequencies: event.data.frequencies })
+              
+          }
+      };
   }
 });
+
diff --git a/src/popup/popup.js b/src/popup/popup.js
index ce1ec4d..5a480f5 100644
--- a/src/popup/popup.js
+++ b/src/popup/popup.js
@@ -54,8 +54,7 @@ document.getElementById("toggleStatsBtn").addEventListener("click", async () =>
   document.getElementById("stats-options").classList.toggle("hidden", !newState);
   // Mettre à jour l'affichage des options utilisateur
   await updateOptionsUI()
-  // Envoi des messages au background script
-  //toggle-stats
+  // Envoi du message au background script : toggle-stats
   browser.runtime.sendMessage({ command: "toggle-stats", isActive: newState });
   //pyodide-simplemma : récupère pyodide et simplemma si besoin
   if (newState) {
@@ -63,6 +62,41 @@ document.getElementById("toggleStatsBtn").addEventListener("click", async () =>
       browser.runtime.sendMessage({ command: "pyodide-simplemma" });  }
 });
 
+// === Cocherajout automatique ===
+document.getElementById("auto-add").addEventListener("change", async () => {
+  const isChecked = document.getElementById("auto-add").checked; 
+  // Affichage dynamique
+  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 });
+  }
+  
+});
+
+// === Sauvegarder les options ===
+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);
+  //Message d'erreur si une langue n'est pas au moins sélectionée
+  const errorMessage = document.getElementById("error-message");
+  // Vérifier si au moins une langue est sélectionnée
+  if (autoAdd && selectedLanguages.length === 0) {
+  errorMessage.classList.remove("hidden");  // Afficher le message d'erreur
+  return;
+  }
+  // Masquer l'erreur si elle était affichée
+  errorMessage.classList.add("hidden");
+  //Enregistrer les préférences
+  await browser.storage.local.set({
+      autoAdd,
+      threshold,
+      trackedLanguages: selectedLanguages
+  });
+  console.log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
+});
 
 // ─────────────────────────────────────────────────────────────────────────────
 // Fonctions d'affichage et gestion des boutons pour les statistiques
@@ -76,9 +110,6 @@ async function updateStatsBtn() {
   toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques";
 }
 
-
-// === Affichage des langues de l'utilisateur ===
-// === Affichage et gestion de la sélection des langues de l'utilisateur ===
 // === Affichage et gestion de la sélection des langues de l'utilisateur ===
 async function updateLanguageSelection() {
   const languageSelection = document.getElementById("language-selection");
@@ -120,9 +151,6 @@ async function updateLanguageSelection() {
   console.log("Sélection des langues :", userLanguages);
 }
 
-
-
-
 // === Options des statistiques ===
 async function updateOptionsUI() {
   const { accessToken } = await browser.storage.local.get("accessToken");
@@ -157,42 +185,6 @@ async function updateOptionsUI() {
   }
 }
 
-
-// === Gestion de l'affichage dynamique des options Auto-Add ===
-document.getElementById("auto-add").addEventListener("change", async () => {
-  const isChecked = document.getElementById("auto-add").checked; 
-  // Affichage dynamique
-  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 });
-  }
-});
-
-// === Sauvegarde des options ===
-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);
-  //Message d'erreur si une langue n'est pas au moins sélectionée
-  const errorMessage = document.getElementById("error-message");
-  // Vérifier si au moins une langue est sélectionnée
-  if (autoAdd && selectedLanguages.length === 0) {
-  errorMessage.classList.remove("hidden");  // Afficher le message d'erreur
-  return;
-  }
-  // Masquer l'erreur si elle était affichée
-  errorMessage.classList.add("hidden");
-  //Enregistrer les préférences
-  await browser.storage.local.set({
-      autoAdd,
-      threshold,
-      trackedLanguages: selectedLanguages
-  });
-  console.log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
-});
-
 // ─────────────────────────────────────────────────────────────────────────────
 // Mise à jour des boutons dans le menu d'extension
 // ─────────────────────────────────────────────────────────────────────────────
diff --git a/src/utils/stats.js b/src/utils/stats.js
index 404ae6e..d650b82 100644
--- a/src/utils/stats.js
+++ b/src/utils/stats.js
@@ -3,138 +3,304 @@
         return;
     }
     window.hasRun = true;
+    let workerPort = null; // Port unique vers le WebWorker
+    let statsWidget = null;
 
-    let isTrackingActive = false;
-    let scrollListenerAttached = false;
-    const seenContent = new Set();
-
-    /**
-     * Fonction pour extraire le texte visible et l'envoyer au background script.
-     */
-    function trackVisibleContent() {
-        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 createStatsWidget() {
+        console.log("[Stats] Création du widget...");
+        if (document.getElementById("stats-widget")) return; // Empêche la duplication
+
+        statsWidget = document.createElement("div");
+        statsWidget.id = "stats-widget";
+        statsWidget.style.position = "fixed";
+        statsWidget.style.bottom = "10px";
+        statsWidget.style.right = "10px";
+        statsWidget.style.background = "rgba(0, 0, 0, 0.8)";
+        statsWidget.style.color = "white";
+        statsWidget.style.padding = "10px";
+        statsWidget.style.borderRadius = "8px";
+        statsWidget.style.fontSize = "14px";
+        statsWidget.style.zIndex = "1000000";
+        statsWidget.style.maxWidth = "300px";
+        statsWidget.style.boxShadow = "2px 2px 10px rgba(0, 0, 0, 0.5)";
+        statsWidget.style.overflow = "hidden";
+        statsWidget.style.whiteSpace = "nowrap";
+        statsWidget.style.textOverflow = "ellipsis";
+        statsWidget.innerHTML = "<strong>📊 Statistiques</strong><br>Analyse en cours...";
+        document.body.appendChild(statsWidget);
     }
 
-    function cleanText(text) {
-        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
-        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
-        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 updateStatsWidget(stats) {
+        if (!statsWidget) {
+            createStatsWidget();
+        }
+        console.log("[Stats] Mise à jour du widget avec :", stats);
+
+        let content = "<strong>📊 Statistiques</strong><br>";
+        content += `Mots totaux : ${stats.totalWords}<br>`;
+        content += `Mots uniques : ${stats.uniqueWords}<br>`;
+        content += "<hr>";
+
+        for (const lang of Object.keys(stats.languages)) {
+            let langData = stats.languages[lang];
+            content += `<strong>${lang.toUpperCase()}</strong> - ${langData.wordCount} mots<br>`;
+            content += `Mot le plus lu : <strong>${langData.mostFrequentWord || "Aucun"}</strong> (${langData.mostFrequentCount} fois)<br><hr>`;
         }
+
+        statsWidget.innerHTML = content;
     }
 
-    function startTracking() {
-        console.log("[Stats] Suivi des statistiques activé.");
-        addViewportBorder();
-        attachScrollListener();
+// ─────────────────────────────────────────────────────────────────────────────
+// Connexion/Transmission des données avec le WebWorker
+// ─────────────────────────────────────────────────────────────────────────────
+//Connexion au port
+function connectToWorker() {
+    if (!workerPort) {
+        // console.log("[Stats] Connexion au WebWorker...");
+        workerPort = browser.runtime.connect({ name: "stats-worker-port" });
+        workerPort.onMessage.addListener((message) => {
+            console.log("[Stats] Message reçu du Worker :", message);
+            if (message.command === "update-frequencies") {
+                console.log("[Stats] Fréquences mises à jour :", message.frequencies);
+                checkThreshold(message.frequencies);
+                
+                let totalWords = 0;
+                let uniqueWordsSet = new Set();
+                let languagesData = {};
+
+                for (const lang in message.frequencies) {
+                    let wordCount = 0;
+                    let mostFrequentWord = "";
+                    let mostFrequentCount = 0;
+
+                    for (const [word, count] of Object.entries(message.frequencies[lang])) {
+                        totalWords += count;
+                        wordCount += count;
+                        uniqueWordsSet.add(word);
+
+                        if (count > mostFrequentCount) {
+                            mostFrequentWord = word;
+                            mostFrequentCount = count;
+                        }
+                    }
+
+                    languagesData[lang] = {
+                        wordCount,
+                        mostFrequentWord,
+                        mostFrequentCount
+                    };
+                }
+
+                updateStatsWidget({
+                    totalWords,
+                    uniqueWords: uniqueWordsSet.size,
+                    languages: languagesData
+                });
+            }
+        });
+        workerPort.onDisconnect.addListener(() => {
+            // console.log("[Stats] Déconnexion du WebWorker.");
+            workerPort = null;
+        });
+    }
+}
+//Fonction pour envoyer le texte directement au Worker
+function sendTextToWorker(text) {
+    if (!workerPort) {
+        connectToWorker();
     }
+    if (workerPort) {
+        console.log("[Stats] Envoi du texte au Worker :", text);
+        workerPort.postMessage({ command: "process-text", text: text });
+    }
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Vérification du seuil
+// ─────────────────────────────────────────────────────────────────────────────
+// Stockage des mots déjà signalés (évite d'afficher plusieurs fois la même alerte)
+let notifiedWords = {};
 
-    function stopTracking() {
-        console.log("[Stats] Suivi des statistiques désactivé.");
-        removeViewportBorder();
-        detachScrollListener();
+// Fonction de vérification du seuil
+async function checkThreshold(frequencies) {
+    // Récupérer les préférences de l'utilisateur
+    const { threshold, trackedLanguages } = await browser.storage.local.get(["threshold", "trackedLanguages"]) || { threshold: 10, trackedLanguages: [] };
+
+    if (!threshold || !trackedLanguages.length) {
+        console.log("[Stats] Aucun seuil défini ou aucune langue suivie.");
+        return;
     }
 
-    function attachScrollListener() {
-        if (!scrollListenerAttached) {
-            window.addEventListener("scroll", trackVisibleContent);
-            scrollListenerAttached = true;
-            console.log("[Stats] Écouteur de défilement attaché.");
+    console.log(`[Stats] Seuil défini à : ${threshold}`);
+    console.log(`[Stats] Langues suivies : ${trackedLanguages.join(", ")}`);
+
+    let wordsAboveThreshold = {};
+
+    // Vérifier chaque langue suivie
+    for (const lang of trackedLanguages) {
+        if (frequencies[lang]) {
+            console.log(`[Stats] Vérification des fréquences pour la langue ${lang}...`);
+
+            // Vérifier quels mots dépassent le seuil et qui n'ont pas encore été notifiés
+            const exceededWords = Object.entries(frequencies[lang])
+                .filter(([word, count]) => count >= threshold && !(notifiedWords[lang] && notifiedWords[lang].includes(word)))
+                .map(([word]) => word);
+
+            if (exceededWords.length > 0) {
+                // Ajouter ces mots aux notifications pour éviter les doublons
+                if (!notifiedWords[lang]) {
+                    notifiedWords[lang] = [];
+                }
+                notifiedWords[lang].push(...exceededWords);
+
+                wordsAboveThreshold[lang] = exceededWords;
+            }
         }
     }
 
-    function detachScrollListener() {
-        if (scrollListenerAttached) {
-            window.removeEventListener("scroll", trackVisibleContent);
-            scrollListenerAttached = false;
-            console.log("[Stats] Écouteur de défilement détaché.");
+    // Si des mots dépassent le seuil pour la première fois, afficher une alerte
+    if (Object.keys(wordsAboveThreshold).length > 0) {
+        console.log("[Stats] ⚠️ Nouveaux mots dépassant le seuil :", wordsAboveThreshold);
+
+        let alertMessage = "📌 Nouveaux mots dépassant le seuil :\n";
+        for (const [lang, words] of Object.entries(wordsAboveThreshold)) {
+            alertMessage += `\n🔹 ${lang.toUpperCase()} : ${words.join(", ")}`;
         }
+        alert(alertMessage);
+    } else {
+        console.log("[Stats] ✅ Aucun nouveau mot n'a dépassé le seuil.");
     }
+}
 
-    // 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 envoyés depuis le background 
+// ─────────────────────────────────────────────────────────────────────────────
+browser.runtime.onMessage.addListener((message) => {
+    console.log("[Stats] Message reçu :", message);
+    if (message.command === "activate-stats") {
+        startTracking();
     }
+    if (message.command === "deactivate-stats") {
+        stopTracking();
+    }
+});
+
+//Enregistrement manuel auprès du background pour écouter activate-stats
+browser.runtime.sendMessage({ command: "register-stats-script" });
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Extraction du texte sur les pages
+// ─────────────────────────────────────────────────────────────────────────────
+let scrollListenerAttached = false;
+const seenContent = new Set();
+
+/**
+ * Fonction pour extraire le texte visible
+ */
+function trackVisibleContent() {
+    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
+    ];
 
-    // GESTION DES MESSAGES envoyé depuis le background -----------------------------------------------------------------------------------------
-    browser.runtime.onMessage.addListener((message) => {
-        if (message.command === "activate-stats") {
-            console.log("[Stats] Activation demandée par le background script.");
-            startTracking();
+    document.querySelectorAll(selectors).forEach((element) => {
+        if (excludeSelectors.some(sel => element.closest(sel))) {
+            return; // On ignore cet élément
         }
-        if (message.command === "deactivate-stats") {
-            console.log("[Stats] Désactivation du tracking.");
-            stopTracking();
+
+        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 (!seenContent.has(text)) {
+                seenContent.add(text);
+                //Envoyer le texte au WebWorker
+                console.log("[Stats] Envoi du texte filtré au Worker:", text);
+                sendTextToWorker(text);
+            }
         }
     });
+}
+
+/**
+ * Fonction pour nettoyer le texte
+ */
+function cleanText(text) {
+    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
+    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
+    text = text.trim();
+    return text;
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Gestion de l'activation/désactivation des statistiques
+// ─────────────────────────────────────────────────────────────────────────────
+function startTracking() {
+        console.log("[Stats] Suivi des statistiques activé.");
+        addViewportBorder();
+        attachScrollListener();
+    }
+
+function stopTracking() {
+    console.log("[Stats] Suivi des statistiques désactivé.");
+    removeViewportBorder();
+    detachScrollListener();
+}
 
-    // Initialisation
-    // initializeTrackingState();
+function attachScrollListener() {
+    if (!scrollListenerAttached) {
+        window.addEventListener("scroll", trackVisibleContent);
+        scrollListenerAttached = true;
+        console.log("[Stats] Écouteur de défilement attaché.");
+    }
+}
 
-    // Envoyer les données au WebWorker pour qu'il traite le texte scrappé
+function detachScrollListener() {
+    if (scrollListenerAttached) {
+        window.removeEventListener("scroll", trackVisibleContent);
+        scrollListenerAttached = false;
+        console.log("[Stats] Écouteur de défilement détaché.");
+    }
+}
 
+// Indice visuel des statistiques actives 
+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();
+    }
+}
 })();
diff --git a/src/workers/pyodide_worker.js b/src/workers/pyodide_worker.js
index aac6fc7..2c7abc1 100644
--- a/src/workers/pyodide_worker.js
+++ b/src/workers/pyodide_worker.js
@@ -4,6 +4,7 @@ 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é
+let storedFrequencies = {}; // Stockage des fréquences accumulées
 
 // Écouteur des messages reçus du background script
 self.onmessage = async (event) => {
@@ -78,8 +79,73 @@ self.onmessage = async (event) => {
     }
 
     // Traitement du texte (envoyé par stats.js)
-    if (data.command === "toggle-stats") {
-        console.log(`[WebWorker] Statistiques ${data.isActive ? "activées" : "désactivées"}`);
-        return;
+    if (data.command === "process-text") {
+        console.log("[Worker] Texte reçu pour analyse :", data.text);
+        try {
+            const result = await pyodide.runPythonAsync(`
+            import json
+            import re
+            import simplemma
+            from simplemma import langdetect
+    
+            def detect_language(text):
+                lang_scores = simplemma.langdetect(text, lang=("fr", "en", "es", "de", "it", "pt"))
+                return lang_scores[0][0] if lang_scores else "unk"
+    
+            def preprocess(text):
+                contractions = ["l'", "d'", "j'", "c'", "s'", "t'", "qu'", "m'", "n'"]
+                for contraction in contractions:
+                    text = re.sub(fr"{contraction}", f"{contraction} ", text)
+                return text
+    
+            def tokenize(text):
+                return re.findall(r"\\b[a-zA-ZÀ-ÿ'-]+\\b", text.lower())
+    
+            text = """${data.text.replace(/\"/g, '\\"')}"""
+            detected_lang = detect_language(text)
+    
+            if detected_lang == "unk":
+                detected_lang = "other"
+    
+            processed_text = preprocess(text)
+            tokens = tokenize(processed_text)
+            lemmatized_tokens = [simplemma.lemmatize(token, lang=detected_lang) for token in tokens]
+    
+            freq = {}
+            for token in lemmatized_tokens:
+                freq[token] = freq.get(token, 0) + 1
+    
+            json.dumps({"lang": detected_lang, "frequencies": freq}, ensure_ascii=False)
+            `);
+            
+            const parsedResult = JSON.parse(result);
+            const detectedLang = parsedResult.lang;
+            //console.log("[Worker] Résultat du traitement :", parsedResult);
+
+            // Vérifier si la langue existe dans storedFrequencies, sinon l'initialiser
+            if (!storedFrequencies[detectedLang]) {
+                storedFrequencies[detectedLang] = {};
+            }
+
+            // Mise à jour des fréquences stockées
+            for (const [word, count] of Object.entries(parsedResult.frequencies)) {
+                storedFrequencies[detectedLang][word] = (storedFrequencies[detectedLang][word] || 0) + count;
+            }
+
+            // self.postMessage({ 
+            //     type: "update-frequencies", 
+            //     lang: detectedLang, 
+            //     frequencies: storedFrequencies[detectedLang] 
+            // });
+
+            // Envoyer le message update-frequencies => màj des fréquences et sauvegarde dans le local storage
+            self.postMessage({ 
+                type: "update-frequencies", 
+                frequencies: storedFrequencies
+            });
+                
+        } catch (error) {
+            console.error("[Worker] Erreur dans l'analyse du texte :", error);
+        }
     }
 };
-- 
GitLab