Search with Gemini

Lets you use Gemini as a custom search engine. Reads 'prompt' parameter from URL and automatically submits it to Gemini chat.

// ==UserScript==
// @name         Search with Gemini
// @namespace    https://github.com/alexey-kudryavtsev/
// @version      2025.07.27.1
// @description  Lets you use Gemini as a custom search engine. Reads 'prompt' parameter from URL and automatically submits it to Gemini chat.
// @author       Alexey Kudryavtsev
// @match        https://gemini.google.com/*
// @grant        none
// @run-at       document-start
// @license      Apache-2.0
// @homepageURL  https://github.com/alexey-kudryavtsev/search-with-gemini
// @supportURL   https://github.com/alexey-kudryavtsev/search-with-gemini/issues
// ==/UserScript==


(function() {
    'use strict';

    // --- Configuration ---
    const log = (message) => console.log(`[Gemini Userscript] ${message}`);
    const PROMPT_SELECTOR = '.ql-editor[contenteditable="true"] > p';
    const SUBMIT_BUTTON_SELECTOR = 'button[aria-label="Send message"]';

    log('Script initialized (document-start).');

    // Ensure window.geminiScript object exists
    if (typeof window.geminiScript === 'undefined') {
        window.geminiScript = {};
    }

    // --- Early Execution: Extract prompt and clean URL ---
    const currentUrl = new URL(window.location.href);
    const promptValue = currentUrl.searchParams.get('prompt');

    if (promptValue) {
        log(`Found 'prompt' URL parameter during early execution: "${promptValue}"`);
        window.geminiScript.currentPrompt = promptValue; // Store the prompt
        currentUrl.searchParams.delete('prompt'); // Remove the 'prompt' parameter

        // Replace the URL in the browser's history without reloading
        const cleanUrl = currentUrl.pathname + currentUrl.search + currentUrl.hash;
        history.replaceState(null, '', cleanUrl);
        log(`🧹 URL cleaned. New URL: ${cleanUrl}`);
    } else {
        log("No 'prompt' URL parameter found during early execution. Script is idle for prefill.");
        window.geminiScript.currentPrompt = null; // Ensure it's explicitly null if not found
    }

    /**
     * Finds the prompt input, fills it, finds the submit button, and clicks it.
     * This function will be called AFTER the document is ready.
     * @param {string} textToFill - The prompt text to inject.
     */
    function prefillThenSubmit(textToFill) {
        log('🕵️‍♂️ Starting observer for the input field...');
        const inputObserver = new MutationObserver((mutations, inputObs) => {
            const promptInput = document.querySelector(PROMPT_SELECTOR);
            if (promptInput) {
                log('✅ Step 1: Input field found.');
                inputObs.disconnect();

                // Inject prompt text
                promptInput.textContent = textToFill;
                log(`📝 Text set to: "${textToFill}"`);

                // Now, wait for the submit button to become available
                const submitObserver = new MutationObserver((mutations, submitObs) => {
                    const submitButton = document.querySelector(SUBMIT_BUTTON_SELECTOR);
                    // Also check that it's not disabled
                    if (submitButton && !submitButton.disabled) {
                        log('✅ Step 2: Submit button found and enabled.');
                        submitObs.disconnect();

                        submitButton.click();
                        log('🚀 Prompt submitted!');
                    }
                });

                submitObserver.observe(document.body, {
                    childList: true,
                    subtree: true,
                    attributes: true // Also watch for attribute changes like 'disabled'
                });
            }
        });

        inputObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // --- Main Execution (after document is ready) ---
    // Wait for the DOM to be fully loaded before trying to interact with elements
    document.addEventListener('DOMContentLoaded', () => {
        log('DOMContentLoaded fired. Executing main logic.');
        if (window.geminiScript.currentPrompt) {
            prefillThenSubmit(window.geminiScript.currentPrompt);
        } else {
            log("No prompt stored from early execution. No prefill action needed.");
        }
    });

})();