ChatGPT 对话问题导航

在ChatGPT页面添加一个对话导航目录,并为每个项添加序号,加入可控制显示/隐藏的按钮,默认状态为显示

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         ChatGPT 对话问题导航
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  在ChatGPT页面添加一个对话导航目录,并为每个项添加序号,加入可控制显示/隐藏的按钮,默认状态为显示
// @author       Angury
// @match        https://chatgpt.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    const tocContainer = document.createElement('div');
    tocContainer.id = 'tocContainer';
    tocContainer.style.cssText = 'position: fixed; right: 0; top: 50px; width: 200px; height: 80vh; overflow-y: auto; background-color: #fff; border: 1px solid #ccc; padding: 10px; z-index: 1000; display: block;';

    const toggleButton = document.createElement('div');
    toggleButton.innerHTML = '▶'; // Unicode character for rightward triangle, indicating the menu is open
    toggleButton.style.cssText = 'position: fixed; right: 0; top: 30px; width: 30px; height: 20px; padding: 5px; text-align: center; cursor: pointer; z-index: 1001; background-color: transparent; border: none;';

    document.body.appendChild(toggleButton);
    document.body.appendChild(tocContainer);

    toggleButton.onclick = function() {
        tocContainer.style.display = tocContainer.style.display === 'block' ? 'none' : 'block';
        toggleButton.innerHTML = tocContainer.style.display === 'none' ? '◀' : '▶';
    };

    function updateTOC() {
        const messages = document.querySelectorAll('div[data-message-author-role="user"]');
        if (messages.length === tocContainer.childNodes.length) {
            return; // Skip update if the count of messages hasn't changed
        }

        tocContainer.innerHTML = ''; // Clear previous entries

        messages.forEach((message, index) => {
            const lastDiv = message.querySelector('div:last-child div:last-child');
            if (!lastDiv) return; // Skip if no div found

            const questionId = `question-${index}`;
            lastDiv.id = questionId;

            let questionText = lastDiv.textContent.trim() || `......`;
            questionText = questionText.length > 9 ? `${questionText.substring(0, 8)}...` : questionText;
            let formattedIndex = ("0" + (index + 1)).slice(-2);  // Two-digit numbering

            const tocItem = document.createElement('div');
            tocItem.textContent = `${formattedIndex} ${questionText}`;
            tocItem.style.cssText = 'cursor: pointer; margin-bottom: 5px;';
            tocItem.onclick = function() {
                document.getElementById(questionId).scrollIntoView({ behavior: 'smooth' });
            };

            tocContainer.appendChild(tocItem);
        });
    }

    // Use MutationObserver to listen for changes in the DOM
    const observer = new MutationObserver(mutations => {
        let shouldUpdate = false;
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length) shouldUpdate = true;
        });
        if (shouldUpdate) updateTOC();
    });

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

    updateTOC();  // Initial update
})();