diff --git a/manifest.json b/manifest.json index 399a0d408af048ee4abf6b8d04e5c0e37faa062c..d67fa4584bd94feb482d4b7525bd0177a48453dc 100644 --- a/manifest.json +++ b/manifest.json @@ -61,7 +61,9 @@ "src/utils/definitions.js", "src/sidebar/sidebar.js", "src/context_menu/custom_context_menu.js", - "src/utils/stats.js"], + "src/utils/stats.js", + "src/utils/highlighting.js" + ], "css": ["src/context_menu/custom_context_menu.css"], "run_at": "document_idle" }, @@ -72,6 +74,15 @@ "src/utils/stats.js" ], "run_at": "document_end" + }, + { + "matches": ["<all_urls>"], + "js": [ + "src/utils/logger.js", + "src/utils/api.js", + "src/utils/highlighting.js" + ], + "run_at": "document_start" } ], diff --git a/src/assets/lexicon_icon.js b/src/assets/lexicon_icon.js index 69833f84f9b59c886cdb7dece3627b8633610786..1247bcfbdf8386aea0a1163e2cf950fddb466a1e 100644 --- a/src/assets/lexicon_icon.js +++ b/src/assets/lexicon_icon.js @@ -19,7 +19,7 @@ async function getLexiconsColors(authToken) { log("✅ Couleurs des lexiques récupérées :", colors); return colors; } catch (error) { - console.error("⌠Erreur lors de la récupération des couleurs des lexiques :", error); + log("⌠Erreur lors de la récupération des couleurs des lexiques :", error); return {}; } } @@ -87,7 +87,7 @@ async function getOrCreateLexiconColor(lexiconId) { } } } catch (error) { - console.error("Erreur lors de la récupération des couleurs via l'API :", error); + log("Erreur lors de la récupération des couleurs via l'API :", error); } } // Si aucune couleur n'est associée, on la génère et on la sauvegarde @@ -133,7 +133,7 @@ async function updateLexiconColors(authToken) { await browser.storage.local.set({ lexiconColors: colorMapping }); return colorMapping; } catch (error) { - console.error("⌠Erreur lors de la mise à jour des couleurs :", error); + log("⌠Erreur lors de la mise à jour des couleurs :", error); return {}; } } @@ -147,6 +147,19 @@ async function getColorForLexicon(lexiconId) { const { lexiconColors } = await browser.storage.local.get("lexiconColors"); return (lexiconColors && lexiconColors[String(lexiconId)]) || "#cccccc"; } +/** + * Convertit une couleur hexadécimale en une couleur RGBA. + * @param {string} hex - La couleur en hexadécimal. + * @param {number} opacity - La transparence (0-1). + * @returns {string} La couleur RGBA. + */ +function hexToRgba(hex, opacity) { + const bigint = parseInt(hex.replace('#', ''), 16); + const r = (bigint >> 16) & 255; + const g = (bigint >> 8) & 255; + const b = bigint & 255; + return `rgba(${r}, ${g}, ${b}, ${opacity})`; +} window.updateLexiconColors = updateLexiconColors; window.getColorForLexicon = getColorForLexicon; @@ -155,4 +168,4 @@ window.convertColor = convertColor; window.getOrCreateLexiconColor = getOrCreateLexiconColor; window.createColorCircle = createColorCircle; window.getLexiconsColors = getLexiconsColors; - +window.hexToRgba = hexToRgba; diff --git a/src/background/background.js b/src/background/background.js index cd3d0126e50b61e76c825b31e464ed2f9e77e2e9..1f76e0cbf79a0a4cd3b2de0e060350ce4475b71c 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(); + updateExtension(); browser.runtime.sendMessage({ action: "updateUI", extensionActive: true, @@ -68,7 +68,11 @@ async function isUserConnected() { async function refreshAllUI() { log("🔄 Rafraîchissement global de l'UI..."); - browser.runtime.sendMessage({ action: "refreshUI" }); + try { + await browser.runtime.sendMessage({ action: "refreshUI" }); + } catch (error) { + console.warn("Aucun récepteur pour 'refreshUI' :", error); + } } // ───────────────────────────────────────────────────────────────────────────── @@ -155,7 +159,7 @@ async function saveToken(token) { const { extensionActive } = await browser.storage.local.get("extensionActive"); if (!extensionActive) { await browser.storage.local.set({ extensionActive: true }); - enableExtensionFeatures(); + updateExtension(); browser.runtime.sendMessage({ action: "updateUI", extensionActive: true, @@ -167,7 +171,7 @@ async function saveToken(token) { } // ───────────────────────────────────────────────────────────────────────────── -// Gestion des messages reçus (depuis popup, etc.) +// Gestion des messages reçus // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { log("📩 Message reçu dans background.js :", message); @@ -217,6 +221,34 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { } break; } + case "toggleLexiconHighlight": { + const tabs = await browser.tabs.query({active: true, currentWindow: true}); + if (tabs[0]) { + try { + // S'assurer que le script est injecté + await browser.scripting.executeScript({ + target: { tabId: tabs[0].id }, + files: ["src/utils/highlighting.js"] + }); + + // Envoyer le message d'activation + await browser.tabs.sendMessage(tabs[0].id, { + command: message.isActive ? "activate-highlighting" : "deactivate-highlighting", + lexiconId: message.lexiconId + }); + log(`✅ Message de surlignage transmis à l'onglet ${tabs[0].id}`); + } catch (error) { + log("⌠Erreur lors de la gestion du surlignage:", error); + } + } + break; + } + + case "register-highlighting-script": { + log("📠Script de surlignage enregistré pour l'onglet", sender.tab.id); + break; + } + default: break; } @@ -249,14 +281,14 @@ browser.webNavigation.onCompleted.addListener(async (details) => { log("🔠Token détecté :", token); browser.runtime.sendMessage({ action: "saveToken", token }); } else { - console.error("⌠Token introuvable."); + log("⌠Token introuvable."); } return null; })(); ` }); } catch (error) { - console.error("⌠Erreur lors de la récupération du token :", error); + log("⌠Erreur lors de la récupération du token :", error); } } }, { url: [{ hostContains: "prisms.lezinter.net" }] }); @@ -290,25 +322,25 @@ function showInstructionPopup(details) { popup.style.transform = "translate(-50%, -50%)"; popup.style.backgroundColor = "#a08e9f"; popup.style.color = "#323046"; - popup.style.padding = "20px"; + popup.style.padding = "12px"; popup.style.borderRadius = "10px"; popup.style.boxShadow = "0 2px 10px rgba(0, 0, 0, 0.3)"; popup.style.zIndex = "10000"; popup.style.fontFamily = "Luciole"; - popup.style.fontSize = "14px"; + popup.style.fontSize = "16px"; popup.style.width = "300px"; popup.style.textAlign = "center"; popup.innerHTML = \` - <h5 style="color: #fff; font-weight: bold; margin-top: 0;">🔑 Connexion à l'extension</h5> - <p style="margin: 15px 0;"> - Après avoir renseigné vos identifiants, cliquez sur + <h5 style="color: #fff; font-weight: bold; margin-top: 2px;">Connexion à l'extension</h5> + <p style="margin: 8px 0;"> + Après avoir renseigné vos identifiants, veuillez cliquer sur <strong>"Se connecter avec BaLex"</strong>. </p> <button id="close-popup-btn" style=" width: 100%; margin-top: 15px; - padding: 10px; + padding: 8px; border: none; background-color: #8d5c70; color: #fbfcfc; @@ -334,48 +366,48 @@ let worker = null; function initWorker() { if (!worker) { - console.log("[Background] Initialisation du WebWorker..."); + 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."); + log("[Background] WebWorker initialisé avec succès."); } catch (error) { - console.error("[Background] Échec de l'initialisation du WebWorker :", error); + log("[Background] Échec de l'initialisation du WebWorker :", error); } } } function handleWorkerError(error) { - console.error("Erreur du WebWorker :", error.message); + log("Erreur du WebWorker :", error.message); } function handleWorkerMessage(event) { const data = event.data; - console.log("[Background] Message du WebWorker :", data); + log("[Background] Message du WebWorker :", data); switch (data.type) { case "pyodide-simplemma": if (data.status === "success") { - console.log("[Background] Pyodide et Simplemma prêts. Mise à jour de l'état."); + log("[Background] Pyodide et Simplemma prêts. Mise à jour de l'état."); browser.storage.local.set({ pyodideSimplemmaReady: true }); checkAndUpdateTracking(); } else if (data.status === "error") { - console.error("[Background] Erreur lors du chargement :", data.message); + log("[Background] Erreur lors du chargement :", data.message); } else if (data.status === "already_loaded") { - console.log("[Background] Pyodide et Simplemma déjà chargés."); + log("[Background] Pyodide et Simplemma déjà chargés."); } break; case "update-frequencies": - console.log("[Background] Mise à jour des fréquences :", data.frequencies); + 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); + log("[Background] Mots dépassant le seuil :", data.wordsAboveThreshold); notifyAllTabs({ command: "threshold-exceeded", wordsAboveThreshold: data.wordsAboveThreshold }); break; default: @@ -391,14 +423,14 @@ 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); + 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"}`); + log(`[Background] Statistiques ${message.isActive ? "activées" : "désactivées"}`); const { isActive } = message; await browser.storage.local.set({ isTrackingActive: isActive }); if (!isActive) { @@ -408,7 +440,7 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { } if (message.command === "pyodide-simplemma") { - console.log("[Background] Demande d'initialisation de Pyodide et Simplemma..."); + log("[Background] Demande d'initialisation de Pyodide et Simplemma..."); worker.postMessage({ command: "pyodide-simplemma" }); } @@ -428,7 +460,7 @@ let storedFrequencies = {}; loadStoredFrequencies().then(frequencies => { storedFrequencies = frequencies; - console.log("[Background] Fréquences initialisées :", storedFrequencies); + log("[Background] Fréquences initialisées :", storedFrequencies); }); // ───────────────────────────────────────────────────────────────────────────── @@ -437,10 +469,10 @@ loadStoredFrequencies().then(frequencies => { async function checkAndUpdateTracking() { const { isTrackingActive, pyodideSimplemmaReady } = await browser.storage.local.get(["isTrackingActive", "pyodideSimplemmaReady"]); if (isTrackingActive && pyodideSimplemmaReady) { - console.log("[Background] Activation du tracking."); + log("[Background] Activation du tracking."); notifyAllTabs({ command: "activate-stats" }); } else { - console.log("[Background] Désactivation du tracking."); + log("[Background] Désactivation du tracking."); notifyAllTabs({ command: "deactivate-stats" }); } } @@ -465,7 +497,7 @@ browser.storage.onChanged.addListener(async (changes, area) => { checkAndUpdateTracking(); } if (area === "local" && (changes.accessToken || changes.threshold || changes.trackedLanguages || changes.autoAdd)) { - console.log("[Background] Mise à jour des préférences détectée."); + 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" ]); @@ -485,7 +517,7 @@ browser.storage.onChanged.addListener(async (changes, area) => { // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message, sender) => { if (message.command === "register-stats-script") { - console.log("[Background] stats.js s'est enregistré."); + 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" }) @@ -498,11 +530,11 @@ browser.runtime.onMessage.addListener(async (message, sender) => { // Connexion entre stats.js et le Worker via un port dédié // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onConnect.addListener((port) => { - console.log("[Background] Connexion établie :", port.name); + 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); + log("[Background] Message reçu de stats.js :", message); worker.postMessage(message); }); @@ -529,17 +561,40 @@ function loadStoplist() { .then(response => response.text()) .then(text => { stoplistFr = text.split("\n").map(word => word.trim()); - console.log("[Background] Stoplist chargée :", stoplistFr); + log("[Background] Stoplist chargée :", stoplistFr); sendStoplistToWorker(); }) - .catch(error => console.error("[Background] Erreur lors du chargement de la stoplist :", error)); + .catch(error => log("[Background] Erreur lors du chargement de la stoplist :", error)); } function sendStoplistToWorker() { - console.log("[Background] Envoi de la stoplist au Worker..."); + 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); +async function handleHighlighting(command, lexiconId, tabId) { + log(`🎯 Gestion du surlignage: ${command} pour le lexique ${lexiconId}`); + + try { + // S'assurer que le script est injecté + await browser.scripting.executeScript({ + target: { tabId: tabId }, + files: ["utils/highlighting.js"] + }); + + // Envoyer le message d'activation + const response = await browser.tabs.sendMessage(tabId, { + command: command, + lexiconId: lexiconId + }); + + log("✅ Réponse du content script:", response); + return response; + } catch (error) { + log("⌠Erreur lors de la gestion du surlignage:", error); + return false; + } +} \ No newline at end of file diff --git a/src/context_menu/browser_context_menu.js b/src/context_menu/browser_context_menu.js index 92112337ca829ef9f72b2bed3882f3c8e63387a4..57f5d56500b58ac5490b708a5f5010c5ec440d2f 100644 --- a/src/context_menu/browser_context_menu.js +++ b/src/context_menu/browser_context_menu.js @@ -12,7 +12,7 @@ async function loadAuthToken() { authToken = result.accessToken; log("🔑 Token chargé au démarrage :", authToken); } catch (error) { - console.error("⌠Erreur lors de la récupération du token :", error); + log("⌠Erreur lors de la récupération du token :", error); } } @@ -100,7 +100,7 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => { if (typeof actuallyOpenLoginPage === "function") { actuallyOpenLoginPage(); } else { - console.error("La fonction actuallyOpenLoginPage n'est pas accessible."); + log("La fonction actuallyOpenLoginPage n'est pas accessible."); } return; } @@ -146,7 +146,7 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => { break; } - console.error(`⌠Action inconnue : ${info.menuItemId}`); + log(`⌠Action inconnue : ${info.menuItemId}`); }); // ───────────────────────────────────────────────────────────────────────────── @@ -167,7 +167,7 @@ async function getDefinition(selectedText) { definitions: allDefinitions, }); } catch (error) { - console.error("⌠Erreur lors de la recherche combinée des définitions :", error); + log("⌠Erreur lors de la recherche combinée des définitions :", error); } } @@ -201,7 +201,7 @@ async function searchInLexicons(selectedText) { selectedText, }); } catch (error) { - console.error("⌠Erreur lors de la recherche dans les lexiques :", error); + log("⌠Erreur lors de la recherche dans les lexiques :", error); browser.runtime.sendMessage({ action: "showLexiconResult", lexicons: [], diff --git a/src/context_menu/custom_context_menu.js b/src/context_menu/custom_context_menu.js index a4cb929040c15368a290d98693649dd43f60a348..dc136d9098c621e72e71fbd0713d93acf0156920 100644 --- a/src/context_menu/custom_context_menu.js +++ b/src/context_menu/custom_context_menu.js @@ -20,7 +20,7 @@ async function loadAuthToken() { authToken = result.accessToken || null; log("🔑 Token chargé :", authToken); } catch (error) { - console.error("⌠Erreur lors de la récupération du token :", error); + log("⌠Erreur lors de la récupération du token :", error); authToken = null; } } @@ -297,7 +297,7 @@ async function showPicker(event, selectedText) { try { definitions = await fetchLexiconDefinitions(selectedText); } catch (error) { - console.error("Erreur lors de la récupération des définitions :", error); + log("Erreur lors de la récupération des définitions :", error); } const existingLexiconIds = new Set(); if (Array.isArray(definitions)) { @@ -329,7 +329,7 @@ async function showPicker(event, selectedText) { lexicons: successMsg }); } catch (error) { - console.error("⌠Erreur lors de l'ajout du mot :", error); + log("⌠Erreur lors de l'ajout du mot :", error); const errorMsg = `⌠Erreur lors de l'ajout du mot : ${error.message}`; picker.innerHTML = `<p style="color: red;">${errorMsg}</p>`; setTimeout(() => picker.style.display = "none", 3000); @@ -351,7 +351,7 @@ async function showPicker(event, selectedText) { picker.style.top = event.pageY + "px"; picker.style.display = "flex"; } catch (error) { - console.error("⌠Erreur lors de la récupération des lexiques :", error); + log("⌠Erreur lors de la récupération des lexiques :", error); picker.innerHTML = "<p style='color:#333;'>Erreur lors du chargement des lexiques.</p>"; picker.style.display = "block"; } diff --git a/src/popup/popup.js b/src/popup/popup.js index 5f83c88347f278d6e3a59f91532b202917f35884..5a912ed8a889836e943038439ce8952df30bd1a9 100644 --- a/src/popup/popup.js +++ b/src/popup/popup.js @@ -16,7 +16,7 @@ async function updateConnectionButton() { const button = document.getElementById("auth-button"); if (!button) { - console.error("⌠Le bouton de connexion n'a pas été trouvé."); + log("⌠Le bouton de connexion n'a pas été trouvé."); return; } @@ -360,7 +360,7 @@ function showNotification(message) { notificationBox.classList.add("hidden"); }, { once: true }); } else { - console.error("⌠Impossible d'afficher la notification : élément manquant."); + log("⌠Impossible d'afficher la notification : élément manquant."); } } diff --git a/src/sidebar/sidebar.html b/src/sidebar/sidebar.html index 06f98b50bed31a02366fe242e4edfe96dd17037e..7345d94fcdf41aa9bb1f9e7f3cb025aef193fc0d 100644 --- a/src/sidebar/sidebar.html +++ b/src/sidebar/sidebar.html @@ -259,6 +259,8 @@ transition: filter 0.3s ease-in-out; } + /* Ajout de la classe active */ + .lexique-highlight-toggle.active .feutre-icon, .lexique-highlight-toggle[data-active="true"] .feutre-icon { filter: brightness(0) saturate(100%) invert(83%) sepia(89%) saturate(588%) hue-rotate(360deg); } @@ -293,6 +295,31 @@ transform: translateX(-50%) translateY(-5px); } + .lexicon-highlight { + position: relative; /* Pour positionner le conteneur de bandes en absolu */ + display: inline-block; /* Pour que le span prenne en compte les dimensions */ + padding-bottom: 4px; /* Laisser de l'espace pour les bandes */ + border-bottom: 1px dashed #666; /* Vous pouvez conserver votre bordure si besoin */ + transition: background-color 0.3s; + background-color: rgba(255, 255, 0, 0.15); + + } + + .color-bands { + position: absolute; + left: 0; + right: 0; + bottom: 0; + height: 3px; /* Ajustez la hauteur des bandes */ + display: flex; /* Pour répartir équitablement les bandes */ + } + + .color-bands div { + flex: 1; /* Chaque bande occupe une part égale */ + height: 100%; + } + + .lexicon-section { margin-bottom: 10px; } diff --git a/src/sidebar/sidebar.js b/src/sidebar/sidebar.js index 0b40b7881febb0f0e60c8e0ed73f1435d2eb7cd7..b56fc56332de1dfb456a02c895b1e38be55fc017 100644 --- a/src/sidebar/sidebar.js +++ b/src/sidebar/sidebar.js @@ -116,8 +116,8 @@ async function refreshSidebarState() { } /** - * Bascule l’affichage d’un bloc. - * @param {string} blockId - L’ID du conteneur à basculer. + * Bascule l'affichage d'un bloc. + * @param {string} blockId - L'ID du conteneur à basculer. * @param {HTMLElement} btn - Le bouton qui déclenche la bascule, pour mettre à jour son texte. */ function toggleBlock(blockId, btn) { @@ -134,8 +134,8 @@ function toggleBlock(blockId, btn) { } /** - * Ouvre un bloc s’il est fermé et met à jour le bouton de bascule. - * @param {string} blockId - L’ID du conteneur à ouvrir. + * Ouvre un bloc s'il est fermé et met à jour le bouton de bascule. + * @param {string} blockId - L'ID du conteneur à ouvrir. * @param {HTMLElement} [btn] - (Optionnel) Le bouton de bascule à mettre à jour. */ function openBlock(blockId, btn) { @@ -217,7 +217,7 @@ async function fetchLexicons() { lexiconMap.set(lex.id, lexiconName); }); - displayLexiconsWithCheckbox(lexicons.map((lex) => ({ + await displayLexiconsWithCheckbox(lexicons.map((lex) => ({ lexiconName: lex.category === "User" ? DEBUG @@ -229,8 +229,11 @@ async function fetchLexicons() { lexiconId: lex.id, active: lex.active || false, }))); + + // Restaurer l'état des boutons après l'affichage + await restoreHighlightingState(); } catch (error) { - console.error("⌠Erreur lors du chargement des lexiques :", error.message); + log("⌠Erreur lors du chargement des lexiques :", error.message); const lexiquesContainer = document.getElementById("lexiques"); if (lexiquesContainer) { lexiquesContainer.textContent = error.message || "Erreur lors du chargement des lexiques."; @@ -286,6 +289,9 @@ async function displayLexiconsWithCheckbox(lexicons) { // Conteneur pour le bouton de surlignage (avec tooltip) const highlightButton = document.createElement("button"); highlightButton.className = "tooltip-container lexique-highlight-toggle"; + if (active) { + highlightButton.classList.add('active'); + } highlightButton.dataset.lexiconId = lexiconId; highlightButton.dataset.active = active ? "true" : "false"; const feutreIcon = document.createElement("img"); @@ -296,19 +302,21 @@ async function displayLexiconsWithCheckbox(lexicons) { highlightTooltip.className = "tooltip"; highlightTooltip.textContent = "Activer/Désactiver le surlignage des mots du lexique"; - // Gestion du clic pour activer/désactiver le surlignage + // Mise à jour du gestionnaire d'événements highlightButton.addEventListener("click", async () => { let currentState = highlightButton.dataset.active === "true"; let newState = !currentState; try { - await browser.runtime.sendMessage({ - action: "toggleLexiconHighlight", - lexiconId, - isActive: newState, - }); + await toggleLexiconHighlight(lexiconId, newState); highlightButton.dataset.active = newState ? "true" : "false"; + highlightButton.classList.toggle("active", newState); + + // Sauvegarder l'état dans le storage local + const activeLexicons = Array.from(document.querySelectorAll('.lexique-highlight-toggle[data-active="true"]')) + .map(btn => parseInt(btn.dataset.lexiconId)); + await browser.storage.local.set({ activeLexicons }); } catch (error) { - console.error("Erreur lors du toggle de surlignage pour le lexique", lexiconId, ":", error); + log("Erreur lors du toggle de surlignage pour le lexique", lexiconId, ":", error); } }); @@ -346,6 +354,24 @@ async function displayLexiconsWithCheckbox(lexicons) { }, 100); } +// Ajouter une fonction pour restaurer l'état des boutons au chargement +async function restoreHighlightingState() { + try { + const { activeLexicons } = await browser.storage.local.get("activeLexicons"); + if (activeLexicons && Array.isArray(activeLexicons)) { + const buttons = document.querySelectorAll('.lexique-highlight-toggle'); + buttons.forEach(button => { + const lexiconId = parseInt(button.dataset.lexiconId); + const isActive = activeLexicons.includes(lexiconId); + button.dataset.active = isActive ? "true" : "false"; + button.classList.toggle("active", isActive); + }); + } + } catch (error) { + log("Erreur lors de la restauration de l'état des boutons:", error); + } +} + function initModal() { log("initModal appelé"); const modalOverlay = document.getElementById("modalOverlay"); @@ -355,7 +381,7 @@ function initModal() { log("closeModalBtn =", closeModalBtn); if (!modalOverlay || !modalFullText || !closeModalBtn) { - console.error("Les éléments modaux ne sont pas trouvés !"); + log("Les éléments modaux ne sont pas trouvés !"); return; } @@ -413,12 +439,12 @@ async function handleAddWordClick() { return; } - // 3) Vérifier si le mot existe déjà dans l’un des lexiques sélectionnés + // 3) Vérifier si le mot existe déjà dans l'un des lexiques sélectionnés let definitions = []; try { definitions = await fetchLexiconDefinitions(selectedWord); } catch (error) { - console.error("Erreur lors de la récupération des définitions pour vérification :", error); + log("Erreur lors de la récupération des définitions pour vérification :", error); } const existingLexiconIds = new Set(); @@ -445,7 +471,7 @@ async function handleAddWordClick() { return; } - // 6) Envoi d’une seule requête pour tous les lexiques restants + // 6) Envoi d'une seule requête pour tous les lexiques restants try { log(`📡 Envoi de l'ajout du mot "${selectedWord}" dans les lexiques :`, lexiconsToAdd); const result = await window.AddWord(authToken, selectedWord, lexiconsToAdd, false); @@ -464,9 +490,9 @@ async function handleAddWordClick() { } } catch (error) { - console.error("Erreur lors de l’ajout du mot :", error); + log("Erreur lors de l'ajout du mot :", error); if (lexiconResultElement) { - lexiconResultElement.textContent = "Erreur lors de l’ajout : " + error.message; + lexiconResultElement.textContent = "Erreur lors de l'ajout : " + error.message; } } } @@ -476,95 +502,133 @@ async function handleAddWordClick() { // ───────────────────────────────────────────────────────────────────────────── browser.runtime.onMessage.addListener(async (message) => { log("📩 Message reçu dans sidebar.js :", message); - switch (message.action) { - case "refreshUI": - log("🔄 Demande de rafraîchissement de la barre latérale."); - await refreshSidebarState(); - break; - - case "mot_selectionne": - if (message.selectedText) { - log("ðŸ–‹ï¸ Mot sélectionné :", message.selectedText); - const selectedWordElement = document.getElementById("motSelectionne"); - if (selectedWordElement) { - selectedWordElement.textContent = message.selectedText; - } else { - console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); - } - const lexiconResultElement = document.getElementById("lexiconResult"); - if (lexiconResultElement) { - lexiconResultElement.innerHTML = ""; + + if (message.action) + switch (message.action) { + case "refreshUI": + log("🔄 Demande de rafraîchissement de la barre latérale."); + await refreshSidebarState(); + break; + + case "mot_selectionne": + if (message.selectedText) { + log("ðŸ–‹ï¸ Mot sélectionné :", message.selectedText); + const selectedWordElement = document.getElementById("motSelectionne"); + if (selectedWordElement) { + selectedWordElement.textContent = message.selectedText; + } else { + console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); + } + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = ""; + } + openBlock("etatContent"); } - openBlock("etatContent"); - } - break; - - case "getDefinition": - if (message.selectedText) { - log("📖 Recherche des définitions pour :", message.selectedText); - openBlock("definitionContent"); - await window.showDefinitions(message.selectedText); - } - break; + break; - case "showDefinitions": - if (Array.isArray(message.definitions)) { - window.displayDefinitions(message.definitions); - } - break; + case "getDefinition": + if (message.selectedText) { + log("📖 Recherche des définitions pour :", message.selectedText); + openBlock("definitionContent"); + await window.showDefinitions(message.selectedText); + } + break; - case "fetchWiktionaryDefinitionResponse": - if (message.selectedText) { - log(`📖 Réception de la définition du Wiktionnaire pour '${message.selectedText}'`); - window.displayDefinitions(message.definitions); - } - break; + case "showDefinitions": + if (Array.isArray(message.definitions)) { + window.displayDefinitions(message.definitions); + } + break; - case "showLexiconResult": - log("📚 Résultat des lexiques reçus :", message.lexicons); - window.displayLexiconResults(message.lexicons); - break; - - case "addWordResult": - const lexiconResultElement = document.getElementById("lexiconResult"); - if (lexiconResultElement) { - lexiconResultElement.innerHTML = message.lexicons; - } - break; + case "fetchWiktionaryDefinitionResponse": + if (message.selectedText) { + log(`📖 Réception de la définition du Wiktionnaire pour '${message.selectedText}'`); + window.displayDefinitions(message.definitions); + } + break; - case "addToLexicon": - handleAddWordClick(); - break; - - case "openLexiconBlock": - openBlock("menuContent"); - break; + case "showLexiconResult": + log("📚 Résultat des lexiques reçus :", message.lexicons); + window.displayLexiconResults(message.lexicons); + break; - case "toggleAuth": - break; + case "addWordResult": + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = message.lexicons; + } + break; - case "authStatusChanged": - break; + case "addToLexicon": + handleAddWordClick(); + break; + + case "openLexiconBlock": + openBlock("menuContent"); + break; + + case "toggleAuth": + break; + + case "authStatusChanged": + break; + + case "updateUI": + await refreshSidebarState(); + break; + + case "pyodide-simplemma-ready": + return; + + case "saveToken": + authToken = message.token; + break; + + case "closeSidebarBlocks": + closeBlock("menuContent"); + closeBlock("etatContent"); + closeBlock("definitionContent") + break; + } - case "updateUI": - await refreshSidebarState(); - break; + if (message.command) { + switch (message.command) { + case "activate-highlighting": + const highlightButton = document.querySelector(`button[data-lexicon-id="${message.lexiconId}"]`); + if (highlightButton) { + highlightButton.dataset.active = "true"; + highlightButton.classList.add("active"); + } + break; + + case "deactivate-highlighting": + const highlightButtonOff = document.querySelector(`button[data-lexicon-id="${message.lexiconId}"]`); + if (highlightButtonOff) { + highlightButtonOff.dataset.active = "false"; + highlightButtonOff.classList.remove("active"); + } + break; - case "pyodide-simplemma-ready": - return; + case "register-stats-script": + break; - case "saveToken": - authToken = message.token; - break; + case "register-highlighting-script": + log("✅ Script de surlignage enregistré"); + browser.runtime.sendMessage({ + command: "updateAuthToken", + token: authToken + }); + break; - case "closeSidebarBlocks": - closeBlock("menuContent"); - closeBlock("etatContent"); - closeBlock("definitionContent") - break; + case "updateAuthToken": + authToken = message.token; + window.authToken = message.token; + break; - default: - console.warn("âš ï¸ Action inconnue reçue :", message.action); + default: + console.warn("âš ï¸ Action inconnue reçue :", message.command); + } } }); @@ -600,7 +664,7 @@ document.addEventListener("DOMContentLoaded", async () => { chercherDefButton.style.visibility = "visible"; chercherDefButton.disabled = false; } else { - console.error("⌠ERREUR : Bouton #chercherDef introuvable."); + log("⌠ERREUR : Bouton #chercherDef introuvable."); } // Écouteur pour la case à cocher "toggle-definitions" @@ -677,3 +741,24 @@ document.addEventListener("DOMContentLoaded", async () => { } }); }); + +async function toggleLexiconHighlight(lexiconId, isActive) { + try { + const button = document.querySelector(`button[data-lexicon-id="${lexiconId}"]`); + if (button) { + button.dataset.active = isActive.toString(); + button.classList.toggle('active', isActive); + } + + await browser.tabs.query({ active: true, currentWindow: true }).then((tabs) => { + browser.tabs.sendMessage(tabs[0].id, { + command: isActive ? "activate-highlighting" : "deactivate-highlighting", + lexiconId: lexiconId + }); + }); + + log(`✅ Surlignage ${isActive ? 'activé' : 'désactivé'} pour le lexique ${lexiconId}`); + } catch (error) { + log(`⌠Erreur lors du toggle du surlignage pour le lexique ${lexiconId}:`, error); + } +} diff --git a/src/utils/api.js b/src/utils/api.js index 73d7fd0a1a8533bd75e4c0ad1e744974e9860b85..19d1a12c544fea86a8ac551e9b8d77bcf87a36e3 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -15,7 +15,7 @@ document.addEventListener("mouseup", () => { }); // ───────────────────────────────────────────────────────────────────────────── -// â–Œ Fonction utilitaire pour appeler l’API +// â–Œ Fonction utilitaire pour appeler l'API // ───────────────────────────────────────────────────────────────────────────── /** * Effectue une requête API (GET, POST, etc.) avec ou sans body JSON @@ -44,7 +44,7 @@ async function callApi(url, authToken = null, method = 'GET', data = null) { } return await response.json(); } catch (error) { - console.error(`🚨 Erreur lors de l'appel API [${url}]:`, error); + log(`🚨 Erreur lors de l'appel API [${url}]:`, error); throw error; } } @@ -54,7 +54,7 @@ async function callApi(url, authToken = null, method = 'GET', data = null) { // ───────────────────────────────────────────────────────────────────────────── /** - * Récupère les lexiques de l’utilisateur, + * Récupère les lexiques de l'utilisateur, * en langue par défaut "fr". * * @param {string} authToken - Le token d'authentification. @@ -67,7 +67,7 @@ async function getLexicons(authToken) { } /** - * Récupère tous les lexiques (catégories) de l’utilisateur + * Récupère tous les lexiques (catégories) de l'utilisateur */ async function getAllCategoriesLexicons(authToken) { const categories = ["User", "Group", "Zero", "New words"]; @@ -91,7 +91,7 @@ async function getAllCategoriesLexicons(authToken) { log("✅ Lexiques récupérés (toutes catégories confondues) :", combined); return combined; } catch (error) { - console.error("⌠Erreur lors de la récupération multi-catégories :", error); + log("⌠Erreur lors de la récupération multi-catégories :", error); return []; } } @@ -118,7 +118,7 @@ async function getAllLexiconWords(authToken) { const lexicons = await getLexicons(authToken); if (!Array.isArray(lexicons) || lexicons.length === 0) { - console.warn("âš ï¸ Aucun lexique retourné par l’API pour ces paramètres."); + console.warn("âš ï¸ Aucun lexique retourné par l'API pour ces paramètres."); return {}; } @@ -127,6 +127,11 @@ async function getAllLexiconWords(authToken) { for (const lexicon of lexicons) { const entries = await getLexiconEntries(authToken, lexicon.id); + // Vérification que entries est bien un tableau + if (!Array.isArray(entries)) { + console.warn(`âš ï¸ Format invalide pour les entrées du lexique ${lexicon.id}:`, entries); + continue; + } const allGraphies = entries.map(entry => entry.graphy); // Création d'un libellé unique pour le lexique @@ -141,7 +146,7 @@ async function getAllLexiconWords(authToken) { log("✅ Toutes les graphies récupérées :", allGraphiesByLexicon); return allGraphiesByLexicon; } catch (error) { - console.error("⌠Erreur lors de la récupération des graphies des lexiques :", error); + log("⌠Erreur lors de la récupération des graphies des lexiques :", error); return {}; } } @@ -169,7 +174,7 @@ async function getWiktionaryDefinition(word) { log(`📖 Définition trouvée pour '${word}':`, definition); return [definition]; } catch (error) { - console.error("Erreur lors de la récupération du Wiktionnaire :", error); + log("Erreur lors de la récupération du Wiktionnaire :", error); return ["Erreur : " + error.message]; } } @@ -187,13 +192,13 @@ async function getWiktionaryDefinition(word) { */ async function AddWord(authToken, selectedWord, lexiconIds, force = false) { if (!authToken) { - throw new Error("Aucun token d’authentification fourni."); + throw new Error("Aucun token d'authentification fourni."); } if (!selectedWord) { - throw new Error("Aucun mot n’a été spécifié pour l’ajout."); + throw new Error("Aucun mot n'a été spécifié pour l'ajout."); } if (!Array.isArray(lexiconIds) || lexiconIds.length === 0) { - throw new Error("Aucun lexique sélectionné pour l’ajout."); + throw new Error("Aucun lexique sélectionné pour l'ajout."); } const url = "https://babalex.lezinter.net/api/entry/create"; diff --git a/src/utils/definitions.js b/src/utils/definitions.js index 2010727fc812b69758bbc32022c36ad38d749f0d..b59e496c0fdf72d6b64bdfe288c1f450c942b1c0 100644 --- a/src/utils/definitions.js +++ b/src/utils/definitions.js @@ -116,7 +116,7 @@ async function fetchLexiconDefinitions(word) { log("Résultat final filtré :", allDefinitions); return allDefinitions; } catch (error) { - console.error("⌠Erreur générale lors de la récupération des définitions :", error); + log("⌠Erreur générale lors de la récupération des définitions :", error); return []; } } @@ -155,7 +155,7 @@ async function fetchLexiconDefinitions(word) { // } // ]; // } catch (error) { -// console.error("⌠Erreur Wiktionnaire :", error); +// log("⌠Erreur Wiktionnaire :", error); // return [ // { // source: "Wiktionnaire", @@ -189,7 +189,7 @@ async function wikiApiResponse(word) { 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); + log('Erreur lors de la récupération de la définition depuis le Wiktionnaire :', error); throw error; } } @@ -262,7 +262,7 @@ async function fetchWiktionaryDefinition(word) { } ]; } catch (error) { - console.error("Erreur lors de la récupération de la définition :", error); + log("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." }]; } } @@ -387,7 +387,7 @@ function openDefinitionPopup(fullText) { const modalOverlay = document.getElementById("modalOverlay"); const modalFullText = document.getElementById("modalFullText"); if (!modalOverlay || !modalFullText) { - console.error("Modal elements not found!"); + log("Modal elements not found!"); return; } modalFullText.innerHTML = "<p>" + fullText.replace(/\n/g, "<br>") + "</p>"; @@ -467,7 +467,7 @@ async function showDefinitions(word) { return allDefinitions; } catch (error) { - console.error("⌠[showDefinitions] Erreur : ", error); + log("⌠[showDefinitions] Erreur : ", error); if (noDefinitionsContainer) { noDefinitionsContainer.textContent = @@ -487,7 +487,7 @@ async function fetchDefinition(word) { const noDefinitionsContainer = document.getElementById("noDefinitionsContainer"); if (!noDefinitionsContainer) { - console.error("⌠Élément #noDefinitionsContainer introuvable."); + log("⌠Élément #noDefinitionsContainer introuvable."); return; } @@ -503,7 +503,7 @@ async function fetchDefinition(word) { noDefinitionsContainer.style.display = "none"; } catch (error) { - console.error("⌠Erreur lors de la récupération de la définition :", error); + log("⌠Erreur lors de la récupération de la définition :", error); noDefinitionsContainer.style.display = "block"; } } diff --git a/src/utils/highlighting.js b/src/utils/highlighting.js new file mode 100644 index 0000000000000000000000000000000000000000..4a673e9a1849f7f040d23b055fd09f39948b448e --- /dev/null +++ b/src/utils/highlighting.js @@ -0,0 +1,526 @@ +// Variables globales +window.activeLexiconIds = window.activeLexiconIds || new Set(); + +// Logs immédiats +log("🔵 DÉBUT DU FICHIER highlighting.js"); +try { + log("✅ highlighting.js chargé"); +} catch (e) { + log("⌠Erreur avec la fonction log:", e); +} + +// Vérification de l'environnement +log("🔠Vérification de l'environnement:", { + hasBrowser: typeof browser !== 'undefined', + windowLocation: window.location.href +}); + +// Gestion globale des erreurs +window.onerror = function(msg, url, line, col, error) { + log("🔴 Erreur globale:", { message: msg, url: url, line: line, col: col, error: error }); + return false; +}; + +// Fonction d'initialisation du token depuis le stockage local +async function initAuthToken() { + try { + const { accessToken } = await browser.storage.local.get("accessToken"); + if (accessToken) { + window.authToken = accessToken; + authToken = accessToken; + log("🔑 Token récupéré depuis le stockage local"); + return true; + } else { + log("âš ï¸ Aucun token trouvé dans le stockage local"); + return false; + } + } catch (error) { + log("⌠Erreur lors de la récupération du token:", error); + return false; + } +} +initAuthToken(); + +// Écoute de mise à jour du token +browser.runtime.onMessage.addListener((message) => { + if (message.command === "updateAuthToken" && message.token) { + window.authToken = message.token; + authToken = message.token; + log("🔑 Token mis à jour via message:", !!message.token); + } +}); + +(function () { + try { + if (window.hasRun) { + log("âš ï¸ highlighting.js déjà chargé"); + return; + } + window.hasRun = true; + + // Variables internes + let lexiconWordsCache = new Map(); + let highlightingActive = false; + window.highlightingActive = false; + let observer = null; + + // Gestion de la visibilité/page show pour réappliquer le surlignage + document.addEventListener('visibilitychange', async () => { + if (document.visibilityState === 'visible' && window.highlightingActive && activeLexiconIds.size > 0) { + log("📄 Page redevenue visible, réinitialisation du surlignage"); + removeAllHighlights(); + await updateLexiconCache(); + highlightVisibleContent(); + attachMutationObserver(); + } + }); + window.addEventListener('pageshow', async () => { + if (window.highlightingActive && activeLexiconIds.size > 0) { + log("📄 Page affichée (pageshow), réinitialisation du surlignage"); + removeAllHighlights(); + await updateLexiconCache(); + highlightVisibleContent(); + attachMutationObserver(); + } + }); + + // Gestion des messages reçus du background + browser.runtime.onMessage.addListener((message, sender, sendResponse) => { + log("📨 Message reçu:", message, "Context:", { + highlightingActive, + activeLexiconIds: Array.from(activeLexiconIds), + hasAuthToken: !!window.authToken, + hasGetAllLexiconWords: !!window.getAllLexiconWords + }); + + if (message.command === "activate-highlighting") { + log(`🎯 Activation du surlignage pour le lexique ${message.lexiconId}`); + startHighlighting(message.lexiconId) + .then(() => { + window.highlightingActive = true; + sendResponse(true); + }) + .catch(error => { + log("⌠Erreur lors de l'activation:", error); + sendResponse(false); + }); + return true; + } + + if (message.command === "deactivate-highlighting") { + log(`🚫 Désactivation du surlignage pour le lexique ${message.lexiconId}`); + stopHighlighting(message.lexiconId) + .then(() => { + if (activeLexiconIds.size === 0) { + window.highlightingActive = false; + } + sendResponse(true); + }) + .catch(error => { + log("⌠Erreur lors de la désactivation:", error); + sendResponse(false); + }); + return true; + } + + return false; + }); + + log("📡 Enregistrement du script auprès du background"); + browser.runtime.sendMessage({ command: "register-highlighting-script" }); + + log("🚀 Initialisation de highlighting.js"); + + // Fonction asynchrone pour mettre à jour le style d'un élément surligné en fonction des lexiques qui le concernent + async function updateHighlightStyle(span, lexIds) { + if (!lexIds || lexIds.length === 0) { + span.style.backgroundColor = "rgba(255, 255, 0, 0.3)"; + span.style.backgroundImage = ""; + return; + } + if (lexIds.length === 1) { + const hexColor = await getColorForLexicon(lexIds[0]); + const rgbaColor = hexToRgba(hexColor, 0.3); + span.style.backgroundColor = rgbaColor; + span.style.backgroundImage = ""; + } else { + const hexColors = await Promise.all(lexIds.map(id => getColorForLexicon(id))); + const colors = hexColors.map(hex => hexToRgba(hex, 0.3)); + const n = colors.length; + let stops = []; + for (let i = 0; i < n; i++) { + const start = ((100 * i) / n).toFixed(2) + '%'; + const end = ((100 * (i + 1)) / n).toFixed(2) + '%'; + stops.push(`${colors[i]} ${start}, ${colors[i]} ${end}`); + } + const gradient = `linear-gradient(90deg, ${stops.join(', ')})`; + span.style.backgroundImage = gradient; + + } + } + + // Vérifier si un mot appartient à un lexique (à l'aide de la cache) + function wordIsInLexicon(lexiconId, word) { + const wordsSet = lexiconWordsCache.get(String(lexiconId)); + return wordsSet ? wordsSet.has(word.toLowerCase()) : false; + } + + // Mise à jour incrémentale des éléments déjà surlignés lorsqu'un nouveau lexique est ajouté + async function updateHighlightsForNewLexicon(newLexiconId) { + const spans = document.querySelectorAll('.lexicon-highlight'); + for (let span of spans) { + const word = span.textContent; + if (wordIsInLexicon(newLexiconId, word)) { + let lexIds = []; + try { + lexIds = JSON.parse(span.getAttribute('data-lexicons')); + } catch (e) { + lexIds = []; + } + if (!lexIds.includes(newLexiconId)) { + lexIds.push(newLexiconId); + span.setAttribute('data-lexicons', JSON.stringify(lexIds)); + await updateHighlightStyle(span, lexIds); + } + } + } + } + + // Fonction startHighlighting modifiée pour intégrer la mise à jour incrémentale + async function startHighlighting(lexiconId) { + try { + await initAuthToken(); + if (!window.authToken) { + throw new Error("⌠Pas de token d'authentification disponible"); + } + if (lexiconId) { + if (!activeLexiconIds.has(lexiconId)) { + activeLexiconIds.add(lexiconId); + const activeLexicons = Array.from(activeLexiconIds); + await browser.storage.local.set({ activeLexicons }); + log("📊 Lexiques actifs sauvegardés:", activeLexicons); + // Mise à jour de la cache pour inclure le nouveau lexique + await updateLexiconCache(); + // Mise à jour immédiate des éléments surlignés pour intégrer le nouveau lexique + await updateHighlightsForNewLexicon(lexiconId); + } + } + window.highlightingActive = true; + highlightingActive = true; + highlightVisibleContent(); + attachMutationObserver(); + return true; + } catch (error) { + log("⌠Erreur lors du démarrage du surlignage:", error); + window.highlightingActive = false; + highlightingActive = false; + throw error; + } + } + + async function stopHighlighting(lexiconId) { + try { + if (lexiconId) { + activeLexiconIds.delete(lexiconId); + const activeLexicons = Array.from(activeLexiconIds); + await browser.storage.local.set({ activeLexicons }); + if (activeLexiconIds.size === 0) { + window.highlightingActive = false; + highlightingActive = false; + removeAllHighlights(); + detachMutationObserver(); + } else { + removeAllHighlights(); + await updateLexiconCache(); + highlightVisibleContent(); + } + } else { + window.highlightingActive = false; + highlightingActive = false; + activeLexiconIds.clear(); + removeAllHighlights(); + detachMutationObserver(); + } + return true; + } catch (error) { + log("⌠Erreur lors de l'arrêt du surlignage:", error); + throw error; + } + } + + // Mise à jour du cache des lexiques + async function updateLexiconCache() { + log("📥 updateLexiconCache - Début avec context:", { + authToken: !!window.authToken, + getAllLexiconWords: !!window.getAllLexiconWords, + activeLexiconIds: Array.from(activeLexiconIds) + }); + let allWords; + try { + if (!window.authToken) { + throw new Error("Pas de token d'authentification"); + } + if (typeof window.getAllLexiconWords !== 'function') { + log("âš ï¸ getAllLexiconWords n'est pas une fonction"); + log("Type de getAllLexiconWords:", typeof window.getAllLexiconWords); + log("Contenu de window.getAllLexiconWords:", window.getAllLexiconWords); + throw new Error("getAllLexiconWords n'est pas disponible"); + } + log("📥 Appel de getAllLexiconWords..."); + allWords = await window.getAllLexiconWords(window.authToken); + log("📠Réponse de getAllLexiconWords:", allWords); + if (!allWords || typeof allWords !== 'object') { + throw new Error(`Format de données invalide: ${JSON.stringify(allWords)}`); + } + lexiconWordsCache.clear(); + if (Object.keys(allWords).length === 0) { + log("âš ï¸ Aucun lexique reçu de getAllLexiconWords"); + return false; + } + for (const [lexiconName, words] of Object.entries(allWords)) { + if (!Array.isArray(words)) { + console.warn(`âš ï¸ Format invalide pour le lexique ${lexiconName}:`, words); + continue; + } + const lexiconId = lexiconName.match(/\[(\d+)\]$/)?.[1]; + if (!lexiconId) { + console.warn(`âš ï¸ Impossible d'extraire l'ID du lexique depuis: ${lexiconName}`); + continue; + } + log(`📎 Traitement du lexique ${lexiconName} (ID: ${lexiconId})`); + if (activeLexiconIds.has(Number(lexiconId))) { + lexiconWordsCache.set(lexiconId, new Set(words)); + log(`📖 Lexique ${lexiconId} chargé avec ${words.length} mots`); + } + } + log("✅ Cache des lexiques mis à jour:", + Object.fromEntries([...lexiconWordsCache.entries()].map(([id, words]) => [id, [...words]]))); + return true; + } catch (error) { + log("⌠Erreur dans updateLexiconCache:", { + message: error.message, + stack: error.stack, + authTokenExists: !!window.authToken, + getAllLexiconWordsType: typeof window.getAllLexiconWords, + response: allWords + }); + throw error; + } + } + + // Surlignage du contenu visible + function highlightVisibleContent() { + if (!highlightingActive) { + log("â¸ï¸ Surlignage inactif, sortie"); + return; + } + log("🔠Début du surlignage du contenu visible"); + const BATCH_SIZE = 100; + const BATCH_DELAY = 10; // ms + const textNodes = []; + const walker = document.createTreeWalker( + document.body, + NodeFilter.SHOW_TEXT, + { + acceptNode: (node) => { + const parent = node.parentElement; + if (!parent || parent.closest('script, style, .lexicon-highlight') || !node.textContent.trim() || getComputedStyle(parent).display === 'none') { + return NodeFilter.FILTER_REJECT; + } + return NodeFilter.FILTER_ACCEPT; + } + } + ); + let node; + while (node = walker.nextNode()) { + textNodes.push(node); + } + log(`🔤 ${textNodes.length} nÅ“uds de texte trouvés à traiter`); + const processNextBatch = (startIndex) => { + if (!highlightingActive || startIndex >= textNodes.length) { + return; + } + const endIndex = Math.min(startIndex + BATCH_SIZE, textNodes.length); + const batch = textNodes.slice(startIndex, endIndex); + batch.forEach(processTextNode); + if (endIndex < textNodes.length) { + setTimeout(() => processNextBatch(endIndex), BATCH_DELAY); + } + }; + processNextBatch(0); + } + + // Traitement d'un nÅ“ud de texte + function processTextNode(textNode) { + if (activeLexiconIds.size === 0) { + log("âš ï¸ Aucun lexique actif, sortie du processTextNode"); + return; + } + const text = textNode.textContent; + log(`🔠Traitement du texte: "${text.substring(0, 50)}..."`); + let lastIndex = 0; + let fragments = []; + const allWords = new Set(); + const matchedLexiconIdsMap = new Map(); + for (const [lexiconId, words] of lexiconWordsCache.entries()) { + const numericId = parseInt(lexiconId); + log(`🔄 Vérification du lexique ${lexiconId} (ID: ${numericId})`); + if (activeLexiconIds.has(numericId)) { + log(`✅ Lexique ${lexiconId} actif, ajout de ${words.size} mots`); + words.forEach(word => allWords.add(word)); + words.forEach(word => { + const lowerCaseWord = word.toLowerCase(); + if (!matchedLexiconIdsMap.has(lowerCaseWord)) { // Correction ici ! + matchedLexiconIdsMap.set(lowerCaseWord, []); + } + matchedLexiconIdsMap.get(lowerCaseWord).push(lexiconId); + }); + } + } + log(`🔤 Nombre total de mots à rechercher: ${allWords.size}`); + if (allWords.size === 0) { + log("âš ï¸ Aucun mot à rechercher dans les lexiques actifs"); + return; + } + const wordsPattern = Array.from(allWords) + .sort((a, b) => b.length - a.length) + .map(escapeRegExp) + .join("|"); + if (!wordsPattern) { + log("âš ï¸ Aucun mot à rechercher, sortie"); + return; + } + const regex = new RegExp(`\\b(${wordsPattern})\\b`, "gi"); + let match; + let matchCount = 0; + while ((match = regex.exec(text)) !== null) { + matchCount++; + if (match.index > lastIndex) { + fragments.push(document.createTextNode(text.slice(lastIndex, match.index))); + } + const span = document.createElement("span"); + span.textContent = match[0]; + span.className = "lexicon-highlight"; + span.style.display = "inline-block"; + + const matchedLexiconIds = matchedLexiconIdsMap.get(match[0].toLowerCase()) || []; + span.setAttribute('data-lexicons', JSON.stringify(matchedLexiconIds)); + + if (matchedLexiconIds.length === 0) { + span.style.backgroundColor = "rgba(255, 255, 0, 0.3)"; + } else { + updateHighlightStyle(span, matchedLexiconIds); + } + fragments.push(span); + lastIndex = regex.lastIndex; + } + if (matchCount > 0) { + log(`✨ ${matchCount} correspondances trouvées dans le nÅ“ud`); + } + if (lastIndex < text.length) { + fragments.push(document.createTextNode(text.slice(lastIndex))); + } + if (fragments.length > 0) { + const parent = textNode.parentNode; + fragments.forEach(fragment => parent.insertBefore(fragment, textNode)); + parent.removeChild(textNode); + } + } + + // Suppression de tous les surlignages + function removeAllHighlights() { + log("🧹 Suppression de tous les surlignages"); + const highlights = document.querySelectorAll('.lexicon-highlight'); + log(`📊 ${highlights.length} surlignages à supprimer`); + highlights.forEach(highlight => { + const text = highlight.textContent; + const textNode = document.createTextNode(text); + highlight.parentNode.replaceChild(textNode, highlight); + }); + } + + // Gestion des mutations DOM + function attachMutationObserver() { + log("👀 Attachement de l'observateur de mutations"); + let debounceTimer = null; + const DEBOUNCE_DELAY = 250; // ms + observer = new MutationObserver((mutations) => { + if (debounceTimer) { + clearTimeout(debounceTimer); + } + debounceTimer = setTimeout(() => { + log(`🔄 Traitement groupé de ${mutations.length} mutations DOM`); + let shouldHighlight = false; + for (const mutation of mutations) { + if (mutation.type === 'childList') { + for (const node of mutation.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE && + !node.closest('.lexicon-highlight') && + node.textContent.trim()) { + shouldHighlight = true; + break; + } + } + } + if (shouldHighlight) break; + } + if (shouldHighlight) { + highlightVisibleContent(); + } + }, DEBOUNCE_DELAY); + }); + observer.observe(document.body, { + childList: true, + subtree: true + }); + } + + function detachMutationObserver() { + if (observer) { + log("👋 Détachement de l'observateur de mutations"); + observer.disconnect(); + observer = null; + } + } + + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + function getLexiconIdFromName(lexiconName) { + const match = lexiconName.match(/\[(\d+)\]$/); + const id = match ? parseInt(match[1]) : null; + log(`ðŸ·ï¸ Extraction de l'ID depuis '${lexiconName}': ${id}`); + return id; + } + + log("🔠Vérification des dépendances au chargement:", { + authToken: !!window.authToken, + getAllLexiconWords: typeof window.getAllLexiconWords + }); + + async function checkAndRestoreHighlightingState() { + try { + const { activeLexicons } = await browser.storage.local.get("activeLexicons"); + if (!activeLexicons || !Array.isArray(activeLexicons) || activeLexicons.length === 0) { + window.highlightingActive = false; + highlightingActive = false; + return; + } + log("🔄 État des lexiques trouvé:", activeLexicons); + for (const lexiconId of activeLexicons) { + await startHighlighting(lexiconId); + } + } catch (error) { + log("⌠Erreur lors de la restauration de l'état:", error); + window.highlightingActive = false; + highlightingActive = false; + } + } + + checkAndRestoreHighlightingState(); + + } catch (error) { + log("🔴 Erreur critique dans l'IIFE:", error); + } +})(); diff --git a/src/utils/logger.js b/src/utils/logger.js index 1455361feaef60c3e4dc37181c95465cf3f13dbc..9828f273969a19ea77e758e8a30ca17ae84d36ba 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -5,7 +5,7 @@ (function () { if (typeof window !== 'undefined') { if (typeof window.DEBUG === 'undefined') { - window.DEBUG = true; // ou false en production + window.DEBUG = false; // true en debug } if (!window.log) { function log(...args) { @@ -17,7 +17,7 @@ } } else if (typeof self !== 'undefined') { if (typeof self.DEBUG === 'undefined') { - self.DEBUG = true; + self.DEBUG = false; // true en debug } if (!self.log) { function log(...args) { diff --git a/src/workers/pyodide_worker.js b/src/workers/pyodide_worker.js index 45f9e7745dda83884b2d7a66b539e34e182bb15b..3e8458ee8f68dc9be5e0bc751f4d9c30c645974d 100644 --- a/src/workers/pyodide_worker.js +++ b/src/workers/pyodide_worker.js @@ -37,7 +37,7 @@ self.onmessage = async (event) => { await pyodide.loadPackage("micropip"); pyodideLoaded = true; } catch (err) { - console.error("[Worker] Erreur lors de l'import de pyodide.js :", err); + log("[Worker] Erreur lors de l'import de pyodide.js :", err); self.postMessage({ type: "pyodide-simplemma", status: "error", message: err.toString() }); return; } @@ -77,7 +77,7 @@ await main() // 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); + log("[Worker] Erreur lors du chargement de Pyodide ou Simplemma :", error); self.postMessage({ type: "pyodide-simplemma", status: "error", message: error.toString() }); } } @@ -141,7 +141,7 @@ json.dumps({"lang": detected_lang, "frequencies": freq}, ensure_ascii=False) checkThreshold(detectedLang); } } catch (error) { - console.error("[Worker] Erreur dans l'analyse du texte :", error); + log("[Worker] Erreur dans l'analyse du texte :", error); } }