Open current page in Gemini and summarize. with moveable Button v1.0

Adds a smaller, draggable button to all web pages that opens Gemini with a prompt to summarize the current page.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Open current page in Gemini and summarize. with moveable Button v1.0
// @namespace   http://tampermonkey.net/
// @version     1.0
// @description Adds a smaller, draggable button to all web pages that opens Gemini with a prompt to summarize the current page.
// @author      Gemini (Modified)
// @license MIT
// @match       *://*/*
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_setClipboard
// @run-at      document-idle
// ==/UserScript==

(function() {
    'use strict';

    console.log("Link to Gemini Button script (v2.4) is loading.");
    if (typeof GM_info !== 'undefined') {
        console.log("GM_info:", GM_info);
    } else {
        console.log("GM_info is not available. Script might not be running in a UserScript manager.");
    }

    const BUTTON_ID = 'gemini-button-draggable';
    const POSITION_KEY_X = 'geminiButtonPosX';
    const POSITION_KEY_Y = 'geminiButtonPosY';

    // --- Styling for the button ---
    GM_addStyle(`
        #${BUTTON_ID} {
            position: fixed;
            background-color: #4285F4; /* Google Blue */
            color: white;
            border: none;
            padding: 5px 10px; /* Smaller padding */
            border-radius: 3px; /* Slightly less rounded */
            cursor: grab; /* Indicate it's draggable */
            z-index: 99999;
            font-family: Arial, sans-serif;
            font-size: 12px; /* Smaller font size */
            font-weight: bold;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); /* Smaller shadow */
            transition: background-color 0.2s ease, box-shadow 0.2s ease;
            min-width: unset; /* Ensure no conflicting min-width */
            line-height: normal; /* Ensure text fits */
            text-align: center;
        }
        #${BUTTON_ID}:hover {
            background-color: #3367D6; /* Darker blue on hover */
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
        }
        #${BUTTON_ID}.copied {
            background-color: #28a745; /* Green when copied */
        }
        #${BUTTON_ID}.dragging {
            cursor: grabbing;
            box-shadow: 0 0 0 2px #DB4437; /* Red outline when dragging */
        }
    `);

    // --- Function to create and add the button ---
    async function addGeminiButton() {
        console.log("Attempting to add Gemini button...");

        // Prevent adding multiple buttons
        if (document.getElementById(BUTTON_ID)) {
            console.log("Gemini button already exists. Skipping re-creation.");
            return;
        }

        const button = document.createElement('button');
        button.id = BUTTON_ID;
        button.className = 'gemini-button';
        button.textContent = 'Open Gemini'; // More general text

        // --- Load saved position ---
        const savedX = await GM_getValue(POSITION_KEY_X, null);
        const savedY = await GM_getValue(POSITION_KEY_Y, null);

        if (savedX !== null && savedY !== null) {
            button.style.left = savedX + 'px';
            button.style.top = savedY + 'px';
            button.style.right = 'unset'; // Clear right if left is set
            button.style.bottom = 'unset'; // Clear bottom if top is set
        } else {
            // Default position if no saved position
            button.style.right = '20px';
            button.style.top = '100px';
        }

        // --- Draggable Logic ---
        let isDragging = false;
        let offsetX, offsetY;
        let lastClickX, lastClickY; // To detect if a drag happened vs. a click

        button.addEventListener('mousedown', (e) => {
            isDragging = true;
            button.classList.add('dragging');
            offsetX = e.clientX - button.getBoundingClientRect().left;
            offsetY = e.clientY - button.getBoundingClientRect().top;
            lastClickX = e.clientX;
            lastClickY = e.clientY;
            button.style.userSelect = 'none'; // Prevent text selection during drag
            e.preventDefault(); // Prevent default drag behavior (e.g., image drag)
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            // Clear right/bottom styles as we're setting left/top
            button.style.right = 'unset';
            button.style.bottom = 'unset';
            button.style.left = (e.clientX - offsetX) + 'px';
            button.style.top = (e.clientY - offsetY) + 'px';
        });

        document.addEventListener('mouseup', async (e) => {
            if (!isDragging) return;
            isDragging = false;
            button.classList.remove('dragging');
            button.style.userSelect = 'auto'; // Restore user-select

            // Save new position
            const finalX = button.getBoundingClientRect().left;
            const finalY = button.getBoundingClientRect().top;
            await GM_setValue(POSITION_KEY_X, finalX);
            await GM_setValue(POSITION_KEY_Y, finalY);
            console.log(`Saved button position: X=${finalX}, Y=${finalY}`);

            // If a significant drag occurred, prevent the click event from firing
            const dx = Math.abs(e.clientX - lastClickX);
            const dy = Math.abs(e.clientY - lastClickY);
            if (dx > 5 || dy > 5) {
                console.log("Mouse up after drag, preventing click.");
                e.stopImmediatePropagation(); // Prevent subsequent click event on the button
            }
        });

        // --- Button Click Logic ---
        button.addEventListener('click', async () => {
            const currentUrl = window.location.href;
            const textToCopy = `Summarize this page: ${currentUrl}`; // The prompt text

            try {
                // Use GM_setClipboard if available. We don't need a fallback since we are not displaying the "Copied!" message based on clipboard success anymore.
                if (typeof GM_setClipboard === 'function') {
                    GM_setClipboard(textToCopy);
                    console.log("Prompt copied to clipboard using GM_setClipboard:", textToCopy);
                }

                // Open Gemini in a new tab with the desired URL structure
                // ***************************************************************
                // ** THIS IS THE MODIFIED LINE TO MATCH YOUR REQUESTED SYNTAX **
                // ***************************************************************
                const geminiUrl = `https://gemini.google.com/app/54299f6f7b8a6a75?q=${encodeURIComponent(textToCopy)}`;
                window.open(geminiUrl, '_blank');
                console.log("Opening Gemini tab:", geminiUrl);

                // Flash a confirmation on the button
                button.textContent = 'Opened!';
                button.classList.add('copied'); // Using 'copied' class for green color

                // Revert button text after a delay
                setTimeout(() => {
                    button.textContent = 'Open Gemini';
                    button.classList.remove('copied');
                }, 2000);

            } catch (error) {
                console.error('Failed to perform action:', error);
                button.textContent = 'Error!';
                button.style.backgroundColor = 'red';
                setTimeout(() => {
                    button.textContent = 'Open Gemini';
                    button.style.backgroundColor = '#4285F4'; // Revert to Google Blue
                }, 2000);
                alert("Failed to open Gemini. Check browser console (F12) for details.");
            }
        });

        // Append the button to the body
        if (document.body) {
            document.body.appendChild(button);
            console.log("Gemini button successfully added to the page.");
        } else {
            console.error("Document body not found. Could not add Gemini button.");
            // If body isn't ready yet, try again after a short delay
            setTimeout(addGeminiButton, 100);
        }
    }

    // Run the main logic after the window has fully loaded
    window.addEventListener('load', addGeminiButton);
    // Also run on DOMContentLoaded for faster injection on some pages
    window.addEventListener('DOMContentLoaded', addGeminiButton);

})();