From 727269c09500079ba28dcf9f889e2052020a6800 Mon Sep 17 00:00:00 2001
From: Lucie Bader <167515375+Lucie-Bdr@users.noreply.github.com>
Date: Tue, 18 Feb 2025 15:26:09 +0100
Subject: [PATCH] =?UTF-8?q?R=C3=A9cup=C3=A9ration=20scripts=20Alissa?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 manifest.json                            |   6 +
 src/background/background.js             | 351 ++++++++++++++---------
 src/context_menu/custom_context_menu.css |   7 +
 src/popup/popup.html                     |   4 +-
 src/popup/popup.js                       | 309 ++++++++++----------
 src/sidebar/sidebar.html                 |   8 +-
 src/sidebar/sidebar.js                   |   4 +
 src/utils/api.js                         |   3 +-
 src/utils/definitions.js                 | 159 ++++++++--
 src/utils/stats.js                       | 281 ++++++++++++------
 src/workers/pyodide_worker.js            | 247 +++++++++++-----
 stoplist_fr.txt                          | 116 ++++++++
 12 files changed, 1003 insertions(+), 492 deletions(-)
 create mode 100644 stoplist_fr.txt

diff --git a/manifest.json b/manifest.json
index 4f61904..d465f43 100644
--- a/manifest.json
+++ b/manifest.json
@@ -64,7 +64,13 @@
         "src/utils/stats.js"],
       "css": ["src/context_menu/custom_context_menu.css"],
       "run_at": "document_idle"
+    },
+    {
+      "matches": ["<all_urls>"],
+      "js": ["src/utils/stats.js"],
+      "run_at": "document_end"
     }
