Removes specified words/phrases from all sites (case-insensitive, persistent globally, bilingual UI).
// ==UserScript==
// @name Global Text Remover (FR/EN)
// @name:fr Masqueur global de texte (FR/EN)
// @namespace https://example.com
// @version 3.5
// @description Removes specified words/phrases from all sites (case-insensitive, persistent globally, bilingual UI).
// @description:fr Script ChatGPT qui enlève des mots/phrases de tous les sites (ne respecte pas la casse, persiste).
// @match *://*/*
// @grant GM_getValue
// @license MIT
// @grant GM_setValue
// ==/UserScript==
(function() {
"use strict";
/** 🌐 Language auto-detect **/
const lang = navigator.language.startsWith("fr") ? "fr" : "en";
const T = {
addPlaceholder: lang === "fr" ? "Mot ou phrase à supprimer" : "Word or phrase to remove",
addButton: lang === "fr" ? "Ajouter" : "Add",
panelTitle: lang === "fr" ? "Liste de mots masqués" : "Hidden words list",
deleteTip: lang === "fr" ? "Supprimer ce mot" : "Delete this word",
hideButton: lang === "fr" ? "Cacher le bouton" : "Hide button"
};
const STORAGE_KEY = "masqueur_global_list";
const HIDE_KEY = "masqueur_toggle_hidden";
let filtres = GM_getValue(STORAGE_KEY, []);
let toggleHidden = GM_getValue(HIDE_KEY, false);
/** Utils **/
const escapeRegex = s => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const isIgnored = el => {
if (!el) return true;
if (el.closest("#masqueur-panel") || el.closest("#masqueur-toggle")) return true;
const tag = el.tagName ? el.tagName.toLowerCase() : "";
return ["input","textarea","select","script","style","noscript"].includes(tag);
};
/** Masquage **/
function maskNodeText(node) {
if (node.nodeType !== Node.TEXT_NODE) return;
if (isIgnored(node.parentElement)) return;
const txt = node.nodeValue;
if (!txt || !txt.trim()) return;
let newTxt = txt;
for (const f of filtres) {
if (!f) continue;
const regex = new RegExp(escapeRegex(f), "gi");
newTxt = newTxt.replace(regex, "");
}
if (newTxt !== txt) node.nodeValue = newTxt;
}
function maskAll(root = document.body) {
if (!filtres.length || !root) return;
const walker = document.createTreeWalker(
root,
NodeFilter.SHOW_TEXT,
null,
false
);
let n;
while ((n = walker.nextNode())) maskNodeText(n);
}
/** Observer **/
const observer = new MutationObserver(mutations => {
for (const m of mutations) {
for (const n of m.addedNodes) {
if (n.nodeType === Node.TEXT_NODE)
maskNodeText(n);
else if (n.nodeType === Node.ELEMENT_NODE && !isIgnored(n))
maskAll(n);
}
}
});
/** Interface **/
// --- Bouton rond ---
const toggle = document.createElement("div");
toggle.id = "masqueur-toggle";
toggle.textContent = "🕵️";
Object.assign(toggle.style,{
position:"fixed",
bottom:"14px",
left:"14px",
width:"32px",
height:"32px",
lineHeight:"28px",
textAlign:"center",
background:"rgba(0,0,0,0.7)",
color:"white",
borderRadius:"50%",
cursor:"pointer",
zIndex:"999999999",
fontSize:"18px",
boxShadow:"0 2px 6px rgba(0,0,0,0.4)",
transition:"background 0.2s ease",
userSelect:"none"
});
toggle.onmouseenter = () => toggle.style.background="rgba(0,0,0,0.9)";
toggle.onmouseleave = () => toggle.style.background="rgba(0,0,0,0.7)";
// --- Panneau ---
const panel = document.createElement("div");
panel.id="masqueur-panel";
Object.assign(panel.style,{
position:"fixed",
bottom:"60px",
left:"10px",
background:"rgba(0,0,0,0.85)",
color:"#fff",
padding:"10px",
borderRadius:"10px",
fontFamily:"sans-serif",
zIndex:"999999998",
width:"260px",
boxShadow:"0 2px 6px rgba(0,0,0,0.35)",
display:"none"
});
const title=document.createElement("div");
title.textContent=T.panelTitle;
Object.assign(title.style,{
fontWeight:"bold",
fontSize:"13px",
marginBottom:"6px",
textAlign:"center"
});
const input=document.createElement("input");
Object.assign(input,{
type:"text",
placeholder:T.addPlaceholder
});
Object.assign(input.style,{
width:"100%",
padding:"6px",
borderRadius:"4px",
border:"none",
marginBottom:"6px",
fontSize:"12px",
boxSizing:"border-box"
});
const addBtn=document.createElement("button");
addBtn.textContent=T.addButton;
Object.assign(addBtn.style,{
width:"100%",
padding:"6px",
background:"#2e8b57",
color:"#fff",
border:"none",
borderRadius:"4px",
cursor:"pointer",
fontSize:"12px",
marginBottom:"6px"
});
const hideBtn=document.createElement("button");
hideBtn.textContent=T.hideButton;
Object.assign(hideBtn.style,{
width:"100%",
padding:"6px",
background:"#444",
color:"#fff",
border:"none",
borderRadius:"4px",
cursor:"pointer",
fontSize:"12px",
marginBottom:"6px"
});
const list=document.createElement("ul");
Object.assign(list.style,{
listStyle:"none",
padding:0,
margin:0,
maxHeight:"150px",
overflowY:"auto",
fontSize:"12px"
});
/** Flèche de réapparition **/
const revealZone=document.createElement("div");
Object.assign(revealZone.style,{
position:"fixed",
bottom:"0",
left:"0",
width:"40px",
height:"40px",
zIndex:"999999997"
});
const revealArrow=document.createElement("div");
revealArrow.textContent="➜";
Object.assign(revealArrow.style,{
position:"fixed",
bottom:"14px",
left:"10px",
fontSize:"18px",
color:"white",
background:"rgba(0,0,0,0.7)",
borderRadius:"6px",
padding:"2px 6px",
cursor:"pointer",
display:"none",
zIndex:"999999998"
});
revealZone.onmouseenter=()=>{
if(toggleHidden) revealArrow.style.display="block";
};
revealZone.onmouseleave=()=>{
revealArrow.style.display="none";
};
revealArrow.onclick=()=>{
toggleHidden=false;
GM_setValue(HIDE_KEY,false);
toggle.style.display="block";
revealArrow.style.display="none";
};
/** Liste **/
function renderList(){
list.innerHTML="";
filtres.forEach((f,i)=>{
const li=document.createElement("li");
Object.assign(li.style,{
display:"flex",
justifyContent:"space-between",
alignItems:"center",
padding:"3px 0",
borderBottom:"1px solid rgba(255,255,255,0.1)"
});
const span=document.createElement("span");
span.textContent=f;
const del=document.createElement("button");
del.textContent="✖";
del.title=T.deleteTip;
Object.assign(del.style,{
background:"none",
border:"none",
color:"#ff6666",
cursor:"pointer",
fontSize:"12px"
});
del.onclick=()=>{
filtres.splice(i,1);
GM_setValue(STORAGE_KEY,filtres);
maskAll();
renderList();
};
li.appendChild(span);
li.appendChild(del);
list.appendChild(li);
});
}
addBtn.onclick=()=>{
const val=input.value.trim();
if(!val) return;
if(!filtres.includes(val)){
filtres.push(val);
GM_setValue(STORAGE_KEY,filtres);
maskAll();
renderList();
}
input.value="";
};
hideBtn.onclick=()=>{
toggleHidden=true;
GM_setValue(HIDE_KEY,true);
toggle.style.display="none";
panel.style.display="none";
};
toggle.onclick=()=>{
panel.style.display=panel.style.display==="none"?"block":"none";
};
panel.appendChild(title);
panel.appendChild(input);
panel.appendChild(addBtn);
panel.appendChild(hideBtn);
panel.appendChild(list);
document.body.appendChild(panel);
document.body.appendChild(toggle);
document.body.appendChild(revealZone);
document.body.appendChild(revealArrow);
/** Initialisation **/
function init(){
renderList();
maskAll();
observer.observe(document.body,{
childList:true,
subtree:true
});
if(toggleHidden){
toggle.style.display="none";
}
}
if(document.readyState==="loading"){
document.addEventListener("DOMContentLoaded",init);
} else {
init();
}
})();