// ==UserScript==
// @name Enhanced AutoScroll (Red/Black HUD, Allowlist Mode)
// @namespace https://greasyfork.org/users/1513610
// @version 3.0
// @description Smooth auto-scroll with HUD controls, hotkeys, and site allowlist (dark red theme, compact HUD). Press S to toggle, [ ] adjust speed, + - adjust step, R reset, H hide HUD. Allowlist via console command allowSite("domain"). Manage with listAllowSites(), removeSite("domain"), resetAllowlist(). Console logs info on each domain 👇
// @author NAABO
// @match *://*/*
// @grant none
// ==/UserScript==
/*
📌 Features:
- Press 'S' to start/pause smooth scrolling.
- '[' / ']' decrease/increase scroll speed.
- '+' / '-' adjust speed step size.
- 'R' resets to default speed.
- 'H' shows/hides the HUD.
- Respects "prefers-reduced-motion".
📌 Allowlist Mode:
- Script runs ONLY on sites added to the allowlist.
- To allowlist a site, open DevTools console and run:
allowSite("example.com");
📌 Console Helpers:
- allowSite("example.com") → add a site to allowlist
- removeSite("example.com") → remove a site from allowlist
- listAllowSites() → list all allowlisted sites
- resetAllowlist() → clear the allowlist
*/
(function () {
'use strict';
/************* Configuration *************/
const CONFIG = {
STORAGE_KEY: 'enhanced_autoscroll_config',
ALLOWLIST_KEY: 'enhanced_autoscroll_allowlist',
DEFAULT_SPEED: 250,
DEFAULT_SPEED_STEP: 50,
MIN_SPEED_STEP: 10,
MAX_SPEED_STEP: 100,
HUD_POSITIONS: ['bottom-right', 'bottom-left', 'top-right', 'top-left'],
FLASH_DURATION: 1500,
};
/************* State *************/
const state = {
scrolling: false,
speed: CONFIG.DEFAULT_SPEED,
speedStep: CONFIG.DEFAULT_SPEED_STEP,
hud: null,
hudPositionIndex: 0,
animationFrame: null,
allowlist: JSON.parse(localStorage.getItem(CONFIG.ALLOWLIST_KEY) || '[]'),
};
/************* Utils *************/
function saveConfig() {
const data = {
speed: state.speed,
speedStep: state.speedStep,
hudPositionIndex: state.hudPositionIndex,
};
localStorage.setItem(CONFIG.STORAGE_KEY, JSON.stringify(data));
}
function loadConfig() {
const data = JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEY) || '{}');
if (data.speed) state.speed = data.speed;
if (data.speedStep) state.speedStep = data.speedStep;
if (data.hudPositionIndex != null) state.hudPositionIndex = data.hudPositionIndex;
}
function isAllowedSite() {
return state.allowlist.includes(location.hostname);
}
function allowSite(domain) {
if (!domain) domain = location.hostname;
if (!state.allowlist.includes(domain)) {
state.allowlist.push(domain);
localStorage.setItem(CONFIG.ALLOWLIST_KEY, JSON.stringify(state.allowlist));
console.log(`✅ ${domain} added to Enhanced AutoScroll allowlist. Reload the page to activate.`);
} else {
console.log(`⚠️ ${domain} is already in the allowlist.`);
}
}
function removeSite(domain) {
if (!domain) domain = location.hostname;
if (state.allowlist.includes(domain)) {
state.allowlist = state.allowlist.filter(d => d !== domain);
localStorage.setItem(CONFIG.ALLOWLIST_KEY, JSON.stringify(state.allowlist));
console.log(`🗑️ ${domain} removed from Enhanced AutoScroll allowlist.`);
} else {
console.log(`⚠️ ${domain} was not in the allowlist.`);
}
}
function listAllowSites() {
if (!state.allowlist.length) {
console.log("📭 Allowlist is empty.");
} else {
console.log("📜 Enhanced AutoScroll allowlist:");
state.allowlist.forEach((site, i) => {
console.log(`${i + 1}. ${site}`);
});
}
}
function resetAllowlist() {
state.allowlist = [];
localStorage.setItem(CONFIG.ALLOWLIST_KEY, JSON.stringify([]));
console.log("♻️ Enhanced AutoScroll allowlist reset to empty.");
}
// Expose helpers to console
window.allowSite = allowSite;
window.removeSite = removeSite;
window.listAllowSites = listAllowSites;
window.resetAllowlist = resetAllowlist;
function flashHUD(msg) {
if (!state.hud) return;
const div = document.createElement('div');
div.textContent = msg;
div.style.cssText = `
position:absolute; top:-24px; left:50%; transform:translateX(-50%);
background:#b91c1c; color:#fff; padding:3px 6px;
border-radius:4px; font-size:11px; pointer-events:none;
`;
state.hud.appendChild(div);
setTimeout(() => div.remove(), CONFIG.FLASH_DURATION);
}
function getHUDPositionStyles() {
switch (CONFIG.HUD_POSITIONS[state.hudPositionIndex]) {
case 'bottom-left': return 'bottom:12px; left:12px;';
case 'top-right': return 'top:12px; right:12px;';
case 'top-left': return 'top:12px; left:12px;';
default: return 'bottom:12px; right:12px;';
}
}
/************* HUD *************/
function createHUD() {
if (state.hud) state.hud.remove();
state.hud = document.createElement('div');
state.hud.id = 'enhanced-autoscroll-hud';
state.hud.style.cssText = `
position:fixed; ${getHUDPositionStyles()} z-index:999999;
padding:8px 10px; background:#111; color:#fff;
font-family:monospace, system-ui, sans-serif;
font-size:12px; border-radius:8px;
box-shadow:0 4px 12px rgba(0,0,0,0.6);
border:1px solid #b91c1c; opacity:0.95;
max-width:300px;
`;
state.hud.innerHTML = `
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:6px;">
<div id="hud-status" style="font-weight:bold; font-size:12px; color:#ef4444;">PAUSED</div>
<button id="hud-close" style="
background:none; border:none; color:#ef4444; font-size:14px;
cursor:pointer; padding:0; line-height:1;
" title="Close">×</button>
</div>
<div id="hud-speed" style="margin-bottom:6px; font-size:11px; color:#aaa;"></div>
<div id="hud-buttons" style="display:flex; flex-wrap:wrap; gap:4px;">
${makeButton("S", "toggle", "Toggle scroll")}
${makeButton("[", "speed-down", "Decrease speed")}
${makeButton("]", "speed-up", "Increase speed")}
${makeButton("R", "reset", "Reset speed")}
${makeButton("H", "hide-hud", "Hide HUD")}
${makeButton("+", "step-up", "Increase step")}
${makeButton("-", "step-down", "Decrease step")}
</div>
`;
document.body.appendChild(state.hud);
state.hud.querySelector('#hud-close').addEventListener('click', shutdownScript);
setupHUDButtons();
updateHUD();
}
function makeButton(label, action, title) {
return `
<button class="hud-btn" data-action="${action}" title="${title}"
style="
background:#1a1a1a;
border:1px solid #b91c1c;
color:#f87171; font-size:12px;
padding:2px 6px; border-radius:4px;
cursor:pointer; transition:all 0.2s;
"
onmouseover="this.style.background='#b91c1c'; this.style.color='#fff';"
onmouseout="this.style.background='#1a1a1a'; this.style.color='#f87171';"
>${label}</button>
`;
}
function setupHUDButtons() {
state.hud.querySelectorAll('.hud-btn').forEach(btn => {
btn.addEventListener('click', () => {
handleAction(btn.dataset.action);
});
});
}
function updateHUD() {
if (!state.hud) return;
const status = state.hud.querySelector('#hud-status');
status.textContent = state.scrolling ? 'SCROLLING' : 'PAUSED';
status.style.color = state.scrolling ? '#22c55e' : '#ef4444';
state.hud.querySelector('#hud-speed').textContent = `Speed: ${state.speed}px/s | Step: ${state.speedStep}`;
}
/************* Core *************/
function handleAction(action) {
switch (action) {
case 'toggle': toggleScroll(); break;
case 'speed-down': changeSpeed(-state.speedStep); break;
case 'speed-up': changeSpeed(state.speedStep); break;
case 'reset': resetSpeed(); break;
case 'hide-hud': toggleHUD(); break;
case 'step-up': changeStep(1); break;
case 'step-down': changeStep(-1); break;
}
saveConfig();
updateHUD();
}
function toggleScroll() {
state.scrolling = !state.scrolling;
if (state.scrolling) requestScroll();
else cancelAnimationFrame(state.animationFrame);
updateHUD();
}
function changeSpeed(delta) {
state.speed = Math.max(0, state.speed + delta);
flashHUD(`Speed: ${state.speed}px/s`);
}
function resetSpeed() {
state.speed = CONFIG.DEFAULT_SPEED;
flashHUD("🔄 Speed reset");
}
function changeStep(delta) {
state.speedStep = Math.min(CONFIG.MAX_SPEED_STEP, Math.max(CONFIG.MIN_SPEED_STEP, state.speedStep + delta));
flashHUD(`Step: ${state.speedStep}`);
}
function toggleHUD() {
state.hud.style.display = state.hud.style.display === 'none' ? '' : 'none';
}
function requestScroll() {
const step = state.speed / 60;
window.scrollBy(0, step);
state.animationFrame = requestAnimationFrame(requestScroll);
}
function shutdownScript() {
cancelAnimationFrame(state.animationFrame);
if (state.hud) state.hud.remove();
document.removeEventListener('keydown', keyHandler);
}
/************* Keyboard *************/
function keyHandler(e) {
if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) return;
switch (e.key) {
case 's': case 'S': handleAction('toggle'); break;
case '[': handleAction('speed-down'); break;
case ']': handleAction('speed-up'); break;
case 'r': case 'R': handleAction('reset'); break;
case 'h': case 'H': handleAction('hide-hud'); break;
case '+': case '=': handleAction('step-up'); break;
case '-': case '_': handleAction('step-down'); break;
}
}
/************* Init *************/
function init() {
if (!isAllowedSite()) {
console.log(`⚪ Enhanced AutoScroll inactive on ${location.hostname}`);
console.log(`👉 To allowlist, run: allowSite("${location.hostname}")`);
console.log(`👉 To list allowlisted sites, run: listAllowSites()`);
return;
}
console.log(`✅ Enhanced AutoScroll active on ${location.hostname}`);
console.log(`⚙️ Console helpers: allowSite("${location.hostname}"), removeSite("${location.hostname}"), listAllowSites(), resetAllowlist()`);
loadConfig();
createHUD();
document.addEventListener('keydown', keyHandler);
updateHUD();
}
init();
})();