Minimal floating draggable button (top-left) to open popup with images
// ==UserScript==
// @name Image Extract
// @namespace Image Extract
// @version 1.0
// @description Minimal floating draggable button (top-left) to open popup with images
// @match *://*/*
// ==/UserScript==
(function() {
'use strict';
function createUI(images) {
if (document.querySelector('#image-extract-panel')) return;
const panel = document.createElement('div');
panel.id = 'image-extract-panel';
panel.style.position = 'fixed';
panel.style.top = '10px';
panel.style.left = '10px';
panel.style.background = '#222';
panel.style.color = '#eee';
panel.style.border = '1px solid #555';
panel.style.padding = '10px';
panel.style.zIndex = 999999;
panel.style.maxHeight = '400px';
panel.style.overflowY = 'auto';
panel.style.width = '300px';
const closeBtn = document.createElement('span');
closeBtn.textContent = '×';
closeBtn.style.cursor = 'pointer';
closeBtn.style.float = 'right';
closeBtn.style.fontSize = '18px';
closeBtn.style.fontWeight = 'bold';
closeBtn.onclick = () => panel.remove();
panel.appendChild(closeBtn);
const title = document.createElement('div');
title.textContent = `Found ${images.length} images`;
title.style.fontWeight = 'bold';
panel.appendChild(title);
const list = document.createElement('div');
images.forEach(src => {
const thumb = document.createElement('img');
thumb.src = src;
thumb.style.maxWidth = '100%';
thumb.style.margin = '5px 0';
list.appendChild(thumb);
});
panel.appendChild(list);
document.body.appendChild(panel);
}
function extractImages() {
const imgs = Array.from(document.querySelectorAll('img'))
.map(img => img.src)
.filter(src => src && src.startsWith('http'));
const bgImgs = Array.from(document.querySelectorAll('*'))
.map(el => getComputedStyle(el).backgroundImage)
.filter(bg => bg && bg.startsWith('url'))
.map(bg => bg.replace(/^url\(["']?/, '').replace(/["']?\)$/, ''));
const allImgs = [...imgs, ...bgImgs];
if (allImgs.length > 0) {
createUI(allImgs);
} else {
alert("No images found!");
}
}
// Minimal floating trigger button (top-left)
const triggerBtn = document.createElement('button');
triggerBtn.textContent = '⬇️';
triggerBtn.style.position = 'fixed';
triggerBtn.style.top = '0px';
triggerBtn.style.left = '0px';
triggerBtn.style.bottom = 'auto';
triggerBtn.style.right = 'auto';
triggerBtn.style.zIndex = 999999;
triggerBtn.style.background = 'transparent';
triggerBtn.style.color = '#fff';
triggerBtn.style.border = 'none';
triggerBtn.style.fontSize = '24px';
triggerBtn.style.cursor = 'move';
document.body.appendChild(triggerBtn);
triggerBtn.onclick = extractImages;
// Drag logic
let isDragging = false, offsetX, offsetY;
triggerBtn.addEventListener('mousedown', e => {
isDragging = true;
offsetX = e.clientX - triggerBtn.getBoundingClientRect().left;
offsetY = e.clientY - triggerBtn.getBoundingClientRect().top;
});
document.addEventListener('mousemove', e => {
if (isDragging) {
triggerBtn.style.left = (e.clientX - offsetX) + 'px';
triggerBtn.style.top = (e.clientY - offsetY) + 'px';
triggerBtn.style.bottom = 'auto';
triggerBtn.style.right = 'auto';
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
// Mobile touch support
triggerBtn.addEventListener('touchstart', e => {
isDragging = true;
const touch = e.touches[0];
offsetX = touch.clientX - triggerBtn.getBoundingClientRect().left;
offsetY = touch.clientY - triggerBtn.getBoundingClientRect().top;
});
document.addEventListener('touchmove', e => {
if (isDragging) {
const touch = e.touches[0];
triggerBtn.style.left = (touch.clientX - offsetX) + 'px';
triggerBtn.style.top = (touch.clientY - offsetY) + 'px';
triggerBtn.style.bottom = 'auto';
triggerBtn.style.right = 'auto';
}
});
document.addEventListener('touchend', () => {
isDragging = false;
});
})();