Collapsible resolution list with auto-scroll, card highlighting, and menu sync.
// ==UserScript==
// @name Google Lens Plus
// @version 1.5
// @description Collapsible resolution list with auto-scroll, card highlighting, and menu sync.
// @author LF2005 & Acoolrocket
// @license MIT
// @namespace https://greasyfork.org/users/731843
// @match https://www.google.com/search?*
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
let lastHighlightedCard = null;
let lastHighlightedMenuItem = null;
function verifyPageStructure() {
const rsoDiv = document.getElementById('rso');
if (!rsoDiv) return false;
const ulsxyfDivs = rsoDiv.querySelectorAll('div.ULSxyf');
return ulsxyfDivs.length !== 0;
}
function extractResolutions() {
const outerSpans = document.querySelectorAll('span.cyspcb.DH9lqb.VBZLA');
const resolutions = new Set();
const resolutionRegex = /^\d[\d,]*\s*x\s*\d[\d,]*$/i;
outerSpans.forEach(outerSpan => {
const innerSpan = outerSpan.querySelector('span');
if (innerSpan) {
const text = innerSpan.innerHTML.trim();
if (resolutionRegex.test(text)) {
resolutions.add(text);
}
}
});
return Array.from(resolutions);
}
function sortResolutions(resolutions) {
return resolutions.sort((a, b) => {
const [aWidth, aHeight] = a.replace(/,/g, '').split('x').map(Number);
const [bWidth, bHeight] = b.replace(/,/g, '').split('x').map(Number);
return (bWidth * bHeight) - (aWidth * aHeight);
});
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
return false;
}
}
function injectResultsColumn(resolutions) {
const column = document.createElement('div');
column.id = 'resolutions-column';
Object.assign(column.style, {
position: 'fixed',
top: '20px',
right: '20px',
width: '130px',
height: '40px',
backgroundColor: '#000000',
color: 'white',
boxShadow: '0 4px 15px rgba(0, 0, 0, 0.5)',
padding: '10px',
overflow: 'hidden',
zIndex: '9999',
borderRadius: '8px',
transition: 'height 0.3s ease-in-out',
border: '1px solid #333',
fontFamily: 'Arial, sans-serif'
});
const title = document.createElement('div');
title.textContent = 'Resolutions ▼';
title.style.cssText = 'cursor:pointer; font-weight:bold; text-align:center; margin-bottom:15px; font-size:13px;';
column.appendChild(title);
const listContainer = document.createElement('div');
listContainer.style.cssText = 'overflow-y:auto; height:calc(100% - 40px); padding-right: 5px;';
resolutions.forEach(resolution => {
const resEl = document.createElement('div');
resEl.textContent = resolution;
const setDefaultStyle = (el) => {
Object.assign(el.style, {
marginBottom: '6px', cursor: 'pointer', padding: '6px', borderRadius: '4px',
backgroundColor: '#1a1a1a', fontSize: '11px', textAlign: 'center',
transition: 'all 0.2s', border: '1px solid transparent'
});
};
setDefaultStyle(resEl);
resEl.addEventListener('mouseenter', () => {
if (lastHighlightedMenuItem !== resEl) resEl.style.backgroundColor = '#2a2a2a';
});
resEl.addEventListener('mouseleave', () => {
if (lastHighlightedMenuItem !== resEl) resEl.style.backgroundColor = '#1a1a1a';
});
resEl.addEventListener('click', async () => {
await copyToClipboard(resolution);
const originalText = resEl.textContent;
resEl.textContent = 'Copied!';
setTimeout(() => resEl.textContent = originalText, 1000);
if (lastHighlightedMenuItem) setDefaultStyle(lastHighlightedMenuItem);
resEl.style.backgroundColor = '#0066ff';
resEl.style.border = '1px solid #ffffff';
lastHighlightedMenuItem = resEl;
const xpath = `//span[contains(text(), "${resolution}")]`;
const result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
const targetSpan = result.singleNodeValue;
if (targetSpan) {
const card = targetSpan.closest('.ULSxyf') || targetSpan.parentElement;
if (lastHighlightedCard) {
lastHighlightedCard.style.backgroundColor = "";
lastHighlightedCard.style.outline = "";
lastHighlightedCard.style.boxShadow = "";
}
card.scrollIntoView({ behavior: "smooth", block: "center" });
card.style.backgroundColor = "rgba(0, 102, 255, 0.1)";
card.style.outline = "3px solid #0066ff";
card.style.boxShadow = "0 0 15px rgba(0, 102, 255, 0.5)";
card.style.borderRadius = "8px";
card.style.transition = "all 0.4s ease";
lastHighlightedCard = card;
}
});
listContainer.appendChild(resEl);
});
column.appendChild(listContainer);
let isFolded = true;
title.addEventListener('click', () => {
column.style.height = isFolded ? '80vh' : '40px';
title.textContent = isFolded ? 'Resolutions ▲' : 'Resolutions ▼';
isFolded = !isFolded;
});
document.body.appendChild(column);
}
async function main() {
if (!verifyPageStructure()) return;
const sorted = sortResolutions(extractResolutions());
injectResultsColumn(sorted);
}
window.addEventListener('load', main);
})();