Gemini - AI对话快速导航

在Gemini对话页面添加导航按钮:上下切换AI回答,一键到顶部/底部

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Gemini - Quick Navigation for AI Conversations
// @name:zh-CN   Gemini - AI对话快速导航
// @namespace    http://tampermonkey.net/
// @version      5.1
// @description  Add navigation buttons to the Gemini conversation page: switch up and down between AI answers, and jump to the top/bottom with one click
// @description:zh-CN  在Gemini对话页面添加导航按钮:上下切换AI回答,一键到顶部/底部
// @author       Epodak
// @match        https://gemini.google.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gemini.google.com
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 创建样式
    const style = document.createElement('style');
    style.textContent = `
        .nav-buttons-container {
            position: fixed;
            right: 20px;
            top: 50%;
            transform: translateY(-50%);
            z-index: 10000;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        .nav-button {
            width: 48px;
            height: 48px;
            border-radius: 50%;
            border: 2px solid #4285f4;
            background: white;
            color: #4285f4;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            font-weight: bold;
            transition: all 0.3s ease;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }

        .extreme-button {
            width: 40px;
            height: 40px;
            font-size: 16px;
            border-color: #ea4335;
        }

        .extreme-button:hover {
            background: #ea4335;
            color: white;
        }

        .nav-button:hover {
            background: #4285f4;
            color: white;
            transform: scale(1.1);
        }

        .nav-button.disabled {
            opacity: 0.5;
            pointer-events: none;
        }
    `;
    document.head.appendChild(style);

    // 创建按钮
    const navContainer = document.createElement('div');
    navContainer.className = 'nav-buttons-container';

    const topButton = document.createElement('div');
    topButton.className = 'nav-button extreme-button';
    topButton.innerHTML = '⤴';
    topButton.title = '跳转到顶部';

    const upButton = document.createElement('div');
    upButton.className = 'nav-button';
    upButton.innerHTML = '↑';
    upButton.title = '上一个AI回答';

    const downButton = document.createElement('div');
    downButton.className = 'nav-button';
    downButton.innerHTML = '↓';
    downButton.title = '下一个AI回答';

    const bottomButton = document.createElement('div');
    bottomButton.className = 'nav-button extreme-button';
    bottomButton.innerHTML = '⤵';
    bottomButton.title = '跳转到底部';

    navContainer.append(topButton, upButton, downButton, bottomButton);
    document.body.appendChild(navContainer);

    // 获取所有AI回答
    function getAllResponses() {
        return Array.from(document.querySelectorAll('model-response'));
    }

    // 获取当前中心的AI回答
    function getCurrentResponse() {
        const responses = getAllResponses();
        if (!responses.length) return null;

        const center = window.innerHeight / 2;
        let closest = responses[0];
        let minDistance = Infinity;

        for (const response of responses) {
            const rect = response.getBoundingClientRect();
            const distance = Math.abs(rect.top + rect.height / 2 - center);
            if (distance < minDistance) {
                minDistance = distance;
                closest = response;
            }
        }
        return closest;
    }

    // 跳转到指定回答
    function scrollTo(response) {
        if (response) {
            response.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
    }

    // 上一个回答
    function goPrevious() {
        const responses = getAllResponses();
        if (!responses.length) return;

        const current = getCurrentResponse();
        const index = responses.indexOf(current);

        if (index > 0) {
            scrollTo(responses[index - 1]);
        } else {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        }
    }

    // 下一个回答
    function goNext() {
        const responses = getAllResponses();
        if (!responses.length) return;

        const current = getCurrentResponse();
        const index = responses.indexOf(current);

        if (index < responses.length - 1) {
            scrollTo(responses[index + 1]);
        }
    }

    // 查找实际的滚动容器
    function findScrollContainer() {
        // 从model-response元素向上查找有滚动的父容器
        const firstResponse = document.querySelector('model-response');
        if (firstResponse) {
            let element = firstResponse.parentElement;
            while (element && element !== document.body) {
                const hasScroll = element.scrollHeight > element.clientHeight;
                const overflowY = window.getComputedStyle(element).overflowY;

                if (hasScroll && (overflowY === 'auto' || overflowY === 'scroll')) {
                    return element;
                }
                element = element.parentElement;
            }
        }
        return null;
    }

    // 跳转到顶部
    function goTop() {
        const scrollContainer = findScrollContainer();
        if (scrollContainer) {
            scrollContainer.scrollTop = 0;
        } else {
            window.scrollTo({ top: 0, behavior: 'auto' });
        }
    }

    // 跳转到底部(优化版:先快速到底部,再定位到最后AI回答)
    function goBottom() {
        // 1. 先快速跳转到页面最底部(模拟End键)
        const scrollContainer = findScrollContainer();
        if (scrollContainer) {
            scrollContainer.scrollTop = scrollContainer.scrollHeight;
        } else {
            window.scrollTo({ top: document.body.scrollHeight, behavior: 'auto' });
        }

        // 2. 等待一下让内容加载,然后定位到最后一个AI回答
        setTimeout(() => {
            const responses = getAllResponses();
            if (responses.length > 0) {
                scrollTo(responses[responses.length - 1]);
            }
        }, 200);
    }

    // 更新按钮状态
    function updateButtons() {
        const responses = getAllResponses();
        const current = getCurrentResponse();

        if (!responses.length || !current) {
            upButton.classList.add('disabled');
            downButton.classList.add('disabled');
            return;
        }

        const index = responses.indexOf(current);

        // 只有在页面顶部且是第一个回答时禁用上一个按钮
        upButton.classList.toggle('disabled',
            window.scrollY <= 10 && index === 0);

        // 下一个按钮基本不禁用
        downButton.classList.remove('disabled');

        // TOP和BOTTOM按钮始终可用
        topButton.classList.remove('disabled');
        bottomButton.classList.remove('disabled');
    }

    // 事件监听
    topButton.onclick = goTop;
    upButton.onclick = goPrevious;
    downButton.onclick = goNext;
    bottomButton.onclick = goBottom;

    // 滚动监听
    let timer;
    window.addEventListener('scroll', () => {
        clearTimeout(timer);
        timer = setTimeout(updateButtons, 100);
    });

    // DOM变化监听
    new MutationObserver(() => {
        setTimeout(updateButtons, 200);
    }).observe(document.body, { childList: true, subtree: true });

    // 键盘快捷键
    document.addEventListener('keydown', (e) => {
        if (e.target.matches('input, textarea, [contenteditable="true"]')) return;

        if ((e.ctrlKey || e.metaKey) && e.key === 'ArrowUp') {
            e.preventDefault();
            goPrevious();
        }
        if ((e.ctrlKey || e.metaKey) && e.key === 'ArrowDown') {
            e.preventDefault();
            goNext();
        }
        // 移除Ctrl+Home快捷键,因为它在Gemini中不工作
        // 保留Ctrl+End是为了与goBottom功能一致
        if ((e.ctrlKey || e.metaKey) && e.key === 'End') {
            e.preventDefault();
            goBottom();
        }
    });

    // 初始化
    setTimeout(updateButtons, 1000);

})();