Gemini Toolkit

Automates Gemini: Model selection, temporary chat toggling, prompt auto-filling, and optional auto-sending via URL parameters.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name                Gemini Toolkit
// @name:zh-TW          Gemini 自動化工具箱
// @name:zh-CN          Gemini 自动化工具箱
// @author              WellsTsai
// @namespace           wellstsai.com
// @version             v20251218
// @description         Automates Gemini: Model selection, temporary chat toggling, prompt auto-filling, and optional auto-sending via URL parameters.
// @description:zh-TW   自動化 Gemini 操作:模型選擇、切換臨時對話、自動填入提示詞,並支援經由 URL 參數自動發送。
// @description:zh-CN   自动化 Gemini 操作:模型选择、切换临时对话、自动填充提示词,并支持經由 URL 参数自动发送。
// @match               https://gemini.google.com/*
// @grant               none
// @run-at              document-end
// @license             MIT
// ==/UserScript==

/**
 * === Usage Examples ===
 *
 * 1. Auto-fill text only (Draft mode):
 * https://gemini.google.com/app?q=Hello+World
 *
 * 2. Auto-fill and SEND immediately:
 * https://gemini.google.com/app?q=Hello+World&send=true
 *
 * 3. Switch to specific model (e.g., Gemini Advanced) and fill text:
 * https://gemini.google.com/app?q=Analyze+this&model=2
 *
 * 4. Use Temporary Chat + Model 2 + Auto-send:
 * https://gemini.google.com/app?q=Code+review&model=2&temporary-chat=true&send=true
 *
 * 5. Enable Temporary Chat ONLY (No text, no model switch):
 * https://gemini.google.com/app?temporary-chat=true
 */

(function() {
    'use strict';

    // --- Configuration ---
    const CONFIG = {
        START_DELAY: 0,
        TEMP_CHAT_DELAY: 0,
        MODEL_SWITCH_DELAY: 0,
        TYPE_TO_SEND_DELAY: 0,
        WAIT_TIMEOUT: 10000
    };

    // --- Parse URL Parameters ---
    const params = new URLSearchParams(window.location.search);
    const query = params.get('q');
    const modelParam = parseInt(params.get('model'), 10);
    const modelIndex = (isNaN(modelParam) || modelParam < 1) ? null : modelParam;
    const isTempChat = params.get('temporary-chat') === 'true';
    const shouldAutoSend = params.get('send') === 'true';

    // Exit early if nothing to do
    if (!query && !modelIndex && !isTempChat) return;

    // --- Helpers ---
    const wait = (selector) => {
        return new Promise((resolve) => {
            const timer = setInterval(() => {
                const el = document.querySelector(selector);
                if (el) {
                    clearInterval(timer);
                    resolve(el);
                }
            }, 100);
            setTimeout(() => clearInterval(timer), CONFIG.WAIT_TIMEOUT);
        });
    };

    const sleep = (ms) => new Promise(r => setTimeout(r, ms));

    const aggressiveClick = (el) => {
        ['pointerdown', 'pointerup', 'mousedown', 'click'].forEach(type => {
            const eventClass = type.startsWith('pointer') ? PointerEvent : MouseEvent;
            el.dispatchEvent(new eventClass(type, { bubbles: true, cancelable: true }));
        });
    };

    const cleanUrl = () => {
        const newUrl = `${window.location.origin}${window.location.pathname}`;
        window.history.replaceState({}, '', newUrl);
    };

    // --- Actions ---

    const toggleTempChat = async () => {
        if (!isTempChat) return false;
        try {
            const tempBtn = await wait('button[data-test-id="temp-chat-button"]');
            console.log("[Gemini Enhancement] Activating Temporary Chat...");
            aggressiveClick(tempBtn);
            return true;
        } catch (e) {
            console.warn("[Gemini Enhancement] Temp Chat toggle failed", e);
            return false;
        }
    };

    const switchModel = async () => {
        if (!modelIndex) return false;
        try {
            const menuTrigger = await wait('[data-test-id="bard-mode-menu-button"]');
            menuTrigger.click();
            await sleep(500);

            const overlay = document.querySelector('.cdk-overlay-container');
            if (!overlay) return false;

            const options = overlay.querySelectorAll('button[role="menuitemradio"]');
            const targetBtn = options[modelIndex - 1];

            if (!targetBtn || targetBtn.getAttribute('aria-checked') === 'true') {
                document.body.click(); // Close menu
                return false;
            }

            console.log(`[Gemini Enhancement] Switching to Model Index: ${modelIndex}`);
            aggressiveClick(targetBtn);
            return true;
        } catch (e) {
            console.warn("[Gemini Enhancement] Model switch failed", e);
            return false;
        }
    };

    const fillAndSend = async () => {
        if (!query) return;

        console.log("[Gemini Enhancement] Inputting text...");
        const promptBox = await wait('div[contenteditable="true"]');
        promptBox.focus();

        document.execCommand('insertText', false, query);
        promptBox.dispatchEvent(new Event('input', { bubbles: true }));

        if (!shouldAutoSend) {
            console.log("[Gemini Enhancement] Draft created. Waiting for manual send.");
            cleanUrl();
            return;
        }

        await sleep(CONFIG.TYPE_TO_SEND_DELAY);
        const sendButton = document.querySelector('.send-button');

        if (sendButton && sendButton.getAttribute('aria-disabled') !== 'true') {
            console.log("[Gemini Enhancement] Auto-sending...");
            aggressiveClick(sendButton);
            cleanUrl();
        }
    };

    // --- Main ---
    const main = async () => {
        await sleep(CONFIG.START_DELAY);

        if (await toggleTempChat()) {
            await sleep(CONFIG.TEMP_CHAT_DELAY);
        }

        if (await switchModel()) {
            await sleep(CONFIG.MODEL_SWITCH_DELAY);
        }

        await fillAndSend();
    };

    if (document.readyState === 'complete') {
        main();
    } else {
        window.addEventListener('load', main);
    }

})();