diff --git a/manifest.json b/manifest.json index c2b94cae3c2c3e9d69e078dd0b3301d284765db5..08791cc984e8a3c3939a294b00725d11b93b2a2c 100644 --- a/manifest.json +++ b/manifest.json @@ -20,57 +20,56 @@ "background": { "scripts": [ - "background/background.js", - "definitions.js", - "menu_contextuel/browser_context_menu.js", - "api.js"], + "src/background/background.js", + "src/utils/definitions.js", + "src/utils/api.js", + "src/context_menu/browser_context_menu.js" + ], "persistent": true }, "browser_action": { - "default_popup": "menu_extension/popup.html", + "default_popup": "src/popup/popup.html", "default_icon": { - "16": "icons/logo.png" + "16": "src/assets/icons/logo.png" }, "default_title": "ff2BaLex" }, "options_ui": { - "page": "menu_extension/options.html", + "page": "src/options/options.html", "open_in_tab": false }, "sidebar_action": { "default_title": "BaLex", - "default_panel": "barre_latérale/sidebar.html", + "default_panel": "src/sidebar/sidebar.html", "default_icon": { - "16": "icons/logo.png", - "48": "icons/icon-48.png" + "16": "src/assets/icons/logo.png", + "48": "src/assets/icons/icon-48.png" } }, "content_scripts": [ { "matches": ["<all_urls>"], - "js": ["api.js"] - }, - { - "matches": ["<all_urls>"], - "js": ["menu_contextuel/custom_context_menu.js"], - "css": ["menu_contextuel/custom_context_menu.css"], - "run_at": "document_end" + "js": [ + "src/assets/lexicon_icon.js", + "src/utils/api.js", + "src/utils/definitions.js", + "src/sidebar/sidebar.js", + "src/context_menu/custom_context_menu.js"], + "css": ["src/context_menu/custom_context_menu.css"], + "run_at": "document_idle" } ], "web_accessible_resources": [ - "icons/*.png", - "menu_extension/*", - "barre_latérale/*", - "menu_contextuel/*" + "src/*" ], "icons": { - "16": "icons/logo.png" + "16": "src/assets/icons/logo.png" } } diff --git a/menu_contextuel/custom_context_menu.css b/menu_contextuel/custom_context_menu.css deleted file mode 100644 index 30f66dff8a4f8213de33088ddb3387a077852f4f..0000000000000000000000000000000000000000 --- a/menu_contextuel/custom_context_menu.css +++ /dev/null @@ -1,81 +0,0 @@ -/* === Conteneur principal du menu contextuel === */ -#whiteBox { - position: absolute; - display: none; - min-width: 50px; - max-width: 300px; - background-color: white; - color: #323046; - border: 2px solid #323046; - border-radius: 10px; - padding: 10px; - box-shadow: 0 4px 12px rgba(0,0,0,0.2); - font-family: "Helvetica", sans-serif; - z-index: 10000; - } - - /* === Titre/texte indiquant le mot sélectionné === */ - #whiteBox #selectedWord { - margin: 0; - margin-bottom: 8px; - font-size: 14px; - line-height: 1.3; - color: #323046; - font-weight: bold; - text-align: center; - } - - /* === Conteneur des icônes === */ - #whiteBox .icon-container { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - cursor: pointer; - margin: 0; - padding: 0; - } - - #whiteBox .icon-container:hover { - background-color: rgba(255, 255, 255, 0.1); - border-radius: 6px; - } - - /* === Les icônes elles-mêmes === */ - #whiteBox .icon { - width: 40px; - height: 40px; - transition: transform 0.2s ease; - margin : 0 auto; - display: block; - } - - #whiteBox .icon:hover { - transform: scale(1.15); - } - - /* === Message (tooltips) === */ - #whiteBox .tooltip { - visibility: hidden; - background-color: #333; - color: #fff; - text-align: center; - padding: 6px; - border-radius: 5px; - position: absolute; - bottom: -34px; - left: 50%; - transform: translateX(-50%); - white-space: nowrap; - font-size: 12px; - opacity: 0; - transition: opacity 0.2s ease, visibility 0.2s ease; - z-index: 1000; - } - - #whiteBox .icon-container:hover .tooltip { - visibility: visible; - opacity: 1; - } - \ No newline at end of file diff --git a/menu_contextuel/custom_context_menu.js b/menu_contextuel/custom_context_menu.js deleted file mode 100644 index 510295e6ef12d9c09c6bbf758c14e1afb57a3d47..0000000000000000000000000000000000000000 --- a/menu_contextuel/custom_context_menu.js +++ /dev/null @@ -1,271 +0,0 @@ -console.log("custom_context_menu.js chargé correctement"); - -// === Variables globales === -let authToken = null; -const WHITE_BOX_ID = "whiteBox"; - -// Fonction utilitaire pour envoyer une notification via le background -function sendNotification(title, message, iconPath) { - browser.runtime.sendMessage({ - action: "showNotification", - title, - message, - iconPath - }); -} - -// Récupère le token depuis le stockage local et le stocke dans authToken -async function loadAuthToken() { - try { - const result = await browser.storage.local.get("accessToken"); - authToken = result.accessToken || null; - console.log("🔑 Token chargé :", authToken); - } catch (error) { - console.error("⌠Erreur lors de la récupération du token :", error); - authToken = null; - } -} - -/** - * Crée le menu contextuel personnalisé (whiteBox) s'il n'existe pas déjà . - */ -function injectWhiteBox() { - let whiteBox = document.getElementById(WHITE_BOX_ID); - if (!whiteBox) { - whiteBox = document.createElement("div"); - whiteBox.id = WHITE_BOX_ID; - - // Définition de styles essentiels pour le positionnement et la visibilité - whiteBox.style.position = "absolute"; - whiteBox.style.zIndex = "9999"; - whiteBox.style.backgroundColor = "#fff"; - whiteBox.style.border = "1px solid #ccc"; - whiteBox.style.padding = "5px"; - whiteBox.style.borderRadius = "4px"; - whiteBox.style.boxShadow = "0px 2px 10px rgba(0,0,0,0.2)"; - - // Génération des URLs des icônes - const addLexiconPath = browser.runtime.getURL("icons/ajout_lexique.png"); - const getDefinitionPath = browser.runtime.getURL("icons/definition.png"); - const loginPath = browser.runtime.getURL("icons/connexion.png"); - - // Construction du HTML - whiteBox.innerHTML = ` - <p id="selectedWord" style="margin: 0; padding: 0;">Mot sélectionné : Aucun</p> - <hr style="border: 0; height: 1px; background-color: #323046; margin: 8px 0;"> - <div style="display: flex; flex-wrap: wrap; justify-content: center;"> - <!-- Bouton 1 - Ajouter au lexique --> - <div class="icon-container" title="Ajouter ce mot à un lexique"> - <img src="${addLexiconPath}" alt="Ajouter au lexique" class="icon" id="addLexiconButton"> - </div> - <!-- Bouton 2 - Définition (Babalex + Wiki) --> - <div class="icon-container" title="Obtenir la définition"> - <img src="${getDefinitionPath}" alt="Obtenir la définition" class="icon" id="getDefinitionButton"> - </div> - <!-- Bouton 3 - Connexion --> - <div class="icon-container" title="Connectez-vous à BaLex"> - <img src="${loginPath}" alt="Se connecter" class="icon" id="loginButton" style="display: none;"> - </div> - </div> - `; - document.body.appendChild(whiteBox); - setupWhiteBoxActions(); - } - return whiteBox; -} - -/** - * Renvoie le whiteBox s'il existe, ou le crée via injectWhiteBox(). - */ -function getOrCreateWhiteBox() { - return document.getElementById(WHITE_BOX_ID) || injectWhiteBox(); -} - -/** - * Configure les actions/boutons du menu contextuel. - */ -function setupWhiteBoxActions() { - const addLexiconBtn = document.getElementById("addLexiconButton"); - const getDefinitionBtn = document.getElementById("getDefinitionButton"); - const loginBtn = document.getElementById("loginButton"); - - // Bouton : Ajouter le mot au lexique personnel - addLexiconBtn.onclick = async () => { - const selectedText = getSelectedWord(); - console.log("🔠Ajout au lexique :", selectedText); - if (!selectedText) return; - - if (authToken) { - await searchLexicon(selectedText); - } else { - sendNotification( - "Connexion requise", - "âš ï¸ Veuillez vous connecter pour utiliser cette fonction.", - "icons/connexion.png" - ); - } - }; - - // Bouton : Obtenir une définition (Babalex + Wiktionnaire) - getDefinitionBtn.onclick = () => { - const selectedText = getSelectedWord().trim(); - if (selectedText) { - browser.runtime.sendMessage({ - action: "getDefinition", - selectedText - }); - } - }; - - // Bouton : Connexion - loginBtn.onclick = () => { - sendNotification( - "Redirection", - "Vous allez être redirigé(e) vers la page de connexion.", - "icons/connexion.png" - ); - browser.runtime.sendMessage({ action: "toggleAuth" }); - }; -} - -/** - * Met à jour la visibilité des boutons en fonction de l'état d'authentification. - */ -function updateMenuVisibility() { - // S'assurer que le whiteBox existe - getOrCreateWhiteBox(); - const addLexiconBtn = document.getElementById("addLexiconButton"); - const getDefinitionBtn = document.getElementById("getDefinitionButton"); - const loginBtn = document.getElementById("loginButton"); - - if (!addLexiconBtn || !getDefinitionBtn || !loginBtn) { - console.warn("âš ï¸ Un des boutons n'a pas été trouvé dans le DOM."); - return; - } - - if (authToken) { - // Utilisateur connecté => afficher "Ajouter" et "Définition", masquer "Connexion" - addLexiconBtn.style.display = "inline-block"; - getDefinitionBtn.style.display = "inline-block"; - loginBtn.style.display = "none"; - } else { - // Utilisateur déconnecté => masquer "Ajouter", afficher "Définition" et "Connexion" - addLexiconBtn.style.display = "none"; - getDefinitionBtn.style.display = "inline-block"; - loginBtn.style.display = "inline-block"; - } -} - -/** - * Récupère le mot affiché dans #selectedWord. - */ -function getSelectedWord() { - const selectedWordElement = document.getElementById("selectedWord"); - return selectedWordElement ? selectedWordElement.textContent.trim() : ""; -} - -/** - * Affiche le menu contextuel à la position du clic. - */ -function showWhiteBox(event, selectedText) { - const whiteBox = getOrCreateWhiteBox(); - const selectedWordElement = document.getElementById("selectedWord"); - selectedWordElement.textContent = `${selectedText}`; - - // Calculer la position du menu en fonction de la sélection - const selection = window.getSelection(); - if (!selection.rangeCount) return; // sécurité au cas où - const range = selection.getRangeAt(0); - const rect = range.getBoundingClientRect(); - const top = rect.bottom + window.scrollY; - const left = rect.right + window.scrollX; - - whiteBox.style.left = `${left}px`; - whiteBox.style.top = `${top}px`; - whiteBox.style.display = "block"; - - console.log("Affichage du menu contextuel avec le mot :", selectedText); - updateMenuVisibility(); -} - -function hideWhiteBox() { - const whiteBox = document.getElementById(WHITE_BOX_ID); - if (whiteBox) { - whiteBox.style.display = "none"; - } -} - -// === Écoute des événements de sélection de texte (mouseup) === -document.addEventListener("mouseup", (event) => { - const selectedText = window.getSelection().toString().trim(); - if (selectedText) { - console.log("Événement de sélection détecté. Texte sélectionné :", selectedText); - getOrCreateWhiteBox(); - showWhiteBox(event, selectedText); - - // Envoi du mot sélectionné au background - browser.runtime.sendMessage({ - action: "mot_selectionne", - selectedText, - }); - } else { - hideWhiteBox(); - } -}); - -// Réécoute des messages envoyés -browser.runtime.onMessage.addListener((message) => { - if (message.action === "refreshUI") { - console.log("🔄 Mise à jour du menu contextuel personnalisé."); - loadAuthToken().then(() => { - updateMenuVisibility(); - }); - } -}); - -// Initialisation au démarrage -loadAuthToken().then(() => { - getOrCreateWhiteBox(); - updateMenuVisibility(); -}); - -browser.storage.onChanged.addListener((changes) => { - if (changes.accessToken) { - console.log("🔄 Token changé dans le stockage, mise à jour du menu personnalisé."); - loadAuthToken().then(() => { - updateMenuVisibility(); - }); - } -}); - -// ───────────────────────────────────────────────────────────────────────────── -// â–Œ Fonctions d'API Babalex pour l'ajout ou la vérification d'un mot -// ───────────────────────────────────────────────────────────────────────────── - -async function searchLexicon(selectedText) { - console.log("🔄 Recherche dans le lexique personnel pour :", selectedText); - try { - const lexicons = await getUserLexicons(authToken); - console.log("📚 Lexiques récupérés :", lexicons); - - const frenchLexicon = lexicons.find((lexicon) => lexicon.language === "fr"); - if (!frenchLexicon) { - alert("âš ï¸ Aucun lexique français trouvé."); - return; - } - - const entries = await getLexiconEntriesID(authToken, frenchLexicon.id); - const isWordPresent = entries.some( - (entry) => entry.graphy.toLowerCase() === selectedText.toLowerCase() - ); - - if (isWordPresent) { - alert(`✅ Le mot "${selectedText}" est présent dans votre lexique personnel.`); - } else { - alert(`⌠Le mot "${selectedText}" n'est pas présent dans votre lexique personnel.`); - } - } catch (error) { - console.error("⌠Erreur lors de la recherche dans le lexique :", error); - alert(`Erreur lors de la recherche : ${error.message}`); - } -} diff --git a/icons/ajout_lexique.png b/src/assets/icons/ajout_lexique.png similarity index 100% rename from icons/ajout_lexique.png rename to src/assets/icons/ajout_lexique.png diff --git a/icons/connexion.png b/src/assets/icons/connexion.png similarity index 100% rename from icons/connexion.png rename to src/assets/icons/connexion.png diff --git a/icons/definition.png b/src/assets/icons/definition.png similarity index 100% rename from icons/definition.png rename to src/assets/icons/definition.png diff --git a/icons/definition_wiktionnaire.png b/src/assets/icons/definition_wiktionnaire.png similarity index 100% rename from icons/definition_wiktionnaire.png rename to src/assets/icons/definition_wiktionnaire.png diff --git a/src/assets/icons/feutre.png b/src/assets/icons/feutre.png new file mode 100644 index 0000000000000000000000000000000000000000..ac98bf24c087760a7df319769db987664e72c3a6 Binary files /dev/null and b/src/assets/icons/feutre.png differ diff --git a/icons/logo.png b/src/assets/icons/logo.png similarity index 100% rename from icons/logo.png rename to src/assets/icons/logo.png diff --git a/icons/quel_lexique.png b/src/assets/icons/quel_lexique.png similarity index 100% rename from icons/quel_lexique.png rename to src/assets/icons/quel_lexique.png diff --git a/icons/recherche_lexique.png b/src/assets/icons/recherche_lexique.png similarity index 100% rename from icons/recherche_lexique.png rename to src/assets/icons/recherche_lexique.png diff --git a/src/assets/lexicon_icon.js b/src/assets/lexicon_icon.js new file mode 100644 index 0000000000000000000000000000000000000000..2929fee20b3bb9437f34227045be257e7495b4a9 --- /dev/null +++ b/src/assets/lexicon_icon.js @@ -0,0 +1,69 @@ +/** + * Sélectionne aléatoirement une couleur dans une palette prédéfinie. + * @returns {string} Une couleur au format hexadécimal ou HSL. + */ +function generateRandomColor() { + const palette = [ + "#231942", + "#5E548E", + "#9F86C0", + "#BE95C4", + "#E0B1CB", + "#b7094c", + "#a01a58", + "#892b64", + "#723c70", + "#5b4d7c", + "#455e89", + "#2e6f95", + "#1780a1", + "#0091ad", + "#30343f", + "#e4d9ff", + "#273469", + "#1e2749" + ]; + const index = Math.floor(Math.random() * palette.length); + return palette[index]; +} + +/** + * Obtient (ou crée) la couleur associée à un lexique donné en utilisant browser.storage.local. + * @param {string|number} lexiconId - L'identifiant du lexique. + * @returns {Promise<string>} La couleur associée au lexique. + */ +async function getOrCreateLexiconColor(lexiconId, forceReset = false) { + // Récupère la correspondance stockée dans storage + let { lexiconColors } = await browser.storage.local.get("lexiconColors"); + if (!lexiconColors || forceReset) { + lexiconColors = {}; + } + // Si aucune couleur n'est associée à ce lexiconId, on la génère et on la sauvegarde + if (!lexiconColors[lexiconId]) { + lexiconColors[lexiconId] = generateRandomColor(); + await browser.storage.local.set({ lexiconColors }); + } + return lexiconColors[lexiconId]; +} + +/** + * Crée un élément HTML (div) stylisé en cercle de la couleur donnée. + * @param {string} color - Couleur au format "#RRGGBB". + * @param {number} [size=32] - Taille (largeur et hauteur) en pixels. + * @returns {HTMLElement} Le div stylisé en cercle. + */ +function createColorCircle(color, size = 32) { + const circle = document.createElement("div"); + circle.className = "color-circle"; + circle.style.width = `${size}px`; + circle.style.height = `${size}px`; + circle.style.borderRadius = "50%"; + circle.style.backgroundColor = color; + circle.style.border = "1px solid black"; + return circle; +} + +window.generateRandomColor = generateRandomColor; +window.getOrCreateLexiconColor = getOrCreateLexiconColor; +window.createColorCircle = createColorCircle; + diff --git a/background/background.js b/src/background/background.js similarity index 96% rename from background/background.js rename to src/background/background.js index e1f36c53f1e805ff9d2735b4f99e0bb8af0e5785..6203313d147c33d0c8812efe249530c4e4ac64a6 100644 --- a/background/background.js +++ b/src/background/background.js @@ -88,11 +88,18 @@ async function actuallyOpenLoginPage() { browser.runtime.sendMessage({ action: "authStatusChanged", isLoggedIn: false }); } -// Déconnecte l'utilisateur (sans notification) +// Déconnecte l'utilisateur async function disconnectFromLexicalDB() { console.log("🔓 Déconnexion en cours..."); await browser.storage.local.remove("accessToken"); console.log("🔓 Token supprimé avec succès."); + // Réinitialiser les couleurs des lexiques dans le local storage + try { + await browser.storage.local.remove("lexiconColors"); + console.log("Les couleurs des lexiques ont été réinitialisées dans le local storage."); + } catch (error) { + console.error("Erreur lors de la réinitialisation des couleurs :", error); + } setTimeout(async () => { await refreshAllUI(); }, 500); @@ -135,6 +142,7 @@ browser.runtime.onMessage.addListener(async (message, sender, sendResponse) => { const isConnected = await isUserConnected(); if (isConnected) { await disconnectFromLexicalDB(); + await browser.storage.local.remove("lexiconColors"); } else { actuallyOpenLoginPage(); } diff --git a/menu_contextuel/browser_context_menu.js b/src/context_menu/browser_context_menu.js similarity index 67% rename from menu_contextuel/browser_context_menu.js rename to src/context_menu/browser_context_menu.js index a2ba1fab3be52725c98a005b6603114c0e824100..7c24d4a8e5d5d5829d6135265ddb5e8ffc228e1b 100644 --- a/menu_contextuel/browser_context_menu.js +++ b/src/context_menu/browser_context_menu.js @@ -1,6 +1,7 @@ console.log("browser_context_menu.js chargé correctement"); let authToken = null; +let selectedLexicons = new Set(); /** * Charge le token depuis le stockage local et le stocke dans la variable globale authToken. @@ -15,9 +16,12 @@ async function loadAuthToken() { } } -// ───────────────────────────────────────────────────────────────────────────── -// Création du menu contextuel en fonction de l'authentification -// ───────────────────────────────────────────────────────────────────────────── +/** + * Crée le menu contextuel en fonction de l'authentification. + * Si l'utilisateur est connecté, on ajoute un item pour la recherche et + * un menu parent pour l'ajout du mot avec des cases à cocher pour chaque lexique + * et un item de confirmation. + */ async function createContextMenu() { await browser.contextMenus.removeAll(); @@ -27,18 +31,19 @@ async function createContextMenu() { id: "searchInLexicons", title: "Rechercher dans mes lexiques", contexts: ["selection"], - icons: { "16": "icons/quel_lexique.png" }, + icons: { "16": "src/assets/icons/quel_lexique.png" }, }); - // Item 2 : Ajouter un mot au lexique personnel + // Item 2 : Ajouter le mot au(x) lexique(s) de l’utilisateur browser.contextMenus.create({ - id: "addtoLexicon", - title: "Ajouter ce mot à mon lexique", + id: "addToLexicon", + title: "Ajouter ce mot à mes lexiques", contexts: ["selection"], - icons: { "16": "icons/ajout_lexique.png" }, + icons: { "16": "src/assets/icons/ajout_lexique.png" }, }); } + // Séparateur browser.contextMenus.create({ id: "separatorExtension", type: "separator", @@ -50,7 +55,7 @@ async function createContextMenu() { id: "getDefinition", title: "Obtenir une définition", contexts: ["selection"], - icons: { "16": "icons/definition.png" }, + icons: { "16": "src/assets/icons/definition.png" }, }); browser.contextMenus.create({ @@ -59,6 +64,7 @@ async function createContextMenu() { contexts: ["all"], }); + // Item de connexion/déconnexion browser.contextMenus.create({ id: "login", title: authToken ? "Se déconnecter de BaLex" : "Se connecter à BaLex", @@ -87,7 +93,8 @@ browser.storage.onChanged.addListener((changes, area) => { // ───────────────────────────────────────────────────────────────────────────── browser.contextMenus.onClicked.addListener(async (info, tab) => { console.log("Item de menu cliqué :", info.menuItemId); - + + // Action pour le bouton de connexion/déconnexion if (info.menuItemId === "login") { console.log("🔄 Action login/déconnexion demandée."); if (typeof actuallyOpenLoginPage === "function") { @@ -102,54 +109,58 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => { console.warn("Aucun texte sélectionné pour cette action :", info.menuItemId); return; } - console.log(`📩 Texte sélectionné : ${info.selectionText}`); + const selectedText = info.selectionText.trim(); + console.log(`📩 Texte sélectionné : ${selectedText}`); + + // Item "Ajouter ce mot à mes lexiques" + if (info.menuItemId === "addToLexicon") { + if (!authToken) { + alert("âš ï¸ Vous devez être connecté pour ajouter un mot à un lexique."); + return; + } + browser.runtime.sendMessage({ + action: "addToLexicon", + selectedText, + }); + return; + } + + // Item "Obtenir une définition" + if (info.menuItemId === "getDefinition") { + await browser.runtime.sendMessage({ + action: "getDefinition", + selectedText, + }); + return; + } + + // Item "Rechercher dans mes lexiques" switch (info.menuItemId) { case "searchInLexicons": if (!authToken) { alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); return; } - await searchInLexicons(info.selectionText); - break; - - case "addtoLexicon": - if (!authToken) { - alert("âš ï¸ Vous devez être connecté pour utiliser cette fonction."); - return; - } - await addWordToLexicon(info.selectionText); - break; - - case "getDefinition": - await getDefinition(info.selectionText); + await searchInLexicons(selectedText); break; - - default: - console.error(`⌠Action inconnue : ${info.menuItemId}`); - } + } + + console.error(`⌠Action inconnue : ${info.menuItemId}`); }); // ───────────────────────────────────────────────────────────────────────────── -// Fonctions liées aux définitions (definitions.js) +// Fonctions liées aux définitions // ───────────────────────────────────────────────────────────────────────────── - -/** - * 1) Recherche de la définition combinée (lexiques + Wiktionnaire) - * et envoi des résultats pour affichage. - */ async function getDefinition(selectedText) { try { let lexiconDefs = []; if (authToken) { lexiconDefs = await fetchLexiconDefinitions(selectedText); } - const wikiDefs = await fetchWiktionaryDefinition(selectedText); - const allDefinitions = [...lexiconDefs, ...wikiDefs]; console.log("📠Définitions combinées :", allDefinitions); - browser.runtime.sendMessage({ action: "showDefinitions", selectedText, @@ -160,13 +171,9 @@ async function getDefinition(selectedText) { } } -/** - * 2) Recherche dans les lexiques pour savoir dans quels lexiques se trouve le mot. - */ async function searchInLexicons(selectedText) { try { console.log("🔎 Recherche dans mes lexiques :", selectedText); - const allDefinitions = await fetchLexiconDefinitions(selectedText); if (!allDefinitions || allDefinitions.length === 0) { console.log("⌠Aucun lexique trouvé pour ce mot."); @@ -177,21 +184,17 @@ async function searchInLexicons(selectedText) { }); return; } - const lexMap = new Map(); for (const def of allDefinitions) { if (def.lexiconId) { lexMap.set(def.lexiconId, def.source); } } - const foundInLexicons = []; for (const [id, name] of lexMap.entries()) { foundInLexicons.push({ id, name }); } - console.log("📩 Envoi du message 'showLexiconResult' avec :", foundInLexicons); - browser.runtime.sendMessage({ action: "showLexiconResult", lexicons: foundInLexicons, @@ -207,24 +210,3 @@ async function searchInLexicons(selectedText) { } } -/** - * 3) Ajouter un mot dans le lexique personnel. - */ -async function addWordToLexicon(selectedText) { - try { - console.log("🔠Ajout du mot à mon lexique :", selectedText); - alert(`(Exemple) Le mot "${selectedText}" a été ajouté à votre lexique personnel !`); - } catch (error) { - console.error("⌠Erreur lors de l'ajout au lexique :", error); - alert("⌠Impossible d'ajouter ce mot au lexique."); - } -} - -// ───────────────────────────────────────────────────────────────────────────── -// Écouteur interne pour d’autres messages éventuels -// ───────────────────────────────────────────────────────────────────────────── -browser.runtime.onMessage.addListener(async (message) => { - if (message.action === "getDefinitionAll") { - await getDefinition(message.selectedText); - } -}); diff --git a/src/context_menu/custom_context_menu.css b/src/context_menu/custom_context_menu.css new file mode 100644 index 0000000000000000000000000000000000000000..f763805383df8589ad745876c29b25d9f7852331 --- /dev/null +++ b/src/context_menu/custom_context_menu.css @@ -0,0 +1,157 @@ +/* === Conteneur principal du menu contextuel === */ +#whiteBox { + position: absolute; + display: none; + min-width: 50px; + max-width: 300px; + background-color: white; + color: #323046; + border: 2px solid #323046; + border-radius: 10px; + padding: 10px; + box-shadow: 0 4px 12px rgba(0,0,0,0.2); + font-family: "Helvetica", sans-serif; + z-index: 10000; +} + +/* === Titre/texte indiquant le mot sélectionné === */ +#whiteBox #selectedWord { + margin: 0; + margin-bottom: 8px; + font-size: 14px; + line-height: 1.3; + color: #323046; + font-weight: bold; + text-align: center; +} + +/* === Conteneur des icônes === */ +#whiteBox .icon-container { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + margin: 0; + padding: 0; +} + +#whiteBox .icon-container:hover { + background-color: rgba(255, 255, 255, 0.1); + border-radius: 6px; +} + +/* === Les icônes elles-mêmes === */ +#whiteBox .icon { + width: 40px; + height: 40px; + transition: transform 0.2s ease; + margin : 0 auto; + display: block; +} + +#whiteBox .icon:hover { + transform: scale(1.15); +} + +/* === Message (tooltips) === */ +#whiteBox .tooltip { + visibility: hidden; + background-color: #333; + color: #fff; + text-align: center; + padding: 6px; + border-radius: 5px; + position: absolute; + bottom: -34px; + left: 50%; + transform: translateX(-50%); + white-space: nowrap; + font-size: 12px; + opacity: 0; + transition: opacity 0.2s ease, visibility 0.2s ease; + z-index: 1000; +} + +#whiteBox .icon-container:hover .tooltip { + visibility: visible; + opacity: 1; +} + +/* === Style global du lexiconPicker === */ +#lexiconPicker { + position: absolute; + z-index: 10000; + background-color: rgba(255, 255, 255, 0.98); + border: 1px solid #ddd; + padding: 4px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + width: 150px; + font-family: Arial, sans-serif; + font-size: 10px; + display: flex; + flex-direction: row !important; + flex-wrap: wrap !important; + align-items: center; + justify-content: center; + gap: 2px; +} + +/* Style pour les icônes de lexique */ +#lexiconPicker .lexicon-option { + cursor: pointer; + display: inline-flex !important; + flex-direction: row !important; + align-items: center; + justify-content: center; + border: 2px solid transparent; + border-radius: 50%; + width: 40px; + height: 40px; + transition: border 0.2s ease; + flex: 0 0 auto; +} + +/* Effet au survol pour les icônes */ +#lexiconPicker .lexicon-option:hover { + border: 2px solid #6e76c7; +} + +/* Classe pour indiquer qu'une icône est sélectionnée */ +#lexiconPicker .lexicon-option.selected { + border: 2px solid #323046; +} + +/* Style pour le cercle de couleur (icône interne) */ +#lexiconPicker .color-circle { + width: 28px; + height: 28px; + border-radius: 50%; + display: inline-block; + border: 1px solid black; +} + +/* Style pour le bouton de confirmation */ +#lexiconPicker button.confirmButton { + font-style: italic; + font-size: 10px; + padding: 6px 10px; + cursor: pointer; + border: none; + align-items: center; + text-align: center; + border-radius: 4px; + background-color: #323046; + color: white; + flex-basis: 100%; + margin-top: 8px; +} + +#lexiconPicker p { + font-size: 12px; + font-weight: bold; + text-align: center; +} + diff --git a/src/context_menu/custom_context_menu.js b/src/context_menu/custom_context_menu.js new file mode 100644 index 0000000000000000000000000000000000000000..ae90611bbcbc3fc318caafca72f2dd8d141814f0 --- /dev/null +++ b/src/context_menu/custom_context_menu.js @@ -0,0 +1,408 @@ +console.log("custom_context_menu.js chargé correctement"); + +// === Variables globales === +const WHITE_BOX_ID = "whiteBox"; + +// Fonction utilitaire pour envoyer une notification via le background +function sendNotification(title, message, iconPath) { + browser.runtime.sendMessage({ + action: "showNotification", + title, + message, + iconPath + }); +} + +// Récupère le token depuis le stockage local et le stocke dans authToken +async function loadAuthToken() { + try { + const result = await browser.storage.local.get("accessToken"); + authToken = result.accessToken || null; + console.log("🔑 Token chargé :", authToken); + } catch (error) { + console.error("⌠Erreur lors de la récupération du token :", error); + authToken = null; + } +} + +/** + * Crée le menu contextuel personnalisé (whiteBox) s'il n'existe pas déjà . + */ +function injectWhiteBox() { + let whiteBox = document.getElementById(WHITE_BOX_ID); + if (!whiteBox) { + whiteBox = document.createElement("div"); + whiteBox.id = WHITE_BOX_ID; + whiteBox.style.position = "absolute"; + whiteBox.style.zIndex = "9999"; + whiteBox.style.backgroundColor = "#fff"; + whiteBox.style.border = "1px solid #ccc"; + whiteBox.style.padding = "5px"; + whiteBox.style.borderRadius = "4px"; + whiteBox.style.boxShadow = "0px 2px 10px rgba(0,0,0,0.2)"; + const addLexiconPath = browser.runtime.getURL("src/assets/icons/ajout_lexique.png"); + const getDefinitionPath = browser.runtime.getURL("src/assets/icons/definition.png"); + const loginPath = browser.runtime.getURL("src/assets/icons/connexion.png"); + // Construction du HTML du menu contextuel + whiteBox.innerHTML = ` + <p id="selectedWord" style="margin: 0; padding: 0;">Mot sélectionné : Aucun</p> + <hr style="border: 0; height: 1px; background-color: #323046; margin: 8px 0;"> + <div style="display: flex; flex-wrap: wrap; justify-content: center;"> + <!-- Bouton : Ajouter au lexique --> + <div class="icon-container" title="Ajouter ce mot à un lexique"> + <img src="${addLexiconPath}" alt="Ajouter au lexique" class="icon" id="addLexiconButton"> + </div> + <!-- Bouton : Obtenir une définition --> + <div class="icon-container" title="Obtenir la définition"> + <img src="${getDefinitionPath}" alt="Obtenir la définition" class="icon" id="getDefinitionButton"> + </div> + <!-- Bouton : Connexion --> + <div class="icon-container" title="Connectez-vous à BaLex"> + <img src="${loginPath}" alt="Se connecter" class="icon" id="loginButton" style="display: none;"> + </div> + </div> + `; + document.body.appendChild(whiteBox); + setupWhiteBoxActions(); + } + whiteBox.addEventListener("mouseup", (e) => { + e.stopPropagation(); + }); + return whiteBox; +} + +/** + * Renvoie le whiteBox s'il existe, ou le crée. + */ +function getOrCreateWhiteBox() { + return document.getElementById(WHITE_BOX_ID) || injectWhiteBox(); +} + +/** + * Configure les actions des boutons du menu contextuel. + */ +function setupWhiteBoxActions() { + const addLexiconBtn = document.getElementById("addLexiconButton"); + const getDefinitionBtn = document.getElementById("getDefinitionButton"); + const loginBtn = document.getElementById("loginButton"); + + // Bouton : Ajouter le mot au lexique + addLexiconBtn.onclick = async (e) => { + e.stopPropagation(); + e.preventDefault(); + const selectedText = getSelectedWord().trim(); + console.log("🔠Bouton Ajouter au lexique cliqué avec le mot :", selectedText); + if (!selectedText) return; + if (authToken) { + browser.runtime.sendMessage({ action: "openLexiconBlock" }); + showPicker(e, selectedText); + } else { + alert("Vous devez être connecté pour ajouter un mot."); + } + }; + + // Bouton : Obtenir une définition + getDefinitionBtn.onclick = () => { + const selectedText = getSelectedWord().trim(); + if (selectedText) { + browser.runtime.sendMessage({ + action: "getDefinition", + selectedText + }); + } + }; + + // Bouton : Connexion + loginBtn.onclick = () => { + browser.runtime.sendMessage({ action: "toggleAuth" }); + }; +} + +/** + * Met à jour la visibilité des boutons du menu selon l'authentification. + */ +function updateMenuVisibility() { + getOrCreateWhiteBox(); + const addLexiconBtn = document.getElementById("addLexiconButton"); + const getDefinitionBtn = document.getElementById("getDefinitionButton"); + const loginBtn = document.getElementById("loginButton"); + + if (!addLexiconBtn || !getDefinitionBtn || !loginBtn) { + console.warn("âš ï¸ Un des boutons n'a pas été trouvé dans le DOM."); + return; + } + + if (authToken) { + addLexiconBtn.style.display = "inline-block"; + getDefinitionBtn.style.display = "inline-block"; + loginBtn.style.display = "none"; + } else { + addLexiconBtn.style.display = "none"; + getDefinitionBtn.style.display = "inline-block"; + loginBtn.style.display = "inline-block"; + } +} + +/** + * Récupère le texte affiché dans #selectedWord. + */ +function getSelectedWord() { + const selectedWordElement = document.getElementById("selectedWord"); + return selectedWordElement ? selectedWordElement.textContent.trim() : ""; +} + +/** + * Affiche le menu contextuel à la position du clic. + */ +function showWhiteBox(event, selectedText) { + const whiteBox = getOrCreateWhiteBox(); + const selectedWordElement = document.getElementById("selectedWord"); + selectedWordElement.textContent = selectedText; + + const selection = window.getSelection(); + if (!selection.rangeCount) return; + const range = selection.getRangeAt(0); + const rect = range.getBoundingClientRect(); + const top = rect.bottom + window.scrollY; + const left = rect.right + window.scrollX; + + whiteBox.style.left = left + "px"; + whiteBox.style.top = top + "px"; + whiteBox.style.display = "block"; + + console.log("Affichage du menu contextuel avec le mot :", selectedText); + updateMenuVisibility(); +} + +function hideWhiteBox() { + const whiteBox = document.getElementById(WHITE_BOX_ID); + if (whiteBox) { + whiteBox.style.display = "none"; + } +} + +// Écoute globale pour la sélection de texte +document.addEventListener("mouseup", (event) => { + if (event.target.closest("#whiteBox")) return; + const selectedText = window.getSelection().toString().trim(); + if (selectedText) { + console.log("Texte sélectionné :", selectedText); + getOrCreateWhiteBox(); + showWhiteBox(event, selectedText); + browser.runtime.sendMessage({ + action: "mot_selectionne", + selectedText, + }); + } else { + hideWhiteBox(); + } +}); + +// Écoute des messages entrants +browser.runtime.onMessage.addListener((message) => { + if (message.action === "refreshUI") { + console.log("🔄 Mise à jour du menu contextuel personnalisé."); + loadAuthToken().then(updateMenuVisibility); + } +}); + +// Initialisation au démarrage +loadAuthToken().then(() => { + getOrCreateWhiteBox(); + updateMenuVisibility(); +}); +browser.storage.onChanged.addListener((changes) => { + if (changes.accessToken) { + console.log("🔄 Token modifié dans le stockage, mise à jour du menu contextuel."); + loadAuthToken().then(updateMenuVisibility); + } +}); + +// ───────────────────────────────────────────────────────────────────────────── +// Fonctions d'API pour l'ajout d'un mot via le sélecteur +// ───────────────────────────────────────────────────────────────────────────── + +/** + * Crée (ou récupère) le sélecteur des lexiques. + */ +function createPicker() { + let picker = document.getElementById("lexiconPicker"); + if (!picker) { + picker = document.createElement("div"); + picker.id = "lexiconPicker"; + picker.style.position = "absolute"; + picker.style.zIndex = "10000"; + picker.style.backgroundColor = "rgba(255, 255, 255, 0.98)"; + picker.style.border = "1px solid #ddd"; + picker.style.padding = "8px"; + picker.style.borderRadius = "8px"; + picker.style.boxShadow = "0 4px 8px rgba(0, 0, 0, 0.1)"; + picker.style.width = "220px"; + picker.style.fontFamily = "Arial, sans-serif"; + picker.style.fontSize = "10px"; + picker.style.display = "flex"; + picker.style.flexDirection = "row"; + picker.style.flexWrap = "wrap"; + picker.style.alignItems = "center"; + picker.style.justifyContent = "center"; + picker.style.gap = "5px"; + + picker.addEventListener("mouseup", (e) => { + e.stopPropagation(); + }); + + document.body.appendChild(picker); + } + return picker; +} + +/** + * Affiche le sélecteur pour choisir le lexique dans lequel ajouter le mot. + */ +async function showPicker(event, selectedText) { + let picker = document.getElementById("lexiconPicker"); + if (!picker) { + picker = document.createElement("div"); + picker.id = "lexiconPicker"; + document.body.appendChild(picker); + } + picker.innerHTML = ""; + + const selectedLexicons = new Set(); + + try { + const lexicons = await getLexicons(authToken, "fr"); + console.log("Lexicons récupérés :", lexicons); + + if (!Array.isArray(lexicons) || lexicons.length === 0) { + picker.innerHTML = "<p style='color:#333;'>Aucun lexique trouvé.</p>"; + } else { + for (const lex of lexicons) { + const id = lex.id; + const name = lex.category === "User" + ? `Lexique personnel : ${lex.user?.pseudo || "Inconnu"} (${lex.id})` + : `Lexique de groupe : ${lex.group?.name || "Inconnu"} (${lex.id})`; + + const color = await getOrCreateLexiconColor(id); + const circleIcon = createColorCircle(color, 28); + const iconContainer = document.createElement("div"); + iconContainer.className = "lexicon-option"; + iconContainer.dataset.lexiconId = id; + iconContainer.title = name; + iconContainer.addEventListener("click", () => { + if (selectedLexicons.has(id)) { + selectedLexicons.delete(id); + iconContainer.classList.remove("selected"); + } else { + selectedLexicons.add(id); + iconContainer.classList.add("selected"); + } + }); + iconContainer.appendChild(circleIcon); + picker.appendChild(iconContainer); + } + + const confirmButton = document.createElement("button"); + confirmButton.className = "confirmButton"; + confirmButton.textContent = "Ajouter le mot"; + + confirmButton.addEventListener("click", async () => { + if (selectedLexicons.size === 0) { + alert("Veuillez sélectionner au moins un lexique."); + return; + } + + console.log(`🔠Vérification si le mot "${selectedText}" existe déjà dans les lexiques sélectionnés...`); + + let definitions = []; + try { + definitions = await fetchLexiconDefinitions(selectedText); + } catch (error) { + console.error("Erreur lors de la récupération des définitions :", error); + } + + const existingLexiconIds = new Set(); + if (Array.isArray(definitions)) { + for (const def of definitions) { + if (selectedLexicons.has(def.lexiconId)) { + existingLexiconIds.add(def.lexiconId); + } + } + } + + if (existingLexiconIds.size > 0) { + alert(`âš ï¸ Le mot "${selectedText}" existe déjà dans les lexiques suivants : ${Array.from(existingLexiconIds).join(", ")}`); + } + + const lexiconsToAdd = [...selectedLexicons].filter(id => !existingLexiconIds.has(id)); + if (lexiconsToAdd.length === 0) { + alert(`✅ Le mot "${selectedText}" est déjà présent dans tous les lexiques sélectionnés.`); + return; + } + + try { + console.log(`📡 Ajout du mot "${selectedText}" dans les lexiques :`, lexiconsToAdd); + const result = await AddWord(authToken, selectedText, lexiconsToAdd, false); + console.log("Réponse API :", result); + + await new Promise(resolve => setTimeout(resolve, 300)); + browser.runtime.sendMessage({ action: "refreshUI" }); + + picker.innerHTML = `<p style="color: green;">✅ Mot ajouté avec succès dans : ${lexiconsToAdd.join(", ")}.</p>`; + setTimeout(() => picker.style.display = "none", 2000); + browser.runtime.sendMessage({ + action: "addWordResult", + lexicons: `✅ Mot ajouté avec succès dans : ${lexiconsToAdd.join(", ")}.` + }); + } catch (error) { + console.error("⌠Erreur lors de l'ajout du mot :", error); + picker.innerHTML = `<p style="color: red;">⌠Erreur : ${error.message}</p>`; + setTimeout(() => picker.style.display = "none", 3000); + browser.runtime.sendMessage({ + action: "addWordResult", + lexicons: `⌠Erreur lors de l'ajout du mot : ${error.message}` + }); + } + }); + + picker.appendChild(confirmButton); + } + + picker.style.left = event.pageX + "px"; + picker.style.top = event.pageY + "px"; + picker.style.display = "flex"; + } catch (error) { + console.error("⌠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"; + } +} + +function hideLexiconPicker() { + const picker = document.getElementById("lexiconPicker"); + if (picker) { + picker.style.display = "none"; + } +} + +document.addEventListener("mouseup", (event) => { + const whiteBox = document.getElementById(WHITE_BOX_ID); + const picker = document.getElementById("lexiconPicker"); + + if (whiteBox && !whiteBox.contains(event.target)) { + hideWhiteBox(); + } + if (picker && !picker.contains(event.target)) { + hideLexiconPicker(); + } + + const selectedText = window.getSelection().toString().trim(); + if (selectedText) { + console.log("Texte sélectionné :", selectedText); + showWhiteBox(event, selectedText); + browser.runtime.sendMessage({ + action: "mot_selectionne", + selectedText, + }); + } +}); diff --git a/menu_extension/options.html b/src/options/options.html similarity index 100% rename from menu_extension/options.html rename to src/options/options.html diff --git a/menu_extension/options.js b/src/options/options.js similarity index 100% rename from menu_extension/options.js rename to src/options/options.js diff --git a/menu_extension/popup.html b/src/popup/popup.html similarity index 100% rename from menu_extension/popup.html rename to src/popup/popup.html diff --git a/menu_extension/popup.js b/src/popup/popup.js similarity index 100% rename from menu_extension/popup.js rename to src/popup/popup.js diff --git "a/barre_lat\303\251rale/sidebar.html" b/src/sidebar/sidebar.html similarity index 51% rename from "barre_lat\303\251rale/sidebar.html" rename to src/sidebar/sidebar.html index 5ea40ba7d782810b15615b1871ce659499938cf8..84ab2a28c0d0e272f945eac156287775ce676ace 100644 --- "a/barre_lat\303\251rale/sidebar.html" +++ b/src/sidebar/sidebar.html @@ -4,8 +4,9 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>BaLex - Barre Latérale</title> - <script src="../api.js" defer></script> - <script src="../definitions.js" defer></script> + <script src="../assets/lexicon_icon.js" defer></script> + <script src="../utils/api.js" defer></script> + <script src="../utils/definitions.js" defer></script> <script src="sidebar.js" defer></script> <style> @@ -25,23 +26,57 @@ margin-bottom: 10px; border-radius: 10px; background-color: #a08e9f; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 2px 5px rgba(0,0,0,0.2); + overflow: visible; } /* En-têtes de blocs */ .block-header { position: relative; - text-align: center; + text-align: center; margin-top: 2px; } - .block-header h3 { display: inline-block; margin-top: 5px; margin-bottom: 0; } - /* Utilisation de la classe pour les boutons de bascule */ + /* Bouton de connexion */ + #auth-button { + width: auto; + display: inline-flex; + padding: 6px 12px; + font-size: 18px; + font-family: Bradley Hand, cursive; + background: none; + border: none; + color: white; + align-items: center; + gap: 6px; + border-radius: 20px; + cursor: pointer; + transition: background 0.3s; + } + #auth-button:hover { + background: rgba(255,255,255,0.2); + } + #auth-button svg { + width: 18px; + height: 18px; + fill: white; + transition: transform 0.3s ease-in-out; + } + #auth-button:hover svg { + transform: scale(1.1); + } + #auth-section { + display: flex; + justify-content: flex-end; + margin-bottom: 10px; + } + + /* Boutons de bascule (toggle) */ .toggle-btn { position: absolute; right: 8px; @@ -49,23 +84,22 @@ margin: 0; border: none; color: #fff; - font-size: 15px; - padding: 3px 5px; + font-size: 15px; + padding: 3px 5px; cursor: pointer; - width: auto; - display: inline-block; + width: auto; + display: inline-block; } /* Contenu des blocs */ .block-content { padding-top: 2px; } - .hidden { display: none; } - /* Boutons */ + /* Boutons standards */ button { width: 100%; margin-top: 5px; @@ -78,7 +112,6 @@ text-align: center; border-radius: 5px; } - button:hover { background-color: #dddedd; color: #8d5c70; @@ -96,39 +129,24 @@ display: flex; align-items: center; justify-content: space-between; - padding: 8px; + padding: 6px; margin-bottom: 5px; border-radius: 5px; background-color: #dcdde1; - width: 100%; + position: relative; } - .lexique-item:hover { background-color: #c4c7ce; } - .lexique-label { font-weight: bold; color: #323046; flex-grow: 1; + font-size: 15px; text-align: center; } - .lexique-checkbox { - transform: scale(1.2); - cursor: pointer; - flex-shrink: 0; - margin-left: 10px; - } - - /* Texte explicatif */ - #highlight-note { - font-size: small; - margin-bottom: 10px; - text-align: center; - } - - /* Espace pour les pictogrammes */ + /* Icône du lexique */ .lexique-icon { width: 25px; height: 25px; @@ -138,10 +156,138 @@ flex-shrink: 0; } + /* Conteneur pour tooltip (pour checkbox et surlignage) */ + .tooltip-container { + position: relative; + display: inline-block; + cursor: pointer; + overflow: visible; + } + + /* Style pour tous les tooltips */ + .tooltip { + all: unset; + display: block; + box-sizing: border-box; + position: absolute; + bottom: 120%; + left: 50%; + transform: translateX(-50%); + background-color: rgba(0,0,0,0.75); + color: #fff; + font-size: 14px !important; + font-weight: lighter !important; + padding: 6px 10px; + border-radius: 5px; + white-space: normal; + overflow-wrap: break-word; + width: 180px; + text-align: center; + opacity: 0; + transition: opacity 0.3s ease-in-out, transform 0.2s ease-in-out; + pointer-events: none; + z-index: 10; + line-height: normal; + } + + .tooltip-container:hover .tooltip { + opacity: 1; + transform: translateX(-50%) translateY(-5px); + } + .tooltip-container.left .tooltip { + left: 0; + transform: translateX(0) translateY(-5px); + } + .tooltip-container.right .tooltip { + right: 0; + left: auto; + transform: translateX(0) translateY(-5px); + } + + /* Cases à cocher personnalisées */ + .lexique-checkbox { + appearance: none; + width: 20px; + height: 20px; + border: 2px solid #8d5c70; + border-radius: 5px; + background-color: #fff; + transition: background 0.3s ease, border-color 0.3s ease; + cursor: pointer; + position: relative; + } + .lexique-checkbox:hover { + border-color: #6a3e50; + } + .lexique-checkbox:checked { + background-color: #8d5c70; + border-color: #8d5c70; + } + .lexique-checkbox:checked::after { + content: '✔'; + font-size: 16px; + color: white; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + /* Bouton de surlignage */ + .lexique-highlight-toggle { + background: none; + border: none; + cursor: pointer; + padding: 2px; + transition: transform 0.2s ease-in-out; + width: 15%; + position: relative; + } + + .feutre-icon { + width: 20px; + height: 20px; + filter: brightness(0) saturate(100%) invert(40%) sepia(0%) saturate(0%) hue-rotate(0deg); + transition: filter 0.3s ease-in-out; + } + + .lexique-highlight-toggle[data-active="true"] .feutre-icon { + filter: brightness(0) saturate(100%) invert(83%) sepia(89%) saturate(588%) hue-rotate(360deg); + } + + button.lexique-highlight-toggle .tooltip { + all: unset; + display: block; + box-sizing: border-box; + position: absolute; + bottom: 120%; + left: 50%; + transform: translateX(-50%) translateY(-5px); + background-color: rgba(0, 0, 0, 0.75); + color: #fff; + font-size: 14px; + font-weight: lighter; + padding: 6px 10px; + border-radius: 5px; + white-space: normal; + overflow-wrap: break-word; + width: 180px; + text-align: center; + opacity: 0; + transition: opacity 0.3s ease-in-out, transform 0.2s ease-in-out; + pointer-events: none; + z-index: 10; + line-height: normal; + } + + button.lexique-highlight-toggle:hover .tooltip { + opacity: 1; + transform: translateX(-50%) translateY(-5px); + } + .lexicon-section { margin-bottom: 10px; } - .lexicon-header { font-weight: bold; cursor: pointer; @@ -150,72 +296,62 @@ border-radius: 5px; text-align: center; } - .lexicon-header:hover { background-color: #dddedd; color: #8d5c70; } - .lexicon-content { margin-top: 5px; } - + .lexicon-option { + margin-right: -10px; + } #mesLexiquesList { display: inline; padding: 0; align-items: center; } - #mesLexiquesContainer h4 { margin-bottom: 5px; } - - /* Définition */ #definitionContainer { background-color: #444; padding: 10px; border-radius: 10px; color: white; } - #definitionsList { list-style: none; padding: 0; } - #definitionsList li { margin-bottom: 10px; } - .definition-source { font-weight: bold; color: #ffa500; } - - /* Popup (modal) caché par défaut */ .modal-overlay { position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0,0,0,0.5); - display: none; - align-items: center; - justify-content: center; - z-index: 9999; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0,0,0,0.5); + display: none; + align-items: center; + justify-content: center; + z-index: 9999; } - .modal-content { - background: white; + background: white; color: #8d5c70; - padding: 1rem; - max-width: 600px; + padding: 1rem; + max-width: 600px; max-height: 80vh; overflow-y: auto; border-radius: 8px; } - .close-button { float: right; cursor: pointer; @@ -225,46 +361,49 @@ .close-button:hover { color: #000; } - - /* Activer/désactiver le surlignage */ #highlighting-options p { margin: 5px 0; font-size: small; color: #333; } - #noDefinitionsContainer { display: block !important; color: red !important; font-weight: bold; } </style> - </head> <body> + <!-- Bouton de connexion --> + <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> <!-- Bloc 1 : Menu des lexiques --> <div id="menu"> <div class="block-header"> <h3>Lexiques</h3> - <button class="toggle-btn"></button> + <button class="toggle-btn">+</button> </div> - <div id="menuContent" class="block-content"> - <p id="highlight-note" style="display: none;">Cochez un/des lexique(s) pour activer le surlignage des mots sur la page.</p> + <div id="menuContent" class="block-content hidden"> <div id="lexiques">Chargement...</div> - <div id="auth-section"> - <button id="auth-button">Se connecter</button> - </div> </div> </div> - + <!-- Bloc 2 : État de la sélection --> <div id="etat"> <div class="block-header"> <h3>Mot sélectionné</h3> <button class="toggle-btn"></button> </div> - <div id="etatContent" class="block-content"> + <div id="etatContent" class="block-content hidden"> <p id="motSelectionne">Aucun mot sélectionné</p> <p id="lexiconResult"></p> <div id="add-to-lexiques" style="display: none;"> @@ -274,14 +413,14 @@ <div id="possible-definitions" style="display: none;"></div> </div> </div> - + <!-- Bloc 3 : Définitions --> <div id="definitionContainer"> <div class="block-header"> <h3>Définitions</h3> <button class="toggle-btn"></button> </div> - <div id="definitionContent" class="block-content"> + <div id="definitionContent" class="block-content hidden"> <!-- Définitions des lexiques de l'utilisateur --> <div id="mesLexiquesContainer"> <h4>📚 Mes lexiques</h4> diff --git "a/barre_lat\303\251rale/sidebar.js" b/src/sidebar/sidebar.js similarity index 58% rename from "barre_lat\303\251rale/sidebar.js" rename to src/sidebar/sidebar.js index b920188bcb008c93a066822d8efad2d8effaae76..8b8000124f211b1224065f359e63978b6ae60900 100644 --- "a/barre_lat\303\251rale/sidebar.js" +++ b/src/sidebar/sidebar.js @@ -3,14 +3,13 @@ console.log( "🌠Vérification API browser :", typeof browser !== "undefined" ? "✅ Disponible" : "⌠Non disponible" ); - +console.log("getLexicons type:", typeof getLexicons); +console.log("window.getLexicons type:", typeof window.getLexicons); // ───────────────────────────────────────────────────────────────────────────── // â–Œ Variables globales // ───────────────────────────────────────────────────────────────────────────── -let authToken = null; - -window.lexiconMap = lexiconMap; +let authToken = window.authToken; window.authToken = authToken; // ───────────────────────────────────────────────────────────────────────────── @@ -47,6 +46,7 @@ function updateAuthButton(isLoggedIn) { } } + function toggleElementsVisibility(isLoggedIn) { const elementsToShowOrHide = [ { id: "add-to-lexiques", shouldShow: isLoggedIn }, @@ -87,6 +87,19 @@ async function refreshSidebarState() { if (isLoggedIn) { await fetchLexicons(); } else { + // Si l'utilisateur n'est pas connecté, on ferme tous les blocs + document.querySelectorAll('.block-content').forEach(block => { + block.classList.add('hidden'); + // Mise à jour des boutons de bascule associés pour afficher le symbole "+" + const header = block.previousElementSibling; + if (header) { + const toggleBtn = header.querySelector(".toggle-btn"); + if (toggleBtn) { + toggleBtn.textContent = '+'; + } + } + }); + const lexiquesContainer = document.getElementById("lexiques"); if (lexiquesContainer) { lexiquesContainer.textContent = "Veuillez vous connecter pour voir vos lexiques."; @@ -156,7 +169,7 @@ async function fetchLexicons() { if (!authToken) { throw new Error("âš ï¸ Aucun token disponible. Veuillez vous connecter."); } - + console.log("getLexicons type:", typeof getLexicons); const lexicons = await getLexicons(authToken, "fr"); console.log("📚 Réponse brute de l'API :", lexicons); @@ -190,7 +203,11 @@ async function fetchLexicons() { } } -function displayLexiconsWithCheckbox(lexicons) { +/** + * Affiche la liste des lexiques avec des checkboxes dans la barre latérale. + * @param {Array} lexicons - Liste des lexiques à afficher. + */ +async function displayLexiconsWithCheckbox(lexicons) { const lexiquesContainer = document.getElementById("lexiques"); if (!lexiquesContainer) { console.warn("âš ï¸ Ã‰lément #lexiques introuvable."); @@ -202,36 +219,96 @@ function displayLexiconsWithCheckbox(lexicons) { lexiquesContainer.textContent = "Aucun lexique disponible."; return; } - lexicons.forEach(({ lexiconName, lexiconId, active }) => { + + for (const { lexiconName, lexiconId, active } of lexicons) { const lexiqueDiv = document.createElement("div"); lexiqueDiv.className = "lexique-item"; + + const color = await getOrCreateLexiconColor(lexiconId); + const circleIcon = createColorCircle(color, 24); + const iconDiv = document.createElement("div"); iconDiv.className = "lexique-icon"; - iconDiv.style.backgroundColor = "#ccc"; + iconDiv.appendChild(circleIcon); + const labelSpan = document.createElement("span"); labelSpan.className = "lexique-label"; labelSpan.textContent = lexiconName; - const checkbox = document.createElement("input"); - checkbox.type = "checkbox"; - checkbox.className = "lexique-checkbox"; - checkbox.checked = active; - checkbox.addEventListener("change", async () => { - console.log( - `🔄 Changement de surlignage pour ${lexiconName} (ID: ${lexiconId}): ${ - checkbox.checked ? "activé" : "désactivé" - }` - ); - await browser.runtime.sendMessage({ - action: "toggleLexiconHighlight", - lexiconId, - isActive: checkbox.checked, - }); + + // Conteneur pour la checkbox (avec tooltip) + const checkboxContainer = document.createElement("label"); + checkboxContainer.className = "tooltip-container lexique-checkbox-container"; + const addCheckbox = document.createElement("input"); + addCheckbox.type = "checkbox"; + addCheckbox.className = "lexique-checkbox"; + addCheckbox.dataset.lexiconId = lexiconId; + const checkboxTooltip = document.createElement("span"); + checkboxTooltip.className = "tooltip"; + checkboxTooltip.textContent = "Ajouter le mot à ce lexique"; + checkboxContainer.appendChild(addCheckbox); + checkboxContainer.appendChild(checkboxTooltip); + + // Conteneur pour le bouton de surlignage (avec tooltip) + const highlightButton = document.createElement("button"); + highlightButton.className = "tooltip-container lexique-highlight-toggle"; + highlightButton.dataset.lexiconId = lexiconId; + highlightButton.dataset.active = active ? "true" : "false"; + const feutreIcon = document.createElement("img"); + feutreIcon.src = "../assets/icons/feutre.png"; + feutreIcon.alt = "Feutre"; + feutreIcon.className = "feutre-icon"; + const highlightTooltip = document.createElement("span"); + highlightTooltip.className = "tooltip"; + highlightTooltip.textContent = "Activer/Désactiver le surlignage des mots du lexique"; + + // Gestion du clic pour activer/désactiver le surlignage + highlightButton.addEventListener("click", async () => { + let currentState = highlightButton.dataset.active === "true"; + let newState = !currentState; + try { + await browser.runtime.sendMessage({ + action: "toggleLexiconHighlight", + lexiconId, + isActive: newState, + }); + highlightButton.dataset.active = newState ? "true" : "false"; + } catch (error) { + console.error("Erreur lors du toggle de surlignage pour le lexique", lexiconId, ":", error); + } }); + + highlightButton.appendChild(feutreIcon); + highlightButton.appendChild(highlightTooltip); lexiqueDiv.appendChild(iconDiv); lexiqueDiv.appendChild(labelSpan); - lexiqueDiv.appendChild(checkbox); + lexiqueDiv.appendChild(checkboxContainer); + lexiqueDiv.appendChild(highlightButton); + lexiquesContainer.appendChild(lexiqueDiv); - }); + } + + setTimeout(() => { + const menu = document.getElementById("menu"); + if (!menu) return; + const menuRect = menu.getBoundingClientRect(); + const containers = document.querySelectorAll('.tooltip-container'); + + containers.forEach(container => { + const tooltip = container.querySelector('.tooltip'); + if (!tooltip) return; + tooltip.style.left = '50%'; + tooltip.style.transform = 'translateX(-50%) translateY(-5px)'; + + const tooltipRect = tooltip.getBoundingClientRect(); + if (tooltipRect.left < menuRect.left) { + const overflowLeft = menuRect.left - tooltipRect.left; + tooltip.style.transform = `translateX(calc(-100% + ${overflowLeft}px)) translateY(-5px)`; + } else if (tooltipRect.right > menuRect.right) { + const overflowRight = tooltipRect.right - menuRect.right; + tooltip.style.transform = `translateX(calc(-100% - ${overflowRight}px)) translateY(-5px)`; + } + }); + }, 100); } function initModal() { @@ -266,6 +343,99 @@ async function handleAuthButtonClick() { await refreshSidebarState(); } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Ajout d'un mot au(x) lexique(s) +// ───────────────────────────────────────────────────────────────────────────── +async function handleAddWordClick() { + openBlock("menuContent"); + // 1) Vérifier la présence du token et du mot + if (!authToken) { + console.warn("âš ï¸ Pas de token d'authentification : impossible d'ajouter le mot."); + return; + } + + const selectedWordElement = document.getElementById("motSelectionne"); + const lexiconResultElement = document.getElementById("lexiconResult"); + + if (!selectedWordElement) { + console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); + return; + } + const selectedWord = selectedWordElement.textContent.trim(); + if (!selectedWord || selectedWord === "Aucun mot sélectionné") { + console.warn("âš ï¸ Aucun mot à ajouter."); + if (lexiconResultElement) lexiconResultElement.textContent = "Aucun mot à ajouter."; + return; + } + + // 2) Récupérer les IDs des lexiques sélectionnés + const checkboxList = document.querySelectorAll("#lexiques .lexique-checkbox:checked"); + const selectedLexiconIds = Array.from(checkboxList).map(cb => parseInt(cb.dataset.lexiconId, 10)); + + if (selectedLexiconIds.length === 0) { + console.warn("âš ï¸ Aucun lexique sélectionné."); + if (lexiconResultElement) lexiconResultElement.textContent = "Veuillez cocher au moins un lexique."; + return; + } + + // 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); + } + + const existingLexiconIds = new Set(); + if (Array.isArray(definitions)) { + for (const def of definitions) { + if (selectedLexiconIds.includes(def.lexiconId)) { + existingLexiconIds.add(def.lexiconId); + } + } + } + + // 4) Si le mot existe déjà dans certains lexiques, on les affiche + if (existingLexiconIds.size > 0) { + if (lexiconResultElement) { + lexiconResultElement.innerHTML = + "Le mot <strong>" + selectedWord + "</strong> existe déjà dans le(s) lexique(s) : " + + Array.from(existingLexiconIds).join(", ") + "."; + } + } + + // 5) Déterminer les lexiques où ajouter le mot + const lexiconsToAdd = selectedLexiconIds.filter(id => !existingLexiconIds.has(id)); + if (lexiconsToAdd.length === 0) { + return; + } + + // 6) Envoi d’une seule requête pour tous les lexiques restants + try { + console.log(`📡 Envoi de l'ajout du mot "${selectedWord}" dans les lexiques :`, lexiconsToAdd); + const result = await window.AddWord(authToken, selectedWord, lexiconsToAdd, false); + + console.log("Réponse API :", result); + + // Rafraîchir l'UI et la liste des entrées + await new Promise(resolve => setTimeout(resolve, 300)); + browser.runtime.sendMessage({ action: "refreshUI" }); + + // 7) Affichage du message de succès + if (lexiconResultElement) { + lexiconResultElement.innerHTML += + "<br>✅ Mot <strong>" + selectedWord + "</strong> ajouté avec succès dans : " + + lexiconsToAdd.join(", ") + "."; + } + + } catch (error) { + console.error("Erreur lors de l’ajout du mot :", error); + if (lexiconResultElement) { + lexiconResultElement.textContent = "Erreur lors de l’ajout : " + error.message; + } + } +} + // ───────────────────────────────────────────────────────────────────────────── // â–Œ Réception des messages // ───────────────────────────────────────────────────────────────────────────── @@ -277,22 +447,22 @@ browser.runtime.onMessage.addListener(async (message) => { await refreshSidebarState(); break; - case "mot_selectionne": - if (message.selectedText) { - console.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"); + case "mot_selectionne": + if (message.selectedText) { + console.log("ðŸ–‹ï¸ Mot sélectionné :", message.selectedText); + const selectedWordElement = document.getElementById("motSelectionne"); + if (selectedWordElement) { + selectedWordElement.textContent = message.selectedText; + } else { + console.warn("âš ï¸ Ã‰lément #motSelectionne introuvable."); } - break; + const lexiconResultElement = document.getElementById("lexiconResult"); + if (lexiconResultElement) { + lexiconResultElement.innerHTML = ""; + } + openBlock("etatContent"); + } + break; case "getDefinition": if (message.selectedText) { @@ -319,7 +489,22 @@ browser.runtime.onMessage.addListener(async (message) => { console.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 "addToLexicon": + handleAddWordClick(); + break; + + case "openLexiconBlock": + openBlock("menuContent"); + break; + case "toggleAuth": break; @@ -343,7 +528,7 @@ document.addEventListener("DOMContentLoaded", async () => { authButton.addEventListener("click", handleAuthButtonClick); } - const chercherDefButton = document.getElementById("chercherDef"); + const chercherDefButton = document.querySelector("#chercherDef"); if (chercherDefButton) { chercherDefButton.addEventListener("click", async () => { openBlock("definitionContent"); @@ -379,9 +564,16 @@ document.addEventListener("DOMContentLoaded", async () => { }); }); } -}); -document.addEventListener("DOMContentLoaded", () => { + // Bouton "Ajouter le mot sélectionné" + const addWordButton = document.getElementById("add-word-button"); + if (addWordButton) { + addWordButton.addEventListener("click", handleAddWordClick); + } else { + console.warn("âš ï¸ Bouton #add-word-button introuvable dans le DOM."); + } + + // Écouteur pour la case à cocher "toggle-definitions" const toggleButtons = document.querySelectorAll(".toggle-btn"); toggleButtons.forEach((btn) => { if (!btn.textContent.trim()) { @@ -397,5 +589,37 @@ document.addEventListener("DOMContentLoaded", () => { btn.textContent = content.classList.contains("hidden") ? "+" : "–"; } }); + + document.querySelectorAll('.block-content').forEach(block => { + block.classList.add('hidden'); + }); + + document.querySelectorAll('.toggle-btn').forEach(btn => { + btn.textContent = '+'; + btn.style.fontSize = '15px'; + + btn.addEventListener('click', (event) => { + event.stopPropagation(); + const header = btn.parentElement; + const content = header.nextElementSibling; + if (content) { + content.classList.toggle('hidden'); + btn.textContent = content.classList.contains('hidden') ? '+' : '–'; + } + }); + }); + }); +}); + +document.querySelectorAll('.toggle-btn').forEach(btn => { + btn.addEventListener('click', function() { + const blockContent = this.parentElement.nextElementSibling; + if (blockContent.classList.contains('hidden')) { + blockContent.classList.remove('hidden'); + this.textContent = '–'; + } else { + blockContent.classList.add('hidden'); + this.textContent = '+'; + } }); }); diff --git a/api.js b/src/utils/api.js similarity index 78% rename from api.js rename to src/utils/api.js index c1a046da58aa1fc2975832be585e502e011b4537..c1bc41c1f1c05a0697308e289a0bcd623925925f 100644 --- a/api.js +++ b/src/utils/api.js @@ -1,5 +1,5 @@ console.log("✅ api.js chargé correctement"); - +window.authToken = null; // ───────────────────────────────────────────────────────────────────────────── // â–Œ Sélection de texte sur la page // ───────────────────────────────────────────────────────────────────────────── @@ -17,19 +17,27 @@ document.addEventListener("mouseup", () => { // â–Œ Fonction utilitaire pour appeler l’API // ───────────────────────────────────────────────────────────────────────────── /** - * Effectue une requête API + * Effectue une requête API (GET, POST, etc.) avec ou sans body JSON * @param {string} url - L'URL de l'API à appeler. * @param {string|null} authToken - Le token d'authentification. * @param {string} [method='GET'] - La méthode HTTP. + * @param {object|null} [data=null] - Les données à envoyer dans le body (pour POST/PUT...). * @returns {Promise<any>} - La réponse en JSON. * @throws {Error} - En cas d'échec. */ -async function callApi(url, authToken = null, method = 'GET') { +async function callApi(url, authToken = null, method = 'GET', data = null) { const headers = { 'Content-Type': 'application/json' }; if (authToken) headers.Authorization = `Bearer ${authToken}`; + const fetchOptions = { method, headers }; + console.log("Envoi de la requête vers :", url); + if (data) { + console.log("Body JSON :", JSON.stringify(data, null, 2)); + fetchOptions.body = JSON.stringify(data); + } + try { - const response = await fetch(url, { method, headers }); + const response = await fetch(url, fetchOptions); if (!response.ok) { throw new Error(`⌠Erreur API (${response.status}): ${response.statusText}`); } @@ -40,6 +48,7 @@ async function callApi(url, authToken = null, method = 'GET') { } } + // ───────────────────────────────────────────────────────────────────────────── // â–Œ Récupération des lexiques de l'utilisateur // ───────────────────────────────────────────────────────────────────────────── @@ -174,13 +183,50 @@ async function getWiktionaryDefinition(word) { } } +// ───────────────────────────────────────────────────────────────────────────── +// â–Œ Ajout d'un mot dans un/des lexique(s) +// ───────────────────────────────────────────────────────────────────────────── +/** + * Ajoute un mot (selectedWord) dans un ou plusieurs lexiques (lexiconIds). + * @param {string} authToken - Jeton d'authentification (Bearer). + * @param {string} selectedWord - Le mot à ajouter. + * @param {number[]} lexiconIds - Tableau d'IDs de lexiques cibles. + * @param {boolean} [force=false] - Paramètre optionnel pour l'API. + * @returns {Promise<any>} - La réponse JSON de l'API, ou une exception si échec. + */ +async function AddWord(authToken, selectedWord, lexiconIds, force = false) { + if (!authToken) { + throw new Error("Aucun token d’authentification fourni."); + } + if (!selectedWord) { + 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."); + } + + const url = "https://babalex.lezinter.net/api/entry/create"; + const body = { + graphy: selectedWord, + force, + target_lex: lexiconIds + }; + + return callApi(url, authToken, "POST", body); +} + + + // ───────────────────────────────────────────────────────────────────────────── // â–Œ Exposition des fonctions pour un usage global // ───────────────────────────────────────────────────────────────────────────── window.callApi = callApi; window.getLexicons = getLexicons; +console.log("getLexicons exposée, type:", typeof window.getLexicons); window.getAllCategoriesLexicons = getAllCategoriesLexicons; window.getLexiconEntries = getLexiconEntries; window.getAllLexiconWords = getAllLexiconWords; -window.getWiktionaryDefinition = getWiktionaryDefinition; \ No newline at end of file +window.getWiktionaryDefinition = getWiktionaryDefinition; +window.AddWord = AddWord; + diff --git a/definitions.js b/src/utils/definitions.js similarity index 99% rename from definitions.js rename to src/utils/definitions.js index 0e22bffd67d3271183fe398dee3c36c2c4a79462..d29516074843c3e9fbe364ad685dbf2e303aa7b5 100644 --- a/definitions.js +++ b/src/utils/definitions.js @@ -2,7 +2,7 @@ // â–Œ Fonctions pour récupérer/afficher les définitions // ───────────────────────────────────────────────────────────────────────────── -const lexiconMap = new Map(); +window.lexiconMap = new Map(); /** * Récupère les définitions d'un mot dans les lexiques de l'utilisateur (ex. user_id=4), @@ -81,6 +81,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); const lexiconId = result.lexiconId; const sourceName = lexiconMap.get(lexiconId) || `Lexique #${lexiconId}`;