Initial commit: userscripts collection with oxlint tooling

This commit is contained in:
2026-07-08 11:59:50 -04:00
commit 672d3d51e1
17 changed files with 2602 additions and 0 deletions
+854
View File
@@ -0,0 +1,854 @@
// ==UserScript==
// @name Reddit Tweaks (Apollo)
// @version 0.3.0
// @match *://old.reddit.com/*
// @match *://www.reddit.com/*
// @downloadURL https://gist.githubusercontent.com/evanreichard/968eee1cf83da38c81c155fb775cf3c6/raw/old-reddit-mobile.user.js
// @grant GM.addStyle
// @grant GM.registerMenuCommand
// @grant GM.unregisterMenuCommand
// @grant GM.getValue
// @grant GM.setValue
// @grant GM_getValue
// @noframes
// @run-at document-start
// ==/UserScript==
/* ============================================================================
* REDIRECT: www.reddit.com -> old.reddit.com
* ----------------------------------------------------------------------------
* Run before anything else so we don't waste cycles styling the new design.
* The /media path is a special case: Reddit's image viewer wraps the image
* in heavy chrome, so we strip everything down to just the <img> tag.
* ========================================================================== */
if (document.location.host === "www.reddit.com") {
if (document.location.pathname === "/media") {
document.addEventListener("DOMContentLoaded", () => {
const img = document.querySelector("img");
if (img) document.body.innerHTML = img.outerHTML;
});
} else {
window.location.href =
"https://old.reddit.com" +
window.location.pathname +
window.location.search +
window.location.hash;
}
}
/* ============================================================================
* STATE
* ----------------------------------------------------------------------------
* `isEnabled` is read synchronously at script start so we can decide whether
* to inject CSS at document-start (before paint). Toggling the script ALWAYS
* reloads the page — see toggleStyle() for the reasoning.
* ========================================================================== */
let isEnabled = GM_getValue("enabled", true);
/* ============================================================================
* STYLES
* ----------------------------------------------------------------------------
* All visual styling lives here. Uses CSS custom properties for theming so
* dark mode is just a media query override on the :root tokens.
* ========================================================================== */
function applyStyle() {
GM.addStyle(`
/* --------------------------------------------------------------
DESIGN TOKENS
-------------------------------------------------------------- */
:root {
--bg: #ffffff;
--bg-elev: #f7f8fa;
--bg-hover: #eef0f3;
--surface: #ffffff;
--surface-2: #f4f5f7;
--border: #e3e6ea;
--border-soft: #ecedef;
--fg: #1c1c1e;
--fg-2: #3c3c43;
--fg-dim: #6e6e73;
--fg-faint: #b0b3b8;
--accent: #ff4500;
--accent-2: #ff6a33;
--link: #2f6feb;
--visited: #8a8d96;
--upvote: #ff4500;
--downvote: #5b8def;
/* Apollo-style thread depth colors (cycle every 6) */
--depth-1: #4f9cf9; /* blue */
--depth-2: #34c759; /* green */
--depth-3: #ffcc00; /* yellow */
--depth-4: #ff9f0a; /* orange */
--depth-5: #ff453a; /* red */
--depth-6: #bf5af2; /* purple */
--radius-sm: 6px;
--radius: 10px;
--radius-lg: 14px;
--font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
"Segoe UI", "Inter", Roboto, system-ui, sans-serif;
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
--shadow: 0 2px 8px rgba(0,0,0,0.06);
--tap: 44px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0e0f12;
--bg-elev: #16181d;
--bg-hover: #1e2128;
--surface: #16181d;
--surface-2: #1c1f25;
--border: #26292f;
--border-soft: #1e2128;
--fg: #f2f2f7;
--fg-2: #d1d1d6;
--fg-dim: #9b9ba1;
--fg-faint: #5a5d63;
--link: #5b8def;
--visited: #6e6f78;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.3);
--shadow: 0 4px 12px rgba(0,0,0,0.4);
}
}
/* --------------------------------------------------------------
GLOBAL RESET / BASE
-------------------------------------------------------------- */
html, body {
background: var(--bg) !important;
color: var(--fg) !important;
font-family: var(--font-sans) !important;
font-size: 15px !important;
line-height: 1.4 !important;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: 100%;
}
a { color: var(--link); text-decoration: none; }
a:visited { color: var(--visited); }
a:hover { color: var(--fg); }
/* Hide all the cruft old.reddit ships with by default */
.side,
.footer-parent,
.rank,
.rank-spacer,
#sr-header-area,
.mobile-web-redirect-bar,
.link.promotedlink,
.infobar.listingsignupbar,
.infobar.welcome,
.listingsignupbar,
.promoted,
[data-promoted="true"],
.res-floatingSideBar,
.bottommenu,
.sr-list,
.sr-bar,
.sr-interest-bar,
.leavemoderator,
.titlebox,
.login-form-side,
.icon-menu .gold-buy,
.reportform,
.panestack-title .res-comments-sortMenu,
.link .flat-list.buttons li.share,
.link .flat-list.buttons .post-sharing-button,
.link .flat-list.buttons .reportbtn {
display: none !important;
}
/* --------------------------------------------------------------
HEADER
-------------------------------------------------------------- */
#header {
background: var(--bg-elev) !important;
border-bottom: 1px solid var(--border) !important;
box-shadow: var(--shadow-sm);
padding: 0 12px !important;
min-height: 48px !important;
height: auto !important;
position: sticky;
top: 0;
z-index: 1000;
backdrop-filter: saturate(180%) blur(12px);
-webkit-backdrop-filter: saturate(180%) blur(12px);
}
#header-bottom-left {
display: flex !important;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 6px 14px;
padding: 8px 0 !important;
}
#header-img, .pagename a {
color: var(--fg) !important;
font-weight: 700 !important;
font-size: 17px !important;
letter-spacing: -0.01em;
}
.pagename { padding: 0 !important; }
.tabmenu {
width: 100%;
display: flex !important;
gap: 4px;
margin: 0 !important;
overflow-x: auto;
scrollbar-width: none;
}
.tabmenu::-webkit-scrollbar { display: none; }
.tabmenu li { margin: 0 !important; }
.tabmenu li a {
display: inline-block;
padding: 6px 12px !important;
border-radius: 999px;
font-size: 13px !important;
font-weight: 500;
color: var(--fg-dim) !important;
background: transparent !important;
border: 1px solid transparent !important;
transition: background 0.15s ease, color 0.15s ease;
}
.tabmenu li.selected a {
background: var(--accent) !important;
color: #fff !important;
}
.tabmenu li:not(.selected) a:hover {
background: var(--bg-hover) !important;
color: var(--fg) !important;
}
#header-bottom-right {
top: 0;
bottom: unset;
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
background: var(--bg-elev) !important;
color: var(--fg-dim) !important;
font-size: 12px !important;
padding: 6px 10px !important;
border-left: 1px solid var(--border);
border-bottom: 1px solid var(--border);
}
#header-bottom-right a { color: var(--fg-dim) !important; }
/* --------------------------------------------------------------
CONTENT WRAPPER
-------------------------------------------------------------- */
.content[role="main"] {
margin: 0 !important;
padding: 0 !important;
max-width: 100% !important;
background: var(--bg) !important;
color: var(--fg) !important;
}
#siteTable { margin: 0 !important; }
/* --------------------------------------------------------------
POSTS
----------------------------------------------------------------
Flex layout that preserves Reddit's native expando behavior.
Order: thumbnail | entry | (votes hidden) | (wrap) expando
-------------------------------------------------------------- */
.link {
position: relative;
display: flex !important;
flex-wrap: wrap;
align-items: flex-start;
column-gap: 12px;
row-gap: 4px;
padding: 12px 14px !important;
margin: 0 !important;
background: var(--surface) !important;
color: var(--fg) !important;
border: 0 !important;
border-bottom: 1px solid var(--border-soft) !important;
transition: background 0.12s ease;
}
.link:active { background: var(--bg-hover) !important; }
.link.last-clicked {
background: var(--bg-elev) !important;
border-bottom: 1px solid var(--border-soft) !important;
}
.link > .thumbnail { order: 1; flex: 0 0 auto; }
.link > .entry { order: 2; flex: 1 1 0; min-width: 0; }
.link > .midcol { order: 3; flex: 0 0 auto; }
.link > .child,
.link > .clearleft { display: none !important; }
/* Hide post-level vote arrows and native expando button (we tap thumbnail instead) */
.link > .midcol { display: none !important; }
.link .expando-button { display: none !important; }
/* Thumbnail container — shared by real and synthetic thumbs */
.link > .thumbnail {
width: 64px !important;
height: 64px !important;
margin: 0 !important;
border-radius: var(--radius);
overflow: hidden;
background: var(--bg-elev);
border: 1px solid var(--border-soft);
display: flex !important;
align-items: center;
justify-content: center;
cursor: pointer;
}
.link > .thumbnail img {
width: 100% !important;
height: 100% !important;
object-fit: cover !important;
display: block;
}
/* Placeholder labels when there's no real image */
.link > .thumbnail.self::after,
.link > .thumbnail.default::after,
.link > .thumbnail.spoiler::after,
.link > .thumbnail.nsfw::after {
font-size: 10px;
font-weight: 700;
letter-spacing: 0.05em;
color: var(--fg-dim);
}
.link > .thumbnail.self::after { content: "TEXT"; }
.link > .thumbnail.default::after { content: "LINK"; }
.link > .thumbnail.spoiler::after { content: "⚠"; font-size: 18px; }
.link.over18 > .thumbnail::after { content: "NSFW"; color: #ff453a; }
/* Entry / title / meta */
.link .entry { margin: 0 !important; color: var(--fg) !important; }
.link .top-matter { padding: 0 !important; margin: 0 !important; }
.link .title {
margin: 0 0 4px 0 !important;
padding: 0 !important;
font-size: 15px !important;
line-height: 1.3 !important;
font-weight: 500;
letter-spacing: -0.01em;
}
.link .title a.title { color: var(--fg) !important; }
.link.visited .title a.title,
.link .title a.title:visited { color: var(--fg-dim) !important; }
.link .title .domain {
display: inline-block;
font-size: 11px !important;
color: var(--fg-dim) !important;
font-weight: 400;
margin-left: 4px;
vertical-align: middle;
}
.link .title .domain a { color: var(--fg-dim) !important; }
.link .linkflairlabel {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 10.5px !important;
font-weight: 600;
padding: 2px 7px !important;
border-radius: 999px;
margin-right: 6px;
vertical-align: middle;
line-height: 1.4;
border: 0 !important;
}
.link .flairemoji { width: 12px !important; height: 12px !important; }
.link .tagline {
font-size: 12px !important;
color: var(--fg-dim) !important;
margin: 0 !important;
line-height: 1.3 !important;
}
.link .tagline a,
.link .tagline time { color: var(--fg-dim) !important; }
.link .tagline .author { color: var(--fg-2) !important; font-weight: 500; }
.link .flat-list.buttons {
display: flex;
gap: 12px;
margin: 4px 0 0 0 !important;
padding: 0 !important;
font-size: 12px !important;
list-style: none;
}
.link .flat-list.buttons li { display: inline-block; margin: 0; padding: 0; }
.link .flat-list.buttons a { color: var(--fg-dim) !important; font-weight: 500; }
.link .flat-list.buttons .comments {
color: var(--accent) !important;
font-weight: 600;
}
/* --------------------------------------------------------------
EXPANDO (full-width row beneath the post)
-------------------------------------------------------------- */
.link > .expando {
order: 4;
flex: 0 0 100%;
width: 100% !important;
max-width: 100% !important;
margin: 8px 0 0 0 !important;
padding: 0 !important;
background: transparent !important;
border: 0 !important;
color: var(--fg) !important;
box-sizing: border-box;
}
.link > .expando .media-preview,
.link > .expando .media-preview-content,
.link > .expando .res-expando-box,
.link > .expando .res-expando-box div {
max-width: 100% !important;
width: 100% !important;
}
.link .expando img,
.link .expando video,
.link .expando iframe,
.link .expando .preview,
.link .expando .media-preview-content > * {
max-width: 100% !important;
height: auto !important;
border-radius: var(--radius);
display: block;
}
/* Self-text body */
.link .expando .usertext-body,
.link .usertext-body,
.commentarea .link .usertext-body {
background: var(--surface-2) !important;
color: var(--fg) !important;
border: 1px solid var(--border-soft) !important;
border-radius: var(--radius);
padding: 10px 14px !important;
margin: 0 !important;
}
.link .expando .md,
.link .usertext-body .md {
background: transparent !important;
color: var(--fg) !important;
max-width: unset !important;
font-size: 14px !important;
line-height: 1.55 !important;
}
.link .expando .md p,
.link .expando .md li,
.link .expando .md h1,
.link .expando .md h2,
.link .expando .md h3,
.link .expando .md blockquote {
color: var(--fg) !important;
}
/* --------------------------------------------------------------
COMMENTS — bubble-in-bubble with depth-colored left border
-------------------------------------------------------------- */
.commentarea {
background: var(--bg) !important;
color: var(--fg) !important;
padding: 8px 10px !important;
margin: 0 !important;
}
.commentarea > .menuarea {
background: transparent !important;
border: 0 !important;
padding: 8px 4px !important;
margin: 0 0 4px 0 !important;
font-size: 12px;
color: var(--fg-dim) !important;
}
.comment {
background: var(--surface) !important;
color: var(--fg) !important;
border: 1px solid var(--border-soft) !important;
border-left: 3px solid var(--depth-1) !important;
border-radius: var(--radius);
padding: 10px 12px !important;
margin: 6px 0 !important;
box-shadow: var(--shadow-sm);
}
.comment .child,
.comment .showreplies {
border-left: 0 !important;
margin: 6px 0 0 6px !important;
padding: 0 !important;
}
/* Cycle border colors by nesting depth */
.comment .comment { border-left-color: var(--depth-2) !important; }
.comment .comment .comment { border-left-color: var(--depth-3) !important; }
.comment .comment .comment .comment { border-left-color: var(--depth-4) !important; }
.comment .comment .comment .comment .comment { border-left-color: var(--depth-5) !important; }
.comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-6) !important; }
.comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-1) !important; }
.comment .comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-2) !important; }
.comment .comment .comment .comment .comment .comment .comment .comment .comment { border-left-color: var(--depth-3) !important; }
/* Alternate bg shades to emphasize nesting */
.comment .comment { background: var(--surface-2) !important; }
.comment .comment .comment { background: var(--surface) !important; }
.comment .entry { padding: 0 !important; }
.comment .tagline {
font-size: 12px !important;
color: var(--fg-dim) !important;
margin-bottom: 4px;
}
.comment .tagline .author { color: var(--fg) !important; font-weight: 600; }
.comment .tagline .submitter { color: var(--link) !important; font-weight: 600; }
.comment .tagline .score { color: var(--fg-dim) !important; font-weight: 500; }
.comment .usertext-body,
.comment .usertext-body .md,
.comment .md {
font-size: 14px !important;
line-height: 1.5 !important;
color: var(--fg) !important;
background: transparent !important;
}
.comment .md p { margin: 4px 0 !important; color: var(--fg) !important; }
.comment .md a { color: var(--link) !important; }
.comment .md blockquote {
border-left: 3px solid var(--border);
padding-left: 10px;
color: var(--fg-2) !important;
margin: 6px 0;
}
.comment .md code {
background: var(--bg-elev) !important;
color: var(--fg) !important;
border-radius: 4px;
padding: 1px 5px;
font-family: var(--font-mono);
font-size: 12.5px;
}
.comment .md pre {
background: var(--bg-elev) !important;
color: var(--fg) !important;
padding: 8px 10px;
border-radius: var(--radius-sm);
overflow-x: auto;
}
.comment .midcol {
width: 16px !important;
margin-right: 4px !important;
}
.comment .arrow {
width: 18px !important;
height: 18px !important;
background-size: 14px !important;
border-radius: 4px;
}
.comment .arrow:hover { background-color: var(--bg-hover); }
.comment .flat-list.buttons {
font-size: 12px !important;
margin-top: 4px !important;
}
.comment .flat-list.buttons a { color: var(--fg-dim) !important; font-weight: 500; }
.comment .flat-list.buttons .reply-button a { color: var(--link) !important; }
.comment .expand {
color: var(--fg-dim) !important;
font-weight: 600;
padding: 0 4px;
}
.comment.collapsed {
opacity: 0.7;
padding: 6px 12px !important;
}
/* "load more" buttons */
.morechildren a, .morecomments a {
display: inline-block;
padding: 6px 12px;
border-radius: 999px;
background: var(--bg-elev) !important;
color: var(--link) !important;
font-weight: 600;
font-size: 13px;
border: 1px solid var(--border) !important;
}
/* --------------------------------------------------------------
PAGINATION
-------------------------------------------------------------- */
.nextprev {
display: flex;
justify-content: space-between;
align-items: stretch;
gap: 10px;
padding: 16px 14px 28px;
margin: 0;
}
.nextprev .separator { display: none; }
.nextprev a {
flex: 1 1 0;
min-height: var(--tap);
display: flex;
align-items: center;
justify-content: center;
padding: 12px 16px;
border-radius: var(--radius);
font-weight: 600;
font-size: 15px;
text-decoration: none;
background: var(--bg-elev) !important;
color: var(--fg) !important;
border: 1px solid var(--border);
transition: transform 0.06s ease, background 0.15s ease;
}
.nextprev a:hover { background: var(--bg-hover) !important; }
.nextprev a:active { transform: scale(0.98); }
.nextprev a[rel*="next"] {
background: var(--accent) !important;
color: #fff !important;
border-color: transparent;
}
.nextprev a[rel*="next"]:hover { background: var(--accent-2) !important; }
/* --------------------------------------------------------------
FORMS / MENUS / MISC
-------------------------------------------------------------- */
input[type=text],
input[type=password],
input[type=email],
textarea {
background: var(--bg-elev) !important;
color: var(--fg) !important;
border: 1px solid var(--border) !important;
border-radius: var(--radius-sm) !important;
padding: 8px 10px !important;
font-family: var(--font-sans) !important;
font-size: 14px !important;
}
.btn, button, .morelink a {
background: var(--bg-elev) !important;
color: var(--fg) !important;
border: 1px solid var(--border) !important;
border-radius: var(--radius-sm) !important;
padding: 6px 12px !important;
font-weight: 600;
font-family: var(--font-sans) !important;
}
.menuarea, .panestack-title {
background: transparent !important;
border: 0 !important;
color: var(--fg-dim) !important;
padding: 8px 12px !important;
}
.dropdown, .drop-choices {
background: var(--surface) !important;
color: var(--fg) !important;
border: 1px solid var(--border) !important;
border-radius: var(--radius-sm) !important;
box-shadow: var(--shadow);
}
table, th, td {
background: var(--surface) !important;
color: var(--fg) !important;
border-color: var(--border) !important;
}
pre, code {
background: var(--bg-elev) !important;
color: var(--fg) !important;
font-family: var(--font-mono) !important;
}
/* Dim Reddit's default vote arrows in dark mode (they're black PNGs) */
@media (prefers-color-scheme: dark) {
.arrow:not(.upmod):not(.downmod) { filter: brightness(0.85) invert(0.9); }
}
a, button { touch-action: manipulation; }
body > .content .link .midcol { width: auto !important; }
body > .content .link .rank, .rank-spacer { width: 0 !important; }
`);
// Mobile viewport — Reddit's default doesn't include viewport-fit=cover,
// which we want for safe-area handling on notched devices.
if (document.head) {
const meta = document.createElement("meta");
meta.setAttribute("name", "viewport");
meta.setAttribute(
"content",
"width=device-width, initial-scale=1, viewport-fit=cover",
);
document.head.appendChild(meta);
}
// Nuke subreddit-specific stylesheet ASAP so it can't fight ours
document.head?.querySelector("[ref=applied_subreddit_stylesheet]")?.remove();
}
/* ============================================================================
* DOM TWEAKS
* ----------------------------------------------------------------------------
* Things CSS alone can't do: rewriting nodes, injecting placeholders,
* relocating elements within the post layout.
* ========================================================================== */
function applyDOMTweaks() {
// Belt-and-suspenders subreddit stylesheet removal (in case it loaded after us)
document.querySelector('link[title="applied_subreddit_stylesheet"]')?.remove();
// Replace the subreddit logo/banner anchor with a plain "reddit" home link
const headerAnchor = document.querySelector("#header-bottom-left a");
if (headerAnchor) {
headerAnchor.outerHTML =
`<a href="/" id="header-img" class="default-header" title="">reddit</a>`;
}
// Process every post on the page
document.querySelectorAll(".link").forEach(processLink);
// Pagination polish — shorten "view more" wording and add arrows
const nextprev = document.querySelector(".nextprev");
if (nextprev) {
// The first text node is often "view more:" — drop it
const first = nextprev.childNodes[0];
if (first && first.textContent.trim().toLowerCase().startsWith("view more")) {
first.remove();
}
nextprev.querySelectorAll("a").forEach((a) => {
const rel = (a.getAttribute("rel") || "").toLowerCase();
if (rel.includes("next")) a.textContent = "Next ";
else if (rel.includes("prev")) a.textContent = " Prev";
});
// Center the lone button if there's only one direction available
if (nextprev.querySelectorAll("a").length === 1) {
nextprev.style.justifyContent = "center";
nextprev.querySelector("a").style.flex = "0 1 280px";
}
}
}
/**
* Per-post tweaks:
* 1. Move .expando out of .entry so it can become a full-width flex row
* (flex order: thumbnail | entry | expando-on-new-line).
* 2. Inject a synthetic .thumbnail <div> when the subreddit suppresses
* thumbnails server-side (e.g. r/cars). This guarantees every post
* has a tappable thumb area showing "TEXT", "LINK", "NSFW", etc.
*/
function processLink(link) {
// --- 1. Promote .expando to be a direct child of .link ---
const expando = link.querySelector(":scope > .entry > .expando");
if (expando) link.appendChild(expando);
// --- 2. Inject synthetic thumbnail if missing ---
if (!link.querySelector(":scope > .thumbnail")) {
const thumb = document.createElement("div");
// Pick the right placeholder class so CSS ::after shows the right label
if (link.classList.contains("over18")) {
thumb.className = "thumbnail nsfw";
} else if (link.classList.contains("spoiler")) {
thumb.className = "thumbnail spoiler";
} else if (link.classList.contains("self")) {
thumb.className = "thumbnail self";
} else {
thumb.className = "thumbnail default";
}
link.prepend(thumb);
}
}
/* ============================================================================
* INTERACTION HANDLERS
* ----------------------------------------------------------------------------
* Delegated click handlers — attached once at script start, gated on
* `isEnabled` so they no-op when the user disables the script live.
* ========================================================================== */
// Tap a comment body to collapse/expand it (Apollo-style)
document.addEventListener("click", (evt) => {
if (!isEnabled) return;
// Ignore clicks on interactive descendants
if (evt.target.closest("a")) return;
if (evt.target.classList.contains("expand")) return;
if (evt.target.closest(".flat-list.buttons")) return;
if (evt.target.closest(".usertext-edit")) return;
if (evt.target.closest(".midcol")) return;
const comment = evt.target.closest(".comment");
if (!comment) return;
const expander = comment.querySelector(":scope > .entry .expand");
if (!expander) return;
expander.click();
});
// Tap a thumbnail to toggle the post's expando.
// On synthetic thumbnails (link posts with no expando) this is a no-op.
document.addEventListener("click", (evt) => {
if (!isEnabled) return;
let thumb;
if (evt.target.classList?.contains("thumbnail")) {
thumb = evt.target;
} else if (evt.target.parentElement?.classList?.contains("thumbnail")) {
thumb = evt.target.parentElement;
}
if (!thumb) return;
const link = thumb.closest(".link");
if (!link) return;
const btn = link.querySelector(".expando-button");
evt.preventDefault();
evt.stopPropagation();
if (!btn) return; // synthetic thumb, nothing to expand
btn.click();
});
/* ============================================================================
* MENU / TOGGLE
* ----------------------------------------------------------------------------
* Toggling always reloads the page. Why:
* - GM.addStyle injects a <style> tag that can't be cleanly removed without
* tracking references and tearing them down.
* - DOM tweaks (moved expandos, injected thumbnails, replaced header anchor)
* can't be reversed without remembering the original state.
* - A reload gives a clean, predictable state for both directions.
* ========================================================================== */
async function toggleStyle() {
const currentlyEnabled = await GM.getValue("enabled", true);
await GM.setValue("enabled", !currentlyEnabled);
window.location.reload();
}
function registerMenu() {
const label = isEnabled ? "Style - Disable" : "Style - Enable";
GM.registerMenuCommand(label, toggleStyle);
}
/* ============================================================================
* BOOT
* ========================================================================== */
if (isEnabled) {
applyStyle(); // inject CSS at document-start
window.addEventListener("load", applyDOMTweaks); // DOM tweaks once page is ready
}
registerMenu();