Google AI - Default to AI Mode & 2.5 Pro (Floating UI)

Defaults Google searches to AI Mode, selects 2.5 Pro, and adds a floating button to switch back to Web Search.

当前为 2025-07-29 提交的版本,查看 最新版本

// ==UserScript==
// @name         Google AI - Default to AI Mode & 2.5 Pro (Floating UI)
// @namespace    http://tampermonkey.net/
// @version      4.1
// @description  Defaults Google searches to AI Mode, selects 2.5 Pro, and adds a floating button to switch back to Web Search.
// @author       JP - Discord: @Organism
// @match        https://www.google.com/search*
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- Part 1: Redirect Logic (with loop prevention) ---
    const url = new URL(window.location.href);

    if (sessionStorage.getItem('forceWebSearch') === 'true') {
        sessionStorage.removeItem('forceWebSearch');
    } else {
        if (url.searchParams.has('q') && url.searchParams.get('udm') !== '50') {
            url.searchParams.set('udm', '50');
            window.location.href = url.toString();
            return;
        }
    }

    // --- Part 2: AI Mode Page Setup ---
    function setupAiPage() {
        // This is the proven logic for selecting the model. It runs first.
        function setupModelSelector() {
            const TOGGLE_BUTTON_XPATH = "//div[contains(@class, 'tk4Ybd') and .//span[starts-with(text(),'AI Mode')]]";
            const PRO_MODEL_ITEM_XPATH = "//g-menu-item[.//span[text()='2.5 Pro']]";

            function findElement(xpath) {
                return document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            }

            const observer = new MutationObserver((mutations, obs) => {
                const proMenuItem = findElement(PRO_MODEL_ITEM_XPATH);
                if (proMenuItem) {
                    if (proMenuItem.getAttribute('aria-checked') === 'false') {
                        proMenuItem.click();
                    }
                    obs.disconnect();
                    return;
                }
                const toggleButton = findElement(TOGGLE_BUTTON_XPATH);
                if (toggleButton) {
                    toggleButton.click();
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        }

        // This function creates and injects the floating button UI.
        function createFloatingButton() {
            if (document.getElementById('web-search-flt-btn')) return;

            // Use GM_addStyle to inject CSS for the button.
            GM_addStyle(`
                #web-search-flt-container {
                    position: fixed;
                    bottom: 20px;
                    right: 20px;
                    z-index: 9999;
                    user-select: none;
                }
                #web-search-flt-btn {
                    background-color: rgba(30, 30, 30, 0.85);
                    color: white;
                    border: 1px solid rgba(255, 255, 255, 0.2);
                    border-radius: 8px;
                    padding: 10px 16px;
                    font-family: "Google Sans", Roboto, Arial, sans-serif;
                    font-size: 14px;
                    font-weight: 500;
                    cursor: grab;
                    backdrop-filter: blur(10px);
                    -webkit-backdrop-filter: blur(10px);
                    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
                    transition: background-color 0.2s;
                }
                #web-search-flt-btn:hover {
                    background-color: rgba(50, 50, 50, 0.9);
                }
                #web-search-flt-btn:active {
                    cursor: grabbing;
                    background-color: rgba(0, 0, 0, 0.9);
                }
            `);

            const container = document.createElement('div');
            container.id = 'web-search-flt-container';

            const button = document.createElement('button');
            button.id = 'web-search-flt-btn';
            button.textContent = 'Web Search';

            container.appendChild(button);
            document.body.appendChild(container);

            // --- Click and Drag Logic ---
            let isDragging = false;
            let offsetX, offsetY;
            let clickThreshold = 5; // Pixels a user can move mouse before it's a drag
            let movedDistance = 0;

            const onPointerDown = (e) => {
                isDragging = true;
                movedDistance = 0;
                const rect = container.getBoundingClientRect();
                const pointerX = e.clientX || e.touches[0].clientX;
                const pointerY = e.clientY || e.touches[0].clientY;
                offsetX = pointerX - rect.left;
                offsetY = pointerY - rect.top;
                button.style.cursor = 'grabbing';
            };

            const onPointerMove = (e) => {
                if (!isDragging) return;
                e.preventDefault();
                const pointerX = e.clientX || e.touches[0].clientX;
                const pointerY = e.clientY || e.touches[0].clientY;
                let newX = pointerX - offsetX;
                let newY = pointerY - offsetY;
                movedDistance += Math.abs(newX - container.offsetLeft) + Math.abs(newY - container.offsetTop);
                container.style.left = `${newX}px`;
                container.style.top = `${newY}px`;
                container.style.right = 'auto';
                container.style.bottom = 'auto';
            };

            const onPointerUp = (e) => {
                isDragging = false;
                button.style.cursor = 'grab';
                // If the mouse moved less than the threshold, treat it as a click.
                if (movedDistance < clickThreshold) {
                    sessionStorage.setItem('forceWebSearch', 'true');
                    const currentUrl = new URL(window.location.href);
                    currentUrl.searchParams.delete('udm');
                    window.location.href = currentUrl.toString();
                }
            };

            // Mouse events
            button.addEventListener('mousedown', onPointerDown);
            document.addEventListener('mousemove', onPointerMove);
            document.addEventListener('mouseup', onPointerUp);

            // Touch events
            button.addEventListener('touchstart', onPointerDown, { passive: false });
            document.addEventListener('touchmove', onPointerMove, { passive: false });
            document.addEventListener('touchend', onPointerUp);
        }

        // Run the functions.
        setupModelSelector();
        createFloatingButton();
    }

    // --- Part 3: Execution ---
    if (url.searchParams.get('udm') === '50') {
        document.addEventListener('DOMContentLoaded', setupAiPage);
    }
})();