// ==UserScript== // @name WhatsApp Web - Clean UI // @description Fix & debloat WhatsApp Web's interface // @author Gamba // @version 1.8 // @license MIT // @namespace http://tampermonkey.net/ // @match https://web.whatsapp.com/* // @icon https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=https://web.whatsapp.com/&size=256 // @grant none // @run-at document-start // @downloadURL https://update.greasyfork.icu/scripts/535092/WhatsApp%20Web%20-%20Clean%20UI.user.js // @updateURL https://update.greasyfork.icu/scripts/535092/WhatsApp%20Web%20-%20Clean%20UI.meta.js // ==/UserScript== "use strict"; // Add original dark theme { const themeKey = "theme"; const darkThemeValue = '"dark"'; const lightThemeValue = '"light"'; const systemKey = "system-theme-mode"; const originalKey = "original-theme"; document.addEventListener("DOMContentLoaded", onLoad); function onLoad() { const body = document.body; function observe(callback, options) { const observer = new MutationObserver(callback); observer.observe(body, options); } observe(onTreeChange, { childList: true, subtree: true }); function onTreeChange() { onThemePreview(); onOptionsDialog(); } // Replace theme preview label let previewActive = false; function onThemePreview() { const preview = document.querySelector("#app > div > div.x78zum5.xdt5ytf.x5yr21d > div > div.x10l6tqk.x13vifvy.x1o0tod.x78zum5.xh8yej3.x5yr21d.x6ikm8r.x10wlt62.x47corl > div._aigw._as6h.xevlxbw.x9f619.x1n2onr6.x5yr21d.x17dzmu4.x1i1dayz.x2ipvbc.x1w8yi2h.x78zum5.xdt5ytf.x14bqcqg.x18dvir5.xxljpkc.xwfak60.x6ikm8r.x10wlt62.x1ks9yow.xpilrb4.x1t7ytsu.x1m2ixmg > span > div > span > div > div > div > div > div > div:nth-child(1) > div:nth-child(2) > div > div.x98rzlu > div.x1f6kntn.xd4r4e8.x16zc8z2"); const previewExists = preview != null; if (previewExists != previewActive) { previewActive = previewExists; if (previewExists) { const original = localStorage.getItem(originalKey) == "true"; if (original) preview.innerHTML += " (2024)"; } } } // Replace system theme option with original dark let dialogActive = false; function onOptionsDialog() { const dialog = document.querySelector("#app > div > span:nth-child(3) > div > div > div > div > div > div"); const dialogExists = dialog != null; if (dialogExists != dialogActive) { dialogActive = dialogExists; if (dialogExists) { const darkOption = getOption(1); const darkLabel = getLabel(darkOption); const systemOption = getOption(2); setLabel(systemOption, darkLabel + " (2024)"); const original = localStorage.getItem(originalKey) == "true"; if (original) systemOption.click(); const okButton = dialog.children[2].firstElementChild.children[1]; okButton.addEventListener("click", onConfirm, true); function onConfirm() { const darkSelected = getSelected(darkOption); const systemSelected = getSelected(systemOption); localStorage.setItem(systemKey, false); localStorage.setItem(originalKey, systemSelected); localStorage.setItem(themeKey, (darkSelected || systemSelected) ? darkThemeValue : lightThemeValue); location.reload(); } } } function getOption(index) { return dialog.children[1].firstElementChild.firstElementChild.firstElementChild.children[index].children[1]; } function getLabel(option) { const label = option.innerHTML; const child = option.firstElementChild.outerHTML; return label.replace(child, ""); } function setLabel(option, text) { const child = option.firstElementChild.outerHTML; option.innerHTML = text + child; } function getSelected(option) { const radio = option.previousElementSibling.children[1]; return radio.ariaChecked == "true"; } } // Apply theme on startup observe(onThemeChange, { attributes: true, attributeFilter: ["class"] }); function onThemeChange() { const system = localStorage.getItem(systemKey) == "true"; const theme = localStorage.getItem(themeKey); const original = localStorage.getItem(originalKey) == "true"; if (system) { localStorage.setItem(systemKey, false); localStorage.setItem(themeKey, darkThemeValue); localStorage.setItem(originalKey, true); location.reload(); } if (theme == darkThemeValue && original) applyOriginalDark(); } function applyOriginalDark() { const targetClass = "dark"; if (body.className == targetClass) return; body.className = targetClass; } } } // Fix menu background exit { window.addEventListener("keydown", onKeyboard, true); const exceptions = [ "#app > div > span:nth-child(4) *", "#app > div > div > div > div:nth-child(3) > div:first-child > span > div > span > div > header > div > div:first-child > div", "#app > div > div > div > div:nth-child(3) > div:first-child > span > div > span > div > span > div > header > div > div:first-child > div", "#app > div > span:nth-child(3) > div" ]; function onKeyboard(event) { if (event.key == "Escape") { const isException = exceptions.some(exception => event.target.matches(exception)); const isValid = isChatsSelected() || isException; if (!isValid) event.stopImmediatePropagation(); } } function isChatsSelected() { let chatsButton = document.querySelector("#app > div > div > div > header > div > div:first-child > div > div:first-child > button"); return chatsButton.ariaPressed == "true"; } } // Exit chat on change tab { document.addEventListener("click", onClick, true); function onClick(event) { const button = event.target.closest("button"); if (button) { const header = button.closest("header"); if (header && header.parentElement.hasAttribute("tabindex")) { const keyEvent = new KeyboardEvent("keydown", { key: "Escape" }); document.dispatchEvent(keyEvent); } } } } // Apply CSS { const css = ` /*Desktop app advertisement*/ #app > div > div > div { > div:nth-child(5) > div:not(#main) > div { > div:nth-child(1), > div:nth-child(2) { display: none; } } > div:nth-child(3) > div:last-child > span { border: none; margin: 1px; > div[tabindex] > div:not([tabindex]) { display: none; } } } #side > div:nth-child(5) * { display: none; } /*Channels & communities*/ #app > div > div > div > header > div > div:first-child > div { > div:nth-child(3), > div:nth-child(4) { display: none; } } /*Video call button*/ #main > header > div:last-child > div > div:first-child { display: none; } /*Chat lists*/ #side > [role="tablist"] { display: none; } /*Chat dropdowns arrows*/ #pane-side > div:nth-last-child(2) > div > div > div > div > div > div:not(:hover) > div:last-child > div:last-child > div:last-child > span:nth-child(3) { display: none; } /*Archived chats separator*/ #app > div > div > div > div:nth-child(3) > div:first-child > span > div > span > div > div:last-child > div > div:nth-child(2) > div.xjm9jq1.xkh2ocl.x1uew315 { display: none; } /*Popover tooltips*/ #wa-popovers-bucket * { display: none; } /*Scrollbar*/ * { scrollbar-width: unset !important; scrollbar-color: unset !important; } div { ::-webkit-scrollbar { width: 14px !important; } ::-webkit-scrollbar-button { display: none !important; } ::-webkit-scrollbar-track, ::-webkit-scrollbar-track-piece { background-color: transparent !important; } ::-webkit-scrollbar-thumb { height: 50px !important; border: 3px solid transparent !important; border-radius: 100px; background-clip: padding-box !important; background-color: rgba(var(--black-rgb), 20%) !important; } } .dark div ::-webkit-scrollbar-thumb { background-color: rgba(var(--white-rgb), 16%) !important; } /*Original dark theme*/ .dark:not(.color-refresh) { --drawer-background-deep: #111b21; --compose-input-background: #202c33; --compose-input-border: #202c33; ._alyo._alyw { --WDS-background-wash-inset: #0a1014; } .x1vmw6bp.x1vmw6bp { --WDS-surface-default: #111b21; } } `; const style = document.createElement("style"); style.textContent = css; document.head.appendChild(style); }