Gemini - Quick Navigation for AI Conversations

Add navigation buttons to the Gemini conversation page: switch up and down between AI answers, and jump to the top/bottom with one click

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);

})();