+
   ],
 
   "web_accessible_resources": [
diff --git a/src/background/background.js b/src/background/background.js
index b21ad4a..65ce6f0 100644
--- a/src/background/background.js
+++ b/src/background/background.js
@@ -45,7 +45,7 @@ browser.storage.onChanged.addListener((changes, area) => {
         if (!extensionActive) {
           log("Token ajouté, activation automatique de l'extension.");
           browser.storage.local.set({ extensionActive: true });
-          enableExtensionFeatures(); 
+          enableExtensionFeatures();
           browser.runtime.sendMessage({
             action: "updateUI",
             extensionActive: true,
@@ -71,90 +71,6 @@ async function refreshAllUI() {
   browser.runtime.sendMessage({ action: "refreshUI" });
 }
 
-// ─────────────────────────────────────────────────────────────────────────────
-// Initialisation du WebWorker
-// ─────────────────────────────────────────────────────────────────────────────
-let worker = null;
-
-function initWorker() {
-  if (!worker) {
-    log("[Background] Initialisation du WebWorker...");
-    try {
-      worker = new Worker("src/workers/pyodide_worker.js");
-      worker.onmessage = (event) => {
-        log("[Background] Message reçu du WebWorker :", event.data);
-      };
-      worker.onerror = (error) => {
-        console.error("[Background] Erreur dans le WebWorker :", error);
-      };
-      log("[Background] WebWorker initialisé avec succès.");
-    } catch (error) {
-      console.error("[Background] Échec de l'initialisation du WebWorker :", error);
-    }
-  }
-}
-// Initialisation
-initWorker();
-worker.postMessage({ command: "pyodide-simplemma" });
-
-// ─────────────────────────────────────────────────────────────────────────────
-// Écoute des messages de la popup et transmission au WebWorker
-// ─────────────────────────────────────────────────────────────────────────────
-browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
-  log("[Background] Message reçu :", message);
-
-  if (!worker) {
-    initWorker();
-  }
-
-  if (message.command === "toggle-stats") {
-    log(`[Background] Statistiques ${message.isActive ? "activées" : "désactivées"}`);
-    await browser.storage.local.set({ isTrackingActive: message.isActive });
-    // Transmettre l'activation des statistiques au WebWorker
-    worker.postMessage({ command: "toggle-stats", isActive: message.isActive });
-  }
-  
-  if (message.command === "pyodide-simplemma") {
-    log("[Background] Demande d'initialisation de Pyodide et Simplemma...");
-    worker.postMessage({ command: "pyodide-simplemma" });
-  }
-
-  return true; 
-});
-
-// ─────────────────────────────────────────────────────────────────────────────
-// Écouter les réponses du WebWorker
-// ─────────────────────────────────────────────────────────────────────────────
-worker.onmessage = (event) => {
-  log("[Background] Message du WebWorker :", event.data);
-
-  // Pyodide et Simplemma prêts : notifier tous les onglets
-  if (event.data.type === "pyodide-simplemma" && event.data.status === "success") {
-    browser.tabs.query({}).then((tabs) => {
-      tabs.forEach((tab) => {
-        if (!tab.url || (!tab.url.startsWith("https://") && (!tab.url.startsWith("https://")))) {
-          return;
-        }
-        if (tab.url.includes("sidebar.html")) {
-          return;
-        }
-
-        try {
-          let response = browser.tabs.sendMessage(tab.id, { command: "pyodide-simplemma-ready" })
-          if (response && typeof response.then === "function") {
-            response.then(() => {
-            }).catch((error) => {
-              log("[Background] Impossible d'envoyer un message à l'onglet ${tab.id} : ${error}");
-            });
-          }
-        } catch (error) {
-          log("[Background] Erreur lors de l'envoi d'un message à l'onglet ${tab.id} : ${error}");
-        }
-      });
-    })
-  }
-}
-
 // ─────────────────────────────────────────────────────────────────────────────
 // Fonctions d'authentification & de redirection
 // ─────────────────────────────────────────────────────────────────────────────
@@ -191,21 +107,20 @@ async function actuallyOpenLoginPage() {
   browser.runtime.sendMessage({ action: "authStatusChanged", isLoggedIn: false });
 }
 
-// Déconnecte l'utilisateur 
 async function disconnectFromLexicalDB() {
   await browser.storage.local.remove("accessToken");
   log("🔓 Token supprimé avec succès.");
-  
+
   await browser.storage.local.remove("lexiconColors");
-  
+
   // Désactivation automatique de l'extension
   await browser.storage.local.set({ extensionActive: false });
-  disableExtensionFeatures(); 
-  browser.runtime.sendMessage({ 
-    action: "updateUI", 
-    extensionActive: false, 
-    isTrackingActive: false, 
-    autoAdd: false 
+  disableExtensionFeatures();
+  browser.runtime.sendMessage({
+    action: "updateUI",
+    extensionActive: false,
+    isTrackingActive: false,
+    autoAdd: false
   });
 
   setTimeout(async () => {
@@ -213,7 +128,6 @@ async function disconnectFromLexicalDB() {
   }, 500);
 }
 
-// Sauvegarde le token et ferme l'onglet de login si nécessaire
 async function saveToken(token) {
   log("✅ Sauvegarde du token :", token);
   await browser.storage.local.set({ accessToken: token });
@@ -253,7 +167,7 @@ async function saveToken(token) {
 }
 
 // ─────────────────────────────────────────────────────────────────────────────
-// Gestion des messages reçus
+// Gestion des messages reçus (depuis popup, etc.)
 // ─────────────────────────────────────────────────────────────────────────────
 browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
   log("📩 Message reçu dans background.js :", message);
@@ -414,65 +328,218 @@ function showInstructionPopup(details) {
 }
 
 // ─────────────────────────────────────────────────────────────────────────────
-// Gestion de l'activation/désactivation de l'extension
+// Initialisation du WebWorker
 // ─────────────────────────────────────────────────────────────────────────────
-// === 1. Initialisation de l'état de l'extension ===
-async function initializeExtensionState() {
-  const { extensionActive } = await browser.storage.local.get("extensionActive");
+let worker = null;
 
-  if (extensionActive === undefined) {
-    await browser.storage.local.set({ extensionActive: true }); // Activation par défaut
-    log("🔄 Initialisation : extension activée par défaut.");
-    enableExtensionFeatures();
-  } else {
-    log(`🔄 État actuel de l'extension : ${extensionActive ? "activée" : "désactivée"}`);
-    if (extensionActive) {
-      enableExtensionFeatures();
-    } else {
-      disableExtensionFeatures();
+function initWorker() {
+  if (!worker) {
+    console.log("[Background] Initialisation du WebWorker...");
+    try {
+      worker = new Worker(browser.runtime.getURL("src/workers/pyodide_worker.js"));
+      // Centralisation de l'écoute des messages et erreurs du Worker
+      worker.addEventListener("message", handleWorkerMessage);
+      worker.addEventListener("error", handleWorkerError);
+      console.log("[Background] WebWorker initialisé avec succès.");
+    } catch (error) {
+      console.error("[Background] Échec de l'initialisation du WebWorker :", error);
     }
   }
 }
 
-// === 2. Gestion de l'activation/désactivation de l'extension ===
-browser.runtime.onMessage.addListener(async (message) => {
-  if (message.action === "toggleExtension") {
-    log(`🔄 Changement d'état de l'extension : ${message.isActive ? "activée" : "désactivée"}`);
-    await browser.storage.local.set({ extensionActive: message.isActive });
+function handleWorkerMessage(event) {
+  const data = event.data;
+  console.log("[Background] Message du WebWorker :", data);
 
-    if (!message.isActive) {
-      disableExtensionFeatures();
-    } else {
-      enableExtensionFeatures();
+  switch (data.type) {
+    case "pyodide-simplemma":
+      if (data.status === "success") {
+        console.log("[Background] Pyodide et Simplemma prêts. Mise à jour de l'état.");
+        browser.storage.local.set({ pyodideSimplemmaReady: true });
+        checkAndUpdateTracking();
+      }
+      break;
+    case "update-frequencies":
+      console.log("[Background] Mise à jour des fréquences :", data.frequencies);
+      notifyAllTabs({ command: "update-frequencies", frequencies: data.frequencies });
+      // Si un port stats est connecté, vous pouvez lui transmettre également :
+      // port.postMessage({ command: "update-frequencies", frequencies: data.frequencies });
+      browser.storage.local.set({ lemmaFrequencies: data.frequencies });
+      break;
+    case "threshold-exceeded":
+      console.log("[Background] Mots dépassant le seuil :", data.wordsAboveThreshold);
+      notifyAllTabs({ command: "threshold-exceeded", wordsAboveThreshold: data.wordsAboveThreshold });
+      break;
+    default:
+      console.warn("[Background] Message non traité du Worker :", data);
+      break;
+  }
+}
+
+function handleWorkerError(error) {
+  console.error("[Background] Erreur dans le WebWorker :", error);
+}
+
+// Initialisation du worker dès le démarrage
+initWorker();
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Écoute des messages de la popup et transmission au WebWorker
+// ─────────────────────────────────────────────────────────────────────────────
+browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
+  console.log("[Background] Message reçu :", message);
+
+  if (!worker) {
+    initWorker();
+  }
+
+  if (message.command === "toggle-stats") {
+    console.log(`[Background] Statistiques ${message.isActive ? "activées" : "désactivées"}`);
+    const { isActive } = message;
+    await browser.storage.local.set({ isTrackingActive: isActive });
+    if (!isActive) {
+      await browser.storage.local.set({ autoAdd: false });
     }
-    browser.runtime.sendMessage({ action: "updateUI" });
+    checkAndUpdateTracking();
   }
+
+  if (message.command === "pyodide-simplemma") {
+    console.log("[Background] Demande d'initialisation de Pyodide et Simplemma...");
+    worker.postMessage({ command: "pyodide-simplemma" });
+  }
+
+  return true;
 });
 
-// === 3. Fonction pour désactiver les fonctionnalités de l'extension ===
-async function disableExtensionFeatures() {
-  log("Désactivation des fonctionnalités de l'extension.");
-  browser.runtime.sendMessage({ 
-    action: "updateUI", 
-    autoAdd: false, 
-    isTrackingActive: false,
-    areStatsActive: false
-  });
-  // Suppression d'éventuelles actions spécifiques à la sidebar
-  log("Les fonctionnalités de la sidebar ne sont plus utilisées.");
+// ─────────────────────────────────────────────────────────────────────────────
+// Chargement et sauvegarde des fréquences stockées
+// ─────────────────────────────────────────────────────────────────────────────
+async function loadStoredFrequencies() {
+  const { storedFrequencies } = await browser.storage.local.get("storedFrequencies");
+  return storedFrequencies || {};
 }
 
-// === 4. Fonction pour activer les fonctionnalités de l'extension ===
-async function enableExtensionFeatures() {
-  log("Réactivation des fonctionnalités de l'extension.");
-  browser.runtime.sendMessage({ 
-    action: "updateUI", 
-    autoAdd: true, 
-    isTrackingActive: true,
-    areStatsActive: true
-  }).catch(err => {
-    console.warn("Aucun récepteur pour le message updateUI :", err);
+let storedFrequencies = {};
+
+loadStoredFrequencies().then(frequencies => {
+  storedFrequencies = frequencies;
+  console.log("[Background] Fréquences initialisées :", storedFrequencies);
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Statistiques : Vérification et activation/désactivation du tracking
+// ─────────────────────────────────────────────────────────────────────────────
+async function checkAndUpdateTracking() {
+  const { isTrackingActive, pyodideSimplemmaReady } = await browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]);
+  if (isTrackingActive && pyodideSimplemmaReady) {
+    console.log("[Background] Activation du tracking.");
+    notifyAllTabs({ command: "activate-stats" });
+  } else {
+    console.log("[Background] Désactivation du tracking.");
+    notifyAllTabs({ command: "deactivate-stats" });
+  }
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Statistiques : Gestion des onglets
+// ─────────────────────────────────────────────────────────────────────────────
+async function notifyAllTabs(message) {
+  browser.tabs.query({}).then((tabs) => {
+    tabs.forEach((tab) => {
+      browser.tabs.sendMessage(tab.id, message)
+        .catch((error) => console.warn(`[Background] Impossible d'envoyer un message à l'onglet ${tab.id} : ${error}`));
+    });
   });
 }
 
-initializeExtensionState();
+// ─────────────────────────────────────────────────────────────────────────────
+// 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();
+  }
+  if (area === "local" && (changes.accessToken || changes.threshold || changes.trackedLanguages || changes.autoAdd)) {
+    console.log("[Background] Mise à jour des préférences détectée.");
+    const { accessToken, trackedLanguages, threshold, autoAdd } = await browser.storage.local.get([
+      "accessToken", "trackedLanguages", "threshold", "autoAdd"
+    ]);
+    const isAuthenticated = !!accessToken;
+    worker.postMessage({
+      command: "update-preferences",
+      isAuthenticated,
+      trackedLanguages: trackedLanguages || [],
+      threshold: threshold || 10,
+      autoAdd: autoAdd || false
+    });
+  }
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Écoute de 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}`));
+    }
+  }
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Connexion entre stats.js et le Worker via un port dédié
+// ─────────────────────────────────────────────────────────────────────────────
+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 le Worker
+    port.onMessage.addListener((message) => {
+      console.log("[Background] Message reçu de stats.js :", message);
+      worker.postMessage(message);
+    });
+
+    // Transmettre les réponses du Worker à stats.js via le port
+    worker.addEventListener("message", (event) => {
+      const data = event.data;
+      if (data.type === "update-frequencies") {
+        port.postMessage({ command: "update-frequencies", frequencies: data.frequencies });
+      }
+      if (data.type === "threshold-exceeded") {
+        port.postMessage({ command: "threshold-exceeded", wordsAboveThreshold: data.wordsAboveThreshold });
+      }
+    });
+  }
+});
+
+// ─────────────────────────────────────────────────────────────────────────────
+// Stoplists : Chargement et envoi au Worker
+// ─────────────────────────────────────────────────────────────────────────────
+let stoplistFr = [];
+
+function loadStoplist() {
+  fetch(browser.runtime.getURL("stoplist_fr.txt"))
+    .then(response => response.text())
+    .then(text => {
+      stoplistFr = text.split("\n").map(word => word.trim());
+      console.log("[Background] Stoplist chargée :", stoplistFr);
+      sendStoplistToWorker();
+    })
+    .catch(error => console.error("[Background] Erreur lors du chargement de la stoplist :", error));
+}
+
+function sendStoplistToWorker() {
+  console.log("[Background] Envoi de la stoplist au Worker...");
+  worker.postMessage({ command: "update-stoplist", stoplist: stoplistFr });
+}
+
+browser.runtime.onStartup.addListener(loadStoplist);
+browser.runtime.onInstalled.addListener(loadStoplist);
+
+// ─────────────────────────────────────────────────────────────────────────────
+// (Code commenté concernant l'activation/désactivation de l'analyse)
+// ─────────────────────────────────────────────────────────────────────────────
+// async function initializeExtensionState() { ... }
+// initializeExtensionState();
diff --git a/src/context_menu/custom_context_menu.css b/src/context_menu/custom_context_menu.css
index 622a8c6..82c76a1 100644
--- a/src/context_menu/custom_context_menu.css
+++ b/src/context_menu/custom_context_menu.css
@@ -1,4 +1,11 @@
 
+@font-face {
+  font-family: 'Luciole';
+  src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
+      url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
+  font-weight: normal;
+  font-style: normal;
+}
 
 /* === Conteneur principal du menu contextuel === */
 #whiteBox {
diff --git a/src/popup/popup.html b/src/popup/popup.html
index f993c52..b9c17fd 100644
--- a/src/popup/popup.html
+++ b/src/popup/popup.html
@@ -8,8 +8,8 @@
   <style>
     @font-face {
       font-family: 'Luciole';
-      src: url('chemin/vers/Luciole-Regular.woff2') format('woff2'),
-          url('chemin/vers/Luciole-Regular.woff') format('woff');
+      src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
+          url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
       font-weight: normal;
       font-style: normal;
     }
diff --git a/src/popup/popup.js b/src/popup/popup.js
index 2fdf2e3..8ccc3fc 100644
--- a/src/popup/popup.js
+++ b/src/popup/popup.js
@@ -1,45 +1,60 @@
 log("✅ popup.js chargé avec succès !");
 
-// === 1. Mise à jour du bouton Connexion/Déconnexion ===
-async function updateConnectionButton() {
+// ==========================
+// Fonctions utilitaires
+// ==========================
+async function getAccessToken() {
   const { accessToken } = await browser.storage.local.get("accessToken");
+  return accessToken;
+}
+
+// ==========================
+// Gestion de la connexion
+// ==========================
+async function updateConnectionButton() {
+  const accessToken = await getAccessToken();
   const button = document.getElementById("auth-button");
 
-  if (button) {
-    if (accessToken) {
-      button.textContent = "Se déconnecter";
-      button.title = "En vous déconnectant, vous perdrez l'accès à vos lexiques personnels, ainsi que les fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
-    } else {
-      button.textContent = "Se connecter";
-      button.title = "En vous connectant, vous pourrez accéder à vos lexiques personnels, ainsi qu'aux fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
-    }
-    button.onclick = async () => {
-      await browser.runtime.sendMessage({ action: "toggleAuth" });
-    };
-  } else {
+  if (!button) {
     console.error("❌ Le bouton de connexion n'a pas été trouvé.");
+    return;
   }
+
+  if (accessToken) {
+    button.textContent = "Se déconnecter";
+    button.title = "En vous déconnectant, vous perdrez l'accès à vos lexiques personnels, ainsi que les fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
+  } else {
+    button.textContent = "Se connecter";
+    button.title = "En vous connectant, vous pourrez accéder à vos lexiques personnels, ainsi qu'aux fonctionnalités d'ajout automatique et de statistiques d'utilisation.";
+  }
+
+  button.onclick = async () => {
+    await browser.runtime.sendMessage({ action: "toggleAuth" });
+  };
 }
 
-// === 2. Mise à jour dynamique de la sélection des langues ===
+// ==========================
+// Gestion 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");
+  const accessToken = await getAccessToken();
   if (!accessToken) {
     languageSelection.innerHTML = "<p style='color: red;'>Veuillez vous connecter.</p>";
     return;
   }
 
+  // Utilisation de la fonction de récupération de lexiques
+  // Choisissez ici entre getLexicons et getUserLexicons en fonction de votre implémentation
   const lexicons = await getLexicons(accessToken);
   const userLanguages = [...new Set(lexicons.map(lex => lex.language))];
 
   // Récupérer les langues suivies depuis le stockage
   const { trackedLanguages } = await browser.storage.local.get("trackedLanguages") || { trackedLanguages: [] };
 
-  languageSelection.innerHTML = ""; 
-
+  languageSelection.innerHTML = "";
   if (userLanguages.length === 0) {
     languageSelection.innerHTML = "<p style='color: red;'>Aucun lexique personnel trouvé.</p>";
     return;
@@ -50,64 +65,62 @@ async function updateLanguageSelection() {
     langButton.classList.add("lang-option");
     langButton.textContent = lang.toUpperCase();
     langButton.dataset.value = lang;
-
     if (trackedLanguages && trackedLanguages.includes(lang)) {
       langButton.classList.add("selected");
     }
-
     langButton.addEventListener("click", () => {
       langButton.classList.toggle("selected");
     });
-
     languageSelection.appendChild(langButton);
   });
 
   log("✅ Sélection des langues mise à jour avec :", userLanguages);
 }
 
-// === 3. Mise à jour des options utilisateur et des statistiques ===
+// ==========================
+// Gestion des options et des statistiques
+// ==========================
 async function updateOptionsUI() {
-  const { accessToken, extensionActive } = await browser.storage.local.get(["accessToken", "extensionActive"]);
+  const accessToken = await getAccessToken();
   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");
+  const toggleStatsBtn = document.getElementById("toggleStatsBtn");
   const openStats = document.getElementById("open-stats");
 
+  // Affichage de l'option "Ajout automatique" selon la connexion
   if (autoAddContainer) {
     autoAddContainer.style.display = isLoggedIn ? "block" : "none";
   }
 
+  // Chargement des préférences
   const { isTrackingActive, autoAdd, threshold } = await browser.storage.local.get([
     "isTrackingActive",
     "autoAdd",
     "threshold"
   ]) || { isTrackingActive: false };
 
+  // Gestion du bouton de statistiques
   if (toggleStatsBtn) {
     if (!isLoggedIn) {
-      openStats.style.display = "none";
-    } else {
-      openStats.style.display = "block";
-    }
-    if (!accessToken) {
-      toggleStatsBtn.textContent = "Activer les statistiques";
       toggleStatsBtn.style.opacity = "0.5";
       toggleStatsBtn.title = "Connectez-vous pour activer les statistiques";
+      if (openStats) openStats.style.display = "none";
     } else {
       toggleStatsBtn.style.opacity = "1";
-      toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques";
       toggleStatsBtn.title = "";
+      if (openStats) openStats.style.display = "block";
     }
+    toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques";
   }
 
+  // Affichage des options statistiques
   if (statsOptions) {
-    statsOptions.classList.toggle("hidden", !extensionActive || !isTrackingActive);
+    statsOptions.classList.toggle("hidden", !isTrackingActive);
   }
 
   if (isLoggedIn) {
@@ -136,96 +149,38 @@ async function updateOptionsUI() {
   }
 }
 
-// === 4. Gestion des événements pour options/statistiques ===
-document.getElementById("toggleStatsBtn").addEventListener("click", async () => {
-  const { accessToken } = await browser.storage.local.get("accessToken");
-  if (!accessToken) {
-    return;
-  }
-  const current = await browser.storage.local.get("isTrackingActive");
-  const newState = !(current.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) {
-    log("[Popup] Demande d'initialisation de Pyodide...");
-    browser.runtime.sendMessage({ command: "init-pyodide" });
-  }
-});
-
-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
-  });
-
-  log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
-});
-
-document.getElementById("open-stats").addEventListener("click", function() {
-  window.open("stats.html", "_blank");
-});
-
-
-// === 5. Gestion de l'activation/désactivation de l'analyse ===
+// ==========================
+// Gestion de l'activation/désactivation de l'extension
+// ==========================
 async function updateExtensionToggleButton() {
   const { accessToken, extensionActive } = await browser.storage.local.get(["accessToken", "extensionActive"]);
   const toggleButton = document.getElementById("toggleExtensionBtn");
 
-  if (toggleButton) {
-    if (!accessToken) {
-      toggleButton.textContent = "Activer l'analyse";
-      toggleButton.style.opacity = "0.5";
-      toggleButton.title = "Connectez-vous pour activer l'analyse";
-    } else {
-      toggleButton.style.opacity = "1";
-      toggleButton.textContent = extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
-      toggleButton.title = "";
-    }
+  if (!toggleButton) {
+    console.error("❌ Le bouton d'activation de l'analyse n'a pas été trouvé.");
+    return;
+  }
 
-    if (!toggleButton.dataset.listenerAdded) {
-      toggleButton.addEventListener("click", handleToggleExtension);
-      toggleButton.dataset.listenerAdded = "true";
-    }
+  if (!accessToken) {
+    toggleButton.textContent = "Activer l'analyse";
+    toggleButton.style.opacity = "0.5";
+    toggleButton.title = "Connectez-vous pour activer l'analyse";
   } else {
-    console.error("Le bouton d'activation de l'anlyse n'a pas été trouvé.");
+    toggleButton.style.opacity = "1";
+    toggleButton.textContent = extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
+    toggleButton.title = "";
+  }
+
+  // Ajout d'un écouteur unique
+  if (!toggleButton.dataset.listenerAdded) {
+    toggleButton.addEventListener("click", handleToggleExtension);
+    toggleButton.dataset.listenerAdded = "true";
   }
 }
 
-// Gestion du clic sur le bouton d'activation/désactivation
-function handleToggleExtension(event) {
-  browser.storage.local.get("accessToken").then(({ accessToken }) => {
-    if (!accessToken) {
-      return;
-    }
+function handleToggleExtension() {
+  getAccessToken().then(accessToken => {
+    if (!accessToken) return;
     proceedToggleExtension();
   });
 }
@@ -236,8 +191,8 @@ async function proceedToggleExtension() {
     isTrackingActive: false
   });
   const newState = !extensionActive;
-
   await browser.storage.local.set({ extensionActive: newState });
+
   const toggleButton = document.getElementById("toggleExtensionBtn");
   if (toggleButton) {
     toggleButton.textContent = newState ? "Désactiver l'analyse" : "Activer l'analyse";
@@ -247,52 +202,94 @@ async function proceedToggleExtension() {
   
   if (!newState) {
     await browser.storage.local.set({ isTrackingActive: false });
-    if (isTrackingActive) {
-      window.open("stats.html", "_blank");
-    }
+    // Si les statistiques étaient activées, ouvrir la page stats
+    if (isTrackingActive) window.open("stats.html", "_blank");
     browser.runtime.sendMessage({ action: "closeSidebarBlocks" });
   }
 
   await updateOptionsUI();
 }
 
-// === 6. Mise à jour de l'UI du popup en fonction d'un message ===
-async function updatePopupUI(message) {
-  log("🔄 Mise à jour du popup avec :", message);
+// ==========================
+// Écouteurs d'événements
+// ==========================
+document.addEventListener("DOMContentLoaded", async () => {
+  await updateConnectionButton();
+  await updateOptionsUI();
+  await updateLanguageSelection();
+  await updateExtensionToggleButton();
+});
 
-  const statsOptions = document.getElementById("stats-options");
-  const autoAddCheckbox = document.getElementById("auto-add");
-  const autoAddOptions = document.getElementById("auto-add-options");
+// Gestion des statistiques et options
+document.getElementById("toggleStatsBtn")?.addEventListener("click", async () => {
+  const accessToken = await getAccessToken();
+  if (!accessToken) return;
 
-  if (message.isTrackingActive !== undefined) {
-    if (statsOptions) {
-      statsOptions.classList.toggle("hidden", !message.isTrackingActive);
-    } else {
-      console.warn("⚠️ Élément #stats-options introuvable.");
-    }
+  const current = await browser.storage.local.get("isTrackingActive");
+  const newState = !current.isTrackingActive;
+  await browser.storage.local.set({ isTrackingActive: newState });
+
+  // Si désactivation, désactiver aussi l'ajout automatique
+  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");
   }
 
-  if (message.autoAdd !== undefined) {
-    if (autoAddCheckbox) {
-      autoAddCheckbox.checked = message.autoAdd;
-    } else {
-      console.warn("⚠️ Élément #auto-add introuvable.");
-    }
+  document.getElementById("toggleStatsBtn").textContent = newState ? "Désactiver les statistiques" : "Activer les statistiques";
+  document.getElementById("stats-options").classList.toggle("hidden", !newState);
 
-    if (autoAddOptions) {
-      autoAddOptions.classList.toggle("hidden", !message.autoAdd);
-    } else {
-      console.warn("⚠️ Élément #auto-add-options introuvable.");
-    }
+  // Envoi du message au background pour le changement de tracking
+  browser.runtime.sendMessage({ command: "toggle-stats", isActive: newState });
+  if (newState) {
+    log("[Popup] Demande d'initialisation de Pyodide et Simplemma");
+    // Garder la commande de file 2 pour l'initialisation de Pyodide/Simplemma
+    browser.runtime.sendMessage({ command: "pyodide-simplemma" });
   }
+});
 
-  const toggleButton = document.getElementById("toggleExtensionBtn");
-  if (toggleButton && message.extensionActive !== undefined) {
-    toggleButton.textContent = message.extensionActive ? "Désactiver l'analyse" : "Activer l'analyse";
+// Gestion du changement de l'option d'ajout automatique
+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 });
   }
-}
+});
 
-// === 7. Écoute des messages depuis le background ===
+// Sauvegarde des options utilisateur
+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);
+
+  // Vérification : si auto-add est activé, au moins une langue doit être sélectionnée
+  const errorMessage = document.getElementById("error-message");
+  if (autoAdd && selectedLanguages.length === 0) {
+    errorMessage?.classList.remove("hidden");
+    return;
+  }
+  errorMessage?.classList.add("hidden");
+
+  await browser.storage.local.set({
+    autoAdd,
+    threshold,
+    trackedLanguages: selectedLanguages
+  });
+  log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages });
+});
+
+// Bouton pour ouvrir la page des statistiques
+document.getElementById("open-stats")?.addEventListener("click", () => {
+  window.open("stats.html", "_blank");
+});
+
+// ==========================
+// Réception des messages du background
+// ==========================
 browser.runtime.onMessage.addListener(async (message) => {
   log("📩 Message reçu dans popup.js :", message);
 
@@ -301,12 +298,14 @@ browser.runtime.onMessage.addListener(async (message) => {
     await updateConnectionButton();
     await updateOptionsUI();
     await updateLanguageSelection();
-    updatePopupUI(message);
+    // Possibilité de mettre à jour d'autres éléments via le message
   } else if (message.action === "showNotification") {
-    // Logique pour afficher une notification si nécessaire
+    // Gestion des notifications si besoin
+    showNotification(message.text);
   }
 });
 
+// Actualisation de l'UI en cas de changement dans le stockage local
 browser.storage.onChanged.addListener((changes, area) => {
   if (area === "local" && changes.accessToken) {
     updateConnectionButton();
@@ -314,7 +313,9 @@ browser.storage.onChanged.addListener((changes, area) => {
   }
 });
 
-// === 8. Notification d'activation/désactivation (optionnelle) ===
+// ==========================
+// Gestion de l'affichage de notifications (optionnelle)
+// ==========================
 function showNotification(message) {
   const notificationBox = document.getElementById("extension-notification");
   const notificationText = document.getElementById("notification-text");
@@ -323,7 +324,6 @@ function showNotification(message) {
   if (notificationBox && notificationText && closeButton) {
     notificationText.textContent = message;
     notificationBox.classList.remove("hidden");
-
     closeButton.addEventListener("click", () => {
       notificationBox.classList.add("hidden");
     }, { once: true });
@@ -331,12 +331,3 @@ function showNotification(message) {
     console.error("❌ Impossible d'afficher la notification : élément manquant.");
   }
 }
-
-// === 9. Initialisation lors du DOMContentLoaded ===
-document.addEventListener("DOMContentLoaded", async () => {
-  await updateConnectionButton();
-  await updateOptionsUI();
-  await updateLanguageSelection();
-  await updateExtensionToggleButton();
-});
-
diff --git a/src/sidebar/sidebar.html b/src/sidebar/sidebar.html
index eb7d5ee..daea55d 100644
--- a/src/sidebar/sidebar.html
+++ b/src/sidebar/sidebar.html
@@ -9,8 +9,14 @@
   <script src="../utils/api.js" defer></script>
   <script src="../utils/definitions.js" defer></script>
   <script src="sidebar.js" defer></script>
-  
   <style>
+    @font-face {
+      font-family: 'Luciole';
+      src: url('../fonts/Luciole-Regular/Luciole-Regular.woff2') format('woff2'),
+          url('../fonts/Luciole-Regular/Luciole-Regular.woff') format('woff');
+      font-weight: normal;
+      font-style: normal;
+    }
     /* Style global */
     body {
       font-family: Luciole;
diff --git a/src/sidebar/sidebar.js b/src/sidebar/sidebar.js
index 1233404..48b3e37 100644
--- a/src/sidebar/sidebar.js
+++ b/src/sidebar/sidebar.js
@@ -558,6 +558,10 @@ browser.runtime.onMessage.addListener(async (message) => {
     case "pyodide-simplemma-ready":
       return;
 
+    case "saveToken":
+      authToken = message.token;
+      break;
+
     case "closeSidebarBlocks":
       closeBlock("menuContent");
       closeBlock("etatContent");
diff --git a/src/utils/api.js b/src/utils/api.js
index 05d106e..73d7fd0 100644
--- a/src/utils/api.js
+++ b/src/utils/api.js
@@ -1,5 +1,6 @@
 log("✅ api.js chargé correctement");
 window.authToken = null;
+
 // ─────────────────────────────────────────────────────────────────────────────
 // ▌ Sélection de texte sur la page
 // ─────────────────────────────────────────────────────────────────────────────
@@ -218,5 +219,5 @@ window.getLexiconEntries = getLexiconEntries;
 window.getAllLexiconWords = getAllLexiconWords;
 window.getWiktionaryDefinition = getWiktionaryDefinition;
 window.AddWord = AddWord;
-window.getLexiconsColors = getLexiconsColors;
+
 
diff --git a/src/utils/definitions.js b/src/utils/definitions.js
index 7a1f7d0..fd57303 100644
--- a/src/utils/definitions.js
+++ b/src/utils/definitions.js
@@ -125,46 +125,149 @@ async function fetchLexiconDefinitions(word) {
 * Récupère la définition d'un mot depuis le Wiktionnaire (fr).
 * Retourne un tableau d'objets : [{ source: 'Wiktionnaire', text: '...' }]
 */
-async function fetchWiktionaryDefinition(word) {
+// async function fetchWiktionaryDefinition(word) {
+//   try {
+//     log(`🔍 Requête Wiktionnaire pour "${word}"...`);
+//     if (!word || word.trim() === "") {
+//       throw new Error("⚠️ Mot vide, impossible d'envoyer la requête.");
+//     }
+//     const wiktionaryURL = `https://fr.wiktionary.org/w/api.php?action=query&format=json&origin=*&prop=extracts&explaintext=true&redirects=1&titles=${encodeURIComponent(word)}`;
+//     const response = await fetch(wiktionaryURL);
+//     if (!response.ok) {
+//       throw new Error(`❌ Erreur API Wiktionnaire: ${response.statusText}`);
+//     }
+//     const data = await response.json();
+//     log("📖 Réponse API (Wiktionnaire) :", data);
+
+//     const pages = data.query?.pages;
+//     const page = pages ? Object.values(pages)[0] : null;
+    
+//     const definitionText = page && page.extract
+//       ? page.extract.trim()
+//       : "⚠️ Aucune définition trouvée sur le Wiktionnaire.";
+
+//     log("🌍 Définition Wiktionnaire extraite :", definitionText);
+
+//     return [
+//       {
+//         source: "Wiktionnaire",
+//         text: definitionText
+//       }
+//     ];
+//   } catch (error) {
+//     console.error("❌ Erreur Wiktionnaire :", error);
+//     return [
+//       {
+//         source: "Wiktionnaire",
+//         text: "⚠️ Erreur lors de la récupération sur le Wiktionnaire."
+//       }
+//     ];
+//   }
+// }
+
+async function wikiApiResponse(word) {
+  const result = await browser.storage.local.get("accessToken");
+  authToken = result.accessToken;
+  // Construire l'URL de l'API avec le mot sélectionné
+  const wiktionaryApiUrl = `https://babalex.lezinter.net/api/wiktionary/search?graphy=${encodeURIComponent(word)}&language=fr`;
+
   try {
-    log(`🔍 Requête Wiktionnaire pour "${word}"...`);
-    if (!word || word.trim() === "") {
-      throw new Error("⚠️ Mot vide, impossible d'envoyer la requête.");
-    }
-    const wiktionaryURL = `https://fr.wiktionary.org/w/api.php?action=query&format=json&origin=*&prop=extracts&explaintext=true&redirects=1&titles=${encodeURIComponent(word)}`;
-    const response = await fetch(wiktionaryURL);
+    const response = await fetch(wiktionaryApiUrl, {
+      method: 'GET',
+      headers: {
+        Authorization: `Bearer ${authToken}`, 
+        'Content-Type': 'application/json', 
+      },
+    });
+
     if (!response.ok) {
-      throw new Error(`❌ Erreur API Wiktionnaire: ${response.statusText}`);
+      throw new Error(`Erreur lors de la récupération de la définition depuis le Wiktionnaire : ${response.statusText}`);
     }
-    const data = await response.json();
-    log("📖 Réponse API (Wiktionnaire) :", data);
 
-    const pages = data.query?.pages;
-    const page = pages ? Object.values(pages)[0] : null;
     
-    const definitionText = page && page.extract
-      ? page.extract.trim()
-      : "⚠️ Aucune définition trouvée sur le Wiktionnaire.";
+    const data = await response.json();
+    console.log(`Résultats du Wiktionnaire pour le mot "${word}" :`, data);
+    return data;
+  } catch (error) {
+    console.error('Erreur lors de la récupération de la définition depuis le Wiktionnaire :', error);
+    throw error; 
+  }
+}
 
-    log("🌍 Définition Wiktionnaire extraite :", definitionText);
+function formatDefinitionData(apiResponse) {
+  let formattedData = {
+      word: apiResponse[0]?.id.split("-").slice(2).join("-") || "",
+      pronunciations: [],
+      definitions: [],
+      examples: [],
+      translations: {},
+  };
+
+  apiResponse.forEach(entry => {
+      const wordData = entry[entry.id.split(".").slice(-1)[0]]; // Accéder aux données via la clé dynamique
+
+      // Ajout des prononciations
+      if (wordData.pronunciations) {
+          formattedData.pronunciations.push(...wordData.pronunciations);
+      }
 
-    return [
-      {
-        source: "Wiktionnaire",
-        text: definitionText
+      // Ajout des définitions
+      if (wordData.senses) {
+          for (let senseKey in wordData.senses) {
+              let sense = wordData.senses[senseKey];
+              if (sense.Definitions) {
+                  formattedData.definitions.push(...sense.Definitions.map(d => d.definition));
+              }
+              if (sense.Examples) {
+                  formattedData.examples.push(...sense.Examples.map(e => e.example));
+              }
+          }
       }
-    ];
-  } catch (error) {
-    console.error("❌ Erreur Wiktionnaire :", error);
-    return [
-      {
-        source: "Wiktionnaire",
-        text: "⚠️ Erreur lors de la récupération sur le Wiktionnaire."
+
+      // Ajout des traductions
+      if (entry.translations) {
+          entry.translations.forEach(translation => {
+              if (!formattedData.translations[translation.lang_name]) {
+                  formattedData.translations[translation.lang_name] = [];
+              }
+              formattedData.translations[translation.lang_name].push(translation.sense);
+          });
       }
-    ];
+  });
+
+  return formattedData;
+}
+
+async function fetchWiktionaryDefinition(word) {
+  try {
+      console.log(` Recherche de la définition pour : ${word}`);
+
+      // Récupération des données depuis l'API
+      const apiResponse = await wikiApiResponse(word);
+      console.log("Réponse brute de l'API :", apiResponse);
+
+      if (!Array.isArray(apiResponse) || apiResponse.length === 0) {
+          console.warn(`Aucune définition trouvée pour "${word}"`);
+          return [];  // Retourne un tableau vide si aucune définition
+      }
+
+      // Formatage des données
+      const formattedData = formatDefinitionData(apiResponse);
+      console.log("Données formatées :", formattedData);
+
+      return [
+          {
+              source: "Wiktionnaire",
+              text: formattedData.definitions.length > 0 ? formattedData.definitions.join(" | ") : "⚠️ Aucune définition disponible."
+          }
+      ];
+  } catch (error) {
+      console.error("Erreur lors de la récupération de la définition :", error);
+      return [{ source: "Wiktionnaire", text: "Erreur lors de la récupération sur le Wiktionnaire." }];
   }
 }
 
+
 // ─────────────────────────────────────────────────────────────────────────────
 // ▌ Affichage des définitions dans la barre latérale
 // ─────────────────────────────────────────────────────────────────────────────
diff --git a/src/utils/stats.js b/src/utils/stats.js
index 4714001..213f34a 100644
--- a/src/utils/stats.js
+++ b/src/utils/stats.js
@@ -1,96 +1,232 @@
-log("Script stats.js chargé !");
 (function () {
     if (window.hasRun) {
         return;
     }
     window.hasRun = true;
+    let workerPort = null; // Port unique vers le WebWorker
+    // ─────────────────────────────────────────────────────────────────────────────
+    // 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);
+                }
+                if (message.command === "threshold-exceeded") {
+                    console.log("[Stats] Mots dépassant le seuil :", message.wordsAboveThreshold);
+                    let alertMessage = "Nouveaux mots dépassant le seuil :\n";
+                    for (const [lang, words] of Object.entries(message.wordsAboveThreshold)) {
+                        alertMessage += `\n🔹 ${lang.toUpperCase()} : ${words.join(", ")}`;
+                    }
+                    alert(alertMessage);
+                }
+            });
+            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 });
+        }
+    }
+
+    // ─────────────────────────────────────────────────────────────────────────────
+    // 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();
+        }
+    });
 
-    let isTrackingActive = false;
+    //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();
-    let simplemmaReady = false;
+    const READING_SPEED_MS_PER_WORD = 250; // Seuil de lecture 
+    // Stockage des chronos et des éléments lus
+    const readingTimers = new Map();
+    const readContent = new Set();
+    let userIsActive = false;  // Indique si l'utilisateur est actif sur la page
+
+    // Détecte l'activité utilisateur pour éviter les fausses lectures
+    document.addEventListener("mousemove", () => userIsActive = true);
+    document.addEventListener("keydown", () => userIsActive = true);
+    setInterval(() => userIsActive = false, 60000); // Reset toutes les minutes
+
+    // Arrête tous les chronomètres lorsque l'utilisateur change d'onglet
+    document.addEventListener("visibilitychange", () => {
+        if (document.hidden) {
+            // console.log("[Stats] Changement d'onglet détecté");
+            resetAllTimers();
+        }
+    });
 
     /**
-     * Fonction pour extraire le texte visible et l'envoyer au background script.
+     * Fonction pour extraire le texte visible
      */
     function trackVisibleContent() {
-        // pas de tracking tant que Pyodide n’est pas prêt
-        if (!simplemmaReady) {
-            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, span, b";
 
-        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
+            "#p-lang", "#footer", ".navbox", ".infobox", ".sidebar", "script", "style", ".interlanguage-link"
         ];
-    
+
+
         document.querySelectorAll(selectors).forEach((element) => {
             if (excludeSelectors.some(sel => element.closest(sel))) {
-                return; // On ignore cet élément
+                return; // Ignore les éléments exclus
             }
     
             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);
-                    log("[Stats] Envoi du texte filtré au background.js :", text);
+            const isVisible = rect.top >= 0 && rect.bottom <= window.innerHeight;
+            let text = cleanText(element.innerText);
+            let wordCount = text.split(/\s+/).length;
+            let minReadTime = wordCount * READING_SPEED_MS_PER_WORD;
+    
+            if (text.length < 3) return; // Ignore les petits éléments
     
-                    browser.runtime.sendMessage({ command: "process-text", text: text });
+            if (isVisible) {
+                if (!readContent.has(text)) {
+                    startReadingTimer(element, text, minReadTime);
                 }
+                highlightElement(element, true);
+            } else {
+                stopReadingTimer(element, text);
+                highlightElement(element, false);
             }
         });
     }
+    
+    /**
+     * Ajoute une bordure rouge autour de l'élément
+     */
+    function highlightElement(element, shouldHighlight) {
+        if (shouldHighlight) {
+            element.style.outline = "3px solid red";
+        } else {
+            element.style.outline = "";
+            removeReadingIndicator(element);
+        }
+    }
+    
+    /**
+     * Démarre un chronomètre pour vérifier si l'élément est lu
+     */
+    function startReadingTimer(element, text, minReadTime) {
+        if (!readingTimers.has(element)) {
+            let elapsedTime = 0;
+            let counter = document.createElement("div");
+            counter.classList.add("reading-counter");
+            counter.style.position = "absolute";
+            counter.style.background = "black";
+            counter.style.color = "white";
+            counter.style.padding = "4px 6px";
+            counter.style.borderRadius = "5px";
+            counter.style.fontSize = "12px";
+            counter.style.zIndex = "9999";
+    
+            document.body.appendChild(counter);
+    
+            let interval = setInterval(() => {
+                elapsedTime += 1000;
+    
+                // Vérifie si l'utilisateur est actif et si le temps minimum est atteint
+                if (userIsActive && elapsedTime >= minReadTime) {
+                    console.log(`[Stats] Élément lu : ${text}`);
+                    readContent.add(text);
+                    sendTextToWorker(text);
+                    stopReadingTimer(element, text);
+                }
+    
+                // Mise à jour de la position du compteur
+                let rect = element.getBoundingClientRect();
+                counter.style.top = `${rect.top + window.scrollY - 20}px`;
+                counter.style.left = `${rect.left + window.scrollX + rect.width + 10}px`;
+                counter.innerText = `⏳ ${Math.floor(elapsedTime / 1000)}s`;
+    
+            }, 1000);
+    
+            readingTimers.set(element, { interval, counter, elapsedTime });
+        }
+    }
+    
+    /**
+     * Arrête le chronomètre et supprime l'affichage du temps de lecture
+     */
+    function stopReadingTimer(element, text) {
+        if (readingTimers.has(element)) {
+            let { interval, counter } = readingTimers.get(element);
+            clearInterval(interval);
+            counter.remove();
+            readingTimers.delete(element);
+        }
+    }
+    /**
+     * Réinitialise tous les chronomètres lors du changement d'onglet
+     */
+    function resetAllTimers() {
+        for (let [element, { interval, counter }] of readingTimers) {
+            clearInterval(interval);
+            counter.remove();
+        }
+        readingTimers.clear();
+    }
+
+    /**
+     * Supprime l'indicateur de lecture
+     */
+    function removeReadingIndicator(element) {
+        if (readingTimers.has(element)) {
+            let { counter } = readingTimers.get(element);
+            counter.remove();
+        }
+    }
+    /**
+     * Fonction pour nettoyer le texte
+     */
     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;
-
-        log("[Stats] État initial récupéré :", isTrackingActive);
-
-        if (isTrackingActive) {
-            startTracking();
-        } else {
-            stopTracking();
-        }
-    }
 
+    // ─────────────────────────────────────────────────────────────────────────────
+    // Gestion de l'activation/désactivation des statistiques
+    // ─────────────────────────────────────────────────────────────────────────────
     function startTracking() {
-        log("[Stats] Suivi des statistiques activé.");
-        addViewportBorder();
-        attachScrollListener();
-    }
+            console.log("[Stats] Suivi des statistiques activé.");
+            addViewportBorder();
+            attachScrollListener();
+        }
 
     function stopTracking() {
-        log("[Stats] Suivi des statistiques désactivé.");
+        console.log("[Stats] Suivi des statistiques désactivé.");
         removeViewportBorder();
         detachScrollListener();
     }
@@ -99,7 +235,7 @@ log("Script stats.js chargé !");
         if (!scrollListenerAttached) {
             window.addEventListener("scroll", trackVisibleContent);
             scrollListenerAttached = true;
-            log("[Stats] Écouteur de défilement attaché.");
+            console.log("[Stats] Écouteur de défilement attaché.");
         }
     }
 
@@ -107,11 +243,11 @@ log("Script stats.js chargé !");
         if (scrollListenerAttached) {
             window.removeEventListener("scroll", trackVisibleContent);
             scrollListenerAttached = false;
-            log("[Stats] Écouteur de défilement détaché.");
+            console.log("[Stats] Écouteur de défilement détaché.");
         }
     }
 
-    // BORDURE ------------------------------------------------------------------------------------------
+    // Indice visuel des statistiques actives 
     function injectBorder() {
         const css = `
         #border-svg {
@@ -194,33 +330,4 @@ log("Script stats.js chargé !");
         svg.remove();
         }
     }
-  
-  
-  
-  
-  
-
-    // GESTION DES MESSAGES ------------------------------------------------------------------------------------------
-    browser.runtime.onMessage.addListener((message) => {
-        if (message.command === "pyodide-simplemma-ready") {
-            log("[Stats] Pyodide et Simplemma prêt, démarrage du scraping : ");
-            browser.storage.local.get(["extensionActive", "accessToken"].then(({ extensionActive, accessToken }) => {
-                if (extensionActive && accessToken) {
-                    simplemmaReady = true;
-                    initializeTrackingState();
-                } else {
-                    log("[Stats] Extension non activée ou utilisateur déconnecté, désactivation du tracking.")
-                }
-            }))
-        }
-
-        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
index 8d54367..ca68c90 100644
--- a/src/workers/pyodide_worker.js
+++ b/src/workers/pyodide_worker.js
@@ -1,87 +1,190 @@
-importScripts('../utils/logger.js');
+console.log("✅ pyodide_worker.js chargé avec succès !");
 
 // 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é
+let pyodideLoaded = false;      // Indique si Pyodide est en mémoire
+let simplemmaLoaded = false;    // Indique si simplemma est déjà installé
+let storedFrequencies = {};     // Stockage des fréquences accumulées
+
+let autoAddEnabled = false;     // Ajout automatique désactivé par défaut
+let isAuthenticated = false;    // Non connecté par défaut
+let userThreshold = 10;         // Seuil par défaut
+let trackedLanguages = [];      // Aucune langue suivie par défaut
+let notifiedWords = {};         // Stockage des mots déjà signalés (pour éviter les doublons)
+let stoplistFr = new Set();     // Stockage optimisé de la stoplist
 
 // Écouteur des messages reçus du background script
 self.onmessage = async (event) => {
-    const data = event.data;
-
-    log("[WebWorker] Message reçu du Background:", data);
-
-    // Initialisation unique de Pyodide puis de Simplemma
-    if (data.command === "pyodide-simplemma") {
-        if (pyodideLoaded && simplemmaLoaded) {
-            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;
-        }
+  const data = event.data;
+  console.log("[WebWorker] Message reçu du Background:", data);
 
+  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...");
         try {
-            if (!pyodideLoaded) {
-                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;
-                log("[Worker] Pyodide chargé avec succès !");
-            }
-
-            if (!simplemmaLoaded) {
-                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;
-                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() });
+          importScripts(`${LATEST_BASE_URL}pyodide.js`);
+        } catch (err) {
+          console.error("[Worker] Erreur lors de l'import de pyodide.js :", err);
+          self.postMessage({ type: "pyodide-simplemma", status: "error", message: err.toString() });
+          return;
         }
+        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...");
+
+        // On encapsule la logique dans une fonction asynchrone pour faciliter l'usage d'await
+        await pyodide.runPythonAsync(`
+import micropip
+import asyncio
+
+async def main():
+    print("Installation de simplemma...")
+    await micropip.install("simplemma")
+    print("Installation réussie.")
+    import simplemma
+    print("simplemma importé avec succès.")
+    # Test simple : extraction de tokens et lemmatisation
+    import re
+    def tokenize(text):
+        return re.findall(r"\\b\\w+\\b", text.lower())
+    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)
+    return lemmatized_tokens
+
+await main()
+        `);
+        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() });
+    }
+  }
+
+  // --- Traitement du texte envoyé par stats.js ---
+  if (data.command === "process-text") {
+    if (!pyodideLoaded) {
+      console.log("[Worker] Pyodide non chargé.");
+      self.postMessage({ type: "process-text", status: "error", message: "Pyodide pas encore chargé" });
+      return;
     }
+    console.log("[Worker] Texte reçu pour analyse :", data.text);
+    try {
+      const stoplistArray = Array.from(stoplistFr);
+      console.log("Stoplist utilisée :", stoplistArray);
+      const result = await pyodide.runPythonAsync(`
+import json
+import re
+import simplemma
+from simplemma import langdetect
 
-    // Activer/désactiver les statistiques (stats.js)
-    if (data.command === "toggle-stats") {
-        log(`[WebWorker] Statistiques ${data.isActive ? "activées" : "désactivées"}`);
-        return;
+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 tokenize(text):
+    return re.findall(r"\\b[a-zA-ZÀ-ÿ'-]+\\b", text.lower())
+
+# Chargement de la stoplist
+stoplist = set(json.loads('${JSON.stringify(stoplistArray)}'))
+
+text = """${data.text.replace(/\"/g, '\\"')}"""
+detected_lang = detect_language(text)
+if detected_lang == "unk":
+    detected_lang = "other"
+
+tokens = tokenize(text)
+if detected_lang == "fr":
+    tokens = [token.lower().strip() for token in tokens if token.lower().strip() not in stoplist]
+
+lemmatized_tokens = [simplemma.lemmatize(token, lang=detected_lang) for token in tokens]
+
+freq = {}
+for token in lemmatized_tokens:
+    if token not in stoplist:
+        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;
+      if (!storedFrequencies[detectedLang]) {
+        storedFrequencies[detectedLang] = {};
+      }
+      for (const [word, count] of Object.entries(parsedResult.frequencies)) {
+        storedFrequencies[detectedLang][word] = (storedFrequencies[detectedLang][word] || 0) + count;
+      }
+      self.postMessage({ type: "update-frequencies", frequencies: storedFrequencies });
+      if (autoAddEnabled) {
+        checkThreshold(detectedLang);
+      }
+    } catch (error) {
+      console.error("[Worker] Erreur dans l'analyse du texte :", error);
     }
+  }
+
+  if (data.command === "update-preferences") {
+    userThreshold = data.threshold;
+    trackedLanguages = data.trackedLanguages;
+    autoAddEnabled = data.autoAdd;
+    isAuthenticated = data.isAuthenticated;
+    console.log("[Worker] Mise à jour des préférences :", { userThreshold, trackedLanguages, autoAddEnabled, isAuthenticated });
+  }
+
+  if (data.command === "update-stoplist") {
+    stoplistFr = new Set(data.stoplist.map(word => word.toLowerCase().trim()));
+    console.log("[Worker] Stoplist FR mise à jour :", stoplistFr);
+  }
 };
+
+// --- Vérification du seuil et notification ---
+function checkThreshold(lang) {
+  if (!autoAddEnabled || !isAuthenticated) {
+    console.log("[Worker] Auto-Add désactivé ou utilisateur non connecté. Aucune vérification de seuil.");
+    return;
+  }
+  if (!trackedLanguages.includes(lang)) {
+    console.log(`[Worker] La langue ${lang} n'est pas suivie.`);
+    return;
+  }
+  console.log(`[Worker] Vérification des fréquences pour la langue ${lang}...`);
+  let wordsAboveThreshold = {};
+  if (storedFrequencies[lang]) {
+    const exceededWords = Object.entries(storedFrequencies[lang])
+      .filter(([word, count]) => count >= userThreshold && !(notifiedWords[lang] && notifiedWords[lang].includes(word)))
+      .map(([word]) => word);
+    if (exceededWords.length > 0) {
+      if (!notifiedWords[lang]) {
+        notifiedWords[lang] = [];
+      }
+      notifiedWords[lang].push(...exceededWords);
+      wordsAboveThreshold[lang] = exceededWords;
+    }
+  }
+  if (Object.keys(wordsAboveThreshold).length > 0) {
+    console.log("[Worker] Nouveaux mots dépassant le seuil :", wordsAboveThreshold);
+    self.postMessage({ type: "threshold-exceeded", wordsAboveThreshold: wordsAboveThreshold });
+  } else {
+    console.log("[Worker] Aucun nouveau mot n'a dépassé le seuil.");
+  }
+}
diff --git a/stoplist_fr.txt b/stoplist_fr.txt
new file mode 100644
index 0000000..36fa27b
--- /dev/null
+++ b/stoplist_fr.txt
@@ -0,0 +1,116 @@
+alors
+au
+aucuns
+aussi
+autre
+avant
+avec
+avoir
+bon
+car
+ce
+cela
+ces
+ceux
+chaque
+ci
+comme
+comment
+dans
+des
+du
+dedans
+dehors
+depuis
+devrait
+doit
+donc
+dos
+début
+elle
+elles
+en
+encore
+essai
+est
+et
+eu
+fait
+faites
+fois
+font
+hors
+ici
+il
+ils
+je
+juste
+la
+le
+les
+leur
+là
+ma
+maintenant
+mais
+mes
+mien
+moins
+mon
+mot
+même
+ni
+nommés
+notre
+nous
+ou
+où
+par
+parce
+pas
+peut
+peu
+plupart
+pour
+pourquoi
+quand
+que
+quel
+quelle
+quelles
+quels
+qui
+sa
+sans
+ses
+seulement
+si
+sien
+son
+sont
+sous
+soyez
+sujet
+sur
+ta
+tandis
+tellement
+tels
+tes
+ton
+tous
+tout
+trop
+très
+tu
+voient
+vont
+votre
+vous
+vu
+ça
+étaient
+état
+étions
+été
+être
\ No newline at end of file
-- 
GitLab