diff --git a/src/background/background.js b/src/background/background.js index fffbc9d381eea465fe1d3604562760eba27834cc..7f0b373bbbe0769b17e023124ac0142f30111eb2 100644 --- a/src/background/background.js +++ b/src/background/background.js @@ -543,16 +543,18 @@ async function notifyAllTabs(message) { // Écoute du changement des préférences et envoi des valeurs au Worker // ───────────────────────────────────────────────────────────────────────────── 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, @@ -561,6 +563,17 @@ browser.storage.onChanged.addListener(async (changes, area) => { autoAdd: autoAdd || false }); } + + if (area === "local" && changes.includeStopwords) { + const includeStopwords = changes.includeStopwords.newValue; + console.log(`[Background] Inclusion des mots outils activé/désactivé: ${includeStopwords}`); + + if (worker) { + worker.postMessage({ command: "update-include-stopwords", includeStopwords }); + } + } + + }); // ───────────────────────────────────────────────────────────────────────────── diff --git a/src/popup/popup.html b/src/popup/popup.html index 03f40596354cf1bdd59a06ec5a31d368305b7457..b9c17fddfb04dec8a1da18f3cc299c4e0bf35c65 100644 --- a/src/popup/popup.html +++ b/src/popup/popup.html @@ -4,123 +4,397 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Extension BaLex</title> + <script src="../utils/logger.js"></script> <style> - body { - font-family: Arial, sans-serif; - margin: 20px; - width: 200px; - background-color: #323046; + @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; + } + + #extension-notification { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.85); color: white; + padding: 15px; + border-radius: 8px; text-align: center; + width: 80%; + max-width: 250px; + z-index: 1000; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); + transition: opacity 0.3s ease, visibility 0.3s ease; + } + #extension-notification.hidden { + opacity: 0; + visibility: hidden; + } + + #close-notification { + margin-top: 10px; + padding: 5px 10px; + background-color: #8d5c70; + color: white; + border: none; + cursor: pointer; + border-radius: 5px; + font-weight: bold; + } + + #close-notification:hover { + background-color: #dddedd; + color: #8d5c70; + } + + body { + font-family: Luciole; + font-size: medium; + margin: 0; + padding: 8px; + background-color: #525877; + color: #323046; + border-radius: 10px; + width: 200px; } button { + font-family: Luciole; width: 100%; - padding: 10px; - margin-bottom: 10px; - font-size: 15px; - color: #94608a; + padding: 12px; + margin-bottom: 8px; + font-size: 14px; font-weight: bold; border: none; cursor: pointer; + border-radius: 6px; + transition: background 0.3s, transform 0.2s; + background-color: #a08e9f; + color: white; } button:hover { - background-color: #94608a; + background-color: #dddedd; + color: #8d5c70; + } + #auth-button { + width: auto; + display: inline-flex; + padding: 6px 12px; + font-size: 18px; + font-family: Luciole; + background: none; + border: none; color: white; + align-items: center; + gap: 6px; + border-radius: 20px; + cursor: pointer; + transition: background 0.3s; + text-align: center; } - .hidden { - display: none; + #auth-button:hover { + background: rgba(255,255,255,0.2); } - #stats-options { - text-align: left; - margin-top: 15px; + #auth-button svg { + width: 18px; + height: 18px; + fill: white; + transition: transform 0.3s ease-in-out; } - #auto-add-options { - margin-top: 15px; + #auth-button:hover svg { + transform: scale(1.1); + } + #auth-section { + display: flex; + justify-content: center; + margin-bottom: 10px; + } + #extension-name { + font-family: Luciole; + text-align: center; + font-size: 23px; + font-weight: bold; + padding: 5px; + color: #8d5c70; + -webkit-text-stroke-width: 1px; + -webkit-text-stroke-color: white; + } + .option-container { + background: #444; + padding: 12px; + border-radius: 10px; + margin-top: 4px; + margin-bottom: 4px; + box-shadow: 0 2px 6px rgba(0,0,0,0.2); } .option-row { display: flex; align-items: center; justify-content: space-between; - margin-bottom: 10px; - width: 100%; + flex-wrap: nowrap; + padding: 12px; + margin-bottom: 4px; + border-radius: 6px; + background-color: #444; + color: white; + font-size: 0.6rem; + transition: transform 0.2s, box-shadow 0.2s; + } + .option-row:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + } + .option-row label { + font-weight: lighter; + flex: 1; + margin: 0; + font-size: 0.9rem; + align-items: center; + } + + .option-row input[type="checkbox"], + .option-row input[type="number"] { + appearance: none; + width: 20px; + height: 20px; + border: 2px solid #8d5c70; + border-radius: 50%; + background-color: #fff; + cursor: pointer; + transition: background-color 0.3s, border-color 0.3s; + } + + .option-row input[type="checkbox"]:checked { + background-color: #8d5c70; + border-color: #8d5c70; + } + + .toggle-switch { + position: absolute; + display: inline-block; + width: 30px; + height: 16px; + right: 0; + left: auto; + } + .toggle-switch .slider:before { + position: absolute; + content: ""; + width: 12px; + height: 12px; + left: 2px; + bottom: 2px; + background-color: white; + transition: 0.4s; + border-radius: 50%; + } + + .toggle-switch input { + opacity: 0; + width: 0; + height: 0; + } + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: 0.4s; + border-radius: 24px; + } + + input:checked + .slider { + background-color: #8d5c70; + } + + input:checked + .slider:before { + transform: translateX(14px); + } + + .threshold-container input[type="number"] { + width: 45px; + height: 45px; + line-height: 50px; + text-align: center; + border-radius: 50%; + border: 2px solid #8d5c70; + font-size: 0.8rem; + box-sizing: border-box; + background: #fff; + color: #333; + } + + #save-options { + border: none; + background: #8d5c70; + border-radius: 6px; + color: white; + padding: 8px 12px; + font-size: 0.9rem; + cursor: pointer; + transition: background 0.3s, transform 0.2s; + } + #save-options:hover { + background: #ccc; + color: #8d5c70; + transform: translateY(-2px); } .language-selection { display: flex; flex-wrap: wrap; - justify-content: center; - gap: 10px; - margin-top: 5px; + gap: 8px; } .lang-option { - padding: 8px 12px; - background-color: #323046; - color: white; - border: 2px solid #94608a; - border-radius: 5px; + padding: 4px 10px; + border: 2px solid #8d5c70; + border-radius: 16px; + background: rgb(152, 152, 152); + color: #8d5c70; cursor: pointer; - transition: all 0.3s ease; + font-size: 0.85rem; + transition: background 0.3s, color 0.3s, transform 0.2s; + } + .lang-option:hover { + background: #8d5c70; + color: white; + transform: scale(1.05); } .lang-option.selected { - background-color: #94608a; + background: #8d5c70; color: white; + border-color: #8d5c70; } - .threshold-container { + + /* #stats-options { + border: 1px solid #555; + border-radius: 8px; + align-items: center; + } */ + + .option-row.auto-add-row { + position: relative; display: flex; align-items: center; justify-content: space-between; - width: 100%; - margin-top: 15px; + padding: 10px 12px; + margin-bottom: 0; + background: transparent; + border-bottom: 1px solid #555; } - .threshold-container label { - flex: 1; + .option-row.auto-add-row span { + font-size: 16px; + font-weight: lighter; } - .threshold-container input { - width: 60px; - text-align: center; - padding: 5px; - } - #auto-add { - margin-right: 10px; + + .option-row.stopwords { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.9rem; + font-weight: lighter; } - #error-message { - color: rgb(255, 0, 0); - padding: 10px; - text-align: center; - } + /* .option-row.auto-add-row input[type="checkbox"] { + appearance: none; + width: 24px; + margin-left: 10px; + height: 24px; + border: 2px solid #8d5c70; + border-radius: 4px; + background: #fff; + cursor: pointer; + transition: background 0.3s; + } */ + + /* .option-row.auto-add-row input[type="checkbox"]:checked { + background: #8d5c70; + } */ + + #open-stats { + padding: 6px; + font-weight: lighter; + width: auto; + display: block; + margin: 0 auto; + background-color: #525877; + color: white; + border: 2px solid #8d5c70; + border-radius: 8px; + } + .hidden { + display: none; + } </style> </head> <body> - <h3>Extension BaLex</h3> - <button id="auth-button">Se connecter</button> - <button id="toggleExtensionBtn">Activer/Désactiver l'extension</button> - <button id="toggleStatsBtn">Activer les statistiques</button> - <div id="stats-options" class="hidden"> - <label for="auto-add" style="margin-top: 15px; display: flex; align-items: center;"> - <input type="checkbox" id="auto-add"/> Ajout Automatique - </label> - - <div id="auto-add-options" class="hidden"> - <div class="threshold-container"> - <label for="threshold">Seuil</label> - <input type="number" id="threshold" value="10" min="1" /> - </div> - <div class="option-row"> - <label>Langues suivies</label> - <div id="language-selection" class="language-selection"> - <p id="loading-languages" style="color: gray;">Chargement...</p> - </div> + <div id="extension-name">Extension BaLex</div> + <div id="auth-section"> + <button id="auth-button"> + <span id="auth-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <path d="M10 2a1 1 0 0 1 1 1v2h2V3a1 1 0 0 1 2 0v2h2a2 2 0 0 1 2 2v3h-2V7h-2v3a1 1 0 1 1-2 0V7h-2v3a1 1 0 1 1-2 0V7H6v3H4V7a2 2 0 0 1 2-2h2V3a1 1 0 0 1 1-1Z"/> + </svg> + </span> + <span id="auth-text">Se connecter</span> + </button> + </div> + <button id="toggleExtensionBtn">Activer/Désactiver</button> + <button id="toggleStatsBtn">Statistiques</button> + <button id="open-stats">Afficher les statistiques</button> + + <div id="stats-options" class="option-container"> + <div class="option-row auto-add-row"> + <span>Ajout automatique</span> + <label class="toggle-switch"> + <input type="checkbox" id="auto-add"> + <span class="slider"></span> + </label> + </div> + + <div id="auto-add-options" class="hidden"> + <div class="option-row stopwords"> + <span>Inclure mots outils</span> + <label class="toggle-switch"> + <input type="checkbox" id="include-stopwords" /> + <span class="slider"></span> + </label> + </div> + + + <div class="option-row threshold-container"> + <label for="threshold">Seuil d'ajout d'un mot</label> + <input type="number" id="threshold" value="10" min="1" /> + </div> + + <div class="option-row"> + <label>Langues suivies</label> + <div id="language-selection" class="language-selection"> + <p id="loading-languages" style="color: gray;">Chargement...</p> </div> - <!-- Message d'erreur si on ne sélectionne pas une langue à suivre--> - <div id="error-message" class="hidden"> - <p>Veuillez sélectionner au moins une langue avant de sauvegarder.</p> - </div> - <button id="save-options" class="hidden">Valider</button> </div> + + <!-- Message d'erreur si on ne sélectionne pas une langue à suivre--> + <div id="error-message" class="hidden"> + <p>Veuillez sélectionner au moins une langue avant de sauvegarder.</p> + </div> + <button id="save-options" class="hidden">Valider</button> </div> - <script src="popup.js"></script> - <script src="../utils/api.js"></script> + </div> + <div id="extension-notification" class="hidden"> + <p id="notification-text"></p> + <button id="close-notification">OK</button> + </div> + + <script src="../utils/api.js"></script> + <script src="popup.js"></script> </body> </html> diff --git a/src/popup/popup.js b/src/popup/popup.js index 0a552d45e46e18073fb91990705c96816bdc992e..13cb64464807a8ceea3de1b9d9d758d8d8cfcf5d 100644 --- a/src/popup/popup.js +++ b/src/popup/popup.js @@ -29,6 +29,36 @@ async function updateConnectionButton() { } button.onclick = async () => { + const isConnected = !!accessToken; + + // Gestion de la déconnexion + if (isConnected) { + console.log("[Popup] Déconnexion détectée, réinitialisation des options..."); + + await browser.storage.local.set({ + accessToken: null, + autoAdd: false, + includeStopwords: false, + isTrackingActive: false + }); + + // Envoyer les mises à jour au worker + browser.runtime.sendMessage({ + command: "update-preferences", + autoAdd: false, + includeStopwords: false, + isTrackingActive: false + }); + + console.log("✅ Paramètres réinitialisés après déconnexion."); + + // Mettre à jour l'interface + await updateOptionsUI(); + await updateLanguageSelection(); + await updateExtensionToggleButton(); + } + + // Envoyer le message pour basculer l'authentification await browser.runtime.sendMessage({ action: "toggleAuth" }); }; } @@ -91,18 +121,20 @@ async function updateOptionsUI() { const saveOptionsBtn = document.getElementById("save-options"); const toggleStatsBtn = document.getElementById("toggleStatsBtn"); const openStats = document.getElementById("open-stats"); - + const includeStopwordsCheckbox = document.getElementById("include-stopwords"); + // 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([ + const { isTrackingActive, autoAdd, threshold, includeStopwords} = await browser.storage.local.get([ "isTrackingActive", "autoAdd", - "threshold" - ]) || { isTrackingActive: false }; + "threshold", + "includeStopwords" + ]) || { isTrackingActive: false, includeStopwords: false }; // Gestion du bouton de statistiques if (toggleStatsBtn) { @@ -118,15 +150,18 @@ async function updateOptionsUI() { toggleStatsBtn.textContent = isTrackingActive ? "Désactiver les statistiques" : "Activer les statistiques"; } - // Affichage des options statistiques + // Gestion des boutons d'options de statistiques if (statsOptions) { statsOptions.classList.toggle("hidden", !isTrackingActive); } - + //Si l'utilsateur est connecté : afficher les options if (isLoggedIn) { if (autoAddCheckbox) { autoAddCheckbox.checked = autoAdd || false; } + if (includeStopwordsCheckbox) { + includeStopwordsCheckbox.checked = includeStopwords || false; + } if (thresholdInput && threshold !== undefined) { thresholdInput.value = threshold; } @@ -136,10 +171,14 @@ async function updateOptionsUI() { if (saveOptionsBtn) { saveOptionsBtn.classList.toggle("hidden", !autoAdd); } + //Si l'utilsateur n'est connecté : masquer les options } else { if (autoAddCheckbox) { autoAddCheckbox.checked = false; } + if (includeStopwordsCheckbox) { + includeStopwordsCheckbox.checked = false; + } if (autoAddOptions) { autoAddOptions.classList.add("hidden"); } @@ -149,6 +188,22 @@ async function updateOptionsUI() { } } +// ========================== +// Gestion des stoplists +// ========================== +document.getElementById("include-stopwords")?.addEventListener("change", async () => { + const includeStopwords = document.getElementById("include-stopwords").checked; + + // Si on décoche, on met immédiatement à jour le stockage + if (!includeStopwords) { + await browser.storage.local.set({ includeStopwords: false }); + console.log("[Popup] Option 'Inclure mots outils' désactivée immédiatement."); + + browser.runtime.sendMessage({ command: "update-include-stopwords", includeStopwords: false }); + } +}); + + // ========================== // Gestion de l'activation/désactivation de l'extension // ========================== @@ -231,8 +286,13 @@ document.getElementById("toggleStatsBtn")?.addEventListener("click", async () => // Si désactivation, désactiver aussi l'ajout automatique if (!newState) { - await browser.storage.local.set({ autoAdd: false }); + // Si désactivation, désactiver aussi l'ajout automatique et includeStopwords + await browser.storage.local.set({ + autoAdd: false, + includeStopwords: false + }); document.getElementById("auto-add").checked = false; + document.getElementById("include-stopwords").checked = false; document.getElementById("auto-add-options").classList.add("hidden"); document.getElementById("save-options").classList.add("hidden"); } @@ -255,7 +315,13 @@ document.getElementById("auto-add")?.addEventListener("change", async () => { 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 }); + log("[Popup] Désactivation de l'ajout automatique -> Désactivation des options"); + await browser.storage.local.set({ + autoAdd: false, //Désactiver l'ajout automatique + includeStopwords: false // Désactiver includeStopwords + }); + // Mettre à jour l'affichage du bouton + document.getElementById("include-stopwords").checked = false; } }); @@ -265,7 +331,8 @@ document.getElementById("save-options")?.addEventListener("click", async () => { const threshold = parseInt(document.getElementById("threshold").value, 10); const selectedLanguages = Array.from(document.querySelectorAll("#language-selection .lang-option.selected")) .map(option => option.dataset.value); - + const includeStopwords = document.getElementById("include-stopwords").checked; + // 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) { @@ -277,9 +344,20 @@ document.getElementById("save-options")?.addEventListener("click", async () => { await browser.storage.local.set({ autoAdd, threshold, - trackedLanguages: selectedLanguages + trackedLanguages: selectedLanguages, + includeStopwords + }); + log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages, includeStopwords}); + + // Envoyer les nouvelles préférences au Worker + browser.runtime.sendMessage({ + command: "update-preferences", + autoAdd, + threshold, + trackedLanguages: selectedLanguages, + includeStopwords }); - log("Options sauvegardées :", { autoAdd, threshold, trackedLanguages: selectedLanguages }); + }); // Bouton pour ouvrir la page des statistiques @@ -288,8 +366,6 @@ document.getElementById("open-stats")?.addEventListener("click", async () => { window.open("stats.html", "_blank"); }); - - async function displayStatsSummary() { console.log("[Popup] Préparation du résumé des statistiques..."); diff --git a/src/utils/definitions.js b/src/utils/definitions.js index ff1a246a0fccd788ea7e4bd945550872571febc9..fd573039c90721fb65d6cd4a9bf40a10c683154a 100644 --- a/src/utils/definitions.js +++ b/src/utils/definitions.js @@ -16,7 +16,7 @@ window.lexiconMap = new Map(); */ async function fetchLexiconDefinitions(word) { try { - console.log(`🔠Recherche des définitions de '${word}' dans les lexiques de l'utilisateur...`); + log(`🔠Recherche des définitions de '${word}' dans les lexiques de l'utilisateur...`); if (!authToken) { console.warn("Aucun token disponible, impossible de requêter l'API protégée."); @@ -24,8 +24,7 @@ async function fetchLexiconDefinitions(word) { } // 1) Récupérer la liste complète des lexiques de l'utilisateur - const userId = 4; - const lexUrl = `https://babalex.lezinter.net/api/lexicon/search?user_id=${userId}&language=fr`; + const lexUrl = `https://babalex.lezinter.net/api/lexicon/search?`; const lexResponse = await fetch(lexUrl, { headers: { Authorization: `Bearer ${authToken}` } }); @@ -33,7 +32,7 @@ async function fetchLexiconDefinitions(word) { throw new Error(`⌠Erreur API lors de la récupération des lexiques: ${lexResponse.statusText}`); } const userLexicons = await lexResponse.json(); - console.log("ðŸ—‚ï¸ Lexiques de l'utilisateur :", userLexicons); + log("ðŸ—‚ï¸ Lexiques de l'utilisateur :", userLexicons); if (!Array.isArray(userLexicons) || userLexicons.length === 0) { console.warn("âš ï¸ Aucun lexique trouvé pour cet utilisateur."); @@ -49,12 +48,12 @@ async function fetchLexiconDefinitions(word) { : `Lexique de groupe : ${lex.group?.name || "Inconnu"}`; lexiconMap.set(lex.id, lexiconName); }); - console.log("📌 LexiconMap :", lexiconMap); + log("📌 LexiconMap :", lexiconMap); // 2) Pour chaque lexique, rechercher le mot en ajoutant target_lex const definitionsPromises = userLexicons.map(async (lex) => { const searchUrl = `https://babalex.lezinter.net/api/entry/search?graphy=${encodeURIComponent(word)}&language=fr&target_lex=${lex.id}`; - console.log(`🔎 Appel API pour le lexique ${lex.id} avec l'URL : ${searchUrl}`); + log(`🔎 Appel API pour le lexique ${lex.id} avec l'URL : ${searchUrl}`); const searchResponse = await fetch(searchUrl, { headers: { Authorization: `Bearer ${authToken}` } @@ -71,7 +70,7 @@ async function fetchLexiconDefinitions(word) { return Number(entry.lexicon.id) === Number(lex.id); }); - console.log(`Pour le lexique ${lex.id} (${lexiconMap.get(lex.id)}), entrées filtrées :`, filteredEntries); + log(`Pour le lexique ${lex.id} (${lexiconMap.get(lex.id)}), entrées filtrées :`, filteredEntries); return { lexiconId: lex.id, entries: filteredEntries }; }); @@ -81,7 +80,7 @@ async function fetchLexiconDefinitions(word) { // 3) Parcourir les résultats et extraire les définitions let allDefinitions = []; results.forEach(result => { - console.log(`Pour le lexique ${result.lexiconId}, entrées filtrées :`, result.entries); + log(`Pour le lexique ${result.lexiconId}, entrées filtrées :`, result.entries); const lexiconId = result.lexiconId; const sourceName = lexiconMap.get(lexiconId) || `Lexique #${lexiconId}`; @@ -114,7 +113,7 @@ async function fetchLexiconDefinitions(word) { }); }); - console.log("Résultat final filtré :", allDefinitions); + 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); @@ -128,7 +127,7 @@ async function fetchLexiconDefinitions(word) { */ // async function fetchWiktionaryDefinition(word) { // try { -// console.log(`🔠Requête Wiktionnaire pour "${word}"...`); +// log(`🔠Requête Wiktionnaire pour "${word}"...`); // if (!word || word.trim() === "") { // throw new Error("âš ï¸ Mot vide, impossible d'envoyer la requête."); // } @@ -138,7 +137,7 @@ async function fetchLexiconDefinitions(word) { // throw new Error(`⌠Erreur API Wiktionnaire: ${response.statusText}`); // } // const data = await response.json(); -// console.log("📖 Réponse API (Wiktionnaire) :", data); +// log("📖 Réponse API (Wiktionnaire) :", data); // const pages = data.query?.pages; // const page = pages ? Object.values(pages)[0] : null; @@ -147,7 +146,7 @@ async function fetchLexiconDefinitions(word) { // ? page.extract.trim() // : "âš ï¸ Aucune définition trouvée sur le Wiktionnaire."; -// console.log("🌠Définition Wiktionnaire extraite :", definitionText); +// log("🌠Définition Wiktionnaire extraite :", definitionText); // return [ // { @@ -269,8 +268,6 @@ async function fetchWiktionaryDefinition(word) { } - - // ───────────────────────────────────────────────────────────────────────────── // â–Œ Affichage des définitions dans la barre latérale // ───────────────────────────────────────────────────────────────────────────── @@ -281,7 +278,7 @@ const MAX_LENGTH = 200; * Affiche les définitions dans la barre latérale. */ function displayDefinitions(definitions) { - console.log("📖 Affichage des définitions reçues :", definitions); + log("📖 Affichage des définitions reçues :", definitions); if (!Array.isArray(definitions)) return; const mesLexiquesList = document.getElementById("mesLexiquesList"); @@ -419,7 +416,7 @@ function closeDefinitionPopup() { * Puis fusionne les résultats. */ async function combineDefinitions(word) { - console.log(`[combineDefinitions] Récupération des définitions pour "${word}"...`); + log(`[combineDefinitions] Récupération des définitions pour "${word}"...`); const results = await Promise.allSettled([ fetchLexiconDefinitions(word), @@ -431,23 +428,18 @@ async function combineDefinitions(word) { const wiktionaryDefinitions = results[1].status === "fulfilled" ? results[1].value : []; - // Assurer que c'est bien un tableau - if (!Array.isArray(lexiconDefinitions)) lexiconDefinitions = []; - if (!Array.isArray(wiktionaryDefinitions)) wiktionaryDefinitions = []; - const allDefinitions = [...lexiconDefinitions, ...wiktionaryDefinitions]; - console.log("📚 [combineDefinitions] Résultat fusionné :", allDefinitions); + log("📚 [combineDefinitions] Résultat fusionné :", allDefinitions); return allDefinitions; } - /** * Récupère et affiche toutes les définitions (lexiques + Wiktionnaire). */ async function showDefinitions(word) { - console.log(`[showDefinitions] Recherche + affichage pour "${word}"...`); + log(`[showDefinitions] Recherche + affichage pour "${word}"...`); const noDefinitionsContainer = document.getElementById("noDefinitionsContainer"); if (noDefinitionsContainer) { @@ -458,7 +450,7 @@ async function showDefinitions(word) { try { const allDefinitions = await combineDefinitions(word); - console.log("[showDefinitions] Définitions récupérées :", allDefinitions); + log("[showDefinitions] Définitions récupérées :", allDefinitions); if (!allDefinitions || allDefinitions.length === 0) { if (noDefinitionsContainer) { @@ -491,7 +483,7 @@ async function showDefinitions(word) { * (sans Wiktionnaire), puis gérer l’affichage d’erreur ou non. */ async function fetchDefinition(word) { - console.log(`🔠Recherche de la définition pour '${word}'...`); + log(`🔠Recherche de la définition pour '${word}'...`); const noDefinitionsContainer = document.getElementById("noDefinitionsContainer"); if (!noDefinitionsContainer) { @@ -501,7 +493,7 @@ async function fetchDefinition(word) { try { const definition = await fetchLexiconDefinitions(word); - console.log("🔠Résultat API :", definition); + log("🔠Résultat API :", definition); if (!definition || definition.length === 0) { console.warn(`âš ï¸ Aucune définition trouvée pour '${word}'`); @@ -552,7 +544,7 @@ function displayLexiconResults(lexicons) { li.innerHTML = `<strong>${lexiconName}</strong>`; ul.appendChild(li); - console.log(`✅ Lexique ajouté : ${lexiconName} (ID: ${lexicon.id})`); + log(`✅ Lexique ajouté : ${lexiconName} (ID: ${lexicon.id})`); }); resultDiv.appendChild(ul); diff --git a/src/utils/logger.js b/src/utils/logger.js new file mode 100644 index 0000000000000000000000000000000000000000..8b3f55eecbe174ecc639e620aacd823cc0d961c4 --- /dev/null +++ b/src/utils/logger.js @@ -0,0 +1,12 @@ + +/** + * Mode debug : affiche les logs dans la console + * Mode prod : masque les logs +*/ +const DEBUG = false; // false en prod + +function log(...args) { + if (DEBUG) { + console.log(...args); + } +} diff --git a/src/workers/pyodide_worker.js b/src/workers/pyodide_worker.js index 0d09462a9e4b42a1df433ec8a3e6cebd14ddb9b1..01b0f0bed47417058cf249ef012d49d062dfa675 100644 --- a/src/workers/pyodide_worker.js +++ b/src/workers/pyodide_worker.js @@ -14,11 +14,13 @@ 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 +let includeStopwords = false; // Stocker l'état de l'inclusion des mots outils let userLexicons = [] //Contient les lexiques et leurs ID (lexiques personnels) let authToken = null; // Stockage local du token - +// --- Attente de la mise à jour de la stoplist --- +let stoplistReady = new Promise((resolve) => resolve()); // Écouteur des messages reçus du background script self.onmessage = async (event) => { @@ -95,9 +97,10 @@ await main() } 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(` + const stoplistArray = stoplistFr && stoplistFr.size > 0 ? Array.from(stoplistFr) : null; + console.log("[Worker] Stoplist chargée avec process-text ?", stoplistArray ? `Oui (${stoplistArray.length} mots)` : "Non (aucun filtrage)"); + + const result = await pyodide.runPythonAsync(` import json import re import simplemma @@ -119,15 +122,15 @@ 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 + freq[token] = freq.get(token, 0) + 1 + #Si on veut éventuellement ne pas compter du tout les fréquences des mots de la stoplist + #if token not in stoplist: + #freq[token] = freq.get(token, 0) + 1 json.dumps({"lang": detected_lang, "frequencies": freq}, ensure_ascii=False) `); @@ -166,10 +169,23 @@ json.dumps({"lang": detected_lang, "frequencies": freq}, ensure_ascii=False) console.log("[Worker] 🔑 Token mis à jour :", authToken ? "Disponible" : "Aucun token reçu"); } - if (data.command === "update-stoplist") { - stoplistFr = new Set(data.stoplist.map(word => word.toLowerCase().trim())); - console.log("[Worker] Stoplist FR mise à jour :", stoplistFr); - } + if (data.command === "update-stoplist") { + stoplistReady = new Promise((resolve) => { + if (data.stoplist && Array.isArray(data.stoplist)) { + stoplistFr = new Set(data.stoplist.map(word => word.toLowerCase().trim())); + console.log(`[Worker] ✅ Stoplist mise à jour avec ${stoplistFr.size} mots.`); + } else { + console.warn("[Worker] âš Stoplist reçue incorrecte ou vide."); + stoplistFr = new Set(); + } + resolve(); // Indiquer que la stoplist est prête + }); + } + + if (data.command === "update-include-stopwords") { + includeStopwords = data.includeStopwords; + console.log(`[Worker] Mise à jour de includeStopwords : ${includeStopwords}`); + } }; // --- Vérification du seuil et notification --- @@ -177,6 +193,9 @@ let pendingWords = {}; // Stocker temporairement les mots en attente d'ajout let addWordTimeout = null; // Timer pour regrouper les ajouts async function checkThreshold(lang) { + // // Vérifier si la stoplist est définie et contient des mots + await stoplistReady; // Attendre que la stoplist soit chargée + console.log("[Worker] Stoplist chargée avec checkThreshold ?", stoplistFr.size > 0 ? `Oui (${stoplistFr.size} mots)` : "Non (aucun filtrage)"); if (!autoAddEnabled || !isAuthenticated) { console.log("[Worker] Auto-Add désactivé ou utilisateur non connecté."); return; @@ -190,21 +209,48 @@ async function checkThreshold(lang) { if (!storedFrequencies[lang]) return; + // Vérifier si la stoplist a bien été chargée + if (!stoplistFr || !(stoplistFr instanceof Set)) { + console.warn("[Worker] âš La stoplist n'est pas disponible ou mal initialisée. Aucun filtrage appliqué."); + } + + // Vérifier si une stoplist est disponible et si l'utilisateur veut exclure les stopwords + const shouldFilterStopwords = stoplistFr && stoplistFr.size > 0 && (includeStopwords === true); + + if (shouldFilterStopwords) { + console.log("[Worker] Filtrage des mots outils activé."); + } else { + console.log("[Worker] Aucune stoplist détectée ou filtrage désactivé. Tous les mots seront pris en compte."); + } + const exceededWords = Object.entries(storedFrequencies[lang]) .filter(([word, count]) => count >= userThreshold && !(notifiedWords[lang] && notifiedWords[lang].includes(word))) .map(([word]) => word); - if (exceededWords.length === 0) return; + // Appliquer le filtrage si nécessaire + //const finalWords = shouldFilterStopwords ? exceededWords.filter(word => !stoplistFr.has(word)) : exceededWords; + const finalWords = exceededWords.filter(word => { + if (shouldFilterStopwords) { + const isInStoplist = stoplistFr.has(word); + console.log(`[Worker] Mot "${word}" ${isInStoplist ? "EXCLU (dans la stoplist)" : "CONSERVÉ (pas dans la stoplist)"}`); + return !isInStoplist; + } + return true; // Si on ne filtre pas, garder tous les mots + }); + + + + if (finalWords.length === 0) return; if (!notifiedWords[lang]) notifiedWords[lang] = []; - notifiedWords[lang].push(...exceededWords); + notifiedWords[lang].push(...finalWords); + + console.log("Mots dépassant le seuil :", finalWords); + self.postMessage({ type: "threshold-exceeded", wordsAboveThreshold: finalWords }); - console.log("Mots dépassant le seuil :", exceededWords); - self.postMessage({ type: "threshold-exceeded", wordsAboveThreshold: exceededWords }); - // Stocker les mots détectés pour un ajout groupé if (!pendingWords[lang]) pendingWords[lang] = []; - pendingWords[lang].push(...exceededWords); + pendingWords[lang].push(...finalWords); // Déclencher un envoi groupé après un délai (3 secondes) if (!addWordTimeout) { @@ -214,6 +260,7 @@ async function checkThreshold(lang) { } } + //Traiter les ajouts groupés async function processPendingWords() { console.log("Traitement des mots à ajouter en lot..."); @@ -239,10 +286,19 @@ async function addWordToLexicon(lang, word) { console.log(`Tentative d'ajout du mot '${word}' pour la langue '${lang}'`); + // Vérifier si on a une stoplist et si l'utilisateur veut exclure les stopwords + const shouldFilterStopwords = stoplistFr && stoplistFr.size > 0 && !includeStopwords; + + // Si le filtrage est activé et que le mot est un stopword, on ne l'ajoute pas + if (shouldFilterStopwords && stoplistFr.has(word)) { + console.log(`🔹 Mot '${word}' ignoré car présent dans la stoplist.`); + return; + } + // Trouver les lexiques correspondant à la langue détectée const targetLexicons = userLexicons - .filter(lexicon => lexicon.language === lang && lexicon.category === "User") - .map(lexicon => lexicon.id); + .filter(lexicon => lexicon.language === lang && lexicon.category === "User") + .map(lexicon => lexicon.id); if (targetLexicons.length === 0) { console.warn(`Aucun lexique trouvé pour la langue '${lang}'. Impossible d'ajouter '${word}'.`);