Hide Web Elements Pro 15.6.1 — 15.6 base with Auto-Accept Age Verification removed.
// ==UserScript==
// @name Hide Web Elements Pro
// @version 15.6
// @description Hide Web Elements Pro 15.6.1 — 15.6 base with Auto-Accept Age Verification removed.
// @author KTZ
// @match *://*/*
// @icon https://cdn.corenexis.com/f/v9leABxGUbJ.png
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant unsafeWindow
// @connect *
// @run-at document-start
// @license MIT
// @namespace https://greasyfork.org/users/1620673
// ==/UserScript==
(function() {
'use strict';
// ---------- Safe Storage ----------
const gv = (key, def) => {
try {
if (typeof GM_getValue !== 'undefined') return GM_getValue(key, def);
const item = localStorage.getItem(key);
return item !== null ? JSON.parse(item) : def;
} catch {
return def;
}
};
let isSyncingStorage = false;
const sv = (key, val) => {
try {
if (typeof GM_setValue !== 'undefined') {
GM_setValue(key, val);
} else {
localStorage.setItem(key, JSON.stringify(val));
}
} catch {}
if (!isSyncingStorage) {
isSyncingStorage = true;
try {
if (typeof syncCache === 'function') syncCache();
if (typeof requestUpdateStyles === 'function') requestUpdateStyles();
if (isTop && typeof broadcastState === 'function') broadcastState();
} finally {
isSyncingStorage = false;
}
}
};
const doc = document, win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
const UI_HOST_ID = 'hider-ui-root', STEPPER_BAR_ID = 'hider-stepper-bar';
let isTop = false;
try { isTop = (window.self === window.top); } catch { isTop = false; }
let shadowRoot = null;
const shadowBy = id => shadowRoot ? shadowRoot.getElementById(id) : null;
// ---------- Persistent Settings ----------
let initialMem = gv('hider_freeze_memory', null) || (gv('hider_autono_global', false) ? 'block_all' : 'ask');
const CACHE = {
blockedDomainsList: [], blockedDomainsSet: new Set(),
allowedDomainsList: [], allowedDomainsSet: new Set(),
customRules: [], isFrozen: gv('hider_freeze_global', false),
freezeMemory: initialMem, logs: null,
autoTimeSkipper: gv('hider_auto_time_skipper', false),
autoScroll: gv('hider_auto_scroll', true),
enableContextMenu: gv('hider_enable_contextmenu', true),
autoRemoveBlur: gv('hider_auto_remove_blur', false),
universalProtect: true,
antiPaywall: gv('hider_anti_paywall', false),
autoCloseModals: gv('hider_auto_close_modals', false),
cookieConsentMode: gv('hider_cookie_consent_mode', 'ask'),
autoCloseLogins: gv('hider_auto_close_logins', false),
filterLists: gv('hider_filter_lists', [])
};
// ---------- Dock Button Visibility ----------
const DOCK_BUTTONS = [
{ id: 'btn-select', icon: '🎯', label: 'Hide', desc: 'Select page elements to hide' },
{ id: 'btn-scope', icon: '🌐', label: 'Scope', desc: 'Cycle site / page / global scope' },
{ id: 'btn-reveal-quick', icon: '👁️', label: 'Reveal', desc: 'Restore hidden/blurred content' },
{ id: 'btn-links', icon: '🔗', label: 'Links', desc: 'Open link & media lab' },
{ id: 'btn-skip-30', icon: '⏩', label: '+30s', desc: 'Advance detected media timers' },
{ id: 'btn-freeze', icon: '❄️', label: 'Freeze', desc: 'Control navigation prompts' }
];
function getHiddenDockButtons() {
return gv('hider_hidden_dock_buttons', []);
}
function setHiddenDockButtons(arr) {
sv('hider_hidden_dock_buttons', arr);
}
function applyDockButtonVisibility() {
if (!shadowRoot) return;
const hidden = new Set(getHiddenDockButtons());
DOCK_BUTTONS.forEach(btn => {
const el = shadowRoot.getElementById(btn.id);
if (el) {
el.style.setProperty('display', hidden.has(btn.id) ? 'none' : 'flex', 'important');
}
});
const menuBtn = shadowRoot.getElementById('btn-manage');
if (menuBtn) menuBtn.style.setProperty('display', 'flex', 'important');
}
function renderDockButtonOptions() {
if (!shadowRoot) return;
const container = shadowRoot.getElementById('dock-buttons-list');
const countEl = shadowRoot.getElementById('dock-visible-count');
if (!container) return;
const hidden = new Set(getHiddenDockButtons());
container.innerHTML = '';
container.style.cssText = 'display:grid!important;grid-template-columns:repeat(auto-fit,minmax(145px,1fr))!important;gap:6px!important;';
DOCK_BUTTONS.forEach(btn => {
const visible = !hidden.has(btn.id);
const card = document.createElement('button');
card.type = 'button';
card.dataset.btnId = btn.id;
card.setAttribute('aria-pressed', String(visible));
card.title = btn.desc;
card.style.cssText = `
display:flex!important;align-items:center!important;gap:8px!important;
min-height:44px!important;padding:7px 8px!important;border-radius:12px!important;
cursor:pointer!important;text-align:left!important;color:#dbeafe!important;
background:${visible ? 'linear-gradient(145deg,rgba(56,189,248,.12),rgba(255,255,255,.025))' : 'rgba(255,255,255,.018)'}!important;
border:1px solid ${visible ? 'rgba(56,189,248,.22)' : 'rgba(148,163,184,.08)'}!important;
box-shadow:${visible ? '0 8px 18px rgba(0,0,0,.12), inset 0 1px 0 rgba(255,255,255,.05)' : 'none'}!important;
transition:transform .16s ease,background .16s ease,border-color .16s ease,box-shadow .16s ease,opacity .16s ease!important;
opacity:${visible ? '1' : '.62'}!important;
`;
const icon = document.createElement('span');
icon.textContent = btn.icon;
icon.style.cssText = 'width:28px;height:28px;display:grid;place-items:center;border-radius:9px;background:rgba(255,255,255,.05);font-size:15px;flex:0 0 28px;';
card.appendChild(icon);
const copy = document.createElement('span');
copy.style.cssText = 'display:flex;flex-direction:column;gap:2px;min-width:0;flex:1;';
const title = document.createElement('span');
title.textContent = btn.label;
title.style.cssText = 'font-size:9px;font-weight:900;letter-spacing:.2px;color:#e2e8f0;';
const sub = document.createElement('span');
sub.textContent = visible ? 'VISIBLE' : 'HIDDEN';
sub.style.cssText = `font-size:7px;letter-spacing:.7px;font-weight:900;color:${visible ? '#6ee7b7' : '#94a3b8'};`;
copy.append(title, sub);
card.appendChild(copy);
card.addEventListener('mouseenter', () => {
card.style.transform = 'translateY(-1px)';
card.style.borderColor = visible ? 'rgba(125,211,252,.34)' : 'rgba(148,163,184,.15)';
});
card.addEventListener('mouseleave', () => {
card.style.transform = '';
card.style.borderColor = visible ? 'rgba(56,189,248,.22)' : 'rgba(148,163,184,.08)';
});
card.addEventListener('click', e => {
e.stopPropagation();
const nextHidden = new Set(getHiddenDockButtons());
if (nextHidden.has(btn.id)) nextHidden.delete(btn.id);
else nextHidden.add(btn.id);
setHiddenDockButtons(Array.from(nextHidden));
applyDockButtonVisibility();
renderDockButtonOptions();
});
container.appendChild(card);
});
const visibleCount = DOCK_BUTTONS.length - hidden.size;
if (countEl) countEl.textContent = `${visibleCount}/${DOCK_BUTTONS.length} visible`;
}
function setDockButtonsVisible(mode = 'all') {
const next = mode === 'none' ? DOCK_BUTTONS.map(btn => btn.id) : [];
setHiddenDockButtons(next);
applyDockButtonVisibility();
renderDockButtonOptions();
showToast(mode === 'all' ? '✅ All dock controls visible' : '🙈 Optional dock controls hidden');
}
// ---------- Core Variables ----------
let isSelecting = false, isFrozen = CACHE.isFrozen, currentScope = 'site', previewElement = null;
let stepperStack = [], userApprovedNavigation = false, cachedCssString = null, lastUrl = location.href;
let stepperPos = { x: null, y: null }, isDraggingStepper = false, isDraggingDock = false, logSaveTimer = null;
let collapseTimer = null, editingRuleId = null, scrollAnimationFrame = null, styleUpdateRAF = null;
let linkPanelEl = null;
let linkDisplayMode = 'text';
let isProtected = false;
let featuresEnabled = true;
let CURRENT_DOMAIN = '', CURRENT_URL = '';
let previewOutsideListener = null;
let lastHiddenSelector = null;
let globalScopeTemp = false;
// === SMART IMPROVEMENT: Cache for rule signature ===
let lastRuleSignature = '';
let activeHiddenSelector = ''; // Combined selector for the proxy
// ---------- Smart Time Skipper State ----------
let autoSkipInterval = null;
let autoSkipObserver = null;
let autoSkipEmptyCount = 0;
let autoSkipCandidates = new Set();
let autoSkipMedia = new Set();
const AUTO_SKIP_INTERVAL_MS = 700;
const AUTO_SKIP_MAX_EMPTY = 18;
const AUTO_SKIP_HINTS = /(?:skip\s*(?:ad|video)?|skip\s*this\s*ad|dismiss\s*ad|continue\s*(?:without|after)\s*(?:ad|advert)|ad\s*remaining|commercial|advertisement)/i;
const TIMER_HINTS = /(?:timer|countdown|remaining|wait|seconds?|mins?|minutes?)/i;
// ---------- Cleanup ----------
const timers = {
scrollInterval: null,
blurInterval: null,
protectionPoller: null,
logSaveTimer: null,
collapseTimer: null,
paywallObserverTimer: null,
modalObserverTimer: null,
cookieObserverTimer: null,
overlayScanTimer: null,
protectionDebounceTimer: null,
};
const observers = {
protectionObserver: null,
blurObserver: null,
urlChangeObserver: null,
paywallObserver: null,
modalObserver: null,
cookieObserver: null,
adSkipObserver: null,
headMutationObserver: null
};
const styleElements = {
scrollStyleEl: null,
contextMenuStyleEl: null,
blurGlobalStyle: null
};
function dispose() {
Object.keys(timers).forEach(key => {
if (timers[key]) {
clearInterval(timers[key]);
clearTimeout(timers[key]);
timers[key] = null;
}
});
Object.keys(observers).forEach(key => {
if (observers[key]) {
observers[key].disconnect();
observers[key] = null;
}
});
if (timers.overlayScanTimer) { clearInterval(timers.overlayScanTimer); timers.overlayScanTimer = null; }
Object.values(styleElements).forEach(el => {
if (el && el.parentNode) el.remove();
});
const host = doc.getElementById(UI_HOST_ID);
if (host) host.remove();
const stepper = shadowBy(STEPPER_BAR_ID);
if (stepper) stepper.remove();
if (linkPanelEl && linkPanelEl.parentNode) linkPanelEl.remove();
const prompt = shadowBy('hider-freeze-prompt');
if (prompt) prompt.remove();
const toast = doc.getElementById('hider-toast');
if (toast) toast.remove();
const dyn = doc.getElementById('hider-dynamic-styles');
if (dyn) dyn.remove();
stopAutoSkipMonitoring();
}
window.addEventListener('beforeunload', dispose);
// ---------- Context Menu Override ----------
const contextMenuHandler = function(e) {
if (!CACHE.enableContextMenu) return;
const path = e.composedPath ? e.composedPath() : [];
if (path.some(el => el.id === UI_HOST_ID || (el.closest && el.closest('#' + UI_HOST_ID)))) {
e.stopPropagation();
return;
}
};
document.addEventListener('contextmenu', contextMenuHandler, true);
// ---------- Freeze Mode ----------
const FREEZE_LABELS = {
'ask': '❓ Ask Every Time', 'block_all': '⛔ Auto-Block All Navigations',
'allow_same': '🔗 Allow Same Domain Only', 'allow_all': '🟢 Allow All Navigations'
};
const resolveUrl = u => {
if (!u || typeof u !== 'string') return '';
const trimmed = u.trim();
if (trimmed === '*') return '*';
if (!/^[a-zA-Z][a-zA-Z0-9+-.]*:\/\//.test(trimmed)) {
if (trimmed.startsWith('//')) return 'http:' + trimmed;
if (!trimmed.startsWith('/') && !trimmed.startsWith('./') && !trimmed.startsWith('../')) {
return 'http://' + trimmed;
}
}
try { return new URL(trimmed, win.location.href).href; } catch { return trimmed; }
};
const cleanUrl = () => CURRENT_URL || (CURRENT_URL = location.origin + location.pathname);
const cleanDomain = url => {
try {
if (!url) return '';
if (url === '*') return '*';
const abs = resolveUrl(url);
return new URL(abs).hostname.replace(/^www\./, '').toLowerCase();
} catch {
return String(url).trim().toLowerCase();
}
};
const updateCurrentLocCache = () => {
CURRENT_URL = location.origin + location.pathname;
CURRENT_DOMAIN = cleanDomain(location.href);
updateProtectionFlag();
applyAllSettings();
};
function getParentDomain(url) {
if (!url) return '';
try {
const host = cleanDomain(url);
if (!host || host === '*') return host;
const parts = host.split('.');
if (parts.length <= 2) return host;
const multiPartTlds = ['co.uk', 'com.au', 'org.uk', 'gov.uk', 'co.jp', 'com.br', 'co.id', 'or.id', 'ac.uk', 'net.au', 'com.tw', 'co.nz', 'com.sg', 'com.mx', 'co.kr', 'com.tr'];
const lastTwo = parts.slice(-2).join('.');
return (multiPartTlds.includes(lastTwo) && parts.length > 2) ? parts.slice(-3).join('.') : lastTwo;
} catch {
return cleanDomain(url) || '';
}
}
const FEATURE_BLACKLIST = [];
const MEDIA_SENSITIVE_DOMAINS = ['facebook.com', 'fb.com', 'instagram.com'];
let blacklistToastShown = false;
function isFeatureBlacklisted() {
if (!CURRENT_DOMAIN || !FEATURE_BLACKLIST.length) return false;
return FEATURE_BLACKLIST.some(domain =>
CURRENT_DOMAIN === domain || CURRENT_DOMAIN.endsWith('.' + domain)
);
}
// Some social platforms wrap real media in highly dynamic UI containers.
// Keep destructive overlay/auto-close/pause helpers away from those media surfaces.
function isMediaSensitiveDomain() {
if (!CURRENT_DOMAIN) return false;
return MEDIA_SENSITIVE_DOMAINS.some(domain =>
CURRENT_DOMAIN === domain || CURRENT_DOMAIN.endsWith('.' + domain)
);
}
// ============ REVEAL & UNBLOCK ENGINE ============
function isProtectedPage() {
try {
if (win.__cf_chl_opt || win.__cfRLUnblockHandlers || win._cf) return true;
if (win.turnstile) {
if (doc.querySelector('.cf-turnstile, #turnstile-wrapper, .turnstile-container, #cf-challenge')) return true;
}
const challengeSelectors = [
'#challenge-running', '#cf-please-wait', '.cf-browser-verification',
'#cf-content', '#cf-stage', '#cf-challenge', '.cf-challenge',
'#cf-error-details', '#cf-waiting', '.cf-please-wait',
'#cf-content-wrapper', '#challenge-form', '.challenge-form', '#cf-intercept'
];
for (const sel of challengeSelectors) {
if (doc.querySelector(sel)) return true;
}
if (doc.querySelector('.ray-id, [data-ray-id], [data-cf-ray]')) return true;
const title = doc.title || '';
if (title.includes('Just a moment...') || title.includes('Attention Required!') ||
title.includes('Security Check') || title.includes('Verify you are human') ||
title.includes('Checking your browser')) {
return true;
}
const forms = doc.querySelectorAll('form[action*="__cf_chl"], form[action*="cf-challenge"]');
if (forms.length > 0) return true;
const scripts = doc.querySelectorAll('script[src*="challenges.cloudflare.com"], script[src*="/cdn-cgi/challenge-platform/"]');
if (scripts.length > 0) return true;
if (doc.querySelector('meta[name="cf-options"]')) return true;
} catch (e) { /* ignore */ }
return false;
}
function updateProtectionFlag() {
const was = isProtected;
isProtected = isProtectedPage();
featuresEnabled = isProtected ? !CACHE.universalProtect : true;
if (was !== isProtected) requestUpdateStyles();
}
function setupProtectionObserver() {
if (observers.protectionObserver) return;
let pending = false;
const schedule = () => {
if (pending) return;
pending = true;
clearTimeout(timers.protectionDebounceTimer);
timers.protectionDebounceTimer = setTimeout(() => {
pending = false;
timers.protectionDebounceTimer = null;
updateProtectionFlag();
}, 250);
};
observers.protectionObserver = new MutationObserver(mutations => {
// Challenge pages are normally discovered through added nodes/title changes.
// Do not watch every attribute change: React sites mutate thousands of attrs.
for (const m of mutations) {
if (m.type === 'childList' && m.addedNodes?.length) { schedule(); return; }
}
});
try {
observers.protectionObserver.observe(doc.documentElement, { childList: true, subtree: true });
} catch {}
// One delayed check catches document-start challenges without creating a permanent poller.
setTimeout(updateProtectionFlag, 600);
}
// ---------- Scroll Defeater ----------
// Do NOT rewrite html/body position/overflow globally. That breaks site menus,
// search drawers, nested scrollers and pages that intentionally lock the body
// while keeping an inner container scrollable.
function forceEnableScroll() {
if (!CACHE.autoScroll) return;
if (isFeatureBlacklisted() || !featuresEnabled) return;
const html = doc.documentElement, body = doc.body;
if (!html && !body) return;
// Only remove an actual document-level scroll lock when there is strong
// evidence that a blocking overlay is active. Never force position/static.
try {
const roots = [html, body].filter(Boolean);
for (const root of roots) {
const style = win.getComputedStyle(root);
if (style.overflow === 'hidden' || style.overflowY === 'hidden') {
root.style.setProperty('overflow-y', 'auto', 'important');
}
if (style.overflowX === 'hidden') {
// Keep horizontal overflow behavior unchanged unless both axes
// were explicitly locked.
if (style.overflow === 'hidden') {
root.style.setProperty('overflow-x', 'auto', 'important');
}
}
}
} catch {}
}
function checkAndAutoUnblockScroll() {
if (!CACHE.autoScroll || isFeatureBlacklisted() || !featuresEnabled) {
stopScrollDefeater();
return;
}
const html = doc.documentElement, body = doc.body;
if (!html || !body) return;
try {
// Do not treat fixed/absolute positioning by itself as a scroll lock.
// It is commonly used by drawers, search boxes, sticky headers, etc.
const hStyle = win.getComputedStyle(html);
const bStyle = win.getComputedStyle(body);
const locked =
hStyle.overflow === 'hidden' || hStyle.overflowY === 'hidden' ||
bStyle.overflow === 'hidden' || bStyle.overflowY === 'hidden';
if (locked && hasBlockingOverlayInDocument()) {
forceEnableScroll();
}
} catch {}
}
function startScrollDefeater() {
if (isFeatureBlacklisted() || !featuresEnabled) {
stopScrollDefeater();
return;
}
if (timers.scrollInterval) clearInterval(timers.scrollInterval);
if (CACHE.autoScroll) {
// Delayed/lazy checks only; do not modify the document on every page.
timers.scrollInterval = setInterval(checkAndAutoUnblockScroll, 2500);
}
}
function stopScrollDefeater() {
if (timers.scrollInterval) {
clearInterval(timers.scrollInterval);
timers.scrollInterval = null;
}
}
// ---------- Context Menu Styles ----------
function updateContextMenuStyles() {
if (isFeatureBlacklisted() || !featuresEnabled) {
if (styleElements.contextMenuStyleEl) {
styleElements.contextMenuStyleEl.remove();
styleElements.contextMenuStyleEl = null;
}
return;
}
if (!CACHE.enableContextMenu) {
if (styleElements.contextMenuStyleEl) {
styleElements.contextMenuStyleEl.remove();
styleElements.contextMenuStyleEl = null;
}
return;
}
if (!styleElements.contextMenuStyleEl) {
styleElements.contextMenuStyleEl = doc.createElement('style');
styleElements.contextMenuStyleEl.id = 'hider-contextmenu-style';
(doc.head || doc.documentElement)?.appendChild(styleElements.contextMenuStyleEl);
}
styleElements.contextMenuStyleEl.textContent = `
* {
-webkit-touch-callout: default !important;
-webkit-user-select: text !important;
user-select: text !important;
}
`;
}
// ---------- Blur Removal ----------
function removeBlurFromElements(roots = [doc]) {
if (!CACHE.autoRemoveBlur || isFeatureBlacklisted() || !featuresEnabled || isMediaSensitiveDomain()) {
stopBlurRemoval();
return;
}
const selectors = [
'[style*="blur"]', '[style*="backdrop-filter"]',
'[class*="blur" i]', '[data-blur]', '[data-backdrop]'
].join(',');
for (const root of roots) {
if (!root?.querySelectorAll) continue;
let nodes = [];
try { nodes = root.querySelectorAll(selectors); } catch { continue; }
for (const el of nodes) {
if (el.id === UI_HOST_ID || el.closest?.('#' + UI_HOST_ID)) continue;
try {
const style = win.getComputedStyle(el);
if (style.filter?.includes('blur')) el.style.setProperty('filter', 'none', 'important');
if (style.backdropFilter?.includes('blur')) {
el.style.setProperty('backdrop-filter', 'none', 'important');
el.style.setProperty('-webkit-backdrop-filter', 'none', 'important');
}
} catch {}
}
}
}
function scheduleBlurRemoval() {
if (isFeatureBlacklisted() || !featuresEnabled || isMediaSensitiveDomain()) {
stopBlurRemoval();
return;
}
if (styleElements.blurGlobalStyle) {
styleElements.blurGlobalStyle.remove();
styleElements.blurGlobalStyle = null;
}
if (CACHE.autoRemoveBlur) {
removeBlurFromElements();
if (!observers.blurObserver) {
observers.blurObserver = new MutationObserver(mutations => {
if (!CACHE.autoRemoveBlur) return;
const roots = [];
for (const m of mutations) {
for (const n of m.addedNodes || []) {
if (n.nodeType === Node.ELEMENT_NODE) roots.push(n);
}
}
if (roots.length) removeBlurFromElements(roots);
});
try {
observers.blurObserver.observe(doc.documentElement, { childList: true, subtree: true });
} catch {}
}
} else {
stopBlurRemoval();
}
}
function stopBlurRemoval() {
if (timers.blurInterval) { clearInterval(timers.blurInterval); timers.blurInterval = null; }
if (observers.blurObserver) { observers.blurObserver.disconnect(); observers.blurObserver = null; }
if (styleElements.blurGlobalStyle) { styleElements.blurGlobalStyle.remove(); styleElements.blurGlobalStyle = null; }
}
// ---------- SAFE HELPER: Check if element is main content ----------
function isMainContentElement(el) {
if (!el) return false;
if (el === doc.documentElement || el === doc.body) return true;
if (el.id === UI_HOST_ID || el.closest && el.closest('#' + UI_HOST_ID)) return true;
const text = el.textContent || '';
if (text.length > 500) {
const style = win.getComputedStyle(el);
if (style.position === 'fixed' || style.position === 'absolute') {
const z = parseInt(style.zIndex, 10);
if (z > 1000) return false;
}
return true;
}
if (el.matches && el.matches('article, main, section, div[role="main"]')) {
if (text.length > 100) return true;
}
return false;
}
// ================================================================
// NEW HELPER: Detect side panels (smart)
// ================================================================
function isSidePanel(el) {
if (!el || el === doc.documentElement || el === doc.body) return false;
if (el.closest && el.closest('#' + UI_HOST_ID)) return false;
const rect = el.getBoundingClientRect();
const vw = win.innerWidth, vh = win.innerHeight;
const widthRatio = rect.width / vw;
const heightRatio = rect.height / vh;
const isLeft = rect.left < 10;
const isRight = (vw - rect.right) < 10;
// 1. Position & size
if ((isLeft || isRight) && widthRatio < 0.4 && heightRatio > 0.3) return true;
// 2. Class/id/role keywords
const classId = (el.className + ' ' + el.id).toLowerCase();
const role = el.getAttribute('role') || '';
const combined = classId + ' ' + role;
const sideKeywords = ['sidebar', 'drawer', 'menu', 'navigation', 'nav', 'sidepanel', 'offcanvas', 'slide', 'panel', 'sidenav'];
if (sideKeywords.some(kw => combined.includes(kw))) return true;
// 3. ARIA roles that are typical for menus/navigation
const menuRoles = ['navigation', 'menu', 'menubar', 'listbox', 'tree', 'tablist'];
if (menuRoles.includes(role)) return true;
// 4. Many links and not full‑screen → likely a menu
const links = el.querySelectorAll('a');
if (links.length > 5 && widthRatio < 0.5 && heightRatio < 0.9) return true;
// 5. Contains a search input or hamburger‑like button
const hasSearch = el.querySelector('input[type="search"], input[placeholder*="search"], .search-input');
if (hasSearch && widthRatio < 0.5) return true;
// 6. Check for transform/transition that suggests sliding panel
const style = win.getComputedStyle(el);
if (style.transform && style.transform !== 'none' && (style.transform.includes('translateX') || style.transform.includes('translateY'))) {
return true;
}
return false;
}
// ================================================================
// COMPOSED-TREE / OVERLAY HELPERS
// ================================================================
function forEachOpenShadowRoot(callback) {
const seen = new Set();
const visit = root => {
if (!root || seen.has(root)) return;
seen.add(root);
callback(root);
const hosts = root.querySelectorAll ? root.querySelectorAll('*') : [];
for (const host of hosts) {
if (host && host.shadowRoot) visit(host.shadowRoot);
}
};
visit(doc);
}
function getOverlayRoots() {
const roots = [];
forEachOpenShadowRoot(root => roots.push(root));
return roots;
}
function isVisibleElement(el, rootWin = win) {
if (!el || el.nodeType !== Node.ELEMENT_NODE) return false;
try {
const s = rootWin.getComputedStyle(el);
if (s.display === 'none' || s.visibility === 'hidden' || parseFloat(s.opacity) < 0.01) return false;
const r = el.getBoundingClientRect();
return r.width > 1 && r.height > 1;
} catch {
return false;
}
}
function getElementMeta(el) {
const cls = typeof el.className === 'string' ? el.className : (el.getAttribute('class') || '');
return (
`${el.id || ''} ${cls} ${el.getAttribute('role') || ''} ` +
`${el.getAttribute('aria-label') || ''} ${el.getAttribute('data-testid') || ''}`
).toLowerCase();
}
const ESSENTIAL_UI_TERMS = [
'search', 'sidebar', 'side-bar', 'drawer', 'navigation', 'navbar', 'nav-menu',
'menu', 'offcanvas', 'dropdown', 'autocomplete', 'suggestion', 'command-palette',
'settings', 'filter', 'sort', 'toolbar', 'dialog', 'tooltip', 'popover',
'datepicker', 'calendar', 'select', 'combobox', 'listbox', 'accessibility',
'player', 'video', 'audio', 'volume', 'caption', 'share', 'login', 'signin',
'sign-in', 'register', 'account', 'profile'
];
const BLOCKING_UI_TERMS = [
'paywall', 'subscription', 'premium', 'subscribe', 'adblock', 'ad-block',
'anti-adblock', 'anti adblock', 'disable ad blocker', 'disable adblock',
'whitelist', 'detected ad blocker', 'detect adblock', 'please disable',
'content is locked', 'article is locked', 'members only', 'members-only'
];
function isLikelyEssentialUI(el) {
if (!el || el === doc.documentElement || el === doc.body) return true;
if (el.id === UI_HOST_ID || el.closest?.('#' + UI_HOST_ID)) return true;
const meta = getElementMeta(el);
if (ESSENTIAL_UI_TERMS.some(term => meta.includes(term))) return true;
const role = (el.getAttribute('role') || '').toLowerCase();
if (['navigation', 'menu', 'menubar', 'listbox', 'tree', 'tablist', 'combobox'].includes(role)) return true;
try {
const r = el.getBoundingClientRect();
const vw = win.innerWidth || 1;
const vh = win.innerHeight || 1;
const wr = r.width / vw;
const hr = r.height / vh;
// Narrow edge panels/drawers are almost always intentional UI.
const nearLeft = r.left <= 12;
const nearRight = (vw - r.right) <= 12;
if ((nearLeft || nearRight) && wr <= 0.48 && hr >= 0.18) return true;
// Anything containing an active form control is generally user-facing UI.
if (el.querySelector?.(
'input, textarea, select, button, [role="button"], [contenteditable="true"]'
)) {
// Do not exempt obvious blocking notices.
const text = (el.textContent || '').toLowerCase();
if (!BLOCKING_UI_TERMS.some(term => text.includes(term))) return true;
}
} catch {}
return false;
}
function getOverlayScore(el) {
if (!isVisibleElement(el)) return -Infinity;
if (isLikelyEssentialUI(el)) return -Infinity;
const style = win.getComputedStyle(el);
if (style.position !== 'fixed' && style.position !== 'absolute' && style.position !== 'sticky') {
return -Infinity;
}
const r = el.getBoundingClientRect();
const vw = Math.max(1, win.innerWidth);
const vh = Math.max(1, win.innerHeight);
const coverage = (Math.max(0, r.width) * Math.max(0, r.height)) / (vw * vh);
let score = 0;
const meta = getElementMeta(el);
const text = (el.textContent || '').trim().toLowerCase().slice(0, 8000);
if (style.position === 'fixed' || style.position === 'absolute') score += 1;
if (coverage >= 0.35) score += 2;
if (coverage >= 0.60) score += 2;
if (r.left <= 5 && r.top <= 5) score += 1;
if (r.width >= vw * 0.90 && r.height >= vh * 0.85) score += 2;
const z = parseInt(style.zIndex, 10);
if (Number.isFinite(z)) {
if (z >= 100) score += 1;
if (z >= 1000) score += 1;
}
if (style.backdropFilter?.includes('blur')) score += 2;
if (style.pointerEvents !== 'none') score += 1;
const blockingTermHits = BLOCKING_UI_TERMS.reduce((n, term) => n + (meta.includes(term) || text.includes(term) ? 1 : 0), 0);
if (blockingTermHits >= 1) score += 4;
if (blockingTermHits >= 2) score += 2;
const closeButton = el.querySelector?.(
'[aria-label*="close" i], [data-testid*="close" i], .close, .dismiss, button'
);
if (closeButton) score += 1;
return score;
}
function isBlockingOverlayElement(el) {
const score = getOverlayScore(el);
return Number.isFinite(score) && score >= 6;
}
function getOverlayCandidates(root) {
if (!root?.querySelectorAll) return [];
const selectors = [
'[role="dialog"]', '[role="alertdialog"]',
'[class*="modal" i]', '[id*="modal" i]',
'[class*="popup" i]', '[id*="popup" i]',
'[class*="overlay" i]', '[id*="overlay" i]',
'[class*="paywall" i]', '[id*="paywall" i]',
'[class*="adblock" i]', '[id*="adblock" i]',
'[class*="subscribe" i]', '[id*="subscribe" i]',
'[class*="premium" i]', '[id*="premium" i]',
'[class*="gate" i]', '[id*="gate" i]',
'[class*="wall" i]', '[id*="wall" i]'
];
try { return Array.from(root.querySelectorAll(selectors.join(','))).slice(0, 80); }
catch { return []; }
}
function hasBlockingOverlayInDocument() {
for (const root of getOverlayRoots()) {
for (const el of getOverlayCandidates(root)) {
if (isBlockingOverlayElement(el)) return true;
}
}
return false;
}
function hideOverlayElement(el, reason) {
if (!el || isLikelyEssentialUI(el)) return false;
try {
el.setAttribute('data-hider-overlay-removed', reason || 'overlay');
el.style.setProperty('display', 'none', 'important');
el.style.setProperty('visibility', 'hidden', 'important');
el.style.setProperty('pointer-events', 'none', 'important');
// Remove only body-level scroll lock that belongs to the now-hidden
// blocking overlay. Do not touch fixed/absolute positioning.
forceEnableScroll();
return true;
} catch {
return false;
}
}
// ================================================================
// CLIENT-SIDE BLOCKING OVERLAY CLEANUP
// ================================================================
function setupPaywallBypass() {
if (observers.paywallObserver) { observers.paywallObserver.disconnect(); observers.paywallObserver = null; }
if (timers.paywallObserverTimer) { clearInterval(timers.paywallObserverTimer); timers.paywallObserverTimer = null; }
if (!CACHE.antiPaywall || !featuresEnabled || isFeatureBlacklisted() || isMediaSensitiveDomain()) return;
const inspectNode = node => {
if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
const candidates = [node];
try { candidates.push(...node.querySelectorAll?.('[role="dialog"], [role="alertdialog"], [class*="modal" i], [class*="overlay" i], [class*="paywall" i], [class*="adblock" i], [class*="subscribe" i], [class*="premium" i], [class*="gate" i], [class*="wall" i]') || []); } catch {}
for (const el of candidates.slice(0, 100)) {
if (!isVisibleElement(el)) continue;
if (isBlockingOverlayElement(el)) hideOverlayElement(el, 'blocking-overlay');
}
};
const process = mutations => {
if (isMediaSensitiveDomain()) return;
for (const m of mutations) for (const n of m.addedNodes || []) inspectNode(n);
};
observers.paywallObserver = new MutationObserver(process);
try { observers.paywallObserver.observe(doc.documentElement, { childList: true, subtree: true }); } catch {}
// Single startup pass only over semantic overlay selectors.
for (const root of getOverlayRoots()) {
for (const el of getOverlayCandidates(root)) if (isVisibleElement(el) && isBlockingOverlayElement(el)) hideOverlayElement(el, 'blocking-overlay');
}
}
function getAgeText(el) {
if (!el) return '';
const attrs = [
el.getAttribute?.('aria-label'), el.getAttribute?.('title'), el.getAttribute?.('placeholder'),
el.getAttribute?.('name'), el.getAttribute?.('id'),
typeof el.className === 'string' ? el.className : '',
el.getAttribute?.('data-testid'), el.getAttribute?.('data-test'), el.getAttribute?.('autocomplete')
];
return `${el.textContent || ''} ${attrs.filter(Boolean).join(' ')}`.toLowerCase().replace(/\s+/g, ' ').trim();
}
// ================================================================
// AUTO-CLOSE MODALS & OVERLAYS ENGINE
// ================================================================
function setupModalAutoClose() {
if (observers.modalObserver) { observers.modalObserver.disconnect(); observers.modalObserver = null; }
if (timers.modalObserverTimer) { clearTimeout(timers.modalObserverTimer); timers.modalObserverTimer = null; }
if (timers.overlayScanTimer) { clearInterval(timers.overlayScanTimer); timers.overlayScanTimer = null; }
if ((!CACHE.autoCloseModals) || !featuresEnabled || isFeatureBlacklisted() || isMediaSensitiveDomain()) return;
const closeKeywords = /(close|dismiss|cancel|got it|no thanks)/i;
const findCloseButton = modal => Array.from(modal.querySelectorAll?.('button, a[role="button"], input[type="button"], input[type="submit"], [aria-label*="close" i], [data-testid*="close" i], .close, .dismiss') || []).find(btn => closeKeywords.test(getAgeText(btn)));
const processModal = modal => {
if (!modal || modal.id === UI_HOST_ID || modal.closest?.('#' + UI_HOST_ID)) return;
if (!isVisibleElement(modal)) return;
if (!CACHE.autoCloseModals || isLikelyEssentialUI(modal)) return;
const text = (modal.textContent || '').toLowerCase().slice(0, 5000);
const meta = getElementMeta(modal);
const blocking = BLOCKING_UI_TERMS.some(term => text.includes(term) || meta.includes(term));
const hasForm = !!modal.querySelector?.('input, textarea, select, button, [contenteditable="true"]');
if (hasForm && !blocking && !CACHE.autoCloseLogins) return;
if (isBlockingOverlayElement(modal)) {
const btn = findCloseButton(modal);
if (btn) { try { btn.click(); return; } catch {} }
hideOverlayElement(modal, 'modal-overlay');
} else if (CACHE.autoCloseLogins && /(login|sign\s*in|register)/i.test(text)) {
const btn = findCloseButton(modal);
if (btn) { try { btn.click(); } catch {} }
}
};
const inspectNode = node => {
if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
if (CACHE.autoCloseModals) {
try {
for (const el of node.querySelectorAll?.('[role="dialog"], [role="alertdialog"], [class*="modal" i], [class*="popup" i], [class*="overlay" i], [class*="gate" i], [class*="paywall" i], [class*="adblock" i]') || []) processModal(el);
} catch {}
}
};
observers.modalObserver = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type !== 'childList') continue;
for (const n of m.addedNodes || []) inspectNode(n);
}
});
try { observers.modalObserver.observe(doc.documentElement, { childList: true, subtree: true }); } catch {}
inspectNode(doc.body || doc.documentElement);
}
// ---------- Cookie Consent ----------
function setupCookieConsent() {
if (observers.cookieObserver) {
observers.cookieObserver.disconnect();
observers.cookieObserver = null;
}
if (CACHE.cookieConsentMode === 'ask' || !featuresEnabled || isFeatureBlacklisted()) return;
const cookieSelectors = [
'#cookie-consent', '#cookie-banner', '.cookie-consent', '.cookie-banner',
'[class*="cookie"]', '[id*="cookie"]', '[aria-label*="cookie"]'
];
const acceptKeywords = ['accept', 'agree', 'allow', 'yes', 'ok', 'got it'];
const rejectKeywords = ['reject', 'decline', 'no', 'deny'];
function handleCookieBanner() {
const banners = doc.querySelectorAll(cookieSelectors.join(','));
for (const banner of banners) {
if (banner === doc.body || banner === doc.documentElement) continue;
if (banner.closest && banner.closest('#' + UI_HOST_ID)) continue;
const style = win.getComputedStyle(banner);
if (style.display === 'none' || style.visibility === 'hidden') continue;
if (isMainContentElement(banner)) continue;
const buttons = banner.querySelectorAll('button, a[role="button"], input[type="button"]');
const mode = CACHE.cookieConsentMode;
let targetTexts = mode === 'accept' ? acceptKeywords : rejectKeywords;
for (const btn of buttons) {
const text = btn.textContent.trim().toLowerCase();
if (targetTexts.some(kw => text.includes(kw))) {
try { btn.click(); } catch {}
return;
}
}
const closeBtn = banner.querySelector('[aria-label*="close"], [aria-label*="Close"], .close, .dismiss');
if (closeBtn) {
try { closeBtn.click(); } catch {}
}
}
}
observers.cookieObserver = new MutationObserver(() => {
handleCookieBanner();
});
observers.cookieObserver.observe(doc.documentElement, { childList: true, subtree: true });
setTimeout(handleCookieBanner, 500);
setTimeout(handleCookieBanner, 2000);
}
// ---------- Apply all settings ----------
function applyAllSettings() {
if (isFeatureBlacklisted() && !blacklistToastShown) {
showToast('⚠️ Some Reveal features disabled on this domain');
blacklistToastShown = true;
}
if (CACHE.autoScroll) startScrollDefeater();
else stopScrollDefeater();
updateContextMenuStyles();
if (CACHE.autoRemoveBlur) scheduleBlurRemoval();
else stopBlurRemoval();
updateProtectionFlag();
setupPaywallBypass();
setupModalAutoClose();
setupCookieConsent();
if (CACHE.autoTimeSkipper && featuresEnabled && !isMediaSensitiveDomain()) {
startAutoSkipMonitoring();
} else {
stopAutoSkipMonitoring();
}
}
// ---------- Sync Cache ----------
function syncCache() {
CACHE.blockedDomainsList = gv('hider_blocked_domains', []);
CACHE.blockedDomainsSet = new Set(CACHE.blockedDomainsList.map(cleanDomain).filter(Boolean));
CACHE.allowedDomainsList = gv('hider_allowed_domains', []);
CACHE.allowedDomainsSet = new Set(CACHE.allowedDomainsList.map(cleanDomain).filter(Boolean));
CACHE.customRules = gv('hider_custom_rules_v4', []);
CACHE.isFrozen = gv('hider_freeze_global', false);
isFrozen = CACHE.isFrozen;
CACHE.freezeMemory = gv('hider_freeze_memory', CACHE.freezeMemory);
CACHE.autoTimeSkipper = gv('hider_auto_time_skipper', false);
CACHE.autoScroll = gv('hider_auto_scroll', true);
CACHE.enableContextMenu = gv('hider_enable_contextmenu', true);
CACHE.autoRemoveBlur = gv('hider_auto_remove_blur', false);
CACHE.universalProtect = true;
CACHE.antiPaywall = gv('hider_anti_paywall', false);
CACHE.autoCloseModals = gv('hider_auto_close_modals', false);
CACHE.cookieConsentMode = gv('hider_cookie_consent_mode', 'ask');
CACHE.autoCloseLogins = gv('hider_auto_close_logins', false);
CACHE.filterLists = gv('hider_filter_lists', []);
if (gv('hider_last_log_clear_day', '') !== new Date().toDateString()) {
CACHE.logs = []; sv('hider_global_logs', []); sv('hider_last_log_clear_day', new Date().toDateString());
}
applyAllSettings();
}
syncCache();
function disableFreezeMode() {
isFrozen = false; CACHE.isFrozen = false;
sv('hider_freeze_global', false);
shadowBy('btn-freeze')?.classList.remove('is-frozen');
broadcastState();
}
// ---------- Stealth Engine (SMART: no class, just proxy) ----------
const styleProxyCache = new WeakMap();
try {
const origGetComputedStyle = win.getComputedStyle;
win.getComputedStyle = function(el, pseudo) {
const style = origGetComputedStyle.apply(this, arguments);
if (el && el instanceof Element && el.matches && activeHiddenSelector && el.matches(activeHiddenSelector)) {
let cachedProxy = styleProxyCache.get(style);
if (!cachedProxy) {
cachedProxy = new Proxy(style, {
get(target, prop) {
switch(prop) {
case 'display': return 'block';
case 'visibility': return 'visible';
case 'opacity': return '1';
case 'pointerEvents': return 'auto';
default:
const val = target[prop];
return typeof val === 'function' ? val.bind(target) : val;
}
}
});
styleProxyCache.set(style, cachedProxy);
}
return cachedProxy;
}
return style;
};
} catch {}
const isSameDomain = u => cleanDomain(u) && cleanDomain(u) === CURRENT_DOMAIN;
const isDomainBlocked = u => {
if (!u || !CACHE.blockedDomainsSet.size) return false;
const d = cleanDomain(u), lower = String(u).toLowerCase();
return d && (CACHE.blockedDomainsSet.has(d) || [...CACHE.blockedDomainsSet].some(b => b !== '*' && (d.endsWith('.' + b) || lower.includes(b))));
};
const isDomainAllowed = u => {
if (!u || !CACHE.allowedDomainsSet.size) return false;
const d = cleanDomain(u), lower = String(u).toLowerCase();
return d && (CACHE.allowedDomainsSet.has('*') || CACHE.allowedDomainsSet.has(d) || [...CACHE.allowedDomainsSet].some(a => a !== '*' && (d.endsWith('.' + a) || lower.includes(a))));
};
// ---------- WILDCARD CONVERSION ----------
function getBaseName(name) {
let base = name.replace(/[-_][0-9a-fA-F]+$/, '');
base = base.replace(/[0-9]+$/, '');
base = base.replace(/[-_]+$/, '');
return base || name;
}
function convertToWildcardSelector(sel) {
if (!sel || typeof sel !== 'string') return '';
return sel.replace(/(?<![#.])(#|\.)([a-zA-Z0-9_-]+)/g, (match, p, name) => {
if (name.startsWith('hider-')) return match;
const base = getBaseName(name);
if (!base) return match;
return p === '#' ? `[id*="${base}"]` : `[class*="${base}"]`;
});
}
function logBlockedAttempt(url, triggerType) {
if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
CACHE.logs.unshift({ url: url || 'about:blank', time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }), type: triggerType || 'Popup' });
if (CACHE.logs.length > 50) CACHE.logs.pop();
if (!timers.logSaveTimer) timers.logSaveTimer = setTimeout(() => { timers.logSaveTimer = null; sv('hider_global_logs', CACHE.logs); }, 1000);
}
// ---------- Freeze FX ----------
function triggerFreezeFx() {
const fx = doc.createElement('div');
fx.className = 'hider-freeze-snow-overlay';
const frag = doc.createDocumentFragment();
const flakeChars = ['❄', '❅', '❆', '✨', '⚡'];
for (let i = 0; i < 22; i++) {
const flake = doc.createElement('div');
flake.className = 'hider-snow-flake';
flake.textContent = flakeChars[Math.floor(Math.random() * flakeChars.length)];
flake.style.cssText = `left:${Math.random() * 98}vw; top:${Math.random() * 60}vh; animation-delay:${Math.random() * 0.35}s; font-size:${14 + Math.random() * 18}px;`;
frag.appendChild(flake);
}
fx.appendChild(frag);
(doc.body || doc.documentElement)?.appendChild(fx);
setTimeout(() => fx.remove(), 1450);
}
function forceExposeAndPlayVideos() {
// Never forcibly rewrite social-media players. Their React/media pipeline
// may legitimately replace the <video> element while a post is playing.
if (isMediaSensitiveDomain()) return;
doc.querySelectorAll('video').forEach(v => {
['display', 'visibility', 'opacity', 'pointer-events'].forEach(p => v.style.setProperty(p, p === 'display' ? 'block' : p === 'pointer-events' ? 'auto' : '1', 'important'));
try { v.play()?.catch?.(() => {}); } catch {}
});
}
function triggerVideoResumeChain() {
if (isMediaSensitiveDomain()) return;
forceExposeAndPlayVideos();
[150, 400, 800].forEach(t => setTimeout(forceExposeAndPlayVideos, t));
broadcastToFrames(window, { type: 'HIDER_RESUME_VIDEOS' });
}
function simulateAdWindowSuccess() {
try {
win.dispatchEvent(new Event('blur')); doc.dispatchEvent(new Event('visibilitychange'));
win.onblur?.();
setTimeout(() => { win.dispatchEvent(new Event('focus')); win.onfocus?.(); }, 60);
triggerVideoResumeChain();
} catch {}
}
function createDummyWindow() {
const dummy = {
closed: false, focus(){}, blur(){}, close(){ this.closed = true; }, postMessage(){},
location: { href: 'about:blank', replace(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Replace'); }, assign(u){ if(isDomainBlocked(u)) logBlockedAttempt(u, 'Blocked Dummy Assign'); } },
document: { write(){}, close(){}, body:{} }, opener: win
};
return new Proxy(dummy, {
get: (t, p) => p in t ? t[p] : () => {},
set: (t, p, v) => { if (p === 'location' && isDomainBlocked(v)) logBlockedAttempt(v, 'Blocked Dummy Location Set'); else t[p] = v; return true; }
});
}
function handleFreezeNavigation(url, triggerType, onConfirm, onDeny) {
if (isDomainBlocked(url)) {
logBlockedAttempt(url, triggerType + ' (Auto-Block Domain)');
onDeny?.(); simulateAdWindowSuccess(); return;
}
if (isDomainAllowed(url)) {
userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
const mem = CACHE.freezeMemory || 'ask';
if (mem === 'allow_all') {
disableFreezeMode(); userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
if (mem === 'block_all') {
logBlockedAttempt(url, triggerType + ' (Auto-Block)');
onDeny?.(); simulateAdWindowSuccess(); return;
}
if (mem === 'allow_same' && isSameDomain(url)) {
const domain = getParentDomain(url) || cleanDomain(url);
if (domain) executeAddAllowedDomain(domain);
userApprovedNavigation = true; onConfirm?.();
setTimeout(() => userApprovedNavigation = false, 300); return;
}
showFreezePrompt(url, triggerType, onConfirm, onDeny);
}
// ---------- Interceptors ----------
let interceptorsInstalled = false;
function installInterceptors() {
if (interceptorsInstalled) return;
interceptorsInstalled = true;
try {
const lp = win.Location ? win.Location.prototype : Object.getPrototypeOf(win.location);
if (lp) {
const origA = lp.assign, origR = lp.replace, hrefDesc = Object.getOwnPropertyDescriptor(lp, 'href');
const checkAndInterceptNav = (u, origFn, contextThis, triggerName) => {
if (!featuresEnabled) {
return origFn.call(contextThis, u);
}
if (isDomainBlocked(u)) {
logBlockedAttempt(u, `Blocked ${triggerName}`);
showToast(`⛔ Blocked ${triggerName}`); simulateAdWindowSuccess(); return;
}
if (isFrozen && !userApprovedNavigation) {
if (isDomainAllowed(u)) {
userApprovedNavigation = true; const res = origFn.call(contextThis, u);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(u, triggerName, () => {
userApprovedNavigation = true; origFn.call(contextThis, u);
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
return;
}
return origFn.call(contextThis, u);
};
if (origA) lp.assign = function(u) { return checkAndInterceptNav(u, origA, this, 'Redirect (assign)'); };
if (origR) lp.replace = function(u) { return checkAndInterceptNav(u, origR, this, 'Redirect (replace)'); };
if (hrefDesc?.set) {
try {
Object.defineProperty(lp, 'href', {
set(u) { checkAndInterceptNav(u, hrefDesc.set, this, 'Redirect (href)'); },
get() { return hrefDesc.get.call(this); }
});
} catch {}
}
}
const origClick = HTMLAnchorElement.prototype.click;
HTMLAnchorElement.prototype.click = function() {
if (!featuresEnabled) return origClick.apply(this, arguments);
if (isDomainBlocked(this.href)) { logBlockedAttempt(this.href, 'Blocked Click'); showToast('⛔ Blocked link click'); simulateAdWindowSuccess(); return; }
if (isFrozen && !userApprovedNavigation) {
if (isDomainAllowed(this.href)) {
userApprovedNavigation = true; const res = origClick.apply(this, arguments);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(this.href, 'Anchor Click', () => {
userApprovedNavigation = true; origClick.apply(this, arguments);
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
return;
}
return origClick.apply(this, arguments);
};
const origOpen = win.open;
win.open = function(url, target, features) {
if (!featuresEnabled) return origOpen.apply(win, arguments);
if (isDomainBlocked(url)) { logBlockedAttempt(url || 'about:blank', 'Blocked Popup'); simulateAdWindowSuccess(); showToast('⛔ Blocked Popup'); return createDummyWindow(); }
if (!url || url === 'about:blank') {
const realWin = origOpen.apply(win, arguments);
return realWin ? new Proxy(realWin, {
get: (t, p) => p === 'location' ? new Proxy(t.location, { set: (l, lp, lv) => (lp==='href'||lp==='assign') && isDomainBlocked(lv) ? (logBlockedAttempt(lv, 'Blocked Popup Nav'), showToast('⛔ Blocked popup nav'), t.close(), true) : (l[lp]=lv, true) }) : (typeof t[p]==='function'?t[p].bind(t):t[p]),
set: (t, p, v) => p === 'location' && isDomainBlocked(v) ? (logBlockedAttempt(v, 'Blocked Popup Location'), showToast('⛔ Blocked popup nav'), t.close(), true) : (t[p]=v, true)
}) : createDummyWindow();
}
if (isFrozen && !userApprovedNavigation) {
if (isDomainAllowed(url)) {
userApprovedNavigation = true; const res = origOpen.apply(win, arguments);
setTimeout(() => userApprovedNavigation = false, 300); return res;
}
handleFreezeNavigation(url, 'win.open()', () => {
userApprovedNavigation = true; origOpen.call(win, url, target, features);
setTimeout(() => userApprovedNavigation = false, 300);
}, () => {});
return createDummyWindow();
}
return origOpen.apply(win, arguments);
};
} catch {}
}
// ---------- Broadcast / Sync ----------
function broadcastToFrames(w, msg) { try { for (let i = 0; i < w.frames.length; i++) { w.frames[i].postMessage(msg, '*'); broadcastToFrames(w.frames[i], msg); } } catch {} }
function broadcastState() { if (isTop) broadcastToFrames(window, { type: 'HIDER_SYNC_STATE', isSelecting, currentScope, isFrozen }); }
window.addEventListener('message', e => {
if (!e.data) return;
if (e.data.type === 'HIDER_REQUEST_STATE' && isTop) broadcastState();
if (e.data.type === 'HIDER_SYNC_STATE') {
({ isSelecting, currentScope, isFrozen } = e.data);
CACHE.isFrozen = isFrozen;
syncCache(); requestUpdateStyles();
shadowBy('btn-select')?.classList.toggle('active', isSelecting);
shadowBy('btn-scope')?.classList.toggle('active', currentScope === 'link');
shadowBy('btn-scope')?.classList.toggle('scope-global', currentScope === 'global');
shadowBy('btn-freeze')?.classList.toggle('is-frozen', isFrozen);
if (!isSelecting) clearSelectionState();
applyDockButtonVisibility();
}
if (e.data.type === 'HIDER_RESUME_VIDEOS') triggerVideoResumeChain();
if (e.data.type === 'HIDER_SKIP_30') skip30Seconds();
});
window.addEventListener('storage', e => {
if (e.key && e.key.startsWith('hider_')) {
syncCache(); requestUpdateStyles();
if (isTop) broadcastState();
applyDockButtonVisibility();
}
});
if (typeof GM_addValueChangeListener !== 'undefined') {
['hider_freeze_global', 'hider_freeze_memory', 'hider_blocked_domains', 'hider_allowed_domains', 'hider_custom_rules_v4', 'hider_auto_time_skipper',
'hider_auto_scroll', 'hider_enable_contextmenu', 'hider_auto_remove_blur', 'hider_hidden_dock_buttons',
'hider_anti_paywall', 'hider_auto_close_modals', 'hider_cookie_consent_mode', 'hider_auto_close_logins', 'hider_filter_lists'
].forEach(key => {
try { GM_addValueChangeListener(key, () => { syncCache(); requestUpdateStyles(); if (isTop) broadcastState(); applyDockButtonVisibility(); }); } catch {}
});
}
if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}
// ---------- Style Update (SMART: signature-based) ----------
function getRuleSignature() {
const host = location.hostname;
const siteRules = gv('hider_site_' + host, []);
const linkRules = gv('hider_link_' + cleanUrl(), []);
const customActiveRules = (CACHE.customRules || [])
.filter(r => {
if (!r?.selector) return false;
const t = cleanDomain(r.target);
return t === '*' || t === host || (t && host.endsWith('.' + t));
})
.map(r => r.selector);
const combined = [...new Set([...siteRules, ...linkRules, ...customActiveRules])].filter(Boolean);
return combined.sort().join('|');
}
function requestUpdateStyles(force = false) {
if (styleUpdateRAF) {
cancelAnimationFrame(styleUpdateRAF);
styleUpdateRAF = null;
}
styleUpdateRAF = requestAnimationFrame(() => {
styleUpdateRAF = null;
updateStyles(force);
});
}
function updateStyles(force = false) {
const currentSignature = getRuleSignature();
if (!force && lastRuleSignature === currentSignature) {
if (CACHE.autoScroll && featuresEnabled) checkAndAutoUnblockScroll();
return;
}
let el = doc.getElementById('hider-dynamic-styles');
if (!el) {
el = doc.createElement('style');
el.id = 'hider-dynamic-styles';
(doc.head || doc.documentElement)?.appendChild(el);
}
const host = location.hostname;
const siteRules = gv('hider_site_' + host, []);
const linkRules = gv('hider_link_' + cleanUrl(), []);
const customActiveRules = (CACHE.customRules || [])
.filter(r => {
if (!r?.selector) return false;
const t = cleanDomain(r.target);
return t === '*' || t === host || (t && host.endsWith('.' + t));
})
.map(r => r.selector);
const combined = [...new Set([...siteRules, ...linkRules, ...customActiveRules])].filter(Boolean);
const safeCombined = combined.filter(sel => !sel.match(/^document$/i));
activeHiddenSelector = safeCombined.join(',');
const hideCss = (featuresEnabled && safeCombined.length)
? `${activeHiddenSelector}{opacity:0!important;pointer-events:none!important;position:absolute!important;top:-99999px!important;left:-99999px!important;width:0!important;height:0!important;max-width:0!important;max-height:0!important;overflow:hidden!important;visibility:hidden!important;clip:rect(0,0,0,0)!important}`
: '';
const baseAnimationCss = `
.hider-preview-highlight { outline: none !important; border: 2px solid #38bdf8 !important; background: rgba(56, 189, 248, 0.15) !important; box-shadow: 0 0 24px rgba(56, 189, 248, 0.6), inset 0 0 12px rgba(56, 189, 248, 0.3) !important; transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1) !important; position: relative !important; z-index: 2147483640 !important; cursor: crosshair !important; border-radius: 6px !important; }
.hider-freeze-snow-overlay { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; pointer-events: none !important; z-index: 2147483647 !important; overflow: hidden !important; background: radial-gradient(circle at center, rgba(56, 189, 248, 0.18) 0%, transparent 70%) !important; animation: hiderSnowOverlayFade 1.4s cubic-bezier(0.16, 1, 0.3, 1) forwards !important; }
@keyframes hiderSnowOverlayFade { 0% { opacity: 0; backdrop-filter: blur(0px); } 30% { opacity: 1; backdrop-filter: blur(3px); } 80% { opacity: 1; backdrop-filter: blur(3px); } 100% { opacity: 0; backdrop-filter: blur(0px); } }
.hider-snow-flake { position: absolute !important; color: #e0f2fe !important; user-select: none !important; pointer-events: none !important; opacity: 0.95 !important; animation: hiderSnowFall 1.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards !important; filter: drop-shadow(0 0 8px rgba(56, 189, 248, 0.9)) !important; }
@keyframes hiderSnowFall { 0% { transform: translateY(-25px) scale(0.5) rotate(0deg); opacity: 0; } 20% { opacity: 1; } 100% { transform: translateY(140px) scale(1.15) rotate(180deg); opacity: 0; } }`;
const css = baseAnimationCss + '\n' + hideCss;
if (cachedCssString !== css) {
el.textContent = css;
cachedCssString = css;
}
lastRuleSignature = currentSignature;
if (CACHE.autoScroll && featuresEnabled) checkAndAutoUnblockScroll();
}
updateStyles(true);
// === MOBILE-FRIENDLY PERSISTENCE: Reapply on visibility/focus ===
function reapplyHiddenStyles() {
requestUpdateStyles(true);
}
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
reapplyHiddenStyles();
}
});
window.addEventListener('focus', reapplyHiddenStyles, { passive: true });
if (doc.head) {
observers.headMutationObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.removedNodes) {
if (node.id === 'hider-dynamic-styles') {
requestUpdateStyles(true);
return;
}
}
}
});
observers.headMutationObserver.observe(doc.head, { childList: true });
}
// ---------- Selector Helpers ----------
function safeCSSEscape(str) { return (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') ? CSS.escape(str) : str.replace(/([!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, '\\$1'); }
function getExactSelector(el) {
if (!el || el.nodeType !== 1) return '';
const path = []; let curr = el;
while (curr && curr.nodeType === 1 && curr.tagName.toLowerCase() !== 'html') {
let tag = curr.tagName.toLowerCase();
if (tag === 'body') { path.unshift('body'); break; }
if (curr.id && !curr.id.startsWith('hider-') && !/^\d+$/.test(curr.id) && curr.id.length < 18) { path.unshift(`#${safeCSSEscape(curr.id)}`); break; }
const rawC = typeof curr.className === 'string' ? curr.className : curr.getAttribute('class') || '';
const classes = rawC.trim().split(/\s+/).filter(c => c.length > 1 && !c.startsWith('hider-') && !/^[a-zA-Z0-9]{10,}$/.test(c) && !/^hover:/i.test(c));
if (classes.length) { tag += `.${safeCSSEscape(classes[0])}` + (classes[1] ? `.${safeCSSEscape(classes[1])}` : ''); }
else {
let idx = 1, sib = curr.previousElementSibling;
while (sib) { if (sib.tagName === curr.tagName) idx++; sib = sib.previousElementSibling; }
tag += `:nth-of-type(${idx})`;
}
path.unshift(tag); curr = curr.parentElement;
}
return path.join(' > ');
}
// ---------- Drag Listeners ----------
function attachDragListeners(barEl, handleEl) {
if (!handleEl) return;
let startX, startY, initialX, initialY;
const onMove = e => {
if (!isDraggingStepper) return;
if (e.cancelable) e.preventDefault();
const p = e.touches ? e.touches[0] : e, maxX = win.innerWidth - barEl.offsetWidth, maxY = win.innerHeight - barEl.offsetHeight;
stepperPos = { x: Math.max(0, Math.min(maxX, initialX + (p.clientX - startX))), y: Math.max(0, Math.min(maxY, initialY + (p.clientY - startY))) };
barEl.style.setProperty('left', `${stepperPos.x}px`, 'important'); barEl.style.setProperty('top', `${stepperPos.y}px`, 'important');
barEl.style.setProperty('right', 'auto', 'important'); barEl.style.setProperty('bottom', 'auto', 'important'); barEl.style.setProperty('transform', 'none', 'important');
};
const onEnd = () => {
if (isDraggingStepper) { isDraggingStepper = false; }
win.removeEventListener('mousemove', onMove); win.removeEventListener('mouseup', onEnd);
win.removeEventListener('touchmove', onMove); win.removeEventListener('touchend', onEnd); win.removeEventListener('touchcancel', onEnd);
};
const onStart = e => {
if (e.target.tagName === 'BUTTON') return;
if (e.cancelable) e.preventDefault();
isDraggingStepper = true;
const p = e.touches ? e.touches[0] : e, r = barEl.getBoundingClientRect();
startX = p.clientX; startY = p.clientY; initialX = r.left; initialY = r.top;
barEl.style.setProperty('right', 'auto', 'important');
barEl.style.setProperty('bottom', 'auto', 'important');
barEl.style.setProperty('transform', 'none', 'important');
barEl.style.setProperty('margin', '0', 'important');
barEl.style.setProperty('left', `${initialX}px`, 'important');
barEl.style.setProperty('top', `${initialY}px`, 'important');
win.addEventListener('mousemove', onMove, { passive: false }); win.addEventListener('mouseup', onEnd);
win.addEventListener('touchmove', onMove, { passive: false }); win.addEventListener('touchend', onEnd); win.addEventListener('touchcancel', onEnd);
};
handleEl.addEventListener('mousedown', onStart, { passive: false });
handleEl.addEventListener('touchstart', onStart, { passive: false });
}
function clearSelectionState() {
previewElement?.classList.remove('hider-preview-highlight'); previewElement = null; stepperStack = [];
const stepper = shadowBy(STEPPER_BAR_ID);
if (stepper) {
stepper.remove();
}
globalScopeTemp = false;
}
function turnOffHideMode() {
if (isSelecting) {
isSelecting = false; shadowBy('btn-select')?.classList.remove('active');
clearSelectionState(); broadcastState();
showToast('🎯 Selection mode OFF');
}
globalScopeTemp = false;
}
function closeAllMenus(e, force = false) {
if (!shadowRoot) return false;
if (!force) {
if (isSelecting || isDraggingDock || isDraggingStepper) return false;
const active = shadowRoot.activeElement || doc.activeElement;
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA')) return false;
const path = e?.composedPath?.() || [];
if (path.length > 0 && path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return false;
if (shadowBy('hider-panel')?.querySelector('.is-editing')) return false;
}
const panel = shadowBy('hider-panel'), menu = shadowBy('hider-dock-menu'), mainBtn = shadowBy('btn-toggle-dock'), dockEl = shadowBy('hider-main-dock');
let closed = false;
shadowRoot.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
if (linkPanelEl && linkPanelEl.classList.contains('is-visible')) {
linkPanelEl.classList.remove('is-visible');
setTimeout(() => linkPanelEl.style.display = 'none', 300);
closed = true;
}
if (panel && panel.classList.contains('is-visible')) {
panel.classList.remove('is-visible');
setTimeout(()=> { if(!panel.classList.contains('is-visible')) panel.style.display='none'; }, 300);
shadowBy('btn-manage')?.classList.remove('active');
closed = true;
}
if (menu && menu.classList.contains('is-open')) {
menu.classList.remove('is-open'); mainBtn?.classList.remove('expanded'); dockEl?.classList.remove('expanded'); dockEl?.classList.add('manual-hidden');
if (timers.collapseTimer) clearTimeout(timers.collapseTimer);
timers.collapseTimer = setTimeout(() => { dockEl?.classList.add('is-collapsed'); timers.collapseTimer = null; }, 1000);
closed = true;
}
return closed;
}
// ---------- Stepper UI ----------
function renderTouchStepperUI() {
if (!shadowRoot) return;
let stepper = shadowBy(STEPPER_BAR_ID);
if (!previewElement) { stepper?.remove(); return; }
if (!stepper) {
stepper = doc.createElement('div'); stepper.id = STEPPER_BAR_ID;
shadowRoot.appendChild(stepper);
}
stepper.className = 'h-glass h-stepper-pill';
if (stepperPos.x !== null && stepperPos.y !== null) {
stepper.style.setProperty('left', `${stepperPos.x}px`, 'important');
stepper.style.setProperty('top', `${stepperPos.y}px`, 'important');
stepper.style.setProperty('bottom', 'auto', 'important');
stepper.style.setProperty('right', 'auto', 'important');
stepper.style.setProperty('transform', 'none', 'important');
stepper.style.setProperty('margin', '0', 'important');
} else {
stepper.style.setProperty('left', '50%', 'important');
stepper.style.setProperty('top', 'auto', 'important');
stepper.style.setProperty('bottom', '24px', 'important');
stepper.style.setProperty('right', 'auto', 'important');
stepper.style.setProperty('transform', 'translateX(-50%)', 'important');
stepper.style.setProperty('margin', '0', 'important');
}
const tag = previewElement.tagName.toLowerCase(), c = typeof previewElement.className === 'string' ? previewElement.className.trim().split(/\s+/)[0] : '';
const classStr = c && !c.startsWith('hider-') ? `.${c}` : '', idStr = previewElement.id && !previewElement.id.startsWith('hider-') ? `#${previewElement.id}` : '';
const fullText = `${tag}${idStr}${classStr}`;
const isLongText = fullText.length > 14;
stepper.innerHTML = `
<div id="hider-drag-handle" class="h-drag-dots" title="Drag Selector Bar">⠿</div>
<div class="h-stepper-row">
<button id="hider-step-up" class="h-btn-icon" title="Select Parent Element">▲</button>
<button id="hider-step-down" class="h-btn-icon" title="Select Child Element">▼</button>
</div>
<div class="h-tag-badge-box" title="${fullText}">
<span class="h-tag-badge-text ${isLongText ? 'is-animating' : ''}">${fullText}</span>
</div>
<div class="h-stepper-row" style="display:flex;flex-direction:column;gap:4px;width:100%;">
<button id="hider-step-confirm" class="h-btn-pill btn-blue" style="width:100%;">🙈 Hide</button>
<button id="hider-step-undo" class="h-btn-pill btn-gray" style="width:100%;">↩️ Undo</button>
<button id="hider-step-cancel" class="h-btn-pill btn-red" style="width:100%;">✖ Cancel</button>
</div>
`;
attachDragListeners(stepper, stepper.querySelector('#hider-drag-handle'));
stepper.querySelector('#hider-step-up').onclick = e => {
e.stopPropagation(); const p = previewElement.parentElement;
if (p && p !== doc.body && p !== doc.documentElement && p.id !== UI_HOST_ID) {
previewElement.classList.remove('hider-preview-highlight'); stepperStack.push(previewElement);
previewElement = p; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
} else showToast('⛰️ Top parent reached');
};
stepper.querySelector('#hider-step-down').onclick = e => {
e.stopPropagation();
if (stepperStack.length || previewElement.firstElementChild) {
previewElement.classList.remove('hider-preview-highlight');
previewElement = stepperStack.length ? stepperStack.pop() : previewElement.firstElementChild;
previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
}
};
stepper.querySelector('#hider-step-confirm').onclick = e => { e.stopPropagation(); confirmHideSelectedElement(); };
stepper.querySelector('#hider-step-undo').onclick = e => { e.stopPropagation(); undoLastHide(); };
stepper.querySelector('#hider-step-cancel').onclick = e => {
e.stopPropagation();
clearSelectionState();
showToast('❌ Selection cancelled');
};
}
function confirmHideSelectedElement() {
if (!previewElement) return;
const el = previewElement;
el.classList.remove('hider-preview-highlight');
const sel = getExactSelector(el);
if (sel) {
if (/^(html|body)$/i.test(sel)) {
if (!confirm('⚠️ Warning: You are about to hide the entire page (html/body). Are you sure?')) {
clearSelectionState();
showToast('❌ Cancelled hiding entire page');
return;
}
}
lastHiddenSelector = sel;
if (currentScope === 'global') {
const rule = { id: 'rule_' + Date.now(), selector: sel, target: '*' };
CACHE.customRules.push(rule);
sv('hider_custom_rules_v4', CACHE.customRules);
showToast('🌐 Global hide rule added!');
} else if (currentScope === 'site') {
const key = 'hider_site_' + location.hostname;
const s = gv(key, []); if (!s.includes(sel)) { s.push(sel); sv(key, s); }
showToast('🌐 Site-wide hide rule added!');
} else { // link
const key = 'hider_link_' + cleanUrl();
const s = gv(key, []); if (!s.includes(sel)) { s.push(sel); sv(key, s); }
showToast('📄 Page-only hide rule added!');
}
}
requestUpdateStyles(); clearSelectionState();
}
// ---------- Undo Last Hide ----------
function undoLastHide() {
if (!lastHiddenSelector) {
showToast('⚠️ No hidden element to undo');
return;
}
let found = false;
for (let i = 0; i < CACHE.customRules.length; i++) {
if (CACHE.customRules[i].selector === lastHiddenSelector) {
CACHE.customRules.splice(i, 1);
sv('hider_custom_rules_v4', CACHE.customRules);
found = true;
break;
}
}
if (!found) {
const key = 'hider_site_' + location.hostname;
let rules = gv(key, []);
let idx = rules.indexOf(lastHiddenSelector);
if (idx !== -1) {
rules.splice(idx, 1);
sv(key, rules);
found = true;
} else {
const linkKey = 'hider_link_' + cleanUrl();
rules = gv(linkKey, []);
idx = rules.indexOf(lastHiddenSelector);
if (idx !== -1) {
rules.splice(idx, 1);
sv(linkKey, rules);
found = true;
}
}
}
if (found) {
requestUpdateStyles();
showToast('↩️ Undone last hide');
lastHiddenSelector = null;
} else {
showToast('⚠️ Selector not found in rules');
}
}
// ---------- Toast ----------
function showToast(msg) {
let targetRoot = shadowRoot;
if (!targetRoot) targetRoot = doc.body;
if (!targetRoot) return;
const existing = targetRoot.querySelector('#hider-toast');
if (existing) existing.remove();
const toast = doc.createElement('div');
toast.id = 'hider-toast';
toast.className = 'h-glass h-toast';
toast.textContent = msg;
Object.assign(toast.style, {
position: 'fixed',
bottom: '70px',
left: '50%',
transform: 'translateX(-50%) translateY(16px)',
opacity: '0',
padding: '6px 16px',
fontSize: '11px',
fontWeight: '700',
zIndex: '2147483647',
pointerEvents: 'none',
transition: 'all .3s ease',
textAlign: 'center',
borderRadius: '20px',
border: '1px solid rgba(56,189,248,.5)',
color: '#e0f2fe',
background: 'rgba(13,18,30,0.95)',
backdropFilter: 'blur(12px)',
WebkitBackdropFilter: 'blur(12px)',
boxShadow: '0 12px 30px rgba(0,0,0,0.6)',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif',
lineHeight: '1.3',
maxWidth: '90vw',
whiteSpace: 'nowrap'
});
targetRoot.appendChild(toast);
requestAnimationFrame(() => {
toast.style.opacity = '1';
toast.style.transform = 'translateX(-50%) translateY(0)';
});
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(-50%) translateY(12px)';
setTimeout(() => {
if (toast.parentNode) toast.remove();
}, 400);
}, 2500);
}
// ---------- Clipboard ----------
function copyToClipboard(text) {
if (!text) return;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
showToast('📋 Copied!');
}).catch(() => {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.left = '-9999px';
textarea.style.top = '-9999px';
textarea.style.width = '1px';
textarea.style.height = '1px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showToast('📋 Copied!');
} catch (err) {
showToast('❌ Copy failed');
}
document.body.removeChild(textarea);
}
// ---------- Custom Dropdown ----------
function setupCustomDropdown(container, initialValue, onChangeCallback) {
if (!container) return;
const trigger = container.querySelector('.h-custom-trigger'), textSpan = container.querySelector('.h-custom-value-text'), options = container.querySelectorAll('.h-custom-opt');
let currentVal = initialValue || 'ask'; textSpan.textContent = FREEZE_LABELS[currentVal] || FREEZE_LABELS['ask'];
options.forEach(opt => {
opt.classList.toggle('is-selected', opt.getAttribute('data-val') === currentVal);
opt.onclick = e => {
e.stopPropagation(); currentVal = opt.getAttribute('data-val');
textSpan.textContent = FREEZE_LABELS[currentVal] || opt.textContent.trim();
options.forEach(o => o.classList.toggle('is-selected', o === opt)); container.classList.remove('is-open'); onChangeCallback?.(currentVal);
};
});
trigger.onclick = e => {
e.stopPropagation(); const isOpen = container.classList.contains('is-open');
shadowRoot.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
if (!isOpen) container.classList.add('is-open');
};
}
// ---------- Domain Management ----------
function executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl) {
if (parentDomain && !CACHE.blockedDomainsSet.has(parentDomain)) {
CACHE.blockedDomainsList.push(parentDomain); CACHE.blockedDomainsSet.add(parentDomain);
sv('hider_blocked_domains', CACHE.blockedDomainsList);
}
logBlockedAttempt(parentDomain, triggerType + ' (User Domain Block)'); promptEl?.remove();
showToast(`🚫 Blocked ${parentDomain}! Managed in Control Panel.`); onDeny?.(); simulateAdWindowSuccess();
}
function executeAddAllowedDomain(parentDomain) {
if (parentDomain && !CACHE.allowedDomainsSet.has(parentDomain)) {
CACHE.allowedDomainsList.push(parentDomain); CACHE.allowedDomainsSet.add(parentDomain);
sv('hider_allowed_domains', CACHE.allowedDomainsList);
if (shadowBy('hider-panel')?.classList.contains('is-visible')) renderList();
}
}
function showFreezePrompt(url, triggerType, onConfirm, onDeny) {
if (!shadowRoot) return;
shadowBy('hider-freeze-prompt')?.remove();
const promptEl = Object.assign(doc.createElement('div'), { id: 'hider-freeze-prompt', className: 'h-glass h-prompt' });
const displayUrl = url ? (url.length > 38 ? url.substring(0, 35) + '...' : url) : 'another page';
const parentDomain = getParentDomain(url) || 'unknown domain';
promptEl.innerHTML = `
<div style="font-weight:800;color:#38bdf8;font-size:11px!important;text-transform:uppercase!important;letter-spacing:0.5px">❄️ Navigation Intercepted</div>
<div style="font-size:11px!important;color:#f8fafc!important;font-weight:600">Proceed to page?</div>
<div class="h-url-box">${displayUrl}</div>
<div class="h-custom-select" id="hider-modal-custom-dropdown">
<div class="h-custom-trigger"><span class="h-custom-value-text">${FREEZE_LABELS[CACHE.freezeMemory] || FREEZE_LABELS['ask']}</span><span class="h-custom-arrow">▼</span></div>
<div class="h-custom-options"><div class="h-custom-opt" data-val="ask">❓ Ask Every Time</div><div class="h-custom-opt" data-val="block_all">⛔ Auto-Block All Navigations</div><div class="h-custom-opt" data-val="allow_same">🔗 Allow Same Domain Only</div><div class="h-custom-opt" data-val="allow_all">🟢 Allow All Navigations</div></div>
</div>
<div style="display:flex;gap:6px;width:100%;margin-top:4px"><button id="hider-freeze-yes" class="h-btn-pill btn-green" style="flex:1">Allow Once</button><button id="hider-freeze-no" class="h-btn-pill btn-red" style="flex:1">Deny</button></div>
<button id="hider-freeze-allow-btn" class="h-btn-pill" style="width:100%;margin-top:4px;background:rgba(16,185,129,0.15)!important;border:1px solid rgba(16,185,129,0.4)!important;color:#34d399!important;">🟢 Always Allow (${parentDomain})</button>
<button id="hider-freeze-block-btn" class="h-btn-pill" style="width:100%;margin-top:4px;background:rgba(239,68,68,0.15)!important;border:1px solid rgba(239,68,68,0.4)!important;color:#fca5a5!important;">🚫 Always Block (${parentDomain})</button>
`;
shadowRoot.appendChild(promptEl); let selectedMem = CACHE.freezeMemory || 'ask';
setupCustomDropdown(promptEl.querySelector('#hider-modal-custom-dropdown'), selectedMem, v => { selectedMem = v; });
setTimeout(() => { promptEl.style.opacity = '1'; promptEl.style.transform = 'translateX(-50%) translateY(0)'; }, 10);
const saveMem = () => { if (selectedMem !== CACHE.freezeMemory) { CACHE.freezeMemory = selectedMem; sv('hider_freeze_memory', selectedMem); } };
promptEl.querySelector('#hider-freeze-yes').onclick = () => {
saveMem(); if (selectedMem === 'allow_all') disableFreezeMode(); else if (selectedMem === 'allow_same' && parentDomain !== 'unknown domain') executeAddAllowedDomain(parentDomain);
promptEl.remove(); onConfirm?.();
};
promptEl.querySelector('#hider-freeze-no').onclick = () => {
saveMem(); if (selectedMem === 'allow_all') disableFreezeMode();
logBlockedAttempt(url, triggerType + ' (User)'); promptEl.remove(); onDeny?.(); simulateAdWindowSuccess();
};
promptEl.querySelector('#hider-freeze-allow-btn').onclick = () => {
saveMem(); if (parentDomain !== 'unknown domain') executeAddAllowedDomain(parentDomain);
if (selectedMem === 'allow_all') disableFreezeMode();
promptEl.remove(); showToast(`🟢 Allowed ${parentDomain}!`); onConfirm?.();
};
promptEl.querySelector('#hider-freeze-block-btn').onclick = () => {
saveMem(); const skipConfirm = gv('hider_skip_block_confirm', false);
if (skipConfirm) { executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl); return; }
promptEl.innerHTML = `
<div style="font-weight:800;color:#ef4444;font-size:11px!important;text-transform:uppercase!important;">🚫 Confirm Domain Block</div>
<div style="font-size:11px!important;color:#f8fafc!important;text-align:center;margin:2px 0">Block all future requests to:<br><strong style="color:#38bdf8;font-size:12px!important">${parentDomain}</strong>?</div>
<label style="font-size:10px!important;color:#cbd5e1!important;display:flex;align-items:center;gap:4px;cursor:pointer;margin:4px 0;user-select:none"><input type="checkbox" id="hider-dont-ask-block" style="cursor:pointer;accent-color:#38bdf8;width:12px!important;height:12px!important"> Don't ask confirmation again</label>
<div style="display:flex;gap:6px;width:100%;margin-top:4px"><button id="hider-confirm-block-yes" class="h-btn-pill btn-red" style="flex:1">Yes, Block</button><button id="hider-confirm-block-no" class="h-btn-pill" style="flex:1;background:rgba(255,255,255,0.1)!important;color:#f1f5f9!important;border:1px solid rgba(255,255,255,0.15)!important;">Cancel</button></div>
`;
promptEl.querySelector('#hider-confirm-block-yes').onclick = () => {
if (promptEl.querySelector('#hider-dont-ask-block')?.checked) sv('hider_skip_block_confirm', true);
executeAddBlockDomain(parentDomain, triggerType, onDeny, promptEl);
};
promptEl.querySelector('#hider-confirm-block-no').onclick = () => { showFreezePrompt(url, triggerType, onConfirm, onDeny); };
};
}
// ---------- Enhanced Reveal ----------
function revealHiddenElements() {
if (!doc.body) return;
const targets = new Set();
const inlineHidden = doc.querySelectorAll([
'[style*="display:none"]',
'[style*="display: none"]',
'[style*="visibility:hidden"]',
'[style*="visibility: hidden"]',
'[style*="opacity:0"]',
'[style*="opacity: 0"]',
'[style*="filter:blur"]',
'[style*="filter: blur"]',
'[style*="backdrop-filter:blur"]',
'[style*="backdrop-filter: blur"]'
].join(','));
inlineHidden.forEach(el => targets.add(el));
const overlaySelector = [
'div[style*="position:fixed"]',
'div[style*="position: fixed"]',
'div[style*="position:absolute"]',
'div[style*="position: absolute"]',
'div[style*="z-index: 999"]',
'div[style*="z-index:999"]',
'div[style*="z-index: 9999"]',
'div[style*="z-index:9999"]',
'div[style*="z-index: 99999"]',
'div[style*="z-index:99999"]'
].join(',');
const overlayCandidates = doc.querySelectorAll(overlaySelector);
overlayCandidates.forEach(el => {
const rect = el.getBoundingClientRect();
if (rect.width < 100 || rect.height < 100) return;
if (el.innerText.length > 200) return;
if (el.querySelector('article, main, p, h1, h2, h3, h4, h5, h6')) return;
targets.add(el);
});
doc.querySelectorAll([
'.modal', '.overlay', '.popup', '.lightbox', '.blocker',
'.paywall', '.gate', '.wall', '.restricted', '.locked',
'[data-overlay]', '[data-modal]', '[data-popup]', '[data-paywall]'
].join(',')).forEach(el => targets.add(el));
if (!isMediaSensitiveDomain()) {
doc.querySelectorAll('[style*="display:none"], [style*="display: none"], [style*="visibility:hidden"], [style*="visibility: hidden"], [style*="opacity:0"], [style*="opacity: 0"], [style*="filter:blur"], [style*="filter: blur"], [style*="backdrop-filter:blur"], [style*="backdrop-filter: blur"]').forEach(el => targets.add(el));
}
let count = 0;
let blurCount = 0, hiddenCount = 0, overlayCount = 0;
const processBatch = (arr, start) => {
const end = Math.min(start + 50, arr.length);
for (let i = start; i < end; i++) {
const el = arr[i];
const style = win.getComputedStyle(el);
let changed = false;
if (style.filter && style.filter.includes('blur')) {
el.style.setProperty('filter', 'none', 'important');
el.style.setProperty('backdrop-filter', 'none', 'important');
el.style.setProperty('-webkit-backdrop-filter', 'none', 'important');
blurCount++;
changed = true;
}
if (style.display === 'none') {
el.style.setProperty('display', 'block', 'important');
hiddenCount++;
changed = true;
}
if (style.visibility === 'hidden') {
el.style.setProperty('visibility', 'visible', 'important');
hiddenCount++;
changed = true;
}
if (style.opacity === '0') {
el.style.setProperty('opacity', '1', 'important');
hiddenCount++;
changed = true;
}
if (style.pointerEvents === 'none') {
el.style.setProperty('pointer-events', 'auto', 'important');
}
if (style.userSelect === 'none') {
el.style.setProperty('user-select', 'text', 'important');
el.style.setProperty('-webkit-user-select', 'text', 'important');
}
if (style.position === 'fixed' || style.position === 'absolute') {
const z = parseInt(style.zIndex, 10);
if (z > 900) {
el.style.setProperty('position', 'static', 'important');
el.style.setProperty('z-index', 'auto', 'important');
overlayCount++;
changed = true;
}
}
if (changed) count++;
}
if (end < arr.length) {
requestAnimationFrame(() => processBatch(arr, end));
} else {
const parts = [];
if (hiddenCount) parts.push(`${hiddenCount} hidden`);
if (blurCount) parts.push(`${blurCount} blurred`);
if (overlayCount) parts.push(`${overlayCount} overlays`);
const summary = parts.length ? `👁️ Revealed: ${parts.join(', ')}` : '👁️ No hidden/blurred elements found.';
showToast(summary);
if (CACHE.autoScroll && featuresEnabled) forceEnableScroll();
}
};
const targetArray = Array.from(targets);
if (targetArray.length === 0) {
showToast('👁️ No hidden/blurred elements found.');
return;
}
requestAnimationFrame(() => processBatch(targetArray, 0));
}
// ---------- Time Skipper (Manual 30s) ----------
function skip30Seconds() {
let mediaCount = 0;
doc.querySelectorAll('video, audio').forEach(el => {
try {
if (!isNaN(el.duration) && isFinite(el.duration)) {
el.currentTime = Math.min(el.duration, el.currentTime + 30);
} else {
el.currentTime += 30;
}
mediaCount++;
} catch {}
});
let timerCount = 0;
const timerElements = isMediaSensitiveDomain() ? [] : doc.querySelectorAll(
'[class*="timer"], [class*="countdown"], [id*="timer"], [id*="countdown"], ' +
'[class*="time"], [id*="time"], [class*="remaining"], [id*="remaining"]'
);
timerElements.forEach(el => {
const text = el.textContent.trim();
if (/^\d{1,2}:\d{2}$/.test(text) || /^\d+\s*(s|sec|seconds?)$/i.test(text) || /^\d{1,2}:\d{2}\s*$/.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
doc.querySelectorAll('*:not([class*="timer"]):not([id*="timer"]):not([class*="countdown"]):not([id*="countdown"])')
.forEach(el => {
const text = el.textContent.trim();
if (/^(?:[0-9]{1,2}:[0-5][0-9]|[1-9][0-9]?s?)$/i.test(text)) {
el.textContent = '0';
el.dispatchEvent(new Event('input', { bubbles: true }));
timerCount++;
}
});
let clickCount = 0;
const skipBtns = doc.querySelectorAll('button, a[role="button"], [role="button"]');
skipBtns.forEach(btn => {
const text = btn.textContent.trim().toLowerCase();
if (text && (text.includes('skip') || text.includes('close') || text.includes('dismiss'))) {
const rect = btn.getBoundingClientRect();
if (rect.width > 0 && rect.height > 0) {
try { btn.click(); clickCount++; } catch {}
}
}
});
broadcastToFrames(window, { type: 'HIDER_SKIP_30' });
const msgParts = [];
if (mediaCount) msgParts.push(`${mediaCount} media`);
if (timerCount) msgParts.push(`${timerCount} timers`);
if (clickCount) msgParts.push(`${clickCount} buttons`);
const summary = msgParts.length ? `⏩ Skipped: ${msgParts.join(', ')}` : '⏩ Skipped +30s';
showToast(summary);
}
// ================================================================
// SMART AUTO TIME SKIPPER + VIDEO AD SKIPPER
// ================================================================
function isInsideMedia(el) {
let node = el;
while (node && node !== doc) {
if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') return true;
node = node.parentElement;
}
return false;
}
function timerLooksActionable(el) {
if (!el || el.nodeType !== Node.ELEMENT_NODE || isInsideMedia(el)) return false;
const text = (el.textContent || '').trim();
if (!text || text.length > 40) return false;
const meta = getAgeText(el);
if (!TIMER_HINTS.test(meta)) return false;
return /^(?:\d{1,2}:\d{2}|\d{1,3}\s*(?:s|sec|secs|seconds?|m|min|mins|minutes?))$/i.test(text);
}
function registerTimeSkipNode(node) {
if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
if (timerLooksActionable(node)) autoSkipCandidates.add(node);
try {
for (const el of node.querySelectorAll?.('[class*="timer" i], [class*="countdown" i], [id*="timer" i], [id*="countdown" i], [data-timer], [data-countdown], [aria-label*="countdown" i], [aria-label*="timer" i]') || []) {
if (timerLooksActionable(el)) autoSkipCandidates.add(el);
}
for (const media of node.querySelectorAll?.('video, audio') || []) attachSmartMedia(media);
if (node.matches?.('video, audio')) attachSmartMedia(node);
for (const el of node.querySelectorAll?.('button, [role="button"], a[role="button"]') || []) {
const text = getAgeText(el);
if (AUTO_SKIP_HINTS.test(text)) autoSkipCandidates.add(el);
}
} catch {}
}
function isAdLikeMedia(media) {
const meta = getAgeText(media) + ' ' + getAgeText(media.parentElement);
return AUTO_SKIP_HINTS.test(meta) || /(?:ad-player|ad-container|video-ad|preroll|midroll|postroll|commercial)/i.test(meta);
}
function attachSmartMedia(media) {
if (!media || autoSkipMedia.has(media)) return;
autoSkipMedia.add(media);
const onTime = () => {
if (!CACHE.autoTimeSkipper || !featuresEnabled || isMediaSensitiveDomain()) return;
if (!isAdLikeMedia(media) || !Number.isFinite(media.duration) || media.duration <= 0) return;
const remain = media.duration - media.currentTime;
if (remain <= 1.2) return;
if (remain <= 5 || media.currentTime < 0.5) {
try { media.currentTime = Math.max(0, media.duration - 0.05); } catch {}
}
};
media.addEventListener('timeupdate', onTime, { passive: true });
media.addEventListener('loadedmetadata', onTime, { passive: true });
media.addEventListener('durationchange', onTime, { passive: true });
}
function processSmartSkipCandidates() {
let found = false;
for (const el of [...autoSkipCandidates]) {
if (!el?.isConnected) { autoSkipCandidates.delete(el); continue; }
if (el.matches?.('button, [role="button"], a[role="button"]')) {
const text = getAgeText(el);
if (AUTO_SKIP_HINTS.test(text)) {
const r = el.getBoundingClientRect();
if (r.width > 0 && r.height > 0) {
try { el.click(); found = true; } catch {}
}
}
} else if (timerLooksActionable(el)) {
const raw = (el.textContent || '').trim();
const m = raw.match(/^(?:(\d{1,2}):(\d{2})|(\d{1,3})\s*(?:s|sec|secs|seconds?)|(\d{1,3})\s*(?:m|min|mins|minutes?))$/i);
const seconds = m ? (m[1] != null ? Number(m[1]) * 60 + Number(m[2]) : m[3] != null ? Number(m[3]) : Number(m[4]) * 60) : Infinity;
const nearbyButton = el.closest?.('button, [role="button"], a[role="button"]') || el.parentElement?.querySelector?.('button, [role="button"], a[role="button"]');
if (nearbyButton && AUTO_SKIP_HINTS.test(getAgeText(nearbyButton))) {
try { nearbyButton.click(); found = true; } catch {}
} else if (seconds <= 1) {
try { el.textContent = '0'; el.dispatchEvent(new Event('input', {bubbles:true, composed:true})); found = true; } catch {}
}
} else {
autoSkipCandidates.delete(el);
}
}
for (const media of [...autoSkipMedia]) {
if (!media?.isConnected) autoSkipMedia.delete(media);
}
return found;
}
function startAutoSkipMonitoring() {
stopAutoSkipMonitoring();
if (!CACHE.autoTimeSkipper || !featuresEnabled || isMediaSensitiveDomain()) return;
autoSkipCandidates = new Set();
autoSkipMedia = new Set();
registerTimeSkipNode(doc.body || doc.documentElement);
for (const media of doc.querySelectorAll?.('video, audio') || []) attachSmartMedia(media);
autoSkipObserver = new MutationObserver(mutations => {
for (const m of mutations) {
if (m.type !== 'childList') continue;
for (const n of m.addedNodes || []) registerTimeSkipNode(n);
}
});
try { autoSkipObserver.observe(doc.documentElement, {childList:true, subtree:true}); } catch {}
autoSkipInterval = setInterval(() => {
if (!CACHE.autoTimeSkipper || !featuresEnabled || isMediaSensitiveDomain()) return;
const found = processSmartSkipCandidates();
autoSkipEmptyCount = found ? 0 : autoSkipEmptyCount + 1;
if (autoSkipEmptyCount >= AUTO_SKIP_MAX_EMPTY && autoSkipCandidates.size === 0) {
autoSkipEmptyCount = 0;
// Keep observer-driven monitoring alive without scanning the DOM.
}
}, AUTO_SKIP_INTERVAL_MS);
}
function stopAutoSkipMonitoring() {
if (autoSkipInterval) { clearInterval(autoSkipInterval); autoSkipInterval = null; }
if (autoSkipObserver) { autoSkipObserver.disconnect(); autoSkipObserver = null; }
autoSkipCandidates.clear();
autoSkipMedia.clear();
autoSkipEmptyCount = 0;
if (observers.adSkipObserver) { observers.adSkipObserver.disconnect(); observers.adSkipObserver = null; }
}
// ========== AGGRESSIVE PAUSE ==========
function pauseAllVideos() {
// Opening the script's media lab must not pause or reset Facebook/Instagram playback.
if (isMediaSensitiveDomain()) return;
function pauseMedia(el) {
try {
if (el.tagName === 'VIDEO' || el.tagName === 'AUDIO') {
el.pause();
el.currentTime = 0;
el.loop = false;
}
} catch(e) {}
}
function traverse(node) {
if (!node) return;
if (node.nodeType === Node.ELEMENT_NODE) {
pauseMedia(node);
if (node.shadowRoot) {
traverse(node.shadowRoot);
}
if (node.tagName === 'IFRAME' || node.tagName === 'FRAME') {
try {
if (node.contentDocument) {
traverse(node.contentDocument);
}
} catch(e) {}
}
if (node.childNodes) {
node.childNodes.forEach(child => traverse(child));
}
} else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.DOCUMENT_NODE) {
if (node.childNodes) {
node.childNodes.forEach(child => traverse(child));
}
}
}
traverse(document);
document.querySelectorAll('video, audio').forEach(pauseMedia);
}
// ========== LINK EXTRACTOR ==========
function isDirectMedia(url) {
if (!url) return false;
const lower = String(url).toLowerCase();
if (lower.startsWith('blob:') || lower.startsWith('data:image/')) return true;
const mediaExts = [
'.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico', '.tiff', '.tif', '.avif', '.heic', '.heif',
'.mp4', '.webm', '.ogg', '.ogv', '.mov', '.avi', '.mkv', '.ts', '.m4v', '.wmv', '.flv', '.m3u8',
'.mp3', '.wav', '.flac', '.m4a', '.aac', '.wma'
];
try {
const u = new URL(url, location.href);
const pathAndQuery = (u.pathname + u.search).toLowerCase();
return mediaExts.some(ext => pathAndQuery.includes(ext));
} catch {
return mediaExts.some(ext => lower.includes(ext));
}
}
function getMediaType(url) {
if (!url) return null;
const lower = String(url).toLowerCase();
if (lower.startsWith('data:image/')) return 'image';
try {
const u = new URL(url, location.href);
const target = (u.pathname + u.search).toLowerCase();
const imgExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico', '.tiff', '.tif', '.avif', '.heic', '.heif'];
if (imgExts.some(ext => target.includes(ext))) return 'image';
const vidExts = ['.mp4', '.webm', '.ogg', '.ogv', '.mov', '.avi', '.mkv', '.ts', '.m4v', '.wmv', '.flv', '.m3u8'];
if (vidExts.some(ext => target.includes(ext))) return 'video';
const audExts = ['.mp3', '.wav', '.flac', '.m4a', '.aac', '.ogg', '.wma'];
if (audExts.some(ext => target.includes(ext))) return 'audio';
} catch {}
// A blob URL has no extension. Callers that know the element type
// provide the correct media subtype explicitly.
return lower.startsWith('blob:') ? null : null;
}
function isMediaUrl(url) {
return getMediaType(url) !== null;
}
function getAllLinks() {
const linkMap = new Map();
function addLink(url, text, type = 'link', forcedMediaType = null) {
if (!url) return;
try {
const abs = new URL(url, location.href).href;
if (abs.startsWith('javascript:') || abs.startsWith('mailto:') || abs.startsWith('tel:') || abs === '#') return;
let finalType = type;
const mediaType = forcedMediaType || getMediaType(abs);
if (mediaType) finalType = 'media';
if (!linkMap.has(abs)) {
linkMap.set(abs, { url: abs, text: text || abs, type: finalType, mediaSubtype: mediaType });
} else {
const existing = linkMap.get(abs);
if (finalType === 'media' && existing.type === 'link') {
existing.type = 'media';
existing.mediaSubtype = mediaType;
} else if (finalType === 'media' && !existing.mediaSubtype && mediaType) {
existing.mediaSubtype = mediaType;
}
if (text && text !== abs && (existing.text === existing.url || !existing.text)) {
existing.text = text;
}
}
} catch (e) { /* ignore */ }
}
function traverse(node) {
if (!node) return;
if (node.nodeType === Node.ELEMENT_NODE) {
const el = node;
if (el.tagName === 'VIDEO' || el.tagName === 'AUDIO') {
const src = el.getAttribute('src') || el.currentSrc;
const elementMediaType = el.tagName === 'VIDEO' ? 'video' : 'audio';
if (src) {
const text = el.getAttribute('title') || el.getAttribute('aria-label') || el.getAttribute('alt') || el.textContent.trim() || src;
addLink(src, text, 'media', elementMediaType);
}
el.querySelectorAll('source').forEach(source => {
const s = source.getAttribute('src') || source.getAttribute('srcset');
if (s) {
const label = source.getAttribute('label') || source.getAttribute('title') || s;
addLink(s, label, 'media', elementMediaType);
}
});
const poster = el.getAttribute('poster');
if (poster) {
addLink(poster, 'Poster image', 'media', 'image');
}
}
if (el.tagName === 'IMG') {
const src = el.getAttribute('src') || el.currentSrc;
if (src) {
const text = el.alt || el.title || el.getAttribute('aria-label') || src;
addLink(src, text, 'media', 'image');
}
const srcset = el.getAttribute('srcset');
if (srcset) {
const urls = srcset.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => {
if (u) addLink(u, el.alt || el.title || u, 'media');
});
}
const lazyAttrs = ['data-src', 'data-original', 'data-lazy-src', 'data-srcset'];
lazyAttrs.forEach(attr => {
const val = el.getAttribute(attr);
if (val) {
if (attr === 'data-srcset') {
const urls = val.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => addLink(u, el.alt || el.title || u, 'media'));
} else {
addLink(val, el.alt || el.title || val, 'media');
}
}
});
}
if (el.tagName === 'SOURCE' && el.closest('picture')) {
const s = el.getAttribute('src');
if (s) {
const label = el.getAttribute('label') || el.getAttribute('title') || s;
addLink(s, label, 'media', 'image');
}
const srcset = el.getAttribute('srcset');
if (srcset) {
const urls = srcset.split(',').map(s => s.trim().split(/\s+/)[0]).filter(Boolean);
urls.forEach(u => addLink(u, el.getAttribute('label') || u, 'media'));
}
}
const dataAttrs = ['src', 'url', 'link', 'video-src', 'audio-src', 'media-url', 'file', 'image', 'img', 'photo'];
for (const attr of dataAttrs) {
const val = el.getAttribute('data-' + attr);
if (val && isMediaUrl(val)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || val;
addLink(val, text, 'media');
}
}
if (el.matches('a[href], area[href]')) {
const href = el.getAttribute('href');
if (href && isMediaUrl(href)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || href;
addLink(href, text, 'media');
} else if (href) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || href;
addLink(href, text, 'link');
}
}
const onclick = el.getAttribute('onclick');
if (onclick) {
const matches = onclick.match(/(?:location\.href|window\.open)\s*\(\s*['"]([^'"]+)['"]/gi);
if (matches) {
matches.forEach(m => {
const urlMatch = m.match(/['"]([^'"]+)['"]/);
if (urlMatch) {
const url = urlMatch[1];
if (isMediaUrl(url)) {
const text = el.textContent.trim() || el.title || el.getAttribute('aria-label') || url;
addLink(url, text, 'media');
}
}
});
}
}
if (el.matches('form[action]')) {
const action = el.getAttribute('action');
if (action && isMediaUrl(action)) {
const text = el.getAttribute('name') || el.id || 'Form action';
addLink(action, text, 'media');
}
}
if (el.tagName === 'IFRAME') {
const iframeSrc = el.getAttribute('src');
if (iframeSrc) {
const text = el.getAttribute('title') || el.getAttribute('aria-label') || 'iframe';
addLink(iframeSrc, text, 'link');
}
}
if (el.shadowRoot) {
traverseShadow(el.shadowRoot);
}
}
if (node.childNodes) {
for (const child of node.childNodes) {
traverse(child);
}
}
}
function traverseShadow(root) {
if (root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
for (const child of root.children) {
traverse(child);
}
}
}
traverse(document);
return Array.from(linkMap.values());
}
// ---------- Preview Modal ----------
function closePreviewModal(modalEl) {
const modal = modalEl || shadowBy('hider-link-preview-modal');
if (modal && modal.parentNode) {
modal.remove();
}
if (previewOutsideListener) {
document.removeEventListener('click', previewOutsideListener);
previewOutsideListener = null;
}
}
function showLinkPreview(url) {
const existing = shadowBy('hider-link-preview-modal');
if (existing) {
closePreviewModal(existing);
}
const mediaType = getMediaType(url);
const isDirect = isDirectMedia(url);
const modal = doc.createElement('div');
modal.id = 'hider-link-preview-modal';
modal.className = 'h-glass';
modal.style.cssText = `
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 2147483647 !important;
padding: 16px !important;
border-radius: 16px !important;
background: rgba(13, 18, 30, 0.96) !important;
backdrop-filter: blur(16px) !important;
border: 1px solid rgba(255,255,255,0.15) !important;
box-shadow: 0 24px 48px rgba(0,0,0,0.8) !important;
min-width: 280px !important;
max-width: 90vw !important;
max-height: 90vh !important;
pointer-events: auto !important;
display: flex !important;
flex-direction: column !important;
gap: 10px !important;
color: #f8fafc !important;
overflow-y: auto !important;
`;
let typeIcon = '🔗';
if (mediaType === 'image') typeIcon = '🖼️';
else if (mediaType === 'video') typeIcon = '🎬';
else if (mediaType === 'audio') typeIcon = '🔊';
const title = doc.createElement('div');
title.style.cssText = 'font-weight:800; font-size:12px; color:#38bdf8; text-transform:uppercase; letter-spacing:0.5px;';
title.textContent = `${typeIcon} ${mediaType ? mediaType.toUpperCase() : 'Link'} Preview`;
modal.appendChild(title);
if (mediaType) {
const mediaWrapper = doc.createElement('div');
mediaWrapper.style.cssText = `
display:flex; justify-content:center; align-items:center;
background: rgba(0,0,0,0.4); border-radius:8px; padding:4px;
min-height: 80px; max-height: 55vh; overflow:hidden;
`;
if (mediaType === 'image') {
const img = doc.createElement('img');
img.src = url;
img.style.cssText = 'max-width:100%; max-height:55vh; object-fit:contain; border-radius:4px;';
img.onerror = () => {
img.alt = '❌ Failed to load image';
img.style.cssText += 'height:60px; width:auto;';
};
mediaWrapper.appendChild(img);
} else if (mediaType === 'video') {
if (isDirect) {
const video = doc.createElement('video');
video.src = url;
video.controls = true;
video.preload = 'metadata';
video.style.cssText = 'max-width:100%; max-height:55vh; border-radius:4px; background:#000;';
video.onerror = () => {
video.innerHTML = '<div style="padding:20px;color:#f87171;font-size:12px;">❌ Failed to load video</div>';
};
mediaWrapper.appendChild(video);
} else {
mediaWrapper.textContent = '🎬 Video – click "Open" to watch in new tab.';
mediaWrapper.style.cssText += 'padding:20px;color:#94a3b8;font-size:12px;';
}
} else if (mediaType === 'audio') {
const audio = doc.createElement('audio');
audio.src = url;
audio.controls = true;
audio.preload = 'metadata';
audio.style.cssText = 'width:100%;';
audio.onerror = () => {
audio.innerHTML = '<div style="padding:20px;color:#f87171;font-size:12px;">❌ Failed to load audio</div>';
};
mediaWrapper.appendChild(audio);
} else {
mediaWrapper.textContent = 'Media preview not available';
mediaWrapper.style.cssText += 'padding:20px;color:#94a3b8;font-size:12px;';
}
modal.appendChild(mediaWrapper);
} else {
const linkPreview = doc.createElement('div');
linkPreview.textContent = '🔗 Link Preview';
linkPreview.style.cssText = 'font-size:12px;color:#94a3b8;padding:10px;';
modal.appendChild(linkPreview);
}
const urlDisplay = doc.createElement('div');
urlDisplay.style.cssText = 'font-size:10px; word-break:break-all; background:rgba(0,0,0,0.3); padding:6px 8px; border-radius:6px; border:1px solid rgba(255,255,255,0.1); font-family:monospace; max-height:80px; overflow-y:auto;';
urlDisplay.textContent = url;
modal.appendChild(urlDisplay);
const btnGroup = doc.createElement('div');
btnGroup.style.cssText = 'display:flex; gap:6px; justify-content:flex-end; flex-wrap:wrap;';
const copyBtn = doc.createElement('button');
copyBtn.className = 'hider-btn-small btn-blue';
copyBtn.textContent = '📋 Copy';
copyBtn.onclick = () => {
copyToClipboard(url);
showToast('📋 URL copied!');
};
btnGroup.appendChild(copyBtn);
if (isDirect && (mediaType === 'image' || mediaType === 'video' || mediaType === 'audio')) {
const downloadBtn = doc.createElement('button');
downloadBtn.className = 'hider-btn-small btn-green';
downloadBtn.textContent = '⬇️ Download';
downloadBtn.onclick = function(e) {
e.stopPropagation();
let filename = url.split('/').pop().split(/[?#]/)[0] || 'media_file';
if (!filename.includes('.')) {
if (mediaType === 'image') filename += '.jpg';
else if (mediaType === 'video') filename += '.mp4';
else if (mediaType === 'audio') filename += '.mp3';
else filename += '.bin';
}
try {
GM_download({
url: url,
name: filename,
onerror: function(err) {
console.error('GM_download failed, trying fetch fallback', err);
fetch(url).then(res => {
if (!res.ok) throw new Error('Network error');
return res.blob();
}).then(blob => {
const a = document.createElement('a');
const objectUrl = URL.createObjectURL(blob);
a.href = objectUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(objectUrl);
showToast('⬇️ Downloaded using fallback');
}).catch(() => {
showToast('❌ Download failed – opening in new tab');
window.open(url, '_blank');
});
}
});
showToast(`⬇️ Downloading: ${filename}`);
} catch (err) {
console.error('GM_download error:', err);
showToast('❌ Download error – opening in new tab');
window.open(url, '_blank');
}
};
btnGroup.appendChild(downloadBtn);
}
const openBtn = doc.createElement('button');
openBtn.className = 'hider-btn-small btn-purple';
openBtn.textContent = '↗ Open';
openBtn.onclick = () => {
win.open(url, '_blank');
};
btnGroup.appendChild(openBtn);
const closeBtn = doc.createElement('button');
closeBtn.className = 'hider-btn-small btn-gray';
closeBtn.textContent = '✖ Close';
closeBtn.onclick = () => closePreviewModal(modal);
btnGroup.appendChild(closeBtn);
modal.appendChild(btnGroup);
shadowRoot.appendChild(modal);
const closeModalHandler = (e) => {
if (!modal.parentNode) {
document.removeEventListener('click', closeModalHandler);
return;
}
const path = e.composedPath ? e.composedPath() : [];
if (path.some(el => el === modal || (el && el.contains && el.contains(modal)))) {
return;
}
if (path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) {
return;
}
closePreviewModal(modal);
};
if (previewOutsideListener) {
document.removeEventListener('click', previewOutsideListener);
}
previewOutsideListener = closeModalHandler;
setTimeout(() => {
document.addEventListener('click', previewOutsideListener);
}, 10);
}
// ----- Grid item for Media Mode -----
function buildGridItem(link) {
const div = doc.createElement('div');
div.className = 'hider-grid-item';
div.style.cssText = `
position: relative;
background: rgba(255,255,255,0.04);
border-radius: 8px;
overflow: hidden;
aspect-ratio: 1 / 1;
cursor: pointer;
border: 1px solid rgba(255,255,255,0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease;
`;
div.onmouseover = () => { div.style.transform = 'scale(1.03)'; div.style.boxShadow = '0 4px 12px rgba(0,0,0,0.6)'; };
div.onmouseout = () => { div.style.transform = 'scale(1)'; div.style.boxShadow = 'none'; };
const mediaType = link.mediaSubtype || getMediaType(link.url);
const isDirect = isDirectMedia(link.url);
let icon = '📎';
if (mediaType === 'image') icon = '🖼️';
else if (mediaType === 'video') icon = '🎬';
else if (mediaType === 'audio') icon = '🔊';
let thumbnail = '';
if (mediaType === 'image') {
thumbnail = `<img src="${link.url}" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';">`;
} else if (mediaType === 'video' && isDirect) {
thumbnail = `<video src="${link.url}" muted preload="metadata" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display='none';"></video>`;
} else {
thumbnail = `<div style="display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-size:48px;color:#94a3b8;background:rgba(0,0,0,0.3);">${icon}</div>`;
}
div.innerHTML = thumbnail;
const overlay = doc.createElement('div');
overlay.style.cssText = `
position: absolute;
bottom: 4px;
left: 4px;
background: rgba(0,0,0,0.7);
padding: 2px 6px;
border-radius: 4px;
font-size: 8px;
color: #e0f2fe;
backdrop-filter: blur(4px);
pointer-events: none;
font-weight: 600;
letter-spacing: 0.3px;
text-transform: uppercase;
`;
overlay.textContent = mediaType || 'media';
div.appendChild(overlay);
if (isDirect && (mediaType === 'image' || mediaType === 'video' || mediaType === 'audio')) {
const downloadBtn = doc.createElement('button');
downloadBtn.className = 'hider-btn-small';
downloadBtn.textContent = '⬇️';
downloadBtn.title = 'Download this media';
downloadBtn.style.cssText = `
position: absolute;
bottom: 4px;
right: 4px;
background: rgba(0,0,0,0.7) !important;
backdrop-filter: blur(4px);
border: none;
border-radius: 4px;
color: #fff;
padding: 2px 6px;
font-size: 10px;
cursor: pointer;
z-index: 2;
pointer-events: auto;
transition: background 0.2s;
`;
downloadBtn.onmouseover = () => { downloadBtn.style.background = 'rgba(56,189,248,0.8) !important'; };
downloadBtn.onmouseout = () => { downloadBtn.style.background = 'rgba(0,0,0,0.7) !important'; };
downloadBtn.addEventListener('click', function(e) {
e.stopPropagation();
const url = link.url;
if (!url) {
showToast('❌ No media URL');
return;
}
let filename = url.split('/').pop().split(/[?#]/)[0] || 'media_file';
if (!filename.includes('.')) {
if (mediaType === 'image') filename += '.jpg';
else if (mediaType === 'video') filename += '.mp4';
else if (mediaType === 'audio') filename += '.mp3';
else filename += '.bin';
}
try {
GM_download({
url: url,
name: filename,
onerror: function(err) {
console.error('GM_download failed, trying fetch fallback', err);
fetch(url).then(res => {
if (!res.ok) throw new Error('Network error');
return res.blob();
}).then(blob => {
const a = document.createElement('a');
const objectUrl = URL.createObjectURL(blob);
a.href = objectUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(objectUrl);
showToast('⬇️ Downloaded using fallback');
}).catch(() => {
showToast('❌ Download failed – opening in new tab');
window.open(url, '_blank');
});
}
});
showToast(`⬇️ Downloading: ${filename}`);
} catch (err) {
console.error('GM_download error:', err);
showToast('❌ Download error – opening in new tab');
window.open(url, '_blank');
}
});
div.appendChild(downloadBtn);
}
div.addEventListener('click', (e) => {
if (e.target.closest('button')) return;
showLinkPreview(link.url);
});
return div;
}
// ----- List item for normal mode -----
function buildListItem(link, filterText, hideInternal, mediaOnly) {
const currentHost = location.hostname;
if (hideInternal) {
try {
const urlObj = new URL(link.url);
if (urlObj.hostname === currentHost) return null;
} catch { /* ignore */ }
}
if (mediaOnly && link.type !== 'media') return null;
if (filterText) {
const lower = filterText.toLowerCase();
if (!link.text.toLowerCase().includes(lower) && !link.url.toLowerCase().includes(lower)) {
return null;
}
}
const div = doc.createElement('div');
div.className = 'list-item';
div.style.cssText = 'display:flex; justify-content:space-between; align-items:center; gap:4px;';
let icon = '🔗';
if (link.type === 'media') {
const subtype = link.mediaSubtype || getMediaType(link.url);
if (subtype === 'image') icon = '🖼️';
else if (subtype === 'video') icon = '🎬';
else if (subtype === 'audio') icon = '🔊';
else icon = '🎬';
}
let displayText = link.text;
if (link.type === 'media') {
displayText = icon + ' ' + displayText;
}
if (linkDisplayMode === 'url') {
displayText = link.url + (link.type === 'media' ? ' ' + icon : '');
}
const textSpan = doc.createElement('span');
textSpan.className = 'rule-text';
textSpan.textContent = displayText;
textSpan.title = link.url;
textSpan.style.cssText = 'flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;';
const btnGroup = doc.createElement('div');
btnGroup.style.cssText = 'display:flex; gap:2px; flex-shrink:0;';
const previewBtn = doc.createElement('button');
previewBtn.className = 'hider-btn-small btn-purple';
previewBtn.textContent = '🔍';
previewBtn.title = 'Preview full link (media preview if applicable)';
previewBtn.onclick = (e) => {
e.stopPropagation();
showLinkPreview(link.url);
};
btnGroup.appendChild(previewBtn);
const copyBtn = doc.createElement('button');
copyBtn.className = 'hider-btn-small btn-blue';
copyBtn.textContent = '📋';
copyBtn.title = 'Copy URL';
copyBtn.onclick = (e) => {
e.stopPropagation();
copyToClipboard(link.url);
showToast('📋 Copied!');
};
btnGroup.appendChild(copyBtn);
const openBtn = doc.createElement('button');
openBtn.className = 'hider-btn-small btn-green';
openBtn.textContent = '↗';
openBtn.title = 'Open in new tab';
openBtn.onclick = (e) => {
e.stopPropagation();
win.open(link.url, '_blank');
};
btnGroup.appendChild(openBtn);
div.appendChild(textSpan);
div.appendChild(btnGroup);
return div;
}
function populateLinkList() {
const listContainer = shadowBy('hider-link-list');
if (!listContainer) return;
const searchInput = shadowBy('hider-link-search');
const hideInternalCheck = shadowBy('hider-link-hide-internal');
const mediaModeCheck = shadowBy('hider-link-media-mode');
const filterText = searchInput ? searchInput.value.toLowerCase() : '';
const hideInternal = hideInternalCheck ? hideInternalCheck.checked : false;
const mediaMode = mediaModeCheck ? mediaModeCheck.checked : false;
const links = getAllLinks();
const title = shadowBy('link-panel-title');
if (title) title.textContent = `🔗 Extracted Links (${links.length})`;
listContainer.innerHTML = '';
let displayed = 0;
const frag = doc.createDocumentFragment();
if (mediaMode) {
const grid = doc.createElement('div');
grid.style.cssText = 'display:grid;grid-template-columns:repeat(3,1fr);gap:6px;';
links.forEach(link => {
if (link.type !== 'media') return;
if (filterText) {
const lower = filterText;
if (!link.text.toLowerCase().includes(lower) && !link.url.toLowerCase().includes(lower)) return;
}
if (hideInternal) {
try {
const urlObj = new URL(link.url);
if (urlObj.hostname === location.hostname) return;
} catch {}
}
const item = buildGridItem(link);
grid.appendChild(item);
displayed++;
});
if (displayed === 0) {
const empty = doc.createElement('div');
empty.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:8px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;grid-column:1/4;';
empty.textContent = 'No media items.';
grid.appendChild(empty);
}
frag.appendChild(grid);
} else {
links.forEach(link => {
const item = buildListItem(link, filterText, hideInternal, false);
if (item) {
frag.appendChild(item);
displayed++;
}
});
if (displayed === 0) {
const empty = doc.createElement('div');
empty.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:8px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;';
empty.textContent = 'No matching links.';
frag.appendChild(empty);
}
}
listContainer.appendChild(frag);
if (title) title.textContent = `🔗 Extracted Links (${displayed} shown)`;
}
// --- Link panel with Refresh button ---
function createLinkPanel() {
if (linkPanelEl) return linkPanelEl;
const panel = doc.createElement('div');
panel.id = 'hider-link-panel';
panel.className = 'h-glass';
panel.style.cssText = `
position: fixed !important;
right: 14px !important;
top: 55px !important;
z-index: 2147483645 !important;
width: min(310px, 90vw) !important;
max-height: min(580px, 82vh) !important;
border-radius: 16px !important;
padding: 10px !important;
display: none;
flex-direction: column !important;
overflow-y: auto !important;
overscroll-behavior: contain !important;
touch-action: pan-y !important;
pointer-events: auto !important;
gap: 6px !important;
opacity: 0;
transform: scale(0.96) translateY(-8px);
transition: opacity 0.2s ease, transform 0.2s ease !important;
`;
const header = doc.createElement('div');
header.className = 'panel-header';
header.style.cssText = 'display:flex; justify-content:space-between; align-items:center;';
const title = doc.createElement('span');
title.id = 'link-panel-title';
title.textContent = '🔗 Extracted Links';
const headerRight = doc.createElement('div');
headerRight.style.cssText = 'display:flex; gap:4px;';
const modeBtn = doc.createElement('button');
modeBtn.className = 'hider-btn-small btn-gray';
modeBtn.textContent = '📝';
modeBtn.title = 'Toggle display: text / URL';
modeBtn.onclick = (e) => {
e.stopPropagation();
linkDisplayMode = linkDisplayMode === 'text' ? 'url' : 'text';
modeBtn.textContent = linkDisplayMode === 'text' ? '📝' : '🔗';
populateLinkList();
};
headerRight.appendChild(modeBtn);
const refreshBtn = doc.createElement('button');
refreshBtn.className = 'hider-btn-small btn-purple';
refreshBtn.textContent = '🔄';
refreshBtn.title = 'Refresh links & pause media';
refreshBtn.onclick = (e) => {
e.stopPropagation();
pauseAllVideos();
populateLinkList();
showToast('🔄 Links refreshed');
};
headerRight.appendChild(refreshBtn);
const closeBtn = doc.createElement('button');
closeBtn.className = 'hider-btn-small';
closeBtn.style.cssText = 'background:rgba(255,255,255,0.12)!important; padding:2px 6px!important;';
closeBtn.textContent = '✖';
closeBtn.onclick = () => { toggleLinkPanel(); };
headerRight.appendChild(closeBtn);
header.appendChild(title);
header.appendChild(headerRight);
panel.appendChild(header);
const searchWrapper = doc.createElement('div');
searchWrapper.style.cssText = 'display:flex; gap:4px; align-items:center; margin-bottom:2px; flex-wrap:wrap;';
const searchInput = doc.createElement('input');
searchInput.id = 'hider-link-search';
searchInput.className = 'h-select';
searchInput.placeholder = '🔍 Filter links...';
searchInput.style.cssText = 'flex:1; height:26px; font-size:10px; min-width:80px;';
searchInput.oninput = () => populateLinkList();
searchWrapper.appendChild(searchInput);
const hideCheck = doc.createElement('label');
hideCheck.style.cssText = 'font-size:9px; color:#cbd5e1; display:flex; align-items:center; gap:4px; cursor:pointer; user-select:none; white-space:nowrap;';
const checkBox = doc.createElement('input');
checkBox.id = 'hider-link-hide-internal';
checkBox.type = 'checkbox';
checkBox.style.cssText = 'accent-color:#38bdf8; width:12px; height:12px; cursor:pointer;';
checkBox.onchange = () => populateLinkList();
hideCheck.appendChild(checkBox);
hideCheck.appendChild(doc.createTextNode('Hide internal'));
searchWrapper.appendChild(hideCheck);
const mediaCheck = doc.createElement('label');
mediaCheck.style.cssText = 'font-size:9px; color:#cbd5e1; display:flex; align-items:center; gap:4px; cursor:pointer; user-select:none; white-space:nowrap;';
const mediaBox = doc.createElement('input');
mediaBox.id = 'hider-link-media-mode';
mediaBox.type = 'checkbox';
mediaBox.style.cssText = 'accent-color:#f59e0b; width:12px; height:12px; cursor:pointer;';
mediaBox.onchange = () => populateLinkList();
mediaCheck.appendChild(mediaBox);
mediaCheck.appendChild(doc.createTextNode('📺 Media mode'));
searchWrapper.appendChild(mediaCheck);
panel.appendChild(searchWrapper);
const listContainer = doc.createElement('div');
listContainer.id = 'hider-link-list';
listContainer.style.cssText = 'display:flex; flex-direction:column; gap:4px; overflow-y:auto; flex:1; overscroll-behavior:contain;';
panel.appendChild(listContainer);
shadowRoot.appendChild(panel);
linkPanelEl = panel;
return panel;
}
function toggleLinkPanel() {
if (!shadowRoot) return;
if (!linkPanelEl || !linkPanelEl.parentNode) {
linkPanelEl = createLinkPanel();
}
const isVisible = linkPanelEl.classList.contains('is-visible');
if (isVisible) {
linkPanelEl.classList.remove('is-visible');
setTimeout(() => linkPanelEl.style.display = 'none', 300);
} else {
pauseAllVideos();
populateLinkList();
linkPanelEl.style.display = 'flex';
void linkPanelEl.offsetWidth;
linkPanelEl.classList.add('is-visible');
}
}
// ================================================================
// RULE OVERLAY (ADD / EDIT) – ENLARGED HEIGHT, TARGET FIELD
// ================================================================
let currentRuleOverlayMode = 'add';
let currentRuleEditInfo = null;
function showRuleOverlay(options) {
const existing = shadowRoot?.getElementById('hider-rule-overlay');
if (existing) existing.remove();
const mode = options.mode || 'add';
const initialSelector = options.selector || '';
const initialScope = options.scope || 'global';
const initialTarget = options.target || '';
currentRuleOverlayMode = mode;
currentRuleEditInfo = options.editInfo || null;
const overlay = doc.createElement('div');
overlay.id = 'hider-rule-overlay';
overlay.className = 'h-glass';
overlay.style.cssText = `
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
z-index: 2147483647 !important;
padding: 22px !important;
border-radius: 16px !important;
min-width: 360px !important;
max-width: 92vw !important;
width: 420px !important;
min-height: 280px !important;
display: flex !important;
flex-direction: column !important;
gap: 14px !important;
pointer-events: auto !important;
background: rgba(13,18,30,0.96) !important;
backdrop-filter: blur(16px) !important;
border: 1px solid rgba(255,255,255,0.15) !important;
box-shadow: 0 24px 48px rgba(0,0,0,0.8) !important;
`;
const title = doc.createElement('div');
title.style.cssText = 'font-weight:800;font-size:14px;color:#38bdf8;margin-bottom:4px;';
title.textContent = mode === 'add' ? '➕ Add Rule' : '✏️ Edit Rule';
overlay.appendChild(title);
const scopeContainer = doc.createElement('div');
scopeContainer.className = 'h-custom-select';
scopeContainer.id = 'rule-scope-select';
scopeContainer.innerHTML = `
<div class="h-custom-trigger"><span class="h-custom-value-text">Global</span><span class="h-custom-arrow">▼</span></div>
<div class="h-custom-options">
<div class="h-custom-opt" data-val="global">🌍 Global</div>
<div class="h-custom-opt" data-val="site">🌐 Site‑Wide</div>
<div class="h-custom-opt" data-val="link">📄 Page‑Only</div>
</div>
`;
overlay.appendChild(scopeContainer);
const targetContainer = doc.createElement('div');
targetContainer.id = 'rule-target-container';
targetContainer.style.cssText = 'display:none; flex-direction:column; gap:4px;';
const targetLabel = doc.createElement('div');
targetLabel.style.cssText = 'font-size:10px;color:#94a3b8;font-weight:600;text-transform:uppercase;letter-spacing:0.3px;';
targetLabel.textContent = '🎯 Target (domain or URL)';
targetContainer.appendChild(targetLabel);
const targetInput = doc.createElement('input');
targetInput.id = 'rule-target-input';
targetInput.className = 'h-select';
targetInput.placeholder = 'example.com or /path';
targetInput.value = initialTarget || '';
targetInput.style.cssText = 'width:100%;height:34px;font-size:11px;padding:4px 10px;';
targetContainer.appendChild(targetInput);
overlay.appendChild(targetContainer);
const input = doc.createElement('input');
input.id = 'rule-selector-input';
input.className = 'h-select';
input.placeholder = 'CSS Selector (e.g. .ad, #banner)';
input.value = initialSelector;
input.style.cssText = 'width:100%;height:34px;font-size:11px;padding:4px 10px;';
overlay.appendChild(input);
const btnGroup = doc.createElement('div');
btnGroup.style.cssText = 'display:flex;gap:8px;justify-content:flex-end;margin-top:6px;';
const saveBtn = doc.createElement('button');
saveBtn.className = 'hider-btn-small btn-green';
saveBtn.textContent = '💾 Save';
saveBtn.style.cssText = 'height:32px;padding:0 16px;font-size:11px;';
const cancelBtn = doc.createElement('button');
cancelBtn.className = 'hider-btn-small btn-gray';
cancelBtn.textContent = '✖ Cancel';
cancelBtn.style.cssText = 'height:32px;padding:0 16px;font-size:11px;';
btnGroup.appendChild(saveBtn);
btnGroup.appendChild(cancelBtn);
overlay.appendChild(btnGroup);
shadowRoot.appendChild(overlay);
let selectedScope = initialScope;
const trigger = scopeContainer.querySelector('.h-custom-trigger');
const valueSpan = trigger.querySelector('.h-custom-value-text');
const optionsList = scopeContainer.querySelectorAll('.h-custom-opt');
const updateScopeLabel = (val) => {
const labels = { global: '🌍 Global', site: '🌐 Site‑Wide', link: '📄 Page‑Only' };
valueSpan.textContent = labels[val] || 'Global';
optionsList.forEach(opt => opt.classList.toggle('is-selected', opt.dataset.val === val));
selectedScope = val;
if (val === 'global') {
targetContainer.style.display = 'none';
} else {
targetContainer.style.display = 'flex';
if (!targetInput.value) {
if (val === 'site') targetInput.value = location.hostname;
else if (val === 'link') targetInput.value = cleanUrl();
}
}
};
optionsList.forEach(opt => {
opt.addEventListener('click', (e) => {
e.stopPropagation();
updateScopeLabel(opt.dataset.val);
scopeContainer.classList.remove('is-open');
});
});
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = scopeContainer.classList.contains('is-open');
shadowRoot.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
if (!isOpen) scopeContainer.classList.add('is-open');
});
document.addEventListener('click', function closeDropdown(e) {
if (scopeContainer && !scopeContainer.contains(e.target)) {
scopeContainer.classList.remove('is-open');
document.removeEventListener('click', closeDropdown);
}
});
updateScopeLabel(initialScope);
const saveRule = () => {
const selector = input.value.trim();
if (!selector) {
showToast('⚠️ Please enter a CSS selector');
return;
}
let newScope = selectedScope;
let targetValue = targetInput.value.trim();
if (newScope === 'global') {
targetValue = '*';
} else {
if (!targetValue) {
showToast(`⚠️ Please enter a target ${newScope === 'site' ? 'domain' : 'URL'}`);
return;
}
if (newScope === 'site') {
targetValue = cleanDomain(targetValue);
if (!targetValue) {
showToast('⚠️ Invalid domain');
return;
}
} else if (newScope === 'link') {
try {
const abs = resolveUrl(targetValue);
if (!abs) throw new Error();
targetValue = abs;
} catch {
showToast('⚠️ Invalid URL');
return;
}
}
}
if (mode === 'edit' && currentRuleEditInfo) {
const info = currentRuleEditInfo;
if (info.listType === 'custom') {
CACHE.customRules = CACHE.customRules.filter(r => r.id !== info.id);
sv('hider_custom_rules_v4', CACHE.customRules);
} else if (info.listType === 'site') {
const key = 'hider_site_' + location.hostname;
let rules = gv(key, []);
if (info.index >= 0 && info.index < rules.length) {
rules.splice(info.index, 1);
sv(key, rules);
}
} else if (info.listType === 'link') {
const key = 'hider_link_' + cleanUrl();
let rules = gv(key, []);
if (info.index >= 0 && info.index < rules.length) {
rules.splice(info.index, 1);
sv(key, rules);
}
}
}
if (newScope === 'global') {
const newRule = { id: 'rule_' + Date.now() + '_' + Math.random().toString(36).substr(2,4), selector: selector, target: '*' };
CACHE.customRules.push(newRule);
sv('hider_custom_rules_v4', CACHE.customRules);
showToast(`🌍 Added global rule: ${selector}`);
} else if (newScope === 'site') {
const key = 'hider_site_' + targetValue;
let rules = gv(key, []);
if (!rules.includes(selector)) {
rules.push(selector);
sv(key, rules);
showToast(`🌐 Added site-wide rule for ${targetValue}: ${selector}`);
} else {
showToast('⚠️ Rule already exists for this site');
}
} else if (newScope === 'link') {
const key = 'hider_link_' + targetValue;
let rules = gv(key, []);
if (!rules.includes(selector)) {
rules.push(selector);
sv(key, rules);
showToast(`📄 Added page-only rule for ${targetValue}: ${selector}`);
} else {
showToast('⚠️ Rule already exists for this page');
}
}
overlay.remove();
requestUpdateStyles();
renderList();
currentRuleEditInfo = null;
};
const cancel = () => {
overlay.remove();
currentRuleEditInfo = null;
};
saveBtn.addEventListener('click', saveRule);
cancelBtn.addEventListener('click', cancel);
}
// ---------- UI Creation ----------
function createShadowUI() {
if (!isTop) return;
const parentEl = doc.documentElement || doc.body;
if (!parentEl) return;
let hostEl = doc.getElementById(UI_HOST_ID);
if (!hostEl) {
hostEl = doc.createElement('div'); hostEl.id = UI_HOST_ID;
hostEl.style.cssText = 'position:fixed!important;top:0!important;left:0!important;width:0!important;height:0!important;z-index:2147483647!important;pointer-events:none!important;display:block!important;visibility:visible!important;opacity:1!important;overflow:visible!important;';
parentEl.appendChild(hostEl);
}
shadowRoot = hostEl.shadowRoot || hostEl.attachShadow({ mode: 'open' });
const style = doc.createElement('style');
style.textContent = `
* { box-sizing: border-box !important; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, sans-serif !important; line-height: 1.3 !important; }
.h-glass {
background: rgba(13, 18, 30, 0.92) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border: 1px solid rgba(255, 255, 255, 0.12) !important;
color: #f8fafc !important;
border-radius: 14px !important;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6) !important;
}
::-webkit-scrollbar { width: 4px !important; height: 4px !important; }
::-webkit-scrollbar-track { background: transparent !important; }
::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.2) !important; border-radius: 8px !important; }
#hider-panel {
position: fixed !important;
right: 14px !important;
top: 55px !important;
z-index: 2147483645 !important;
width: min(480px, 92vw) !important;
max-height: min(700px, 85vh) !important;
border-radius: 16px !important;
padding: 0 !important;
display: none;
flex-direction: column !important;
overflow: hidden !important;
pointer-events: auto !important;
opacity: 0;
transform: scale(0.96) translateY(-8px);
transition: opacity 0.2s ease, transform 0.2s ease !important;
}
#hider-panel.is-visible {
opacity: 1 !important;
transform: scale(1) translateY(0) !important;
display: flex !important;
}
#hider-link-panel.is-visible {
opacity: 1 !important;
transform: scale(1) translateY(0) !important;
display: flex !important;
}
.panel-header {
padding: 8px 12px;
border-bottom: 1px solid rgba(255,255,255,0.08);
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.panel-header .title {
font-weight: 800;
font-size: 12px;
color: #f8fafc;
}
.panel-header .close-btn {
background: rgba(255,255,255,0.08);
border: none;
border-radius: 6px;
color: #cbd5e1;
padding: 2px 8px;
cursor: pointer;
font-size: 11px;
}
.panel-header .close-btn:hover { background: rgba(255,255,255,0.16); }
.panel-body {
display: flex;
flex: 1;
overflow: hidden;
}
.panel-sidebar {
flex: 0 0 110px;
background: rgba(0,0,0,0.2);
padding: 6px 4px;
overflow-y: auto;
border-right: 1px solid rgba(255,255,255,0.06);
}
.panel-sidebar .tab-btn {
display: block;
width: 100%;
text-align: left;
background: transparent;
border: none;
border-radius: 6px;
padding: 5px 8px;
color: #94a3b8;
font-size: 10px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s ease;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.panel-sidebar .tab-btn:hover {
background: rgba(255,255,255,0.08);
color: #e2e8f0;
}
.panel-sidebar .tab-btn.active {
background: rgba(56,189,248,0.15);
color: #38bdf8;
border-left: 2px solid #38bdf8;
}
.panel-content {
flex: 1;
padding: 8px 10px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 6px;
}
.panel-content .tab-content {
display: none;
flex-direction: column;
gap: 6px;
}
.panel-content .tab-content.active {
display: flex;
}
.h-dock {
position: fixed !important; right: 10px !important; top: 35%; z-index: 2147483643 !important;
display: flex !important; flex-direction: column !important; align-items: center !important; gap: 5px !important;
padding: 5px !important; pointer-events: auto !important; transition: transform 0.3s ease, opacity 0.3s ease !important;
user-select: none !important; border-radius: 20px !important;
}
.h-dock.is-collapsed { transform: translateX(65%) !important; opacity: 0.5 !important; }
.h-dock.manual-hidden { transform: translateX(65%) !important; opacity: 0.5 !important; }
.h-dock.is-collapsed:not(.manual-hidden):hover, .h-dock.expanded { transform: translateX(0) !important; opacity: 1 !important; }
.h-dock-main {
width: 34px !important; height: 34px !important; font-size: 16px !important;
background: linear-gradient(135deg, rgba(56, 189, 248, 0.3), rgba(37, 99, 235, 0.4)) !important;
border: 1px solid rgba(56, 189, 248, 0.4) !important; cursor: grab !important; border-radius: 50% !important;
flex-shrink: 0 !important; min-height: 34px !important; min-width: 34px !important; display: flex !important; align-items: center !important; justify-content: center !important;
}
.h-dock-main.expanded {
background: linear-gradient(135deg, #0ea5e9, #2563eb) !important; color: #fff !important;
box-shadow: 0 0 12px rgba(14, 165, 233, 0.5) !important; border-color: transparent !important;
}
.h-dock-menu { display: flex; flex-direction: column !important; gap: 5px !important; overflow: hidden !important; max-height: 0; opacity: 0; transition: max-height 0.3s ease, opacity 0.2s ease !important; }
.h-dock-menu.is-open { max-height: 450px; opacity: 1; }
.h-dock-btn {
width: 32px !important; height: 32px !important; border-radius: 10px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important; background: rgba(255, 255, 255, 0.06) !important;
color: #cbd5e1 !important; font-size: 11px !important; font-weight: 700 !important; display: flex !important;
flex-direction: column !important; align-items: center !important; justify-content: center !important;
cursor: pointer !important; transition: all 0.2s ease !important; margin: 0 !important; padding: 0 !important;
flex-shrink: 0 !important; min-height: 32px !important; min-width: 32px !important;
}
.h-dock-btn:hover { background: rgba(255, 255, 255, 0.18) !important; color: #fff !important; transform: scale(1.05) !important; }
.h-dock-btn.active { background: linear-gradient(135deg, #0ea5e9, #2563eb) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn.scope-link { background: linear-gradient(135deg, #10b981, #059669) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn.scope-global { background: linear-gradient(135deg, #a855f7, #7e22ce) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn.is-frozen { background: linear-gradient(135deg, #8b5cf6, #6d28d9) !important; color: #fff !important; border-color: transparent !important; }
.h-dock-btn span { font-size: 7px !important; font-weight: 800 !important; margin-top: 1px !important; letter-spacing: 0.3px !important; text-transform: uppercase !important; }
.btn-blue { background: linear-gradient(135deg, #0ea5e9, #0284c7) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-green { background: linear-gradient(135deg, #10b981, #059669) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-red { background: linear-gradient(135deg, #f43f5e, #e11d48) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-purple { background: linear-gradient(135deg, #a855f7, #7e22ce) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.2) !important; }
.btn-gray { background: linear-gradient(135deg, #475569, #334155) !important; color: #fff !important; border: 1px solid rgba(255,255,255,0.15) !important; }
.h-stepper-pill {
position: fixed !important; bottom: 20px !important; left: 50% !important; transform: translateX(-50%) !important;
z-index: 2147483644 !important; padding: 6px 8px !important; display: flex !important; flex-direction: column !important;
align-items: center !important; gap: 4px !important; pointer-events: auto !important; width: 130px !important;
user-select: none !important; border-radius: 12px !important;
}
.h-stepper-row { display: flex !important; gap: 4px !important; width: 100% !important; justify-content: center !important; }
.h-stepper-row .h-btn-icon { flex: 1 !important; }
.h-drag-dots { cursor: grab !important; color: #64748b !important; font-size: 11px !important; width: 100% !important; text-align: center !important; line-height: 1 !important; letter-spacing: 2px !important; }
.h-btn-icon { width: 100% !important; height: 22px !important; border-radius: 6px !important; border: 1px solid rgba(255,255,255,.12) !important; background: rgba(255,255,255,.08) !important; color: #f1f5f9 !important; font-size: 9px !important; font-weight: 700 !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; }
.h-btn-icon:hover { background: rgba(255,255,255,.2) !important; }
.h-tag-badge-box { width: 100% !important; height: 22px !important; overflow: hidden !important; position: relative !important; background: rgba(0, 0, 0, 0.5) !important; border: 1px solid rgba(56, 189, 248, 0.3) !important; border-radius: 6px !important; display: flex !important; align-items: center !important; flex-shrink: 0 !important; }
.h-tag-badge-text { color: #38bdf8 !important; font-size: 9px !important; font-family: monospace !important; font-weight: 700 !important; display: block !important; width: 100% !important; text-align: center !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; line-height: 22px !important; }
.h-tag-badge-text.is-animating { position: absolute !important; left: 0 !important; width: auto !important; padding-left: 100% !important; text-align: left !important; animation: hiderMarqueeR2L 8s linear infinite !important; }
@keyframes hiderMarqueeR2L { 0% { transform: translate3d(0,0,0); } 100% { transform: translate3d(-100%,0,0); } }
.h-btn-pill { height: 22px !important; min-height: 22px !important; width: 100% !important; padding: 0 6px !important; border-radius: 6px !important; font-size: 9px !important; font-weight: 800 !important; border: none !important; cursor: pointer !important; display: flex !important; align-items: center !important; justify-content: center !important; }
.h-btn-pill:hover { filter: brightness(1.15) !important; }
.h-toast { /* overridden by inline styles */ }
.h-prompt { position: fixed !important; top: 25px !important; left: 50% !important; transform: translateX(-50%) translateY(-15px) !important; opacity: 0; padding: 12px 14px !important; z-index: 2147483646 !important; display: flex !important; flex-direction: column !important; align-items: center !important; gap: 6px !important; pointer-events: auto !important; width: min(290px, 88vw) !important; max-height: 85vh !important; transition: all 0.3s ease !important; }
.h-url-box { font-size: 10px !important; color: #94a3b8 !important; word-break: break-all !important; max-height: 32px !important; overflow: hidden !important; background: rgba(0,0,0,.4) !important; padding: 4px 8px !important; border-radius: 6px !important; width: 100% !important; border: 1px solid rgba(255,255,255,.1) !important; font-family: monospace !important; }
.h-select { width: 100% !important; background: rgba(255,255,255,.06) !important; border: 1px solid rgba(255,255,255,.14) !important; border-radius: 6px !important; color: #fff !important; padding: 4px 8px !important; font-size: 10px !important; outline: 0 !important; height: 28px !important; }
.h-custom-select { position: relative !important; width: 100% !important; user-select: none !important; margin-top: 2px !important; z-index: 10 !important; }
.h-custom-trigger { background: rgba(30,41,59,.85) !important; border: 1px solid rgba(96,165,250,.4) !important; border-radius: 8px !important; color: #f1f5f9 !important; padding: 6px 10px !important; font-size: 10px !important; font-weight: 700 !important; cursor: pointer !important; display: flex !important; justify-content: space-between !important; align-items: center !important; height: 30px !important; }
.h-custom-arrow { font-size: 9px !important; color: #38bdf8 !important; transition: transform .2s ease !important; }
.h-custom-select.is-open .h-custom-arrow { transform: rotate(-180deg) !important; }
.h-custom-options { display: none; position: relative !important; z-index: 100 !important; flex-direction: column !important; background: rgba(15,23,42,0.98) !important; border: 1px solid rgba(255,255,255,.2) !important; border-radius: 8px !important; max-height: 160px !important; overflow-y: auto !important; margin-top: 4px !important; }
.h-custom-select.is-open .h-custom-options { display: flex !important; }
.h-custom-opt { padding: 6px 10px !important; color: #cbd5e1 !important; font-size: 10px !important; cursor: pointer !important; border-bottom: 1px solid rgba(255,255,255,.05) !important; }
.h-custom-opt:hover, .h-custom-opt.is-selected { background: rgba(56,189,248,.2) !important; color: #38bdf8 !important; font-weight: 700 !important; }
.panel-header { font-weight: 800 !important; font-size: 11px !important; border-bottom: 1px solid rgba(255,255,255,.12) !important; padding-bottom: 6px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; margin-bottom: 2px !important; color: #f8fafc !important; }
.list-item { font-size: 10px !important; background: rgba(255,255,255,.04) !important; border: 1px solid rgba(255,255,255,.08) !important; border-radius: 6px !important; padding: 4px 6px !important; display: flex !important; justify-content: space-between !important; align-items: center !important; gap: 6px !important; }
.list-item span.rule-text { word-break: break-all !important; color: #e2e8f0 !important; font-family: monospace !important; font-size: 9px !important; flex: 1 !important; overflow: hidden !important; text-overflow: ellipsis !important; white-space: nowrap !important; }
.hider-btn-small { cursor: pointer !important; border: none !important; border-radius: 4px !important; padding: 2px 6px !important; color: #fff !important; font-weight: 700 !important; font-size: 9px !important; height: 20px !important; display: flex; align-items: center; justify-content: center; }
/* ==================== PRO UI 15.0 VISUAL SYSTEM ==================== */
:host, #hider-ui-root { --hx-bg:#07101d; --hx-panel:rgba(10,18,31,.90); --hx-line:rgba(148,163,184,.12); --hx-cyan:#38bdf8; --hx-blue:#2563eb; --hx-violet:#8b5cf6; --hx-gold:#fbbf24; }
* { box-sizing:border-box; }
#hider-panel, #hider-link-panel { backdrop-filter:blur(24px) saturate(145%); -webkit-backdrop-filter:blur(24px) saturate(145%); border:1px solid rgba(125,211,252,.14)!important; box-shadow:0 24px 70px rgba(0,0,0,.48), inset 0 1px 0 rgba(255,255,255,.045); }
#hider-panel { background:linear-gradient(145deg,rgba(8,15,28,.96),rgba(9,18,34,.88) 55%,rgba(22,15,39,.82))!important; }
.panel-header { min-height:42px; padding:8px 11px!important; background:linear-gradient(90deg,rgba(56,189,248,.055),transparent 48%,rgba(139,92,246,.045)); }
.panel-header .title { font-size:12px!important; letter-spacing:.2px; }
.panel-sidebar { background:linear-gradient(180deg,rgba(255,255,255,.025),rgba(0,0,0,.18))!important; }
.panel-sidebar .tab-btn { position:relative; transition:background .18s ease,color .18s ease,transform .18s ease!important; }
.panel-sidebar .tab-btn:hover { transform:translateX(2px); }
.panel-sidebar .tab-btn.active { box-shadow:inset 0 0 0 1px rgba(56,189,248,.08),0 5px 18px rgba(14,165,233,.08); }
.panel-content { scroll-behavior:smooth; overscroll-behavior:contain; scrollbar-width:thin; scrollbar-color:rgba(125,211,252,.24) transparent; }
.panel-content::-webkit-scrollbar { width:6px; } .panel-content::-webkit-scrollbar-thumb { background:rgba(125,211,252,.22); border-radius:20px; }
.h-dock { filter:drop-shadow(0 14px 30px rgba(0,0,0,.32)); }
.h-dock-main { transition:transform .22s cubic-bezier(.2,.8,.2,1),box-shadow .22s ease,background .22s ease!important; }
.h-dock-main:hover { transform:scale(1.06); box-shadow:0 0 22px rgba(56,189,248,.30)!important; }
.h-dock-menu { gap:6px!important; }
.h-dock-btn { transform:translateZ(0); transition:transform .16s ease,background .16s ease,border-color .16s ease,box-shadow .16s ease!important; }
.h-dock-btn:active { transform:scale(.94)!important; }
.h-dock-btn:hover { box-shadow:0 8px 20px rgba(0,0,0,.24); }
.hider-btn-small { transition:transform .14s ease,filter .14s ease,box-shadow .14s ease!important; }
.hider-btn-small:hover { filter:brightness(1.08); box-shadow:0 5px 14px rgba(0,0,0,.22); transform:translateY(-1px); }
.hider-btn-small:active { transform:translateY(0) scale(.97); }
.hx-card { position:relative; overflow:hidden; border:1px solid rgba(148,163,184,.10); border-radius:12px; background:linear-gradient(145deg,rgba(255,255,255,.045),rgba(255,255,255,.018)); box-shadow:0 10px 28px rgba(0,0,0,.16); }
.hx-input { width:100%; background:rgba(2,6,23,.46)!important; border:1px solid rgba(148,163,184,.15)!important; color:#f8fafc!important; border-radius:8px!important; outline:none; transition:border-color .18s ease,box-shadow .18s ease,background .18s ease; }
.hx-input:focus { border-color:rgba(56,189,248,.52)!important; box-shadow:0 0 0 3px rgba(56,189,248,.09); background:rgba(2,6,23,.66)!important; }
.hx-cookie-row { transition:background .16s ease,border-color .16s ease,transform .16s ease; }
.hx-cookie-row:hover { background:rgba(56,189,248,.055)!important; border-color:rgba(56,189,248,.18)!important; transform:translateY(-1px); }
.hx-cookie-value { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; }
.hx-chip { display:inline-flex; align-items:center; gap:4px; padding:3px 7px; border-radius:999px; border:1px solid rgba(148,163,184,.12); background:rgba(255,255,255,.035); color:#94a3b8; font-size:8px; font-weight:800; }
.hx-empty { border:1px dashed rgba(148,163,184,.16); border-radius:12px; padding:18px 10px; text-align:center; color:#64748b; }
@media (max-width:600px) { #hider-panel { width:min(94vw,480px)!important; max-height:88vh!important; } .panel-sidebar { flex-basis:92px!important; } .panel-content { padding:8px!important; } }
/* ==================== PRO UI 15.4 — FUTURE GLASS SYSTEM ==================== */
:host, #hider-ui-root {
--hx-bg:#050914;
--hx-surface:rgba(8,14,25,.78);
--hx-surface-2:rgba(15,23,42,.52);
--hx-line:rgba(148,163,184,.14);
--hx-line-bright:rgba(125,211,252,.30);
--hx-cyan:#7dd3fc;
--hx-cyan-strong:#38bdf8;
--hx-violet:#a78bfa;
--hx-green:#6ee7b7;
--hx-red:#fb7185;
--hx-gold:#fbbf24;
--hx-text:#e7eef8;
--hx-muted:#718096;
--hx-radius:14px;
}
* { box-sizing:border-box; }
button,input,select,textarea { font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif !important; }
button { -webkit-font-smoothing:antialiased; }
/* --- Master glass surfaces --- */
.h-glass, #hider-panel, #hider-link-panel, .h-stepper-pill, .h-prompt {
background:
linear-gradient(180deg,rgba(255,255,255,.052),rgba(255,255,255,.018)),
rgba(5,10,20,.86) !important;
border:1px solid var(--hx-line) !important;
box-shadow:
0 30px 80px rgba(0,0,0,.46),
0 1px 0 rgba(255,255,255,.045) inset,
0 0 0 1px rgba(0,0,0,.16) !important;
backdrop-filter:blur(28px) saturate(125%) !important;
-webkit-backdrop-filter:blur(28px) saturate(125%) !important;
}
.h-glass::before, #hider-panel::before, #hider-link-panel::before {
content:"" !important;
position:absolute !important;
inset:0 !important;
pointer-events:none !important;
border-radius:inherit !important;
background:linear-gradient(115deg,rgba(125,211,252,.055),transparent 25%,transparent 72%,rgba(167,139,250,.035)) !important;
opacity:1 !important;
}
/* --- Header: minimal command console --- */
.panel-header {
min-height:50px !important;
padding:8px 10px !important;
margin:0 !important;
border-bottom:1px solid rgba(148,163,184,.10) !important;
background:rgba(255,255,255,.018) !important;
}
.panel-brand { gap:9px !important; }
.panel-brand-copy { min-width:0 !important; }
.panel-brand-title { font-size:12px !important; letter-spacing:.1px !important; }
.panel-brand-sub { color:#718096 !important; font-size:8px !important; letter-spacing:1.15px !important; text-transform:uppercase !important; }
.panel-status {
display:inline-flex !important; align-items:center !important; gap:4px !important;
margin-left:5px !important; padding:2px 5px !important; border-radius:999px !important;
color:#86efac !important; background:rgba(34,197,94,.055) !important;
border:1px solid rgba(74,222,128,.16) !important; font-size:6px !important; letter-spacing:1px !important;
}
.panel-status::before { content:""; width:4px; height:4px; border-radius:50%; background:#4ade80; box-shadow:0 0 8px rgba(74,222,128,.7); }
.hx-brand-icon { width:32px !important; height:32px !important; flex:0 0 32px !important; border-radius:9px !important; overflow:hidden !important; opacity:.96 !important; filter:drop-shadow(0 4px 12px rgba(56,189,248,.16)); }
.hx-brand-icon svg { width:100% !important; height:100% !important; display:block !important; }
.close-btn { width:27px !important; height:27px !important; border-radius:9px !important; border:1px solid rgba(148,163,184,.12) !important; background:rgba(255,255,255,.035) !important; color:#94a3b8 !important; transition:.18s ease !important; }
.close-btn:hover { color:#e7eef8 !important; background:rgba(125,211,252,.07) !important; border-color:rgba(125,211,252,.24) !important; transform:none !important; }
.close-btn:active { transform:scale(.94) !important; }
/* --- Sidebar / navigation: precision rail --- */
.panel-sidebar {
background:rgba(255,255,255,.012) !important;
border-right:1px solid rgba(148,163,184,.08) !important;
padding:7px 5px !important;
}
.panel-sidebar .tab-btn {
position:relative !important; min-height:28px !important; padding:6px 8px 6px 10px !important;
border:1px solid transparent !important; border-radius:9px !important; color:#718096 !important;
font-size:9px !important; font-weight:700 !important; letter-spacing:.15px !important;
transition:background .18s ease,border-color .18s ease,color .18s ease,transform .18s ease !important;
}
.panel-sidebar .tab-btn::before { content:""; position:absolute; left:4px; top:8px; bottom:8px; width:2px; border-radius:4px; background:transparent; transition:.18s ease; }
.panel-sidebar .tab-btn:hover { transform:translateX(1px) !important; background:rgba(255,255,255,.035) !important; color:#b9c7d9 !important; }
.panel-sidebar .tab-btn.active {
background:linear-gradient(90deg,rgba(56,189,248,.075),rgba(56,189,248,.018)) !important;
border-color:rgba(56,189,248,.12) !important; color:#cfeeff !important;
box-shadow:0 6px 20px rgba(0,0,0,.10) !important;
}
.panel-sidebar .tab-btn.active::before { background:#38bdf8; box-shadow:0 0 9px rgba(56,189,248,.7); }
.panel-content { padding:10px 11px !important; gap:8px !important; scrollbar-width:thin !important; scrollbar-color:rgba(125,211,252,.18) transparent !important; }
.panel-content::-webkit-scrollbar { width:5px; }
.panel-content::-webkit-scrollbar-thumb { background:rgba(125,211,252,.18); border-radius:99px; }
.hx-feature-note { margin:-2px 0 1px 26px !important; padding:6px 8px !important; border-left:2px solid rgba(56,189,248,.28) !important; border-radius:7px !important; background:linear-gradient(90deg,rgba(56,189,248,.045),transparent) !important; color:#64748b !important; font-size:8px !important; line-height:1.4 !important; }
.hx-feature-note b { color:#7dd3fc !important; font-weight:900 !important; letter-spacing:.4px !important; }
.hx-toggle-row { min-height:32px !important; padding:6px 8px !important; border:1px solid rgba(148,163,184,.07) !important; border-radius:10px !important; background:rgba(255,255,255,.018) !important; transition:background .16s ease,border-color .16s ease,transform .16s ease !important; }
.hx-toggle-row:hover { background:rgba(125,211,252,.045) !important; border-color:rgba(125,211,252,.12) !important; transform:translateX(1px) !important; }
.h-dock-btn { position:relative !important; overflow:hidden !important; }
.h-dock-btn::after { content:"" !important; position:absolute !important; inset:1px !important; border-radius:inherit !important; background:linear-gradient(135deg,rgba(255,255,255,.05),transparent 45%,rgba(56,189,248,.035)) !important; pointer-events:none !important; opacity:.75 !important; }
.h-dock-btn.active::before,.h-dock-btn.is-frozen::before { content:"" !important; position:absolute !important; top:5px !important; right:5px !important; width:5px !important; height:5px !important; border-radius:50% !important; background:#6ee7b7 !important; box-shadow:0 0 9px rgba(110,231,183,.9) !important; }
/* --- Every small control becomes the same glass language --- */
.hider-btn-small, .h-btn-icon, .h-btn-pill, .h-custom-trigger, .h-select {
border:1px solid rgba(148,163,184,.13) !important;
background:rgba(255,255,255,.035) !important;
color:#cbd5e1 !important;
border-radius:8px !important;
box-shadow:0 1px 0 rgba(255,255,255,.035) inset !important;
transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease,transform .12s ease !important;
}
.hider-btn-small:hover, .h-btn-icon:hover, .h-btn-pill:hover, .h-custom-trigger:hover {
background:rgba(125,211,252,.065) !important; border-color:rgba(125,211,252,.22) !important;
color:#eff8ff !important; filter:none !important; transform:translateY(-1px) !important;
box-shadow:0 7px 18px rgba(0,0,0,.16),0 0 0 1px rgba(125,211,252,.035) inset !important;
}
.hider-btn-small:active, .h-btn-icon:active, .h-btn-pill:active, .h-custom-trigger:active { transform:scale(.965) !important; }
.btn-blue,.btn-green,.btn-red,.btn-purple,.btn-gray {
background:rgba(255,255,255,.035) !important; border-color:rgba(148,163,184,.13) !important;
}
.btn-blue { color:#7dd3fc !important; }
.btn-green { color:#6ee7b7 !important; }
.btn-red { color:#fb7185 !important; }
.btn-purple { color:#c4b5fd !important; }
.btn-gray { color:#cbd5e1 !important; }
.btn-blue:hover { border-color:rgba(56,189,248,.28) !important; background:rgba(56,189,248,.065) !important; }
.btn-green:hover { border-color:rgba(52,211,153,.25) !important; background:rgba(52,211,153,.055) !important; }
.btn-red:hover { border-color:rgba(251,113,133,.25) !important; background:rgba(251,113,133,.055) !important; }
.btn-purple:hover { border-color:rgba(167,139,250,.25) !important; background:rgba(167,139,250,.055) !important; }
input, textarea, select { outline:none !important; }
input:focus, textarea:focus, select:focus { border-color:rgba(56,189,248,.34) !important; box-shadow:0 0 0 3px rgba(56,189,248,.055) !important; }
/* --- Dock: floating glass instrument --- */
.h-dock {
padding:4px !important; gap:4px !important; border-radius:15px !important;
background:rgba(5,10,19,.70) !important; border:1px solid rgba(148,163,184,.12) !important;
box-shadow:0 18px 50px rgba(0,0,0,.34),inset 0 1px 0 rgba(255,255,255,.045) !important;
backdrop-filter:blur(22px) saturate(125%) !important; -webkit-backdrop-filter:blur(22px) saturate(125%) !important;
}
.h-dock-main {
width:36px !important; height:36px !important; min-width:36px !important; min-height:36px !important;
border-radius:11px !important; padding:3px !important; background:rgba(56,189,248,.055) !important;
border:1px solid rgba(125,211,252,.22) !important; box-shadow:0 0 18px rgba(56,189,248,.07) !important;
}
.h-dock-main:hover { transform:none !important; background:rgba(56,189,248,.09) !important; box-shadow:0 0 24px rgba(56,189,248,.16) !important; }
.h-dock-main.expanded { background:rgba(56,189,248,.10) !important; border-color:rgba(125,211,252,.40) !important; box-shadow:0 0 25px rgba(56,189,248,.18) !important; }
.h-dock-btn {
width:34px !important; height:34px !important; min-width:34px !important; min-height:34px !important;
border-radius:10px !important; background:rgba(255,255,255,.025) !important; border:1px solid rgba(148,163,184,.10) !important;
color:#aab8ca !important; transition:background .16s ease,border-color .16s ease,color .16s ease,transform .12s ease,box-shadow .16s ease !important;
}
.h-dock-btn:hover { transform:none !important; background:rgba(255,255,255,.055) !important; color:#eef8ff !important; border-color:rgba(125,211,252,.18) !important; box-shadow:0 8px 18px rgba(0,0,0,.18) !important; }
.h-dock-btn:active { transform:scale(.93) !important; }
.h-dock-btn.active,.h-dock-btn.scope-link,.h-dock-btn.scope-global,.h-dock-btn.is-frozen {
background:rgba(56,189,248,.08) !important; color:#7dd3fc !important; border-color:rgba(56,189,248,.27) !important;
box-shadow:0 0 15px rgba(56,189,248,.07) !important;
}
.h-dock-btn.scope-link { color:#6ee7b7 !important; border-color:rgba(110,231,183,.24) !important; background:rgba(110,231,183,.055) !important; }
.h-dock-btn.scope-global,.h-dock-btn.is-frozen { color:#c4b5fd !important; border-color:rgba(167,139,250,.24) !important; background:rgba(167,139,250,.055) !important; }
.h-dock-btn span { color:inherit !important; opacity:.82 !important; font-size:6.5px !important; letter-spacing:.8px !important; }
.h-dock-menu { gap:4px !important; }
/* --- Cards, rows, selectors, cookie workspace --- */
.list-item,.hx-card,.hx-cookie-row { background:rgba(255,255,255,.025) !important; border:1px solid rgba(148,163,184,.09) !important; border-radius:10px !important; box-shadow:none !important; }
.list-item:hover,.hx-cookie-row:hover { background:rgba(125,211,252,.035) !important; border-color:rgba(125,211,252,.16) !important; transform:none !important; }
.h-custom-options { background:rgba(7,12,22,.96) !important; border-color:rgba(148,163,184,.15) !important; box-shadow:0 18px 40px rgba(0,0,0,.36) !important; }
.h-custom-opt:hover,.h-custom-opt.is-selected { background:rgba(56,189,248,.07) !important; color:#7dd3fc !important; }
.h-tag-badge-box { background:rgba(255,255,255,.025) !important; border-color:rgba(148,163,184,.10) !important; }
.h-tag-badge-text { color:#7dd3fc !important; }
/* --- Stepper / transient controls --- */
.h-stepper-pill { padding:5px !important; border-radius:12px !important; gap:4px !important; }
.h-drag-dots { color:#526176 !important; }
.h-prompt { border-radius:14px !important; }
.h-url-box { background:rgba(0,0,0,.20) !important; border-color:rgba(148,163,184,.10) !important; }
/* --- Motion: restrained, not flashy --- */
@keyframes hxPanelIn { from{opacity:0;transform:translateY(6px) scale(.992)} to{opacity:1;transform:none} }
@keyframes hxGlow { 0%,100%{box-shadow:0 0 0 rgba(56,189,248,0)} 50%{box-shadow:0 0 20px rgba(56,189,248,.10)} }
#hider-panel.is-visible { animation:hxPanelIn .22s cubic-bezier(.2,.75,.2,1) both; }
.h-dock-main.expanded { animation:hxGlow 2.8s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce) { *,*::before,*::after { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; scroll-behavior:auto !important; } }
@media (max-width:600px) {
#hider-panel { width:min(94vw,480px)!important; max-height:90vh!important; border-radius:16px!important; }
.panel-sidebar { flex-basis:94px!important; }
.panel-content { padding:9px!important; }
.h-dock { right:7px !important; }
}
/* ==================== PRO UI 15.4 — SIGNAL / TOGGLE LAYER ==================== */
/* Clearer ON states + futuristic tactile checks. No feature logic changes. */
.h-dock-btn {
color:#c5d1df !important;
background:rgba(255,255,255,.045) !important;
border-color:rgba(180,205,225,.15) !important;
text-shadow:0 1px 8px rgba(0,0,0,.35) !important;
}
.h-dock-btn:hover { color:#f4fbff !important; background:rgba(125,211,252,.10) !important; border-color:rgba(125,211,252,.30) !important; }
.h-dock-btn.active,
.h-dock-btn.scope-link,
.h-dock-btn.scope-global,
.h-dock-btn.is-frozen {
color:#ecfbff !important;
background:linear-gradient(145deg,rgba(56,189,248,.22),rgba(56,189,248,.075)) !important;
border-color:rgba(125,211,252,.48) !important;
box-shadow:0 0 0 1px rgba(125,211,252,.07) inset,0 0 18px rgba(56,189,248,.18),0 8px 22px rgba(0,0,0,.18) !important;
}
.h-dock-btn.scope-link { color:#effff9 !important; background:linear-gradient(145deg,rgba(52,211,153,.20),rgba(52,211,153,.065)) !important; border-color:rgba(110,231,183,.44) !important; box-shadow:0 0 0 1px rgba(110,231,183,.06) inset,0 0 18px rgba(52,211,153,.15),0 8px 22px rgba(0,0,0,.18) !important; }
.h-dock-btn.scope-global,.h-dock-btn.is-frozen { color:#f5f0ff !important; background:linear-gradient(145deg,rgba(167,139,250,.20),rgba(167,139,250,.065)) !important; border-color:rgba(196,181,253,.44) !important; box-shadow:0 0 0 1px rgba(196,181,253,.06) inset,0 0 18px rgba(167,139,250,.15),0 8px 22px rgba(0,0,0,.18) !important; }
.h-dock-btn.active::after,
.h-dock-btn.scope-link::after,
.h-dock-btn.scope-global::after,
.h-dock-btn.is-frozen::after {
content:""; position:absolute; width:4px; height:4px; border-radius:50%; right:5px; top:5px;
background:#a5f3fc; box-shadow:0 0 8px #67e8f9; pointer-events:none;
}
.h-dock-btn.scope-link::after { background:#86efac; box-shadow:0 0 8px #4ade80; }
.h-dock-btn.scope-global::after,.h-dock-btn.is-frozen::after { background:#ddd6fe; box-shadow:0 0 8px #a78bfa; }
/* Command menu toggle cards */
.hx-toggle-row {
position:relative !important;
min-height:36px !important;
padding:7px 9px 7px 8px !important;
margin:0 !important;
gap:9px !important;
border:1px solid rgba(148,163,184,.09) !important;
border-radius:11px !important;
background:linear-gradient(135deg,rgba(255,255,255,.035),rgba(255,255,255,.014)) !important;
transition:background .18s ease,border-color .18s ease,box-shadow .18s ease,transform .15s ease !important;
}
.hx-toggle-row:hover { background:rgba(125,211,252,.045) !important; border-color:rgba(125,211,252,.17) !important; transform:translateX(1px) !important; }
.hx-toggle-row:has(.hx-check:checked) {
background:linear-gradient(100deg,rgba(56,189,248,.095),rgba(56,189,248,.025)) !important;
border-color:rgba(56,189,248,.24) !important;
box-shadow:0 0 0 1px rgba(56,189,248,.025) inset,0 7px 20px rgba(0,0,0,.12) !important;
color:#e8f8ff !important;
}
.hx-toggle-child { margin-left:5px !important; border-left:1px solid rgba(56,189,248,.16) !important; }
.hx-check {
appearance:none !important; -webkit-appearance:none !important;
position:relative !important; flex:0 0 31px !important; width:31px !important; height:18px !important;
margin:0 !important; border-radius:999px !important; cursor:pointer !important;
background:rgba(71,85,105,.46) !important; border:1px solid rgba(148,163,184,.23) !important;
box-shadow:inset 0 2px 4px rgba(0,0,0,.24),0 1px 0 rgba(255,255,255,.035) !important;
transition:background .18s ease,border-color .18s ease,box-shadow .18s ease !important;
}
.hx-check::after {
content:"" !important; position:absolute !important; top:3px !important; left:3px !important;
width:10px !important; height:10px !important; border-radius:50% !important;
background:#aab8c8 !important; box-shadow:0 1px 4px rgba(0,0,0,.35) !important;
transition:transform .20s cubic-bezier(.16,1,.3,1),background .18s ease,box-shadow .18s ease !important;
}
.hx-check:checked {
background:linear-gradient(90deg,#0284c7,#38bdf8) !important;
border-color:rgba(125,211,252,.66) !important;
box-shadow:0 0 14px rgba(56,189,248,.22),inset 0 1px 1px rgba(255,255,255,.22) !important;
}
.hx-check:checked::after {
transform:translateX(13px) !important; background:#f5fdff !important;
box-shadow:0 0 9px rgba(224,242,254,.9),0 1px 4px rgba(0,0,0,.25) !important;
}
#chk-auto-time-skipper:checked,#chk-anti-paywall:checked,#chk-auto-close-modals:checked,#chk-auto-close-logins:checked {
background:linear-gradient(90deg,#b45309,#fbbf24) !important; border-color:rgba(251,191,36,.62) !important; box-shadow:0 0 14px rgba(251,191,36,.20),inset 0 1px 1px rgba(255,255,255,.20) !important;
}
.hx-toggle-row:has(#chk-auto-time-skipper:checked),.hx-toggle-row:has(#chk-anti-paywall:checked),.hx-toggle-row:has(#chk-auto-close-modals:checked),.hx-toggle-row:has(#chk-auto-close-logins:checked) { background:linear-gradient(100deg,rgba(245,158,11,.085),rgba(245,158,11,.018)) !important; border-color:rgba(251,191,36,.20) !important; }
.hx-toggle-row:focus-within { border-color:rgba(125,211,252,.28) !important; box-shadow:0 0 0 3px rgba(56,189,248,.045) !important; }
/* Make section labels feel like compact instrument readouts. */
#tab-tools .panel-section-label { color:#718096 !important; letter-spacing:1.1px !important; }
#tab-tools select { min-height:29px !important; border-radius:9px !important; }
#dock-buttons-list { padding:4px !important; border:1px solid rgba(148,163,184,.07) !important; border-radius:11px !important; background:rgba(0,0,0,.12) !important; }
#dock-buttons-list label { min-height:30px !important; border-radius:8px !important; padding:5px 7px !important; background:rgba(255,255,255,.025) !important; border:1px solid rgba(148,163,184,.07) !important; }
#dock-buttons-list button:hover { transform:translateY(-1px) !important; }
#dock-buttons-list button:active { transform:translateY(1px) scale(.985) !important; }
#cookie-new-editor[hidden] { display:none !important; }
#cookie-new-editor:not([hidden]) { animation:hxPanelIn .2s cubic-bezier(.2,.75,.2,1) both !important; }
@media (max-width:600px) { .hx-toggle-row { min-height:38px !important; padding:8px !important; } .hx-check { flex-basis:33px !important; width:33px !important; } }
`;
const container = doc.createElement('div');
container.innerHTML = `
<div class="h-glass h-dock is-collapsed manual-hidden" id="hider-main-dock">
<button class="h-dock-btn h-dock-main" id="btn-toggle-dock" title="Toggle Control Dock">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%" style="display:block;">
<defs>
<radialGradient id="bgGradient" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#1e293b" />
<stop offset="60%" stop-color="#0d121e" />
<stop offset="100%" stop-color="#050816" />
</radialGradient>
<linearGradient id="neonCyan" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#7dd3fc" />
<stop offset="50%" stop-color="#38bdf8" />
<stop offset="100%" stop-color="#0284c7" />
</linearGradient>
<linearGradient id="proGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#fbbf24" />
<stop offset="100%" stop-color="#f59e0b" />
</linearGradient>
<linearGradient id="glassBorder" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38bdf8" stop-opacity="0.9"/>
<stop offset="50%" stop-color="#1e293b" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#38bdf8" stop-opacity="0.7"/>
</linearGradient>
<filter id="neonGlow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="8" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="proGlow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<circle cx="256" cy="256" r="230" fill="url(#bgGradient)" stroke="url(#glassBorder)" stroke-width="6" filter="url(#neonGlow)"/>
<g stroke="#38bdf8" stroke-opacity="0.12" stroke-width="2">
<line x1="126" y1="180" x2="386" y2="180"/>
<line x1="126" y1="256" x2="386" y2="256"/>
<line x1="126" y1="332" x2="386" y2="332"/>
<line x1="180" y1="126" x2="180" y2="386"/>
<line x1="256" y1="126" x2="256" y2="386"/>
<line x1="332" y1="126" x2="332" y2="386"/>
</g>
<circle cx="256" cy="256" r="145" fill="none" stroke="#38bdf8" stroke-width="2.5" stroke-dasharray="8 8" opacity="0.45" />
<g filter="url(#neonGlow)">
<path d="M 136 256 C 180 176, 332 176, 376 256 C 332 336, 180 336, 136 256 Z"
fill="none" stroke="url(#neonCyan)" stroke-width="12" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="256" cy="256" r="46" fill="#0d121e" stroke="url(#neonCyan)" stroke-width="8"/>
<circle cx="256" cy="256" r="18" fill="#e0f2fe"/>
<line x1="150" y1="362" x2="362" y2="150" stroke="#f8fafc" stroke-width="12" stroke-linecap="round"/>
<line x1="150" y1="362" x2="362" y2="150" stroke="#38bdf8" stroke-width="6" stroke-linecap="round"/>
</g>
<g stroke="#e0f2fe" stroke-width="2.5" opacity="0.8">
<path d="M 360 130 L 360 154 M 348 142 L 372 142 M 351 133 L 369 151 M 351 151 L 369 133" />
</g>
<g transform="translate(85, 335)" filter="url(#proGlow)">
<rect x="0" y="0" width="112" height="50" rx="14" fill="#0d121e" stroke="url(#proGradient)" stroke-width="3.5"/>
<text x="56" y="34" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="25" fill="url(#proGradient)" text-anchor="middle" letter-spacing="3.5">PRO</text>
</g>
</svg>
</button>
<div class="h-dock-menu" id="hider-dock-menu">
<button class="h-dock-btn" id="btn-select" title="Target Element to Hide">🎯<span>Hide</span></button>
<button class="h-dock-btn" id="btn-scope" title="Toggle Scope (Site/Link/Global)">🌐<span>SITE</span></button>
<button class="h-dock-btn" id="btn-reveal-quick" title="Reveal hidden/blurred elements">👁️<span>Reveal</span></button>
<button class="h-dock-btn" id="btn-links" title="Extract all links from the page">🔗<span>Links</span></button>
<button class="h-dock-btn" id="btn-skip-30" title="Skip 30 seconds forward">⏩<span>+30s</span></button>
<button class="h-dock-btn ${isFrozen?'is-frozen':''}" id="btn-freeze" title="Toggle Navigation Freeze">❄️<span>Freeze</span></button>
<button class="h-dock-btn" id="btn-manage" title="Open Control Panel">⚙️<span>Control</span></button>
</div>
</div>
<div id="hider-panel" class="h-glass">
<div class="panel-header">
<div class="panel-brand">
<div class="panel-brand-mark hx-brand-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
<defs>
<radialGradient id="aboutBgGradient" cx="50%" cy="50%" r="50%"><stop offset="0%" stop-color="#1e293b"/><stop offset="60%" stop-color="#0d121e"/><stop offset="100%" stop-color="#050816"/></radialGradient>
<linearGradient id="aboutNeonCyan" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#7dd3fc"/><stop offset="50%" stop-color="#38bdf8"/><stop offset="100%" stop-color="#0284c7"/></linearGradient>
<linearGradient id="aboutProGradient" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#fbbf24"/><stop offset="100%" stop-color="#f59e0b"/></linearGradient>
<linearGradient id="aboutGlassBorder" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#38bdf8" stop-opacity="0.9"/><stop offset="50%" stop-color="#1e293b" stop-opacity="0.3"/><stop offset="100%" stop-color="#38bdf8" stop-opacity="0.7"/></linearGradient>
<filter id="aboutNeonGlow" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="8" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="aboutProGlow" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="4" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="256" cy="256" r="230" fill="url(#aboutBgGradient)" stroke="url(#aboutGlassBorder)" stroke-width="6" filter="url(#aboutNeonGlow)"/>
<g stroke="#38bdf8" stroke-opacity="0.12" stroke-width="2"><line x1="126" y1="180" x2="386" y2="180"/><line x1="126" y1="256" x2="386" y2="256"/><line x1="126" y1="332" x2="386" y2="332"/><line x1="180" y1="126" x2="180" y2="386"/><line x1="256" y1="126" x2="256" y2="386"/><line x1="332" y1="126" x2="332" y2="386"/></g>
<circle cx="256" cy="256" r="145" fill="none" stroke="#38bdf8" stroke-width="2.5" stroke-dasharray="8 8" opacity="0.45"/>
<g filter="url(#aboutNeonGlow)">
<path d="M 136 256 C 180 176, 332 176, 376 256 C 332 336, 180 336, 136 256 Z" fill="none" stroke="url(#aboutNeonCyan)" stroke-width="12" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="256" cy="256" r="46" fill="#0d121e" stroke="url(#aboutNeonCyan)" stroke-width="8"/><circle cx="256" cy="256" r="18" fill="#e0f2fe"/>
<line x1="150" y1="362" x2="362" y2="150" stroke="#f8fafc" stroke-width="12" stroke-linecap="round"/><line x1="150" y1="362" x2="362" y2="150" stroke="#38bdf8" stroke-width="6" stroke-linecap="round"/>
</g>
<g stroke="#e0f2fe" stroke-width="2.5" opacity="0.8"><path d="M 360 130 L 360 154 M 348 142 L 372 142 M 351 133 L 369 151 M 351 151 L 369 133"/></g>
<g transform="translate(85, 335)" filter="url(#aboutProGlow)"><rect x="0" y="0" width="112" height="50" rx="14" fill="#0d121e" stroke="url(#aboutProGradient)" stroke-width="3.5"/><text x="56" y="34" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="25" fill="url(#aboutProGradient)" text-anchor="middle" letter-spacing="3.5">PRO</text></g>
</svg></div>
<div class="panel-brand-copy">
<div class="panel-brand-title">Hide Web Elements <span style="color:#fbbf24;">PRO</span><span class="panel-status">ONLINE</span></div>
<div class="panel-brand-sub">Web control command center · v15.5</div>
</div>
</div>
<button class="close-btn" id="close-p" title="Close control center">✕</button>
</div>
<div class="panel-body">
<div class="panel-sidebar" id="panel-sidebar"></div>
<div class="panel-content" id="panel-content"></div>
</div>
</div>`;
// ===================== 15.4 FUTURE GLASS SYSTEM =====================
// Visual-only layer. Existing IDs, event handlers and feature logic remain unchanged.
style.textContent += `
:host { color-scheme: dark !important; }
*, *::before, *::after { -webkit-tap-highlight-color: transparent !important; }
.h-glass {
background:
radial-gradient(circle at 0% 0%, rgba(56,189,248,.12), transparent 32%),
radial-gradient(circle at 100% 100%, rgba(139,92,246,.11), transparent 34%),
linear-gradient(145deg, rgba(5,10,20,.96), rgba(11,19,35,.92) 52%, rgba(5,8,18,.97)) !important;
border: 1px solid rgba(125,211,252,.19) !important;
box-shadow: 0 28px 80px rgba(0,0,0,.55), 0 0 45px rgba(56,189,248,.055), inset 0 1px 0 rgba(255,255,255,.045) !important;
isolation: isolate !important;
}
.h-glass::before {
content:"" !important; position:absolute !important; inset:0 !important; pointer-events:none !important; border-radius:inherit !important;
background:linear-gradient(120deg,rgba(255,255,255,.065),transparent 16%,transparent 75%,rgba(56,189,248,.025)) !important;
mix-blend-mode:screen !important; opacity:.8 !important;
}
/* COMMAND CENTER */
#hider-panel {
width:min(680px,95vw) !important;
max-height:min(820px,91vh) !important;
border-radius:24px !important;
transform-origin: top right !important;
transition:opacity .22s ease, transform .36s cubic-bezier(.16,1,.3,1), filter .22s ease !important;
overflow:hidden !important;
}
#hider-panel.is-visible { animation:hiderCommandIn .38s cubic-bezier(.16,1,.3,1) both !important; }
@keyframes hiderCommandIn {
from { opacity:0; transform:translate3d(0,-12px,0) scale(.965); filter:blur(7px); }
to { opacity:1; transform:translate3d(0,0,0) scale(1); filter:blur(0); }
}
#hider-panel::after {
content:""; position:absolute; top:0; left:-20%; width:40%; height:1px; pointer-events:none; z-index:20;
background:linear-gradient(90deg,transparent,#38bdf8,#c4b5fd,transparent); box-shadow:0 0 18px rgba(56,189,248,.65);
animation:hiderSweep 5.8s ease-in-out infinite;
}
@keyframes hiderSweep { 0%,70%{transform:translateX(-30%);opacity:0} 76%{opacity:1} 95%,100%{transform:translateX(360%);opacity:0} }
.panel-header {
min-height:70px !important; padding:11px 13px !important; position:relative !important; overflow:hidden !important;
background:
radial-gradient(circle at 16% 50%,rgba(56,189,248,.14),transparent 22%),
radial-gradient(circle at 82% 0%,rgba(167,139,250,.09),transparent 28%),
linear-gradient(90deg,rgba(56,189,248,.06),rgba(255,255,255,.02) 48%,rgba(139,92,246,.06)) !important;
border-bottom:1px solid rgba(125,211,252,.10) !important;
}
.panel-header::before {
content:""; position:absolute; right:-40px; top:-55px; width:150px; height:150px; border-radius:50%; pointer-events:none;
background:radial-gradient(circle,rgba(56,189,248,.16),transparent 68%); filter:blur(2px);
}
.panel-header::after {
content:""; position:absolute; left:14px; right:14px; bottom:0; height:1px; pointer-events:none;
background:linear-gradient(90deg,rgba(56,189,248,.72),rgba(167,139,250,.42),transparent 75%); opacity:.7;
}
.panel-brand { display:flex !important; align-items:center !important; gap:10px !important; min-width:0 !important; position:relative !important; z-index:2 !important; }
.panel-brand-mark {
width:36px !important; height:36px !important; flex:0 0 36px !important; display:grid !important; place-items:center !important; border-radius:12px !important;
background:radial-gradient(circle at 30% 22%,rgba(125,211,252,.30),transparent 38%),linear-gradient(145deg,#0ea5e9,#2563eb 55%,#7c3aed) !important;
border:1px solid rgba(125,211,252,.32) !important; box-shadow:0 0 24px rgba(14,165,233,.25),inset 0 1px 0 rgba(255,255,255,.22) !important;
animation:hiderBrandGlow 4s ease-in-out infinite !important;
}
@keyframes hiderBrandGlow { 0%,100%{box-shadow:0 0 20px rgba(14,165,233,.20),inset 0 1px 0 rgba(255,255,255,.18)} 50%{box-shadow:0 0 34px rgba(56,189,248,.34),inset 0 1px 0 rgba(255,255,255,.25)} }
.panel-brand-copy { min-width:0 !important; }
.panel-brand-title { font-size:14px !important; font-weight:950 !important; letter-spacing:.1px !important; }
.panel-brand-sub { margin-top:3px !important; font-size:8px !important; letter-spacing:1.05px !important; text-transform:uppercase !important; font-weight:850 !important; color:#7dd3fc !important; }
.panel-status {
display:inline-flex !important; align-items:center !important; gap:5px !important; margin-left:7px !important; padding:3px 7px !important; border-radius:999px !important;
font-size:7px !important; letter-spacing:.75px !important; text-transform:uppercase !important; font-weight:900 !important;
color:#86efac !important; background:rgba(34,197,94,.07) !important; border:1px solid rgba(134,239,172,.15) !important; vertical-align:middle !important;
}
.panel-status::before { content:""; width:5px; height:5px; border-radius:50%; background:#4ade80; box-shadow:0 0 10px #4ade80; animation:hiderLivePulse 1.7s ease-in-out infinite; }
@keyframes hiderLivePulse { 0%,100%{opacity:.5;transform:scale(.9)} 50%{opacity:1;transform:scale(1.12)} }
.panel-header .close-btn {
position:relative !important; z-index:3 !important; width:30px !important; height:30px !important; padding:0 !important; border-radius:10px !important;
background:rgba(255,255,255,.035) !important; border:1px solid rgba(255,255,255,.09) !important; color:#94a3b8 !important;
transition:transform .22s cubic-bezier(.16,1,.3,1), background .18s ease, border-color .18s ease, color .18s ease !important;
}
.panel-header .close-btn:hover { transform:rotate(90deg) scale(1.08) !important; background:rgba(244,63,94,.12) !important; border-color:rgba(244,63,94,.28) !important; color:#fda4af !important; }
/* TAB RAIL */
.panel-sidebar {
flex:0 0 138px !important; padding:11px 8px !important; gap:5px !important;
background:linear-gradient(180deg,rgba(2,6,23,.80),rgba(15,23,42,.46)) !important;
border-right:1px solid rgba(125,211,252,.085) !important;
}
.panel-sidebar .tab-btn {
position:relative !important; margin:0 !important; padding:10px 10px 10px 14px !important; border-radius:12px !important;
color:#718096 !important; font-size:9px !important; font-weight:850 !important; letter-spacing:.15px !important;
transition:transform .22s cubic-bezier(.16,1,.3,1), background .22s ease, color .22s ease, box-shadow .22s ease !important;
}
.panel-sidebar .tab-btn::before { content:""; position:absolute; left:4px; top:50%; width:3px; height:0; transform:translateY(-50%); border-radius:999px; background:#38bdf8; box-shadow:0 0 14px rgba(56,189,248,.9); transition:height .22s ease !important; }
.panel-sidebar .tab-btn:hover { transform:translateX(3px) !important; background:rgba(255,255,255,.045) !important; color:#e2e8f0 !important; }
.panel-sidebar .tab-btn.active {
transform:translateX(4px) !important; color:#e0f2fe !important;
background:linear-gradient(100deg,rgba(56,189,248,.13),rgba(56,189,248,.035)) !important;
border:1px solid rgba(56,189,248,.09) !important; box-shadow:inset 0 0 0 1px rgba(255,255,255,.015),0 9px 24px rgba(0,0,0,.16) !important;
}
.panel-sidebar .tab-btn.active::before { height:24px !important; }
.panel-content { padding:12px 14px 16px !important; gap:10px !important; scrollbar-gutter:stable !important; }
.panel-content .tab-content.active { animation:hiderTabRise .30s cubic-bezier(.16,1,.3,1) both !important; }
@keyframes hiderTabRise { from{opacity:0;transform:translateY(7px)} to{opacity:1;transform:translateY(0)} }
/* CARDS / CONTROLS */
.hider-btn-small, .h-btn-icon, .h-btn-pill, .h-dock-btn, .h-custom-trigger { transition:transform .16s ease, box-shadow .18s ease, filter .18s ease, border-color .18s ease, background .18s ease !important; }
.hider-btn-small:hover, .h-btn-icon:hover, .h-btn-pill:hover { transform:translateY(-1px) !important; filter:brightness(1.08) saturate(1.08) !important; box-shadow:0 8px 20px rgba(0,0,0,.20) !important; }
.hider-btn-small:active, .h-btn-icon:active, .h-btn-pill:active, .h-dock-btn:active { transform:translateY(1px) scale(.965) !important; }
.hider-btn-small { border-radius:9px !important; font-weight:850 !important; box-shadow:inset 0 1px 0 rgba(255,255,255,.12) !important; }
input[type="text"],input[type="number"],input[type="date"],textarea,select,.h-select,.h-custom-trigger { background:rgba(2,6,23,.42) !important; border:1px solid rgba(148,163,184,.15) !important; box-shadow:inset 0 1px 0 rgba(255,255,255,.035) !important; }
input[type="text"]:focus,input[type="number"]:focus,input[type="date"]:focus,textarea:focus,select:focus,.h-select:focus,.h-custom-trigger:focus { outline:none !important; border-color:rgba(56,189,248,.55) !important; box-shadow:0 0 0 3px rgba(56,189,248,.07),0 0 22px rgba(56,189,248,.08) !important; }
/* FLOATING DOCK */
.h-dock {
padding:7px !important; gap:7px !important; border-radius:24px !important;
background:linear-gradient(180deg,rgba(3,9,19,.80),rgba(15,23,42,.68)) !important;
border:1px solid rgba(125,211,252,.18) !important; backdrop-filter:blur(20px) !important; -webkit-backdrop-filter:blur(20px) !important;
box-shadow:0 22px 55px rgba(0,0,0,.48),0 0 28px rgba(56,189,248,.075),inset 0 1px 0 rgba(255,255,255,.05) !important;
transition:transform .42s cubic-bezier(.16,1,.3,1),opacity .28s ease,box-shadow .28s ease !important;
}
.h-dock.expanded { box-shadow:0 26px 65px rgba(0,0,0,.54),0 0 40px rgba(56,189,248,.14),inset 0 1px 0 rgba(255,255,255,.07) !important; }
.h-dock-main {
width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; border-radius:15px !important;
background:radial-gradient(circle at 31% 24%,rgba(125,211,252,.30),transparent 36%),linear-gradient(145deg,rgba(14,165,233,.78),rgba(37,99,235,.76) 55%,rgba(124,58,237,.76)) !important;
border:1px solid rgba(125,211,252,.34) !important; box-shadow:0 0 24px rgba(14,165,233,.20),inset 0 1px 0 rgba(255,255,255,.22) !important;
}
.h-dock-main:hover { transform:scale(1.07) !important; box-shadow:0 0 36px rgba(56,189,248,.32),inset 0 1px 0 rgba(255,255,255,.24) !important; }
.h-dock-main.expanded { box-shadow:0 0 42px rgba(56,189,248,.35),0 0 76px rgba(124,58,237,.12) !important; }
.h-dock-menu { gap:6px !important; padding-top:2px !important; transition:max-height .42s cubic-bezier(.16,1,.3,1),opacity .22s ease !important; }
.h-dock-btn { width:39px !important; height:39px !important; min-width:39px !important; min-height:39px !important; border-radius:13px !important; background:rgba(255,255,255,.035) !important; border:1px solid rgba(255,255,255,.08) !important; box-shadow:inset 0 1px 0 rgba(255,255,255,.035) !important; }
.h-dock-btn:hover { background:rgba(255,255,255,.085) !important; color:#fff !important; transform:translateY(-2px) scale(1.045) !important; box-shadow:0 10px 20px rgba(0,0,0,.22),0 0 16px rgba(56,189,248,.08) !important; }
.h-dock-btn span { font-size:7px !important; letter-spacing:.6px !important; }
/* COOKIE WORKSPACE */
#cookie-count { display:inline-flex !important; min-width:20px !important; height:18px !important; align-items:center !important; justify-content:center !important; padding:0 6px !important; border-radius:999px !important; background:rgba(56,189,248,.10) !important; color:#7dd3fc !important; border:1px solid rgba(56,189,248,.14) !important; }
#cookie-search { width:min(190px,46%) !important; height:30px !important; border-radius:10px !important; }
#cookie-new-name,#cookie-new-value,#cookie-new-expiry { height:30px !important; border-radius:10px !important; }
#cookie-list { scrollbar-width:thin !important; }
#cookie-list > div { transition:transform .18s ease,background .18s ease,border-color .18s ease,box-shadow .18s ease !important; }
#cookie-list > div:hover { transform:translateX(2px) !important; border-color:rgba(56,189,248,.18) !important; background:linear-gradient(90deg,rgba(56,189,248,.065),rgba(255,255,255,.03)) !important; box-shadow:0 7px 20px rgba(0,0,0,.14) !important; }
/* RESPONSIVE / REDUCED MOTION */
@media (max-width:560px) {
#hider-panel { left:6px !important; right:6px !important; top:44px !important; width:auto !important; max-height:92vh !important; border-radius:21px !important; }
.panel-sidebar { flex:0 0 100px !important; padding:9px 6px !important; }
.panel-sidebar .tab-btn { padding:9px 7px 9px 10px !important; font-size:8px !important; }
.panel-content { padding:10px !important; }
.panel-header { min-height:60px !important; }
.panel-brand-title { font-size:12px !important; }
.panel-brand-sub { font-size:7px !important; }
.panel-status { display:none !important; }
.h-dock { right:5px !important; }
.h-dock-main { width:41px !important; height:41px !important; min-width:41px !important; min-height:41px !important; }
.h-dock-btn { width:37px !important; height:37px !important; min-width:37px !important; min-height:37px !important; }
}
@media (prefers-reduced-motion:reduce) {
*,*::before,*::after { animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; }
}
`;
shadowRoot.innerHTML = ''; shadowRoot.appendChild(style); shadowRoot.appendChild(container);
buildPanelTabs();
setupShadowUIEvents();
applyDockButtonVisibility();
renderDockButtonOptions();
const panel = shadowRoot.getElementById('hider-panel');
if (panel) {
const stopProp = (e) => e.stopPropagation();
panel.addEventListener('wheel', stopProp, { passive: true, capture: true });
panel.addEventListener('touchmove', stopProp, { passive: true, capture: true });
panel.addEventListener('scroll', stopProp, { passive: true, capture: true });
panel.addEventListener('pointerdown', stopProp, { capture: true });
panel.addEventListener('mousedown', stopProp, { capture: true });
}
const linkPanel = shadowRoot.getElementById('hider-link-panel');
if (linkPanel) {
const stopProp = (e) => e.stopPropagation();
linkPanel.addEventListener('wheel', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('touchmove', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('scroll', stopProp, { passive: true, capture: true });
linkPanel.addEventListener('pointerdown', stopProp, { capture: true });
linkPanel.addEventListener('mousedown', stopProp, { capture: true });
}
const stepper = shadowRoot.getElementById(STEPPER_BAR_ID);
if (stepper) {
const stopProp = (e) => e.stopPropagation();
stepper.addEventListener('wheel', stopProp, { passive: true, capture: true });
stepper.addEventListener('touchmove', stopProp, { passive: true, capture: true });
stepper.addEventListener('pointerdown', stopProp, { capture: true });
}
}
// ---------- Build Panel Tabs ----------
function buildPanelTabs() {
const sidebar = shadowBy('panel-sidebar');
const content = shadowBy('panel-content');
if (!sidebar || !content) return;
const tabs = [
{ id: 'tab-tools', label: '◈ Command', html: `
<div style="display:flex;flex-direction:column;gap:6px;">
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;">Basic</div>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input class="hx-check" type="checkbox" id="chk-auto-scroll" ${CACHE.autoScroll?'checked':''}> Auto Anti‑Scroll Lock
</label>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input class="hx-check" type="checkbox" id="chk-enable-contextmenu" ${CACHE.enableContextMenu?'checked':''}> Auto Right‑Click / Long‑Press
</label>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input class="hx-check" type="checkbox" id="chk-auto-remove-blur" ${CACHE.autoRemoveBlur?'checked':''}> Auto Remove Blur
</label>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;margin-top:2px;">
<input class="hx-check" type="checkbox" id="chk-auto-time-skipper" ${CACHE.autoTimeSkipper?'checked':''}> Auto Time Skipper (Smart)
<div class="hx-feature-note"><b>SMART:</b> targets detected countdowns + ad skip controls without scanning the whole page every second.</div>
</label>
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;margin-top:6px;border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">Overlays & Paywalls</div>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input class="hx-check" type="checkbox" id="chk-anti-paywall" ${CACHE.antiPaywall?'checked':''}> Anti‑Paywall / Anti‑Adblock
</label>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;">
<input class="hx-check" type="checkbox" id="chk-auto-close-modals" ${CACHE.autoCloseModals?'checked':''}> Auto‑Close Modals & Overlays
</label>
<label class="hx-toggle-row" style="font-size:10px;color:#cbd5e1;display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;padding-left:16px;border-left:2px solid rgba(56,189,248,0.3);">
<input class="hx-check" type="checkbox" id="chk-auto-close-logins" ${CACHE.autoCloseLogins?'checked':''}> Auto‑Close Logins
</label>
<div style="display:flex;align-items:center;gap:6px;margin-top:2px;">
<span style="font-size:10px;color:#94a3b8;">🍪 Cookie Consent:</span>
<select id="cookie-consent-mode" style="background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.14);border-radius:6px;color:#fff;padding:2px 6px;font-size:9px;height:22px;flex:1;">
<option value="ask" ${CACHE.cookieConsentMode === 'ask' ? 'selected' : ''}>Ask</option>
<option value="accept" ${CACHE.cookieConsentMode === 'accept' ? 'selected' : ''}>Accept</option>
<option value="reject" ${CACHE.cookieConsentMode === 'reject' ? 'selected' : ''}>Reject</option>
</select>
</div>
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;margin-top:6px;border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">Dock Buttons</div>
<div style="display:flex;align-items:center;justify-content:space-between;gap:6px;margin-bottom:1px;">
<span id="dock-visible-count" style="font-size:8px;color:#6ee7b7;font-weight:900;letter-spacing:.5px;">—</span>
<div style="display:flex;gap:4px;">
<button class="hider-btn-small btn-blue" id="dock-show-all" style="height:24px;padding:0 8px;">Show All</button>
<button class="hider-btn-small btn-gray" id="dock-hide-optional" style="height:24px;padding:0 8px;">Hide Optional</button>
</div>
</div>
<div style="font-size:8px;color:#64748b;margin-bottom:4px;">Tap a card to toggle whether that control stays on the floating dock. Control/Menu remains available.</div>
<div id="dock-buttons-list" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:6px;"></div>
</div>
` },
{ id: 'tab-rules', label: '⌁ Rules', html: `
<div style="display:flex;flex-direction:column;gap:4px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px;">
<span style="font-size:10px;color:#94a3b8;font-weight:700;">Custom Rules (<span id="cnt-custom">0</span>)</span>
<button class="hider-btn-small btn-blue" id="add-rule-btn" style="height:24px;padding:0 8px;">➕ Add Rule</button>
</div>
<div id="list-custom-rules" style="display:flex;flex-direction:column;gap:3px;"></div>
<div style="border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;margin-top:4px;">
<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8;">
<span>🌐 Site‑Wide (<span id="cnt-site">0</span>)</span>
<button class="hider-btn-small btn-red" id="clear-site-rules" style="font-size:8px;padding:1px 5px;height:18px;">Clear All</button>
</div>
<div id="list-site" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
<div style="border-top:1px solid rgba(255,255,255,0.06);padding-top:4px;">
<div style="display:flex;justify-content:space-between;font-size:10px;color:#94a3b8;">
<span>📄 Page‑Only (<span id="cnt-link">0</span>)</span>
<button class="hider-btn-small btn-red" id="clear-page-rules" style="font-size:8px;padding:1px 5px;height:18px;">Clear All</button>
</div>
<div id="list-link" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
<div style="margin-top:4px;text-align:right;">
<button class="hider-btn-small btn-red" id="clear-custom-rules" style="font-size:8px;padding:1px 5px;height:18px;">Clear All Custom Rules</button>
</div>
</div>
` },
{ id: 'tab-freeze', label: '◒ Freeze', html: `
<div style="display:flex;flex-direction:column;gap:6px;">
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;">Freeze Behaviour</div>
<div class="h-custom-select" id="freeze-custom-panel-dropdown">
<div class="h-custom-trigger"><span class="h-custom-value-text">${FREEZE_LABELS[CACHE.freezeMemory] || FREEZE_LABELS['ask']}</span><span class="h-custom-arrow">▼</span></div>
<div class="h-custom-options">
<div class="h-custom-opt" data-val="ask">❓ Ask Every Time</div>
<div class="h-custom-opt" data-val="block_all">⛔ Auto‑Block All</div>
<div class="h-custom-opt" data-val="allow_same">🔗 Allow Same Domain Only</div>
<div class="h-custom-opt" data-val="allow_all">🟢 Allow All</div>
</div>
</div>
<button id="btn-reset-block-confirm" class="hider-btn-small" style="background:rgba(255,255,255,0.06);color:#cbd5e1;width:100%;padding:4px;height:auto;font-size:9px;">Reset "Don't Ask Block" Prompt</button>
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;margin-top:8px;border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">🚫 Blocked Domains</div>
<div style="display:flex;gap:4px;margin-bottom:4px;">
<input type="text" id="manual-domain-input" placeholder="Block domain (e.g. bad.com)" class="h-select" style="flex:1;">
<button class="hider-btn-small btn-red" id="add-domain-btn" style="height:28px;padding:0 8px;">Block</button>
</div>
<div id="list-blocked-domains" style="display:flex;flex-direction:column;gap:3px;"></div>
<div style="font-size:9px;color:#94a3b8;text-transform:uppercase;letter-spacing:0.5px;font-weight:700;margin-top:8px;border-top:1px solid rgba(255,255,255,0.06);padding-top:6px;">🟢 Allowed Domains</div>
<div style="display:flex;gap:4px;margin-bottom:4px;">
<input type="text" id="manual-allowed-domain-input" placeholder="Allow domain (e.g. trusted.com)" class="h-select" style="flex:1;">
<button class="hider-btn-small btn-green" id="add-allowed-domain-btn" style="height:28px;padding:0 8px;">Allow</button>
</div>
<div id="list-allowed-domains" style="display:flex;flex-direction:column;gap:3px;"></div>
</div>
` },
{ id: 'tab-logs', label: '◌ Logs', html: `
<div style="display:flex;justify-content:space-between;align-items:center;">
<span style="font-size:10px;color:#94a3b8;">Blocked (<span id="cnt-logs">0</span>)</span>
<button class="hider-btn-small btn-gray" id="clear-log-btn" style="font-size:8px;padding:1px 5px;height:18px;">Clear</button>
</div>
<div id="list-blocked-log" style="display:flex;flex-direction:column;gap:3px;margin-top:4px;"></div>
` },
{ id: 'tab-export', label: '⇅ Backup', html: `
<div style="display:flex;gap:4px;flex-wrap:wrap;align-items:center;margin-bottom:8px;">
<button class="hider-btn-small btn-blue" id="btn-export-settings" style="flex:1;min-width:70px;height:26px;">📤 Export All</button>
<button class="hider-btn-small btn-green" id="btn-import-settings" style="flex:1;min-width:70px;height:26px;">📥 Import All</button>
<input type="file" id="import-file-input" accept=".json" style="display:none;">
</div>
<div style="font-size:9px;color:#94a3b8;margin-bottom:8px;">Export/Import all settings (rules, domains, toggles, logs).</div>
` },
{ id: 'tab-cookies', label: '◉ Cookies', html: `
<div style="display:flex;flex-direction:column;gap:8px;">
<div class="hx-card" style="padding:12px;background:linear-gradient(145deg,rgba(56,189,248,.075),rgba(8,15,28,.68) 60%,rgba(139,92,246,.055));">
<div style="display:flex;justify-content:space-between;align-items:flex-start;gap:8px;">
<div>
<div style="font-size:14px;font-weight:900;color:#f8fafc;">Cookie Workspace</div>
<div style="font-size:9px;color:#64748b;margin-top:2px;">Inspect · edit · create · remove · backup</div>
</div>
<span class="hx-chip" id="cookie-api-status">Detecting…</span>
</div>
<div style="display:flex;gap:5px;flex-wrap:wrap;margin-top:8px;">
<span class="hx-chip">🌐 <span id="cookie-host-label"></span></span>
<span class="hx-chip">🍪 <b id="cookie-count">0</b> visible</span>
<span class="hx-chip" id="cookie-http-only-note">HttpOnly: browser-protected</span>
</div>
</div>
<div style="display:flex;gap:5px;align-items:center;">
<input class="hx-input" type="search" id="cookie-search" placeholder="Search cookie name or value…" style="height:30px;padding:0 9px;font-size:9px;flex:1;">
<button class="hider-btn-small btn-blue" id="cookie-refresh" style="height:30px;min-width:32px;padding:0 9px;">↻</button>
</div>
<div id="cookie-list" style="display:flex;flex-direction:column;gap:5px;max-height:250px;overflow-y:auto;padding-right:2px;"></div>
<div class="hx-card" style="padding:10px;">
<div style="display:flex;justify-content:space-between;align-items:center;gap:7px;">
<div>
<div style="font-size:10px;font-weight:900;color:#e2e8f0;">➕ New Cookie</div>
<div style="font-size:8px;color:#64748b;margin-top:2px;">Composer stays closed until you choose Add New.</div>
</div>
<button class="hider-btn-small btn-green" id="cookie-add-new" aria-expanded="false" style="height:28px;padding:0 11px;">+ Add New</button>
</div>
<div id="cookie-new-editor" hidden style="margin-top:9px;padding-top:9px;border-top:1px solid rgba(148,163,184,.08);">
<div style="display:grid;grid-template-columns:1fr 1.3fr;gap:5px;">
<input class="hx-input" id="cookie-new-name" placeholder="Name" style="height:27px;padding:0 7px;font-size:9px;">
<input class="hx-input" id="cookie-new-value" placeholder="Value" style="height:27px;padding:0 7px;font-size:9px;">
<input class="hx-input" id="cookie-new-path" value="/" placeholder="Path" style="height:27px;padding:0 7px;font-size:9px;">
<input class="hx-input" id="cookie-new-expiry" type="datetime-local" style="height:27px;padding:0 7px;font-size:9px;">
</div>
<div style="display:flex;gap:7px;flex-wrap:wrap;margin-top:7px;align-items:center;">
<label style="font-size:8px;color:#94a3b8;display:flex;gap:4px;align-items:center;"><input type="checkbox" id="cookie-new-secure"> Secure</label>
<select class="hx-input" id="cookie-new-samesite" style="width:92px;height:25px;padding:0 5px;font-size:8px;"><option value="lax">SameSite=Lax</option><option value="strict">SameSite=Strict</option><option value="none">SameSite=None</option></select>
<button class="hider-btn-small btn-green" id="cookie-add" style="height:27px;padding:0 12px;margin-left:auto;">Create</button>
<button class="hider-btn-small btn-gray" id="cookie-new-cancel" style="height:27px;padding:0 10px;">Cancel</button>
</div>
</div>
</div>
<div style="display:flex;gap:5px;flex-wrap:wrap;">
<button class="hider-btn-small btn-red" id="cookie-clear-all" style="height:26px;padding:0 10px;">Delete Visible</button>
<button class="hider-btn-small btn-blue" id="cookie-export" style="height:26px;padding:0 10px;">Export</button>
<button class="hider-btn-small btn-purple" id="cookie-import" style="height:26px;padding:0 10px;">Import</button>
<input type="file" id="cookie-import-file" accept=".json" style="display:none;">
</div>
<div style="font-size:8px;line-height:1.5;color:#475569;">This manager can only access cookies exposed to page scripts. HttpOnly cookies cannot be read or changed by a userscript. Attribute editing uses the browser Cookie Store API when available and falls back to standard cookie writes.</div>
</div>
` },
{ id: 'tab-about', label: '✦ Core', html: `
<div style="font-family:system-ui,-apple-system,Segoe UI,sans-serif;color:#cbd5e1;font-size:10px;line-height:1.55;padding:2px 0 10px;">
<div style="display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin-bottom:8px;">
<div style="padding:8px 9px;border-radius:11px;background:linear-gradient(145deg,rgba(56,189,248,.08),rgba(255,255,255,.025));border:1px solid rgba(56,189,248,.10);"><div style="font-size:8px;color:#64748b;text-transform:uppercase;letter-spacing:.7px;font-weight:800;">MODE</div><div style="font-size:10px;color:#7dd3fc;font-weight:900;margin-top:3px;">PRO CONTROL</div></div>
<div style="padding:8px 9px;border-radius:11px;background:linear-gradient(145deg,rgba(167,139,250,.08),rgba(255,255,255,.025));border:1px solid rgba(167,139,250,.10);"><div style="font-size:8px;color:#64748b;text-transform:uppercase;letter-spacing:.7px;font-weight:800;">ENGINE</div><div style="font-size:10px;color:#c4b5fd;font-weight:900;margin-top:3px;">SHADOW READY</div></div>
<div style="padding:8px 9px;border-radius:11px;background:linear-gradient(145deg,rgba(52,211,153,.08),rgba(255,255,255,.025));border:1px solid rgba(52,211,153,.10);"><div style="font-size:8px;color:#64748b;text-transform:uppercase;letter-spacing:.7px;font-weight:800;">UX</div><div style="font-size:10px;color:#6ee7b7;font-weight:900;margin-top:3px;">MOBILE SAFE</div></div>
</div>
<div style="position:relative;overflow:hidden;border:1px solid rgba(56,189,248,.20);border-radius:14px;padding:12px;background:linear-gradient(145deg,rgba(14,165,233,.10),rgba(15,23,42,.78) 48%,rgba(168,85,247,.08));box-shadow:0 10px 30px rgba(0,0,0,.28);">
<div style="position:absolute;right:-35px;top:-45px;width:120px;height:120px;border-radius:50%;background:radial-gradient(circle,rgba(56,189,248,.20),transparent 68%);pointer-events:none;"></div>
<div style="display:flex;align-items:center;gap:10px;position:relative;">
<div style="width:52px;height:52px;flex:0 0 52px;display:flex;align-items:center;justify-content:center;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="100%" height="100%">
<defs>
<radialGradient id="aboutBgGradient" cx="50%" cy="50%" r="50%"><stop offset="0%" stop-color="#1e293b"/><stop offset="60%" stop-color="#0d121e"/><stop offset="100%" stop-color="#050816"/></radialGradient>
<linearGradient id="aboutNeonCyan" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#7dd3fc"/><stop offset="50%" stop-color="#38bdf8"/><stop offset="100%" stop-color="#0284c7"/></linearGradient>
<linearGradient id="aboutProGradient" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#fbbf24"/><stop offset="100%" stop-color="#f59e0b"/></linearGradient>
<linearGradient id="aboutGlassBorder" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#38bdf8" stop-opacity="0.9"/><stop offset="50%" stop-color="#1e293b" stop-opacity="0.3"/><stop offset="100%" stop-color="#38bdf8" stop-opacity="0.7"/></linearGradient>
<filter id="aboutNeonGlow" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="8" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
<filter id="aboutProGlow" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="4" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
</defs>
<circle cx="256" cy="256" r="230" fill="url(#aboutBgGradient)" stroke="url(#aboutGlassBorder)" stroke-width="6" filter="url(#aboutNeonGlow)"/>
<g stroke="#38bdf8" stroke-opacity="0.12" stroke-width="2"><line x1="126" y1="180" x2="386" y2="180"/><line x1="126" y1="256" x2="386" y2="256"/><line x1="126" y1="332" x2="386" y2="332"/><line x1="180" y1="126" x2="180" y2="386"/><line x1="256" y1="126" x2="256" y2="386"/><line x1="332" y1="126" x2="332" y2="386"/></g>
<circle cx="256" cy="256" r="145" fill="none" stroke="#38bdf8" stroke-width="2.5" stroke-dasharray="8 8" opacity="0.45"/>
<g filter="url(#aboutNeonGlow)">
<path d="M 136 256 C 180 176, 332 176, 376 256 C 332 336, 180 336, 136 256 Z" fill="none" stroke="url(#aboutNeonCyan)" stroke-width="12" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="256" cy="256" r="46" fill="#0d121e" stroke="url(#aboutNeonCyan)" stroke-width="8"/><circle cx="256" cy="256" r="18" fill="#e0f2fe"/>
<line x1="150" y1="362" x2="362" y2="150" stroke="#f8fafc" stroke-width="12" stroke-linecap="round"/><line x1="150" y1="362" x2="362" y2="150" stroke="#38bdf8" stroke-width="6" stroke-linecap="round"/>
</g>
<g stroke="#e0f2fe" stroke-width="2.5" opacity="0.8"><path d="M 360 130 L 360 154 M 348 142 L 372 142 M 351 133 L 369 151 M 351 151 L 369 133"/></g>
<g transform="translate(85, 335)" filter="url(#aboutProGlow)"><rect x="0" y="0" width="112" height="50" rx="14" fill="#0d121e" stroke="url(#aboutProGradient)" stroke-width="3.5"/><text x="56" y="34" font-family="system-ui, -apple-system, sans-serif" font-weight="900" font-size="25" fill="url(#aboutProGradient)" text-anchor="middle" letter-spacing="3.5">PRO</text></g>
</svg></div>
<div style="min-width:0;">
<div style="font-size:15px;font-weight:900;letter-spacing:-.3px;color:#f8fafc;">Hide Web Elements <span style="color:#fbbf24;">PRO</span></div>
<div style="font-size:9px;color:#7dd3fc;margin-top:2px;letter-spacing:.8px;text-transform:uppercase;font-weight:800;">Web Control Command Center · v15.5</div>
</div>
</div>
<div style="margin-top:10px;font-size:10px;color:#94a3b8;">Take control of the page layer. Hide what gets in the way, reveal what is hidden, and keep the interface usable without sacrificing the site's normal controls.</div>
</div>
<div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px;margin-top:8px;">
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#38bdf8;">🎯 Precision Hide</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Site · page · global rules</div></div>
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#34d399;">❄️ Navigation Freeze</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Control unwanted navigation</div></div>
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#a78bfa;">🔗 Link & Media Lab</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Extract · preview · save</div></div>
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#fbbf24;">⏩ Time Tools</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Skip and control playback</div></div>
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#fb7185;">👁️ Reveal Engine</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Restore hidden or blurred UI</div></div>
<div style="padding:8px;border-radius:10px;background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.06);"><div style="font-weight:800;color:#60a5fa;">🛡️ Smart Protection</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Overlay and challenge awareness</div></div>
</div>
<div style="margin-top:8px;padding:9px 10px;border-radius:10px;background:linear-gradient(90deg,rgba(56,189,248,.07),rgba(168,85,247,.05));border:1px solid rgba(56,189,248,.10);">
<div style="font-size:9px;font-weight:900;color:#e2e8f0;text-transform:uppercase;letter-spacing:.8px;margin-bottom:5px;">Automation Layer</div>
<div style="display:flex;flex-wrap:wrap;gap:5px;">
<span style="padding:3px 6px;border-radius:999px;background:rgba(56,189,248,.09);color:#7dd3fc;">Anti-Paywall</span>
<span style="padding:3px 6px;border-radius:999px;background:rgba(168,85,247,.09);color:#c4b5fd;">Anti-AdBlock</span>
<span style="padding:3px 6px;border-radius:999px;background:rgba(52,211,153,.09);color:#6ee7b7;">Smart Modals</span>
<span style="padding:3px 6px;border-radius:999px;background:rgba(251,191,36,.09);color:#fcd34d;">Cookie Control</span>
<span style="padding:3px 6px;border-radius:999px;background:rgba(251,113,133,.09);color:#fda4af;">Age Gate</span>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:8px;">
<div style="padding:8px;border-radius:10px;border:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.025);"><div style="color:#e2e8f0;font-weight:800;">📱 Mobile Safe</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Tap-aware UI · scroll-safe handling</div></div>
<div style="padding:8px;border-radius:10px;border:1px solid rgba(255,255,255,.06);background:rgba(255,255,255,.025);"><div style="color:#e2e8f0;font-weight:800;">🌐 Shadow DOM Ready</div><div style="font-size:9px;color:#64748b;margin-top:2px;">Works across open component roots</div></div>
</div>
<div style="margin-top:9px;padding-top:8px;border-top:1px solid rgba(255,255,255,.06);color:#64748b;font-size:9px;">
<div><b style="color:#94a3b8;">Esc</b> · cancel selection / close panels</div>
<div style="margin-top:2px;"><b style="color:#94a3b8;">Ctrl + Click</b> · bypass navigation freeze</div>
<div style="margin-top:6px;text-align:center;color:#475569;">Built by KTZ · MIT License · designed for control, stability, and minimal interference</div>
</div>
</div>
` }
];
tabs.forEach((tab, index) => {
const btn = document.createElement('button');
btn.className = 'tab-btn' + (index === 0 ? ' active' : '');
btn.dataset.tab = tab.id;
btn.textContent = tab.label;
btn.title = tab.label;
sidebar.appendChild(btn);
const contentDiv = document.createElement('div');
contentDiv.className = 'tab-content' + (index === 0 ? ' active' : '');
contentDiv.id = tab.id;
contentDiv.innerHTML = tab.html;
content.appendChild(contentDiv);
});
sidebar.addEventListener('click', (e) => {
const btn = e.target.closest('.tab-btn');
if (!btn) return;
const tabId = btn.dataset.tab;
sidebar.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
content.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
btn.classList.add('active');
const contentDiv = content.querySelector('#' + tabId);
if (contentDiv) contentDiv.classList.add('active');
});
}
// ---------- Shadow UI Events ----------
function setupShadowUIEvents() {
shadowBy('close-p').onclick = e => { if (e) { e.stopPropagation(); e.preventDefault(); } turnOffHideMode(); closeAllMenus(e, true); };
setupCustomDropdown(shadowBy('freeze-custom-panel-dropdown'), CACHE.freezeMemory, val => {
CACHE.freezeMemory = val; sv('hider_freeze_memory', val);
const label = FREEZE_LABELS[val] || val;
if (val === 'allow_all') { disableFreezeMode(); showToast(`🟢 Freeze Mode Disabled (Allow All)`); }
else showToast(`❄️ Memory updated: ${label}`);
});
shadowBy('btn-reset-block-confirm').onclick = () => { sv('hider_skip_block_confirm', false); showToast('🔄 Reset Block Domain confirmation!'); };
const chkPaywall = shadowBy('chk-anti-paywall');
if (chkPaywall) {
chkPaywall.onchange = e => {
CACHE.antiPaywall = e.target.checked;
sv('hider_anti_paywall', CACHE.antiPaywall);
applyAllSettings();
showToast(`🚫 Anti-Paywall: ${CACHE.antiPaywall ? 'ON' : 'OFF'}`);
};
}
const chkModal = shadowBy('chk-auto-close-modals');
if (chkModal) {
chkModal.onchange = e => {
CACHE.autoCloseModals = e.target.checked;
sv('hider_auto_close_modals', CACHE.autoCloseModals);
applyAllSettings();
showToast(`🗑️ Auto-Close Modals: ${CACHE.autoCloseModals ? 'ON' : 'OFF'}`);
};
}
const chkAutoCloseLogins = shadowBy('chk-auto-close-logins');
if (chkAutoCloseLogins) {
chkAutoCloseLogins.onchange = e => {
CACHE.autoCloseLogins = e.target.checked;
sv('hider_auto_close_logins', CACHE.autoCloseLogins);
applyAllSettings();
showToast(`🔓 Auto-Close Logins: ${CACHE.autoCloseLogins ? 'ON' : 'OFF'}`);
};
}
const cookieMode = shadowBy('cookie-consent-mode');
if (cookieMode) {
cookieMode.onchange = e => {
CACHE.cookieConsentMode = e.target.value;
sv('hider_cookie_consent_mode', CACHE.cookieConsentMode);
applyAllSettings();
showToast(`🍪 Cookie mode: ${CACHE.cookieConsentMode === 'accept' ? 'Accept' : CACHE.cookieConsentMode === 'reject' ? 'Reject' : 'Ask'}`);
};
}
const chkScroll = shadowBy('chk-auto-scroll');
if (chkScroll) {
chkScroll.onchange = e => {
CACHE.autoScroll = e.target.checked;
sv('hider_auto_scroll', CACHE.autoScroll);
if (CACHE.autoScroll) {
startScrollDefeater();
showToast('🔓 Auto Anti-Scroll Lock enabled');
} else {
stopScrollDefeater();
showToast('🔒 Auto Anti-Scroll Lock disabled');
}
};
}
const chkContext = shadowBy('chk-enable-contextmenu');
if (chkContext) {
chkContext.onchange = e => {
CACHE.enableContextMenu = e.target.checked;
sv('hider_enable_contextmenu', CACHE.enableContextMenu);
updateContextMenuStyles();
showToast(`🖱️ Auto Right-Click/Long-Press: ${CACHE.enableContextMenu ? 'ON' : 'OFF'}`);
};
}
const chkBlur = shadowBy('chk-auto-remove-blur');
if (chkBlur) {
chkBlur.onchange = e => {
CACHE.autoRemoveBlur = e.target.checked;
sv('hider_auto_remove_blur', CACHE.autoRemoveBlur);
if (CACHE.autoRemoveBlur) {
scheduleBlurRemoval();
showToast('👁️ Auto Remove Blur enabled (aggressive)');
} else {
stopBlurRemoval();
showToast('👁️ Auto Remove Blur disabled');
}
};
}
const autoSkipChk = shadowBy('chk-auto-time-skipper');
if (autoSkipChk) {
autoSkipChk.onchange = e => {
CACHE.autoTimeSkipper = e.target.checked;
sv('hider_auto_time_skipper', CACHE.autoTimeSkipper);
if (CACHE.autoTimeSkipper) {
startAutoSkipMonitoring();
showToast('⏩ Auto Time Skipper enabled (smart)');
} else {
stopAutoSkipMonitoring();
showToast('⏩ Auto Time Skipper disabled');
}
};
}
const dockShowAll = shadowBy('dock-show-all');
if (dockShowAll) dockShowAll.onclick = e => { e.stopPropagation(); setDockButtonsVisible('all'); };
const dockHideOptional = shadowBy('dock-hide-optional');
if (dockHideOptional) dockHideOptional.onclick = e => { e.stopPropagation(); setDockButtonsVisible('none'); };
const dockEl = shadowBy('hider-main-dock'), mainBtn = shadowBy('btn-toggle-dock');
const savedY = gv('hider_dock_y', null); if (savedY) dockEl.style.top = savedY;
dockEl.addEventListener('mouseleave', () => dockEl.classList.remove('manual-hidden'));
let dockStartX, dockStartY, dockInitY, dockMoved = false;
const onDockMove = e => {
if (!isDraggingDock) return;
const p = e.touches ? e.touches[0] : e, dy = p.clientY - dockStartY;
if (Math.hypot(p.clientX - dockStartX, dy) > 8) {
dockMoved = true; if (e.cancelable) e.preventDefault();
dockEl.style.top = `${Math.max(0, Math.min(dockInitY + dy, win.innerHeight - dockEl.offsetHeight))}px`;
}
};
const onDockEnd = () => {
if (isDraggingDock) { isDraggingDock = false; dockEl.style.transition = ''; if (dockMoved) sv('hider_dock_y', dockEl.style.top); }
win.removeEventListener('mousemove', onDockMove); win.removeEventListener('mouseup', onDockEnd);
win.removeEventListener('touchmove', onDockMove); win.removeEventListener('touchend', onDockEnd); win.removeEventListener('touchcancel', onDockEnd);
};
const onDockStart = e => {
if (e.target.tagName === 'BUTTON' && e.target.id !== 'btn-toggle-dock') return;
const p = e.touches ? e.touches[0] : e;
dockStartX = p.clientX; dockStartY = p.clientY; dockInitY = dockEl.getBoundingClientRect().top;
isDraggingDock = true; dockMoved = false; dockEl.style.transition = 'none';
win.addEventListener('mousemove', onDockMove, { passive: false }); win.addEventListener('mouseup', onDockEnd);
win.addEventListener('touchmove', onDockMove, { passive: false }); win.addEventListener('touchend', onDockEnd); win.addEventListener('touchcancel', onDockEnd);
};
mainBtn.addEventListener('mousedown', onDockStart, { passive: false });
mainBtn.addEventListener('touchstart', onDockStart, { passive: false });
mainBtn.onclick = e => {
e.stopPropagation(); if (dockMoved) { dockMoved = false; return; }
if (timers.collapseTimer) { clearTimeout(timers.collapseTimer); timers.collapseTimer = null; }
const menu = shadowBy('hider-dock-menu'), isOpening = !menu.classList.contains('is-open');
menu.classList.toggle('is-open', isOpening);
mainBtn.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('expanded', isOpening);
dockEl.classList.toggle('is-collapsed', !isOpening);
if (!isOpening) {
turnOffHideMode();
const panel = shadowBy('hider-panel');
if (panel.classList.contains('is-visible')) { panel.classList.remove('is-visible'); setTimeout(()=> panel.style.display='none', 300); }
shadowBy('btn-manage')?.classList.remove('active');
dockEl.classList.add('manual-hidden');
timers.collapseTimer = setTimeout(() => { dockEl.classList.add('is-collapsed'); timers.collapseTimer = null; }, 1000);
} else dockEl.classList.remove('manual-hidden');
};
const btnScope = shadowBy('btn-scope');
btnScope.onclick = e => {
e.stopPropagation();
const scopes = ['site', 'link', 'global'];
let idx = scopes.indexOf(currentScope);
idx = (idx + 1) % scopes.length;
currentScope = scopes[idx];
btnScope.querySelector('span').textContent = currentScope.toUpperCase();
btnScope.classList.toggle('scope-link', currentScope === 'link');
btnScope.classList.toggle('scope-global', currentScope === 'global');
const label = currentScope === 'site' ? '🌐 Site-wide' : currentScope === 'link' ? '📄 Page-only' : '🌍 Global';
showToast(`Scope: ${label}`);
broadcastState();
};
shadowBy('btn-reveal-quick').onclick = (e) => {
e.stopPropagation();
revealHiddenElements();
};
shadowBy('btn-links').onclick = (e) => {
e.stopPropagation();
toggleLinkPanel();
};
shadowBy('btn-skip-30').onclick = (e) => {
e.stopPropagation();
skip30Seconds();
};
const addRuleBtn = shadowBy('add-rule-btn');
if (addRuleBtn) {
addRuleBtn.onclick = () => {
showRuleOverlay({ mode: 'add', scope: 'global' });
};
}
const addAllowedDomain = () => {
const input = shadowBy('manual-allowed-domain-input'), d = cleanDomain(input.value);
if (d && !CACHE.allowedDomainsSet.has(d)) { CACHE.allowedDomainsList.push(d); CACHE.allowedDomainsSet.add(d); sv('hider_allowed_domains', CACHE.allowedDomainsList); showToast(`🟢 Allowed: ${d}`); }
input.value = ''; renderList();
};
shadowBy('add-allowed-domain-btn').onclick = addAllowedDomain; shadowBy('manual-allowed-domain-input').onkeypress = e => e.key === 'Enter' && addAllowedDomain();
const addDomain = () => {
const input = shadowBy('manual-domain-input'), d = cleanDomain(input.value);
if (d && !CACHE.blockedDomainsSet.has(d)) { CACHE.blockedDomainsList.push(d); CACHE.blockedDomainsSet.add(d); sv('hider_blocked_domains', CACHE.blockedDomainsList); showToast(`🚫 Blocked: ${d}`); }
input.value = ''; renderList();
};
shadowBy('add-domain-btn').onclick = addDomain; shadowBy('manual-domain-input').onkeypress = e => e.key === 'Enter' && addDomain();
shadowBy('btn-select').onclick = e => {
e.stopPropagation(); isSelecting = !isSelecting; e.currentTarget.classList.toggle('active', isSelecting);
const p = shadowBy('hider-panel');
if (isSelecting) {
if(p.classList.contains('is-visible')) { p.classList.remove('is-visible'); setTimeout(()=> p.style.display='none', 300); } shadowBy('btn-manage').classList.remove('active');
showToast('🎯 Selection mode ON – Click element to hide');
} else {
clearSelectionState();
showToast('🎯 Selection mode OFF');
}
broadcastState();
};
const btnFreeze = shadowBy('btn-freeze');
btnFreeze.onclick = e => {
e.stopPropagation(); isFrozen = !isFrozen; CACHE.isFrozen = isFrozen; sv('hider_freeze_global', isFrozen);
if (isFrozen && CACHE.freezeMemory === 'allow_all') { CACHE.freezeMemory = 'ask'; sv('hider_freeze_memory', 'ask'); }
btnFreeze.classList.toggle('is-frozen', isFrozen); if (isFrozen) triggerFreezeFx();
showToast(isFrozen ? '🥶 Freeze ON' : '🔥 Freeze OFF'); broadcastState();
};
shadowBy('btn-manage').onclick = e => {
e.stopPropagation(); const p = shadowBy('hider-panel'), isHidden = p.style.display === 'none' || p.style.display === '';
if (isHidden) {
p.style.display = 'flex';
void p.offsetWidth;
p.classList.add('is-visible');
e.currentTarget.classList.add('active');
turnOffHideMode(); renderList();
} else {
p.classList.remove('is-visible');
setTimeout(() => p.style.display = 'none', 300);
e.currentTarget.classList.remove('active');
}
};
shadowBy('clear-log-btn').onclick = e => { if (e) e.stopPropagation(); CACHE.logs = []; sv('hider_global_logs', []); renderList(); showToast('🧹 Logs cleared'); };
shadowBy('clear-custom-rules').onclick = () => {
if (confirm('Delete all custom rules?')) {
CACHE.customRules = [];
sv('hider_custom_rules_v4', CACHE.customRules);
renderList(); requestUpdateStyles();
showToast('🧹 Custom rules cleared');
}
};
shadowBy('clear-site-rules').onclick = () => {
const key = 'hider_site_' + location.hostname;
if (confirm('Delete all site-wide rules?')) {
sv(key, []);
renderList(); requestUpdateStyles();
showToast('🧹 Site rules cleared');
}
};
shadowBy('clear-page-rules').onclick = () => {
const key = 'hider_link_' + cleanUrl();
if (confirm('Delete all page-only rules?')) {
sv(key, []);
renderList(); requestUpdateStyles();
showToast('🧹 Page rules cleared');
}
};
const exportBtn = shadowBy('btn-export-settings');
const importBtn = shadowBy('btn-import-settings');
const fileInput = shadowBy('import-file-input');
if (exportBtn) exportBtn.onclick = () => {
const data = getAllSettings();
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `hider_backup_${new Date().toISOString().slice(0,10)}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('📤 Settings exported');
};
if (importBtn) importBtn.onclick = () => fileInput?.click();
if (fileInput) {
fileInput.onchange = function() {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const data = JSON.parse(e.target.result);
let count = 0;
for (const [key, val] of Object.entries(data)) {
if (key.startsWith('hider_')) {
sv(key, val);
count++;
}
}
syncCache();
requestUpdateStyles();
renderList();
applyDockButtonVisibility();
showToast(`📥 Imported ${count} settings`);
} catch (err) {
showToast('❌ Invalid JSON file');
}
};
reader.readAsText(this.files[0]);
this.value = '';
}
};
}
// ---------- COOKIE WORKSPACE 15.0 ----------
const cookieListEl = shadowBy('cookie-list');
const cookieCountEl = shadowBy('cookie-count');
const cookieSearch = shadowBy('cookie-search');
const cookieAddNewBtn = shadowBy('cookie-add-new');
const cookieNewEditor = shadowBy('cookie-new-editor');
const cookieNewCancel = shadowBy('cookie-new-cancel');
const cookieAddBtn = shadowBy('cookie-add');
const cookieNameInput = shadowBy('cookie-new-name');
const cookieValueInput = shadowBy('cookie-new-value');
const cookiePathInput = shadowBy('cookie-new-path');
const cookieExpiryInput = shadowBy('cookie-new-expiry');
const cookieSecureInput = shadowBy('cookie-new-secure');
const cookieSameSiteInput = shadowBy('cookie-new-samesite');
const cookieClearAll = shadowBy('cookie-clear-all');
const cookieExportBtn = shadowBy('cookie-export');
const cookieImportBtn = shadowBy('cookie-import');
const cookieImportFile = shadowBy('cookie-import-file');
const cookieRefreshBtn = shadowBy('cookie-refresh');
const cookieApiStatus = shadowBy('cookie-api-status');
const cookieHostLabel = shadowBy('cookie-host-label');
const cookieStoreApi = (typeof win.cookieStore !== 'undefined' && win.cookieStore) || (typeof cookieStore !== 'undefined' ? cookieStore : null);
if (cookieApiStatus) {
cookieApiStatus.textContent = cookieStoreApi ? 'Cookie Store API' : 'document.cookie fallback';
cookieApiStatus.style.color = cookieStoreApi ? '#6ee7b7' : '#fcd34d';
}
if (cookieHostLabel) cookieHostLabel.textContent = location.hostname;
let cookieCache = [];
let cookieEditOpen = null;
function normalizeCookie(c) {
return {
name: String(c?.name ?? ''), value: String(c?.value ?? ''), domain: c?.domain || location.hostname,
path: c?.path || '/', expires: c?.expires ? new Date(c.expires).getTime() : null,
secure: !!c?.secure, sameSite: (c?.sameSite || 'lax').toLowerCase(), httpOnly: !!c?.httpOnly
};
}
function parseDocumentCookies() {
return document.cookie.split(';').map(x => x.trim()).filter(Boolean).map(x => {
const eq = x.indexOf('=');
return normalizeCookie({ name: eq >= 0 ? x.slice(0, eq) : x, value: eq >= 0 ? x.slice(eq + 1) : '' });
});
}
async function readCookies() {
try {
if (cookieStoreApi?.getAll) {
const all = await cookieStoreApi.getAll();
return all.map(normalizeCookie).filter(c => c.name);
}
} catch (err) { console.debug('[Hide Web Elements Pro] Cookie Store read fallback:', err); }
return parseDocumentCookies();
}
function cookieMatchesFilter(c, filter) {
if (!filter) return true;
const q = filter.toLowerCase();
return c.name.toLowerCase().includes(q) || c.value.toLowerCase().includes(q) || String(c.path).toLowerCase().includes(q);
}
function formatCookieExpiry(ts) {
if (!ts) return 'Session';
const d = new Date(ts);
return Number.isNaN(d.getTime()) ? 'Session' : d.toLocaleString();
}
function renderCookieRows(filter='') {
if (!cookieListEl) return;
const filtered = cookieCache.filter(c => cookieMatchesFilter(c, filter));
if (cookieCountEl) cookieCountEl.textContent = filtered.length;
cookieListEl.innerHTML = '';
if (!filtered.length) {
cookieListEl.innerHTML = `<div class="hx-empty"><div style="font-size:20px;opacity:.45;">🍪</div><div style="font-size:9px;margin-top:5px;">${cookieCache.length ? 'No cookies match this search.' : 'No script-visible cookies for this site.'}</div></div>`;
return;
}
const frag = doc.createDocumentFragment();
filtered.forEach(c => {
const row = doc.createElement('div');
row.className='hx-cookie-row';
row.style.cssText='display:flex;flex-direction:column;gap:5px;padding:8px;border-radius:10px;background:rgba(255,255,255,.025);border:1px solid rgba(148,163,184,.08);';
const top=doc.createElement('div'); top.style.cssText='display:flex;align-items:center;gap:7px;';
const name=doc.createElement('div'); name.textContent=c.name; name.style.cssText='font-size:10px;font-weight:900;color:#7dd3fc;word-break:break-all;flex:1;';
const badge=doc.createElement('span'); badge.className='hx-chip'; badge.textContent=c.httpOnly?'HttpOnly':'Script-visible';
top.append(name,badge);
const val=doc.createElement('div'); val.className='hx-cookie-value'; val.textContent=c.value; val.title=c.value; val.style.cssText='font-size:8px;color:#cbd5e1;word-break:break-all;max-height:34px;overflow:auto;';
const meta=doc.createElement('div'); meta.style.cssText='display:flex;gap:5px;flex-wrap:wrap;align-items:center;';
[['Path',c.path],['Domain',c.domain],['Expires',formatCookieExpiry(c.expires)],['SameSite',c.sameSite],['Secure',c.secure?'yes':'no']].forEach(([k,v])=>{const x=doc.createElement('span');x.className='hx-chip';x.textContent=`${k}: ${v}`;meta.appendChild(x);});
const actions=doc.createElement('div'); actions.style.cssText='display:flex;gap:4px;justify-content:flex-end;';
const edit=doc.createElement('button'); edit.className='hider-btn-small btn-blue'; edit.textContent='Edit'; edit.style.height='22px';
const del=doc.createElement('button'); del.className='hider-btn-small btn-red'; del.textContent='Delete'; del.style.height='22px';
edit.onclick=e=>{e.stopPropagation(); openCookieEditor(row,c);};
del.onclick=async e=>{e.stopPropagation(); await deleteCookie(c);};
actions.append(edit,del);
row.append(top,val,meta,actions);
frag.appendChild(row);
});
cookieListEl.appendChild(frag);
}
function closeCookieEditor() { if (cookieEditOpen?.remove) cookieEditOpen.remove(); cookieEditOpen=null; }
function openCookieEditor(row,c) {
closeCookieEditor();
const editor=doc.createElement('div'); editor.className='hx-card'; editor.style.cssText='padding:9px;margin-top:2px;background:rgba(56,189,248,.045);border-color:rgba(56,189,248,.14);';
editor.innerHTML=`<div style="font-size:9px;font-weight:900;color:#e2e8f0;margin-bottom:6px;">Edit cookie</div>
<div style="display:grid;grid-template-columns:1fr 1.3fr;gap:5px;">
<input class="hx-input ce-name" style="height:26px;padding:0 6px;font-size:8px;" value="${escapeHtmlAttr(c.name)}">
<input class="hx-input ce-value" style="height:26px;padding:0 6px;font-size:8px;" value="${escapeHtmlAttr(c.value)}">
<input class="hx-input ce-path" style="height:26px;padding:0 6px;font-size:8px;" value="${escapeHtmlAttr(c.path)}">
<input class="hx-input ce-expiry" type="datetime-local" style="height:26px;padding:0 6px;font-size:8px;" value="${c.expires?toDateTimeLocal(c.expires):''}">
</div>
<div style="display:flex;gap:7px;align-items:center;flex-wrap:wrap;margin-top:6px;">
<label style="font-size:8px;color:#94a3b8;display:flex;gap:3px;align-items:center;"><input class="ce-secure" type="checkbox" ${c.secure?'checked':''}> Secure</label>
<select class="hx-input ce-samesite" style="width:92px;height:24px;padding:0 5px;font-size:8px;"><option value="lax">Lax</option><option value="strict">Strict</option><option value="none">None</option></select>
<button class="hider-btn-small btn-green ce-save" style="height:24px;margin-left:auto;">Save</button>
<button class="hider-btn-small btn-gray ce-cancel" style="height:24px;">Cancel</button>
</div>`;
row.appendChild(editor); cookieEditOpen=editor;
editor.querySelector('.ce-samesite').value=c.sameSite||'lax';
editor.querySelector('.ce-cancel').onclick=e=>{e.stopPropagation();closeCookieEditor();};
editor.querySelector('.ce-save').onclick=async e=>{
e.stopPropagation();
const next=normalizeCookie({name:editor.querySelector('.ce-name').value.trim(),value:editor.querySelector('.ce-value').value,domain:c.domain,path:editor.querySelector('.ce-path').value.trim()||'/',expires:editor.querySelector('.ce-expiry').value?new Date(editor.querySelector('.ce-expiry').value).getTime():null,secure:editor.querySelector('.ce-secure').checked,sameSite:editor.querySelector('.ce-samesite').value});
if (!next.name) return showToast('⚠️ Cookie name cannot be empty');
const ok=await replaceCookie(c,next); if(ok){closeCookieEditor();await refreshCookies();}
};
}
function escapeHtmlAttr(v){ return String(v).replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>'); }
function toDateTimeLocal(ts){ const d=new Date(ts); const p=n=>String(n).padStart(2,'0'); return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`; }
async function setCookie(c) {
try {
if (cookieStoreApi?.set) {
const opts={name:c.name,value:c.value,path:c.path||'/',secure:!!c.secure,sameSite:c.sameSite||'lax'};
if (c.domain && c.domain !== location.hostname) opts.domain=c.domain;
if (c.expires) opts.expires=c.expires;
await cookieStoreApi.set(opts); return true;
}
} catch(err){ console.debug('[Hide Web Elements Pro] Cookie Store set failed:',err); }
try {
let str=`${encodeURIComponent(c.name)}=${encodeURIComponent(c.value)}; path=${c.path||'/'}`;
if(c.domain) str+=`; domain=${c.domain}`;
if(c.expires) str+=`; expires=${new Date(c.expires).toUTCString()}`;
if(c.secure) str+='; Secure';
if(c.sameSite) str+=`; SameSite=${c.sameSite}`;
document.cookie=str; return true;
}catch(err){showToast('❌ Browser rejected this cookie change');return false;}
}
async function deleteCookie(c) {
if (!confirm(`Delete cookie "${c.name}"?`)) return false;
try {
if (cookieStoreApi?.delete) {
await cookieStoreApi.delete({name:c.name,domain:c.domain,path:c.path||'/'});
} else {
const domains=[c.domain,undefined,location.hostname].filter((v,i,a)=>v && a.indexOf(v)===i);
for(const d of domains){ let s=`${encodeURIComponent(c.name)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${c.path||'/'}`; if(d)s+=`; domain=${d}`; document.cookie=s; }
}
showToast(`🗑️ Deleted ${c.name}`); await refreshCookies(); return true;
} catch(err){ console.debug('[Hide Web Elements Pro] Cookie delete failed:',err); showToast('❌ Could not delete cookie'); return false; }
}
async function replaceCookie(oldC,newC) {
try { await deleteCookieSilently(oldC); const ok=await setCookie(newC); showToast(ok?`✅ Updated ${newC.name}`:'❌ Update failed'); return ok; } catch(err){showToast('❌ Update failed');return false;}
}
async function deleteCookieSilently(c){
if(cookieStoreApi?.delete){ try{await cookieStoreApi.delete({name:c.name,domain:c.domain,path:c.path||'/'});return;}catch{} }
try{ let s=`${encodeURIComponent(c.name)}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=${c.path||'/'}`; if(c.domain)s+=`; domain=${c.domain}`; document.cookie=s; }catch{}
}
async function createCookie() {
const name=cookieNameInput?.value.trim()||'', value=cookieValueInput?.value??'', path=cookiePathInput?.value.trim()||'/';
if(!name)return showToast('⚠️ Enter cookie name');
const expires=cookieExpiryInput?.value?new Date(cookieExpiryInput.value).getTime():null;
if(cookieExpiryInput?.value && !Number.isFinite(expires))return showToast('⚠️ Invalid expiry');
const ok=await setCookie({name,value,path,domain:location.hostname,expires,secure:!!cookieSecureInput?.checked,sameSite:cookieSameSiteInput?.value||'lax'});
if(ok){
[cookieNameInput,cookieValueInput].forEach(x=>{if(x)x.value='';});
if(cookiePathInput) cookiePathInput.value='/';
if(cookieExpiryInput) cookieExpiryInput.value='';
if(cookieSecureInput) cookieSecureInput.checked=false;
if(cookieSameSiteInput) cookieSameSiteInput.value='lax';
closeNewCookieComposer();
showToast(`🍪 Created ${name}`);
await refreshCookies();
}
}
async function clearAllCookies() {
if(!cookieCache.length || !confirm(`Delete all ${cookieCache.length} visible cookies for ${location.hostname}?`))return;
for(const c of [...cookieCache]) await deleteCookieSilently(c);
showToast(`🧹 Delete requested for ${cookieCache.length} cookies`); await refreshCookies();
}
async function exportCookies() {
const data={version:2,domain:location.hostname,exportedAt:new Date().toISOString(),cookies:cookieCache};
const blob=new Blob([JSON.stringify(data,null,2)],{type:'application/json'}),url=URL.createObjectURL(blob),a=doc.createElement('a');
a.href=url;a.download=`cookies_${location.hostname}_${new Date().toISOString().slice(0,10)}.json`;doc.body.appendChild(a);a.click();a.remove();URL.revokeObjectURL(url);showToast('📤 Cookies exported');
}
async function importCookies(file) {
try {
const data=JSON.parse(await file.text()); const list=Array.isArray(data)?data:(Array.isArray(data.cookies)?data.cookies:[]); if(!list.length)throw new Error('No cookies');
let ok=0; for(const raw of list){const c=normalizeCookie({...raw,domain:raw.domain||location.hostname});if(c.name&&await setCookie(c))ok++;}
await refreshCookies(); showToast(`📥 Imported ${ok}/${list.length} cookies`);
}catch(err){showToast('❌ Invalid cookie JSON');}
}
async function refreshCookies() {
closeCookieEditor(); cookieCache=await readCookies(); renderCookieRows(cookieSearch?.value||'');
}
function closeNewCookieComposer(clearFields = true) {
if (cookieNewEditor) cookieNewEditor.hidden = true;
cookieAddNewBtn?.setAttribute('aria-expanded', 'false');
if (clearFields) {
if (cookieNameInput) cookieNameInput.value = '';
if (cookieValueInput) cookieValueInput.value = '';
if (cookiePathInput) cookiePathInput.value = '/';
if (cookieExpiryInput) cookieExpiryInput.value = '';
if (cookieSecureInput) cookieSecureInput.checked = false;
if (cookieSameSiteInput) cookieSameSiteInput.value = 'lax';
}
}
if (cookieAddNewBtn) cookieAddNewBtn.onclick = e => {
e.stopPropagation();
const open = !!cookieNewEditor && cookieNewEditor.hidden;
if (cookieNewEditor) cookieNewEditor.hidden = !open;
cookieAddNewBtn.setAttribute('aria-expanded', String(open));
if (open) {
cookieNameInput?.focus();
cookieAddNewBtn.textContent = '✕ Close';
cookieAddNewBtn.classList.remove('btn-green');
cookieAddNewBtn.classList.add('btn-gray');
} else {
closeNewCookieComposer(true);
cookieAddNewBtn.textContent = '+ Add New';
cookieAddNewBtn.classList.remove('btn-gray');
cookieAddNewBtn.classList.add('btn-green');
}
};
if (cookieNewCancel) cookieNewCancel.onclick = e => {
e.stopPropagation();
closeNewCookieComposer(true);
if (cookieAddNewBtn) {
cookieAddNewBtn.textContent = '+ Add New';
cookieAddNewBtn.classList.remove('btn-gray');
cookieAddNewBtn.classList.add('btn-green');
}
};
if(cookieAddBtn)cookieAddBtn.onclick=createCookie;
if(cookieRefreshBtn)cookieRefreshBtn.onclick=refreshCookies;
if(cookieClearAll)cookieClearAll.onclick=clearAllCookies;
if(cookieExportBtn)cookieExportBtn.onclick=exportCookies;
if(cookieImportBtn)cookieImportBtn.onclick=()=>cookieImportFile?.click();
if(cookieImportFile)cookieImportFile.onchange=async()=>{if(cookieImportFile.files?.[0]){await importCookies(cookieImportFile.files[0]);cookieImportFile.value='';}};
if(cookieSearch)cookieSearch.oninput=()=>renderCookieRows(cookieSearch.value);
[cookieNameInput,cookieValueInput,cookiePathInput].forEach(i=>{if(i)i.onkeydown=e=>{if(e.key==='Enter')createCookie();}});
closeNewCookieComposer(true);
refreshCookies();
// ---------- End of Cookie Workspace ----------
function getAllSettings() {
const settings = {};
const knownKeys = [
'hider_freeze_memory', 'hider_freeze_global', 'hider_blocked_domains',
'hider_allowed_domains', 'hider_custom_rules_v4', 'hider_auto_time_skipper',
'hider_auto_scroll', 'hider_enable_contextmenu', 'hider_auto_remove_blur',
'hider_hidden_dock_buttons', 'hider_global_logs',
'hider_last_log_clear_day', 'hider_skip_block_confirm', 'hider_dock_y',
'hider_anti_paywall', 'hider_auto_close_modals', 'hider_cookie_consent_mode',
'hider_auto_close_logins'
];
knownKeys.forEach(k => {
const val = gv(k, undefined);
if (val !== undefined) settings[k] = val;
});
try {
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.startsWith('hider_site_') || key.startsWith('hider_link_'))) {
const val = localStorage.getItem(key);
try { settings[key] = JSON.parse(val); } catch { settings[key] = val; }
}
}
} catch {}
return settings;
}
}
// ---------- Render List ----------
function renderList() {
if (!shadowRoot) return;
if (!CACHE.logs) CACHE.logs = gv('hider_global_logs', []);
const siteKey = 'hider_site_' + location.hostname, linkKey = 'hider_link_' + cleanUrl();
const siteData = gv(siteKey, []), linkData = gv(linkKey, []);
const cCustom = shadowBy('list-custom-rules'), cAllowed = shadowBy('list-allowed-domains'), cDomains = shadowBy('list-blocked-domains'), cSite = shadowBy('list-site'), cLink = shadowBy('list-link'), cLog = shadowBy('list-blocked-log');
if (!cSite || !cLink) return;
['cnt-custom', 'cnt-allowed', 'cnt-domains', 'cnt-logs', 'cnt-site', 'cnt-link'].forEach((id, i) => {
const el = shadowBy(id);
if(el) el.textContent = [CACHE.customRules.length, CACHE.allowedDomainsList.length, CACHE.blockedDomainsList.length, CACHE.logs.length, siteData.length, linkData.length][i];
});
const buildFrag = (arr, emptyMsg, renderItem) => {
const frag = doc.createDocumentFragment();
if (!arr.length) { const d = doc.createElement('div'); d.style.cssText = 'font-size:9px!important;color:#94a3b8!important;padding:4px!important;text-align:center!important;border:1px dashed rgba(255,255,255,0.12)!important;border-radius:6px!important;'; d.textContent = emptyMsg; frag.appendChild(d); return frag; }
arr.forEach((item, i) => frag.appendChild(renderItem(item, i))); return frag;
};
if (cCustom) {
cCustom.innerHTML = '';
cCustom.appendChild(buildFrag(CACHE.customRules, 'No custom rules', (rule, idx) => {
const div = doc.createElement('div'); div.className = 'list-item';
div.innerHTML = `<span class="rule-text" title="${rule.selector}">${rule.selector} <span style="color:${rule.target === '*' ? '#38bdf8' : '#34d399'}!important; font-weight:bold!important;">[${rule.target}]</span></span><div style="display:flex;gap:2px"><button class="hider-btn-small btn-blue btn-edit">✏️</button><button class="hider-btn-small btn-red btn-del">✖</button></div>`;
div.querySelector('.btn-edit').onclick = () => {
showRuleOverlay({
mode: 'edit',
selector: rule.selector,
scope: 'global',
target: rule.target,
editInfo: { listType: 'custom', id: rule.id, index: idx }
});
};
div.querySelector('.btn-del').onclick = () => {
CACHE.customRules = CACHE.customRules.filter(r => r.id !== rule.id);
sv('hider_custom_rules_v4', CACHE.customRules);
requestUpdateStyles(); renderList();
showToast('🗑️ Rule removed');
};
return div;
}));
}
if (cAllowed) {
cAllowed.innerHTML = '';
cAllowed.appendChild(buildFrag(CACHE.allowedDomainsList, 'No allowed domains', (d, i) => {
const div = doc.createElement('div'); div.className = 'list-item'; div.innerHTML = `<span class="rule-text" title="${d}">${d}</span><button class="hider-btn-small btn-red">✖</button>`;
div.querySelector('button').onclick = () => {
CACHE.allowedDomainsList.splice(i, 1);
CACHE.allowedDomainsSet.delete(cleanDomain(d));
sv('hider_allowed_domains', CACHE.allowedDomainsList);
renderList();
showToast('🟢 Allowed domain removed');
};
return div;
}));
}
if (cDomains) {
cDomains.innerHTML = '';
cDomains.appendChild(buildFrag(CACHE.blockedDomainsList, 'No blocked domains', (d, i) => {
const div = doc.createElement('div'); div.className = 'list-item'; div.innerHTML = `<span class="rule-text" title="${d}">${d}</span><button class="hider-btn-small btn-red">✖</button>`;
div.querySelector('button').onclick = () => {
CACHE.blockedDomainsList.splice(i, 1);
CACHE.blockedDomainsSet.delete(cleanDomain(d));
sv('hider_blocked_domains', CACHE.blockedDomainsList);
renderList();
showToast('🚫 Blocked domain removed');
};
return div;
}));
}
if (cLog) {
cLog.innerHTML = '';
cLog.appendChild(buildFrag(CACHE.logs, 'No logs today', item => {
const row = doc.createElement('div'); row.style.cssText = 'font-size:9px!important;border-bottom:1px solid rgba(255,255,255,0.08)!important;padding:2px 0!important;';
row.innerHTML = `<div style="display:flex;justify-content:space-between;color:#6ee7b7!important;font-weight:700!important;"><span>${item.type}</span><span style="color:#94a3b8!important;font-weight:normal!important;">${item.time}</span></div><div style="color:#cbd5e1!important;word-break:break-all!important;font-family:monospace!important;opacity:0.8;">${item.url}</div>`; return row;
}));
}
const renderEditableHiddenItems = (data, key, container) => {
container.innerHTML = '';
container.appendChild(buildFrag(data, 'None', (sel, i) => {
const div = doc.createElement('div'); div.className = 'list-item';
div.innerHTML = `<span class="rule-text" title="${sel}">${sel}</span><div style="display:flex;gap:2px;align-items:center"><button class="hider-btn-small btn-blue btn-edit" title="Edit Rule Inline">✏️</button><button class="hider-btn-small btn-purple btn-wild" title="Convert Wildcard">🪄</button><button class="hider-btn-small btn-red btn-del" title="Delete Rule">✖</button></div>`;
const textSpan = div.querySelector('.rule-text'), editBtn = div.querySelector('.btn-edit');
editBtn.onclick = () => {
let scope = 'site';
let target = location.hostname;
if (key === linkKey) {
scope = 'link';
target = cleanUrl();
}
const listType = (key === siteKey) ? 'site' : 'link';
showRuleOverlay({
mode: 'edit',
selector: sel,
scope: scope,
target: target,
editInfo: { listType: listType, index: i }
});
};
div.querySelector('.btn-wild').onclick = () => {
data[i] = convertToWildcardSelector(data[i]);
sv(key, data); requestUpdateStyles(); renderList();
showToast(`🪄 Wildcard applied`);
};
div.querySelector('.btn-del').onclick = () => {
const removed = data.splice(i, 1)[0]; sv(key, data);
requestUpdateStyles(); renderList();
showToast('🗑️ Hidden rule removed');
};
return div;
}));
};
renderEditableHiddenItems(siteData, siteKey, cSite); renderEditableHiddenItems(linkData, linkKey, cLink);
renderDockButtonOptions();
applyDockButtonVisibility();
}
// ---------- Navigation Click Handler ----------
function handleNavigationClick(e) {
const path = e.composedPath?.() || [];
if (isSelecting || path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return;
const link = e.target.closest('a[href], area[href]');
if (link?.href) {
const rawHref = link.getAttribute('href') || '';
if (rawHref.startsWith('#') || rawHref.toLowerCase().startsWith('javascript:')) return;
const targetUrl = link.href;
if (isDomainBlocked(targetUrl)) {
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
logBlockedAttempt(targetUrl, 'Blocked Link Click'); simulateAdWindowSuccess(); showToast('⛔ Force Blocked'); return;
}
if (!isFrozen || isDomainAllowed(targetUrl)) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
const isNewTab = link.target === '_blank' || e.ctrlKey || e.metaKey || e.button === 1;
handleFreezeNavigation(targetUrl, 'Link Click', () => {
userApprovedNavigation = true; if (isNewTab) win.open(targetUrl, '_blank'); else win.location.href = targetUrl;
setTimeout(() => userApprovedNavigation = false, 300);
}, simulateAdWindowSuccess);
}
}
// ---------- Scroll auto-close ----------
// Never close the script UI merely because the user scrolled the page.
// Page scrolling is unrelated to our dock/menu state and doing this breaks
// real site search boxes, navigation drawers and sticky panels.
function setupScrollAutoClose() {
// Preserve the original working behavior: scrolling the page closes
// script-owned menus/dock UI. Use RAF to avoid repeated close work on
// high-frequency mobile scroll events. Do not touch site-owned UI.
win.addEventListener('scroll', () => {
if (scrollAnimationFrame) return;
scrollAnimationFrame = win.requestAnimationFrame(() => {
scrollAnimationFrame = null;
if (isSelecting || isDraggingDock || isDraggingStepper) return;
closeAllMenus(null);
closePreviewModal();
});
}, { passive: true, capture: true });
}
// ---------- Keyboard shortcuts ----------
window.addEventListener('keydown', e => { if (e.key === 'Escape') { if (isSelecting) { turnOffHideMode(); showToast('🎯 Selection mode cancelled'); } else { closeAllMenus(null, true); closePreviewModal(); } } }, true);
// ---------- Selection mode blockers ----------
['mousedown', 'pointerdown', 'mouseup'].forEach(evtType => {
window.addEventListener(evtType, e => {
if (!isSelecting) return;
if (e.composedPath?.().some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
}, true);
});
// ---------- Click / mobile tap handling ----------
// Close script UI on a genuine outside activation, but NEVER on a scroll gesture.
// Mobile browsers may synthesize a click after touchend, so the gesture state is
// shared by both handlers and a single tap is handled only once.
let outsideTouchX = 0, outsideTouchY = 0;
let outsideTouchMoved = false;
let outsideTouchActive = false;
let outsideTouchIsUI = false;
let lastTouchCloseAt = 0;
const getEventPath = e => {
try { return e?.composedPath?.() || []; } catch { return []; }
};
const pathIsHiderUI = path => path.some(el => el && (
el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID ||
(el.closest && el.closest('#' + UI_HOST_ID))
));
const closeMenusFromOutsideActivation = e => {
if (isSelecting || isDraggingDock || isDraggingStepper) return;
const path = getEventPath(e);
if (pathIsHiderUI(path)) return;
closeAllMenus(e);
shadowRoot?.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
};
window.addEventListener('touchstart', e => {
const t = e.touches?.[0];
if (!t) return;
outsideTouchX = t.clientX;
outsideTouchY = t.clientY;
outsideTouchMoved = false;
outsideTouchActive = true;
outsideTouchIsUI = pathIsHiderUI(getEventPath(e));
}, { passive: true, capture: true });
window.addEventListener('touchmove', e => {
if (!outsideTouchActive) return;
const t = e.touches?.[0];
if (!t) return;
const distance = Math.hypot(t.clientX - outsideTouchX, t.clientY - outsideTouchY);
if (distance > 12) outsideTouchMoved = true;
}, { passive: true, capture: true });
window.addEventListener('touchcancel', () => {
outsideTouchActive = false;
outsideTouchMoved = false;
outsideTouchIsUI = false;
}, { passive: true, capture: true });
window.addEventListener('touchend', e => {
if (!outsideTouchActive) return;
const wasMoved = outsideTouchMoved;
const wasUI = outsideTouchIsUI || pathIsHiderUI(getEventPath(e));
outsideTouchActive = false;
outsideTouchMoved = false;
outsideTouchIsUI = false;
// A swipe/scroll is never an outside activation.
if (wasMoved || wasUI) return;
lastTouchCloseAt = Date.now();
closeMenusFromOutsideActivation(e);
}, { passive: true, capture: true });
// Desktop clicks and the click synthesized by a mobile tap.
window.addEventListener('click', e => {
const path = getEventPath(e);
const isUI = pathIsHiderUI(path);
if (!isUI) {
// If this click was synthesized from our already-processed touch tap,
// don't execute the close operation twice. Navigation handling below is
// still allowed to run normally.
if (Date.now() - lastTouchCloseAt > 500) {
closeMenusFromOutsideActivation(e);
}
shadowRoot?.querySelectorAll('.h-custom-select').forEach(c => c.classList.remove('is-open'));
}
if (isUI) return;
handleNavigationClick(e);
if (!isSelecting) return;
e.preventDefault(); e.stopPropagation();
if (previewElement === e.target) confirmHideSelectedElement();
else {
previewElement?.classList.remove('hider-preview-highlight'); stepperStack = [];
previewElement = e.target; previewElement.classList.add('hider-preview-highlight'); renderTouchStepperUI();
}
}, true);
window.addEventListener('auxclick', handleNavigationClick, true);
window.addEventListener('submit', e => {
const path = e.composedPath?.() || [];
if (path.some(el => el.id === UI_HOST_ID || el.id === STEPPER_BAR_ID)) return;
const form = e.target, action = form.action || location.href;
if (isDomainBlocked(action)) {
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
logBlockedAttempt(action, 'Blocked Form Submit'); simulateAdWindowSuccess(); showToast('⛔ Blocked Form'); return;
}
if (!isFrozen || isDomainAllowed(action)) return;
e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation();
handleFreezeNavigation(action, 'Form Submit', () => { userApprovedNavigation = true; form.submit(); setTimeout(() => userApprovedNavigation = false, 300); }, simulateAdWindowSuccess);
}, true);
window.addEventListener('touchstart', e => {
if (!e.touches?.[0]) return;
const x = e.touches[0].clientX;
// Only mark edge touches as navigation intent. Ordinary taps/scrolls must not
// alter freeze state or interfere with the script UI.
if (x <= 24 || x >= (win.innerWidth - 24)) {
userApprovedNavigation = true;
setTimeout(() => { userApprovedNavigation = false; }, 900);
}
}, { passive: true, capture: true });
// ---------- History navigation ----------
['back', 'forward', 'go'].forEach(method => {
const orig = history[method];
if (orig) { history[method] = function() { userApprovedNavigation = true; setTimeout(() => { userApprovedNavigation = false; }, 1000); return orig.apply(this, arguments); }; }
});
try { win.navigation?.addEventListener('navigate', e => { if (e.navigationType === 'traverse' || e.navigationType === 'reload') { userApprovedNavigation = true; setTimeout(() => { userApprovedNavigation = false; }, 1000); } }); } catch {}
win.addEventListener('beforeunload', e => {
if (userApprovedNavigation) return;
if (isFrozen && !isDomainAllowed(location.href) && (CACHE.freezeMemory === 'ask' || CACHE.freezeMemory === 'block_all')) { e.preventDefault(); return (e.returnValue = 'Page navigation is currently frozen site-wide.'); }
}, true);
// ---------- URL change detection (SMART) ----------
const checkUrlChange = () => {
if (location.href !== lastUrl) {
lastUrl = location.href;
updateCurrentLocCache();
requestUpdateStyles(false);
if (shadowBy('hider-panel')?.classList.contains('is-visible')) renderList();
}
};
window.addEventListener('pageshow', (e) => {
if (e.persisted || e.type === 'pageshow') {
requestUpdateStyles(true);
}
}, { passive: true });
['pushState', 'replaceState'].forEach(fn => {
const orig = history[fn];
if (orig) history[fn] = function() {
orig.apply(this, arguments);
checkUrlChange();
};
});
window.addEventListener('popstate', () => {
userApprovedNavigation = true;
checkUrlChange();
setTimeout(() => { userApprovedNavigation = false; }, 500);
}, { passive: true });
window.addEventListener('hashchange', checkUrlChange, { passive: true });
// ---------- Low-power heartbeat ----------
function setupLowPowerHeartbeat() {
const scheduleIdle = fn => (win.requestIdleCallback ? win.requestIdleCallback(fn, { timeout: 2000 }) : setTimeout(fn, 1000));
const performHealthCheck = () => {
if (isTop && !doc.getElementById(UI_HOST_ID)) createShadowUI();
if (!doc.getElementById('hider-dynamic-styles')) requestUpdateStyles();
if (!isTop) try { window.top.postMessage({ type: 'HIDER_REQUEST_STATE' }, '*'); } catch {}
scheduleIdle(performHealthCheck);
};
scheduleIdle(performHealthCheck);
}
// ---------- Init ----------
function init() {
updateCurrentLocCache();
installInterceptors();
createShadowUI();
requestUpdateStyles(true);
setupScrollAutoClose();
setupLowPowerHeartbeat();
setupProtectionObserver();
applyAllSettings();
if (CACHE.autoTimeSkipper && featuresEnabled && !isMediaSensitiveDomain()) {
startAutoSkipMonitoring();
}
}
if (doc.readyState === 'loading') doc.addEventListener('DOMContentLoaded', init); else init();
})();