Created
December 19, 2025 03:10
-
-
Save un4ckn0wl3z/0c2b5b4fe359d8c92f41d038e7c1e030 to your computer and use it in GitHub Desktop.
Crop and Get links in Webpage
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 characters
| // ==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(); | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment