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