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.2
// @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.
        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;

            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 (FIXED) ---
            let isDragging = false;
            let startX, startY;
            const clickThreshold = 5;

            const onPointerDown = (e) => {
                const pointerX = e.clientX || e.touches[0].clientX;
                const pointerY = e.clientY || e.touches[0].clientY;
                startX = pointerX;
                startY = pointerY;
                isDragging = false;

                const rect = container.getBoundingClientRect();
                const offsetX = pointerX - rect.left;
                const offsetY = pointerY - rect.top;

                button.style.cursor = 'grabbing';

                const onPointerMove = (moveEvent) => {
                    const moveX = moveEvent.clientX || moveEvent.touches[0].clientX;
                    const moveY = moveEvent.clientY || moveEvent.touches[0].clientY;

                    if (Math.abs(moveX - startX) > clickThreshold || Math.abs(moveY - startY) > clickThreshold) {
                        isDragging = true;
                    }

                    if (isDragging) {
                        moveEvent.preventDefault();
                        let newX = moveX - offsetX;
                        let newY = moveY - offsetY;
                        container.style.left = `${newX}px`;
                        container.style.top = `${newY}px`;
                        container.style.right = 'auto';
                        container.style.bottom = 'auto';
                    }
                };

                const onPointerUp = () => {
                    document.removeEventListener('mousemove', onPointerMove);
                    document.removeEventListener('mouseup', onPointerUp);
                    document.removeEventListener('touchmove', onPointerMove);
                    document.removeEventListener('touchend', onPointerUp);
                    button.style.cursor = 'grab';
                };

                document.addEventListener('mousemove', onPointerMove);
                document.addEventListener('mouseup', onPointerUp);
                document.addEventListener('touchmove', onPointerMove, { passive: false });
                document.addEventListener('touchend', onPointerUp);
            };

            button.addEventListener('click', (e) => {
                if (isDragging) {
                    e.preventDefault();
                    e.stopPropagation();
                    return;
                }
                sessionStorage.setItem('forceWebSearch', 'true');
                const currentUrl = new URL(window.location.href);
                currentUrl.searchParams.delete('udm');
                window.location.href = currentUrl.toString();
            });

            button.addEventListener('mousedown', onPointerDown);
            button.addEventListener('touchstart', onPointerDown, { passive: false });
        }

        setupModelSelector();
        createFloatingButton();
    }

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