Google Search Enhancer

Adds a collapsible Google Enhancer panel with optional AI result filter, new tab option, and saved links with better star placement and popup UI.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey to install this script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name Google Search Enhancer
// @namespace https://codymkw.nekoweb.org/
// @version 4.4
// @description Adds a collapsible Google Enhancer panel with optional AI result filter, new tab option, and saved links with better star placement and popup UI.
// @author Cody
// @match https://www.google.com/search*
// @match https://www.google.*.*/search*
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// ==/UserScript==
(function () {
    'use strict';
    const STORAGE_KEY = 'googleEnhancerSettings';
    const LINKS_KEY = 'googleSavedLinks';
    const DEFAULT_SETTINGS = {
        hideAI: true,
        newTab: false
    };
    const loadSettings = () => JSON.parse(GM_getValue(STORAGE_KEY, JSON.stringify(DEFAULT_SETTINGS)));
    const saveSettings = (settings) => GM_setValue(STORAGE_KEY, JSON.stringify(settings));
    const settings = loadSettings();
    const loadLinks = () => JSON.parse(GM_getValue(LINKS_KEY, '[]'));
    const saveLinks = (links) => GM_setValue(LINKS_KEY, JSON.stringify(links));
    // === HANDLE FILTER ===
    const params = new URLSearchParams(window.location.search);
    const q = params.get('q');
    // If hideAI enabled, ensure "-ai" is added
    if (settings.hideAI && q && !q.toLowerCase().includes('-ai')) {
        params.set('q', `${q} -ai`);
        window.location.replace(`${window.location.pathname}?${params.toString()}`);
        return;
    }
    // If hideAI disabled but "-ai" is in query, remove it
    if (!settings.hideAI && q && q.toLowerCase().includes('-ai')) {
        const cleaned = q.replace(/\s*-ai\s*/gi, ' ').trim();
        params.set('q', cleaned);
        window.location.replace(`${window.location.pathname}?${params.toString()}`);
        return;
    }
    // === NEW TAB OPTION ===
    if (settings.newTab) {
        const observer = new MutationObserver(() => {
            document.querySelectorAll('a').forEach(a => {
                if (a.href && !a.target) a.target = '_blank';
            });
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }
    // === ADD SAVE ICONS TO RESULTS ===
    const addSaveIcons = () => {
        document.querySelectorAll('a h3').forEach(h3 => {
            const link = h3.closest('a');
            if (!link || h3.dataset.hasSaveIcon) return;
            h3.dataset.hasSaveIcon = true;
            const wrapper = document.createElement('span');
            wrapper.style.position = 'relative';
            wrapper.style.display = 'inline-block';
            h3.parentNode.insertBefore(wrapper, h3);
            wrapper.appendChild(h3);
            const star = document.createElement('span');
            star.textContent = '⭐';
            star.style.cursor = 'pointer';
            star.style.position = 'absolute';
            star.style.right = '-24px';
            star.style.top = '2px';
            star.style.opacity = '0.6';
            star.style.fontSize = '16px';
            star.title = 'Save for later';
            star.style.transition = 'opacity 0.2s, transform 0.2s';
            const savedLinks = loadLinks();
            if (savedLinks.find(l => l.url === link.href)) {
                star.style.opacity = '1';
                star.style.filter = 'drop-shadow(0 0 2px gold)';
            }
            star.addEventListener('mouseenter', () => {
                star.style.transform = 'scale(1.2)';
            });
            star.addEventListener('mouseleave', () => {
                star.style.transform = 'scale(1)';
            });
            star.addEventListener('click', (e) => {
                e.stopPropagation();
                e.preventDefault();
                const links = loadLinks();
                const existing = links.find(l => l.url === link.href);
                if (existing) {
                    saveLinks(links.filter(l => l.url !== link.href));
                    star.style.opacity = '0.6';
                    star.style.filter = 'none';
                } else {
                    links.push({ title: h3.innerText, url: link.href });
                    saveLinks(links);
                    star.style.opacity = '1';
                    star.style.filter = 'drop-shadow(0 0 2px gold)';
                }
            });
            wrapper.appendChild(star);
        });
    };
    const resultObserver = new MutationObserver(addSaveIcons);
    resultObserver.observe(document.body, { childList: true, subtree: true });
    addSaveIcons();
    // === CONTROL PANEL ===
    const panel = document.createElement('div');
    panel.innerHTML = `
        <div id="ge-toggle" style="
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: var(--bg);
            color: var(--text);
            border: 1px solid var(--border);
            border-radius: 50%;
            width: 40px;
            height: 40px;
            text-align: center;
            line-height: 38px;
            font-size: 18px;
            cursor: pointer;
            box-shadow: 0 2px 8px rgba(0,0,0,0.3);
            z-index: 99999;
        ">⚙️</div>
        <div id="ge-panel" style="
            position: fixed;
            bottom: 70px;
            right: 20px;
            z-index: 9999;
            background: var(--bg);
            color: var(--text);
            border: 1px solid var(--border);
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.2);
            padding: 10px 12px;
            font-family: sans-serif;
            font-size: 13px;
            min-width: 190px;
            display: none;
        ">
            <strong>⚙️ Google Enhancer</strong>
            <div style="margin-top: 8px;">
                <label><input type="checkbox" id="hideAI" ${settings.hideAI ? 'checked' : ''}> Hide AI Results</label><br>
                <label><input type="checkbox" id="newTab" ${settings.newTab ? 'checked' : ''}> Open results in new tab</label>
            </div>
            <button id="showSavedLinks" style="
                margin-top: 10px;
                width: 100%;
                border: none;
                border-radius: 6px;
                padding: 6px;
                background: var(--button-bg);
                color: var(--button-text);
                cursor: pointer;
            ">📁 Saved Links</button>
        </div>
    `;
    document.body.appendChild(panel);
    // === DARK/LIGHT THEME ===
    const dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    const vars = dark
        ? { bg: '#222', text: '#eee', border: '#555', buttonBg: '#333', buttonText: '#fff' }
        : { bg: '#fff', text: '#222', border: '#ccc', buttonBg: '#f5f5f5', buttonText: '#000' };
    for (const [k, v] of Object.entries(vars)) {
        panel.querySelector('#ge-panel').style.setProperty(`--${k}`, v);
        panel.querySelector('#ge-toggle').style.setProperty(`--${k}`, v);
    }
    // === TOGGLE PANEL ===
    const toggleBtn = document.getElementById('ge-toggle');
    const mainPanel = document.getElementById('ge-panel');
    toggleBtn.addEventListener('click', () => {
        mainPanel.style.display = mainPanel.style.display === 'none' ? 'block' : 'none';
    });
    // === SETTINGS ===
    ['hideAI', 'newTab'].forEach(key => {
        document.getElementById(key).addEventListener('change', e => {
            settings[key] = e.target.checked;
            saveSettings(settings);
            location.reload();
        });
    });
    // === SAVED LINKS POPUP ===
    const makeSavedPopup = () => {
        const popup = document.createElement('div');
        popup.innerHTML = `
            <div id="savedLinksPopup" style="
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: ${vars.bg};
                color: ${vars.text};
                border: 1px solid ${vars.border};
                border-radius: 10px;
                box-shadow: 0 4px 20px rgba(0,0,0,0.4);
                padding: 15px;
                z-index: 100000;
                width: 320px;
                max-height: 400px;
                overflow-y: auto;
            ">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <strong>📁 Saved Links</strong>
                    <button id="closePopup" style="
                        background: transparent;
                        color: ${vars.text};
                        border: none;
                        font-size: 16px;
                        cursor: pointer;
                    ">❎</button>
                </div>
                <div id="linksContainer" style="margin-top: 10px;"></div>
                <button id="clearAllLinks" style="
                    margin-top: 10px;
                    width: 100%;
                    border: none;
                    border-radius: 6px;
                    padding: 6px;
                    background: ${vars.buttonBg};
                    color: ${vars.buttonText};
                    cursor: pointer;
                ">🗑️ Clear All</button>
            </div>
        `;
        document.body.appendChild(popup);
        const container = popup.querySelector('#linksContainer');
        const links = loadLinks();
        if (links.length === 0) {
            container.innerHTML = `<p style="opacity: 0.7;">No saved links yet.</p>`;
        } else {
            links.forEach((l, i) => {
                const div = document.createElement('div');
                div.style.marginBottom = '6px';
                div.innerHTML = `
                    <a href="${l.url}" target="_blank" style="color: ${vars.text}; text-decoration: none;">${l.title}</a>
                    <button data-index="${i}" style="margin-left: 8px; font-size: 12px;">❌</button>
                `;
                container.appendChild(div);
            });
            container.querySelectorAll('button[data-index]').forEach(btn => {
                btn.addEventListener('click', e => {
                    const idx = +e.target.dataset.index;
                    const links = loadLinks();
                    links.splice(idx, 1);
                    saveLinks(links);
                    popup.remove();
                    makeSavedPopup();
                });
            });
        }
        popup.querySelector('#clearAllLinks').addEventListener('click', () => {
            if (confirm('Clear all saved links?')) {
                saveLinks([]);
                popup.remove();
            }
        });
        popup.querySelector('#closePopup').addEventListener('click', () => {
            popup.remove();
        });
    };
    document.getElementById('showSavedLinks').addEventListener('click', makeSavedPopup);
})();