Google Lens Plus

Collapsible resolution list with auto-scroll, card highlighting, and menu sync.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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);
})();