Collapsible GPT List for ChatGPT.com

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

  1. // ==UserScript==
  2. // @name Collapsible GPT List for ChatGPT.com
  3. // @namespace https://chatgpt.com/
  4. // @version 1.1
  5. // @description Enhances the sidebar functionality by enabling the GPT List to collapse.
  6. // @author Seth Rose
  7. // @homepage https://x.com/TheSethRose
  8. // @supportURL https://bsky.app/profile/sethrose.dev
  9. // @supportURL https://x.com/TheSethRose
  10. // @match https://chatgpt.com/*
  11. // @match https://chat.com/*
  12. // @match https://chat.openai.com/*
  13. // @grant none
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. (function () {
  18. "use strict";
  19.  
  20. let isFirstTargetProcessed = false;
  21.  
  22. function getElementByXPath(xpath) {
  23. const result = document.evaluate(
  24. xpath,
  25. document,
  26. null,
  27. XPathResult.FIRST_ORDERED_NODE_TYPE,
  28. null
  29. );
  30. return result.singleNodeValue;
  31. }
  32.  
  33. function makeCollapsible() {
  34. if (isFirstTargetProcessed) return;
  35.  
  36. const targetXPath = "/html/body/div[1]/div[1]/div/div/div/nav/div[2]/div/div[4]";
  37. const targetDiv = getElementByXPath(targetXPath);
  38. if (!targetDiv) return;
  39.  
  40. // Avoid duplicates
  41. if (
  42. targetDiv.previousElementSibling &&
  43. targetDiv.previousElementSibling.classList.contains("collapsible-toggle")
  44. ) {
  45. return;
  46. }
  47.  
  48. // Create the toggle container
  49. const toggleButton = document.createElement("div");
  50. toggleButton.className =
  51. "collapsible-toggle z-20 select-none text-xs font-semibold text-token-text-primary " +
  52. "flex items-center justify-between h-[26px] px-2 pl-";
  53. toggleButton.style.cursor = "pointer";
  54. toggleButton.style.margin = "0";
  55. toggleButton.style.paddingBottom = "0"; // extra attempt at removing spacing
  56.  
  57. // Inner HTML: start collapsed
  58. toggleButton.innerHTML = `
  59. <span id="toggleText">Expand GPT List</span>
  60. <div class="flex h-[26px] w-[26px] items-center justify-center text-token-text-secondary" style="margin:0;padding:0;">
  61. <button aria-label="Toggle GPTs" class="ml-3 flex rounded-lg items-center text-token-text-primary">
  62. <svg class="w-6 h-6 text-gray-800 dark:text-white"
  63. aria-hidden="true"
  64. xmlns="http://www.w3.org/2000/svg"
  65. width="24" height="24"
  66. fill="none"
  67. viewBox="0 0 24 24">
  68. <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
  69. stroke-width="2" d="m8 10 4 4 4-4"/>
  70. </svg>
  71. </button>
  72. </div>
  73. `;
  74.  
  75. // Style the div to be collapsible
  76. targetDiv.style.maxHeight = "0";
  77. targetDiv.style.overflow = "hidden";
  78. targetDiv.style.transition = "max-height 0.3s ease";
  79. targetDiv.style.margin = "0";
  80. targetDiv.style.padding = "0";
  81.  
  82. // Insert the toggle button
  83. targetDiv.parentNode.insertBefore(toggleButton, targetDiv);
  84.  
  85. // References for text/icon
  86. const toggleTextSpan = toggleButton.querySelector("#toggleText");
  87. const svgPath = toggleButton.querySelector("svg path");
  88.  
  89. // Click event toggles everything
  90. toggleButton.addEventListener("click", () => {
  91. if (targetDiv.style.maxHeight === "0px") {
  92. // Expand
  93. targetDiv.style.maxHeight = targetDiv.scrollHeight + "px";
  94. toggleTextSpan.textContent = "Collapse GPT List";
  95. svgPath.setAttribute("d", "m16 14-4-4-4 4"); // Arrow up
  96. } else {
  97. // Collapse
  98. targetDiv.style.maxHeight = "0";
  99. toggleTextSpan.textContent = "Expand GPT List";
  100. svgPath.setAttribute("d", "m8 10 4 4 4-4"); // Arrow down
  101. }
  102. });
  103.  
  104. isFirstTargetProcessed = true;
  105. }
  106.  
  107. // Delay to ensure the target element is rendered
  108. window.addEventListener("load", () => {
  109. setTimeout(makeCollapsible, 500);
  110. });
  111. })();