// ==UserScript==
// @name DeepL UI Cleaner
// @namespace http://tampermonkey.net/
// @version 0.6
// @description Streamlines DeepL's interface for translation-focused use by removing footers, cookie banners, and adding toggle buttons for sidebar and header
// @match https://www.deepl.com/*
// @include https://www.deepl.com/*
// @license Unlicense
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
"use strict";
// Define selectors for target elements
const TARGETS = {
remove: {
footer: [
"footer",
".relative.bg-neutral-next-50 > .mobile\\:hidden",
'.bg-white[class*="px-0"][class*="xl:px-"][class*="md:px-"][class*="min-"][class*="px"]',
],
cookieBanner: ['[id*="cookieBanner"]', '[class*="cookieBanner"]'],
writePageElements: [
'.bg-white.px-0[class*="xl:px-"][class*="px"][class*="md:px-"][class*="px"][class*="min-"][class*="px"][class*="px"]',
'.mobile\\:hidden.p-8.px-0[class*="xl:px-"][class*="px"][class*="md:px-"][class*="px"][class*="min-"][class*="px"][class*="px"]',
'.bg-white.min-\\[1280px\\]\\:px-\\[70px\\]'
],
},
toggle: {
topHeader: 'header'
},
};
// CSS styles for toggle buttons and initial header state
const BUTTON_STYLES = `
.toggle-button {
position: fixed;
z-index: 10000;
width: 2em;
height: 2em;
background-color: rgba(240, 240, 240, 0.7);
border: 1px solid #ccc;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: all 0.3s;
}
.toggle-button:hover {
background-color: rgba(220, 220, 220, 0.9);
}
${TARGETS.toggle.topHeader} {
display: none !important;
}
`;
// Function to inject CSS styles into the page
function injectStyles() {
const style = document.createElement("style");
const hideSelectors = [
...Object.values(TARGETS.remove).flat(),
].join(", ");
style.textContent = `
${hideSelectors} { display: none !important; }
${BUTTON_STYLES}
`;
(document.head || document.documentElement).appendChild(style);
}
// Function to remove unwanted elements from the page
function removeElements() {
Object.values(TARGETS.remove)
.flat()
.forEach((selector) => {
document
.querySelectorAll(selector)
.forEach((element) => element.remove());
});
}
// Function to create a toggle button
function createToggleButton(
showIcon,
hideIcon,
onClick,
position,
ariaLabel
) {
const button = document.createElement("button");
button.className = "toggle-button";
button.textContent = showIcon; // Starting with show icon since header is hidden
button.setAttribute("aria-label", ariaLabel);
button.addEventListener("click", onClick);
Object.assign(button.style, position);
return button;
}
// Function to adjust button positions based on header visibility
function adjustButtonPositions() {
const header = document.querySelector(TARGETS.toggle.topHeader);
const topPosition =
header && getComputedStyle(header).display !== "none"
? `${header.offsetHeight}px`
: "0.5em";
document.querySelectorAll(".toggle-button").forEach((button) => {
button.style.top = topPosition;
});
}
// Function to toggle visibility of elements
function toggleElementVisibility(selector, button, showIcon, hideIcon) {
const elements = document.querySelectorAll(selector);
const isVisible = [...elements].some(
(el) => getComputedStyle(el).display !== "none"
);
elements.forEach((el) =>
el.style.setProperty("display", isVisible ? "none" : "block", "important")
);
button.textContent = isVisible ? showIcon : hideIcon;
adjustButtonPositions();
}
// Function to initialize the script
function init() {
try {
removeElements();
const buttons = [
{
selector: TARGETS.toggle.topHeader,
icons: ["▼", "▲"],
position: { left: "0.5em" },
ariaLabel: "Toggle header visibility",
},
];
buttons.forEach(
({ selector, icons: [showIcon, hideIcon], position, ariaLabel }) => {
const button = createToggleButton(
showIcon,
hideIcon,
() => toggleElementVisibility(selector, button, showIcon, hideIcon),
position,
ariaLabel
);
document.body.appendChild(button);
}
);
adjustButtonPositions();
// Set up MutationObserver to handle dynamically added elements
const observer = new MutationObserver(() => {
removeElements();
adjustButtonPositions();
});
observer.observe(document.body, { childList: true, subtree: true, attributes: true });
} catch (error) {
console.error("DeepL UI Cleaner encountered an error:", error);
}
}
// Inject styles immediately
injectStyles();
// Run initialization when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();