Collapsible GPT List for ChatGPT.com

Enhances the sidebar functionality by enabling the GPT List to collapse.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Collapsible GPT List for ChatGPT.com
// @namespace    https://chatgpt.com/
// @version      1.1
// @description  Enhances the sidebar functionality by enabling the GPT List to collapse.
// @author       Seth Rose
// @homepage     https://x.com/TheSethRose
// @supportURL   https://bsky.app/profile/sethrose.dev
// @supportURL   https://x.com/TheSethRose
// @match        https://chatgpt.com/*
// @match        https://chat.com/*
// @match        https://chat.openai.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    "use strict";

    let isFirstTargetProcessed = false;

    function getElementByXPath(xpath) {
        const result = document.evaluate(
            xpath,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        );
        return result.singleNodeValue;
    }

    function makeCollapsible() {
        if (isFirstTargetProcessed) return;

        const targetXPath = "/html/body/div[1]/div[1]/div/div/div/nav/div[2]/div/div[4]";
        const targetDiv = getElementByXPath(targetXPath);
        if (!targetDiv) return;

        // Avoid duplicates
        if (
            targetDiv.previousElementSibling &&
            targetDiv.previousElementSibling.classList.contains("collapsible-toggle")
        ) {
            return;
        }

        // Create the toggle container
        const toggleButton = document.createElement("div");
        toggleButton.className =
            "collapsible-toggle z-20 select-none text-xs font-semibold text-token-text-primary " +
            "flex items-center justify-between h-[26px] px-2 pl-";
        toggleButton.style.cursor = "pointer";
        toggleButton.style.margin = "0";
        toggleButton.style.paddingBottom = "0"; // extra attempt at removing spacing

        // Inner HTML: start collapsed
        toggleButton.innerHTML = `
            <span id="toggleText">Expand GPT List</span>
            <div class="flex h-[26px] w-[26px] items-center justify-center text-token-text-secondary" style="margin:0;padding:0;">
                <button aria-label="Toggle GPTs" class="ml-3 flex rounded-lg items-center text-token-text-primary">
                    <svg class="w-6 h-6 text-gray-800 dark:text-white"
                         aria-hidden="true"
                         xmlns="http://www.w3.org/2000/svg"
                         width="24" height="24"
                         fill="none"
                         viewBox="0 0 24 24">
                        <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
                              stroke-width="2" d="m8 10 4 4 4-4"/>
                    </svg>
                </button>
            </div>
        `;

        // Style the div to be collapsible
        targetDiv.style.maxHeight = "0";
        targetDiv.style.overflow = "hidden";
        targetDiv.style.transition = "max-height 0.3s ease";
        targetDiv.style.margin = "0";
        targetDiv.style.padding = "0";

        // Insert the toggle button
        targetDiv.parentNode.insertBefore(toggleButton, targetDiv);

        // References for text/icon
        const toggleTextSpan = toggleButton.querySelector("#toggleText");
        const svgPath = toggleButton.querySelector("svg path");

        // Click event toggles everything
        toggleButton.addEventListener("click", () => {
            if (targetDiv.style.maxHeight === "0px") {
                // Expand
                targetDiv.style.maxHeight = targetDiv.scrollHeight + "px";
                toggleTextSpan.textContent = "Collapse GPT List";
                svgPath.setAttribute("d", "m16 14-4-4-4 4"); // Arrow up
            } else {
                // Collapse
                targetDiv.style.maxHeight = "0";
                toggleTextSpan.textContent = "Expand GPT List";
                svgPath.setAttribute("d", "m8 10 4 4 4-4"); // Arrow down
            }
        });

        isFirstTargetProcessed = true;
    }

    // Delay to ensure the target element is rendered
    window.addEventListener("load", () => {
        setTimeout(makeCollapsible, 500);
    });
})();