Created
December 19, 2025 03:10
-
-
Save un4ckn0wl3z/0c2b5b4fe359d8c92f41d038e7c1e030 to your computer and use it in GitHub Desktop.
Revisions
-
un4ckn0wl3z created this gist
Dec 19, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,148 @@ // ==UserScript== // @name Ctrl Drag Crop + Auto Scroll (Anchored) // @namespace https://un4ckn0wl3z.dev/tm/crop-links-scroll-fixed // @version 1.3 // @description Ctrl + drag to crop, auto-scroll with anchored start point // @match *://*/* // @grant GM_setClipboard // ==/UserScript== (function () { 'use strict'; let overlay, box; let startX, startY_doc; let selecting = false; let scrollTimer = null; const EDGE = 40; const SPEED = 20; const INTERVAL = 16; function createOverlay() { overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; inset: 0; z-index: 999999; cursor: crosshair; background: rgba(0,0,0,0.05); `; box = document.createElement('div'); box.style.cssText = ` position: absolute; border: 2px dashed red; background: rgba(255,0,0,0.15); pointer-events: none; `; overlay.appendChild(box); document.body.appendChild(overlay); } function destroyOverlay() { stopScroll(); overlay?.remove(); overlay = null; box = null; } function intersect(a, b) { return !( b.right < a.left || b.left > a.right || b.bottom < a.top || b.top > a.bottom ); } function extractLinks(rect) { return [...document.querySelectorAll('a')] .filter(a => intersect(rect, a.getBoundingClientRect())) .map(a => ({ href: a.href, text: a.innerText.trim() })); } function startScroll(dir) { if (scrollTimer) return; scrollTimer = setInterval(() => { window.scrollBy(0, dir * SPEED); }, INTERVAL); } function stopScroll() { clearInterval(scrollTimer); scrollTimer = null; } document.addEventListener('mousedown', e => { if (!e.ctrlKey || e.button !== 0) return; e.preventDefault(); selecting = true; startX = e.clientX; startY_doc = e.clientY + window.scrollY; createOverlay(); }); document.addEventListener('mousemove', e => { if (!selecting) return; const curX = e.clientX; const curY_doc = e.clientY + window.scrollY; const left = Math.min(startX, curX); const right = Math.max(startX, curX); const top_doc = Math.min(startY_doc, curY_doc); const bottom_doc = Math.max(startY_doc, curY_doc); // Convert document Y back to viewport Y box.style.left = `${left}px`; box.style.top = `${top_doc - window.scrollY}px`; box.style.width = `${right - left}px`; box.style.height = `${bottom_doc - top_doc}px`; // Auto scroll if (e.clientY > window.innerHeight - EDGE) { startScroll(1); } else if (e.clientY < EDGE) { startScroll(-1); } else { stopScroll(); } }); document.addEventListener('mouseup', e => { if (!selecting) return; selecting = false; stopScroll(); const endY_doc = e.clientY + window.scrollY; const rect = { left: Math.min(startX, e.clientX), right: Math.max(startX, e.clientX), top: Math.min(startY_doc, endY_doc) - window.scrollY, bottom: Math.max(startY_doc, endY_doc) - window.scrollY }; const links = extractLinks(rect); const output = links //.map(l => `${l.href}${l.text ? ' | ' + l.text : ''}`) .map(l => `${l.href}`) .join('\n'); GM_setClipboard(output); console.log('Extracted links:', links); alert(`Extracted ${links.length} links (copied)`); destroyOverlay(); }); })();