Customize your chatgpt chat for improved workflow - instructions inside

It’s been a while since the last time I modded chatgpt and a lot has changed since then so it was time for an update. First, here’s what the mod looks like:

As a developer, I copy code a lot and the ChatGPT vanilla GUI is not great as the responses are lengthy and you need to scroll up every single time after a response in order to copy the code. Also, navigating to a previous response is a pain and often takes quite some time to find what you were looking for. And wouldn’t it be nice if you could hide/minify responses/conversations that were bad or obsolete?

These are the changes in the mod:

  • Changes view to wide
  • Move copy code button to bottom of code window
  • Bigger copy button
  • Previous/Next buttons to navigate to nearest previous/next conversation
  • Display Conversation ID at each conversation
  • Mark conversations by clicking the conversation ID once (red number)
  • Or hide/minify conversations by clicking the conversation ID twice (grey number). Clicking it a 3rd time resets the view to normal.
    (These changes are not persistent. When you refresh the page the marked or hidden conversations are back to normal)

And here’s a video of this mod:
Youtube video

The conversation ID’s actually start with number 2 in chatgpt, but for ease of use this has been changed to start with number 1.

Due to continuous updates by OpenAI, these changes may not stay relevant and may require script updates in order to keep the modifications functional.

Tested on Windows with Chrome and Edge.

Steps:

  1. Go to the Chrome web store and install Tampermonkey:
  2. Click the little puzzle icon in the right top of the browser and pin the black tampermonkey icon to make it visible.
  3. Click the Tampermonkey icon and select “New script”
  4. Copy/paste the script from here: https://pastebin.com/98v5i6Mv or from the code box below.
  5. Save the script, open the settings tab for the script and select document-end under the General settings.
  6. Open a chatgpt window and refresh the page. The changes should be visible immediately.
// ==UserScript==
// @name         Navigation Script
// @namespace    http://tampermonkey.net/
// @version      2024-06-10
// @description  Add navigation buttons to move between responses and display conversation numbers
// @author       You
// @match        https://chatgpt.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Add CSS for padding and conversation numbers
    const style = document.createElement('style');
    style.innerHTML = `
        [data-testid^="conversation-turn-"] {
            padding-top: 50px !important;
            position: relative;
        }
        .navigation-buttons {
            position: fixed;
            margin: -9px 0 0 14px;
        }
        .navigation-buttons button {
            margin: 5px;
            padding: 5px 30px;
            font-size: 16px;
            cursor: pointer;
            background: mediumslateblue;
            color: white;
            border: none;
            border-radius: 5px;
        }
        .navigation-buttons button:hover {
            background: darkslateblue;
        }
        .conversation-number {
            position: absolute;
            left: 18px;
            top: 60px;
            font-size: 16px;
            font-weight: bold;
            color: white;
            z-index: 1;
            background: #000000;
            padding: 9px 12px;
            border-radius: 7px;
            border: 1px solid white;
            cursor: pointer;
        }

        .xl\\:max-w-\\[48rem\\] {
            max-width: 100%;
        }

        /* Increase side scroll bar width */
        [class^="react-scroll-to-bottom--"]::-webkit-scrollbar {
            width: 40px;
        }

        /* Increase side scroll bar thumb and scroll speed */
        [class^="react-scroll-to-bottom--"]::-webkit-scrollbar-thumb {
            min-height: 10%;
            background: darkslateblue;
        }

        /* Remove prompt suggestions */
        button.btn.relative.btn-neutral.group.w-full.whitespace-nowrap.rounded-xl.text-left.text-gray-700.dark\\:text-gray-300.md\\:whitespace-normal {
            display: none;
        }

        /* Move regenerate button under the prompt input */
        .md\\:items-end {
            align-items: flex-end;
            position: absolute;
            left: 0;
            bottom: -45px;
        }

        /* Add red bg color to regenerate button */
        button.btn.relative.btn-neutral.whitespace-nowrap.-z-0.border-0.md\\:border {
            border-radius: 10px;
            background: #ee0008;
            border: 1px solid #ee0008;
            color: #fff;
        }

        /* Regenerate button hover */
        button.btn.relative.btn-neutral.whitespace-nowrap.-z-0.border-0.md\\:border:hover {
            background: #9c1519;
            border: 1px solid #9c1519;
            transition: 0.25s;
        }

        /* Add "response" to regenerate button */
        button.btn.relative.btn-neutral.whitespace-nowrap.-z-0.border-0.md\\:border .flex.w-full.gap-2.items-center.justify-center:after {
            content: "last response";
        }

        /* Move top bar to bottom */
        .dark.bg-gray-950.rounded-md.border-\\[0\\.5px\\].border-token-border-medium {
            display: flex;
            flex-direction: column;
            height: 100%;
        }

        .overflow-y-auto.p-4 {
            order: 1; /* Content comes first */
            flex-grow: 1; /* Ensure content takes up available space */
        }

        .flex.items-center.relative.text-token-text-secondary.bg-token-main-surface-secondary.px-4.py-2.text-xs.font-sans.justify-between.rounded-t-md {
            order: 2; /* Top bar comes second */
            direction: rtl;
            background: darkslateblue;
        }

        /* Bigger copy button */
        button.flex.gap-1.items-center {
            width: 200px;
            display: flex;
            justify-content: center;
            background: indigo;
            padding: 10px;
        }

        @media (min-width: 1024px) {
            .ZnJ1aXRqdWljZQ .juice\\:lg\\:gap-6 {
                gap: 1.5rem;
                min-width: 100%;
            }
        }
    `;
    document.head.appendChild(style);

    let responses = [];

    // Function to find and log all elements with data-testid attribute
    function logTestIdElements() {
        responses = Array.from(document.querySelectorAll('[data-testid^="conversation-turn-"]'));
        console.log('Total responses found:', responses.length);
        responses.forEach((response, index) => {
            console.log('Response', index, response.getAttribute('data-testid'));
            addConversationNumber(response, index + 1); // Add conversation number
        });
    }

    // Function to add conversation number to each response
    function addConversationNumber(element, number) {
        let numberElement = element.querySelector('.conversation-number');
        if (!numberElement) {
            numberElement = document.createElement('div');
            numberElement.className = 'conversation-number';
            numberElement.addEventListener('click', () => {
                if (numberElement.style.color === 'red') {
                    numberElement.style.color = 'grey';
                    element.style.maxHeight = '110px';
                    element.style.overflow = 'hidden';
                } else if (numberElement.style.color === 'grey') {
                    numberElement.style.color = 'white';
                    element.style.maxHeight = '';
                    element.style.overflow = '';
                } else {
                    numberElement.style.color = 'red';
                }
            });
            element.insertBefore(numberElement, element.firstChild);
        }
        numberElement.textContent = number;
    }

    // Function to add navigation buttons
    function addNavigationButtons() {
        const targetDiv = document.querySelector('.relative.px-2.py-2.text-center.text-xs.text-token-text-secondary.md\\:px-\\[60px\\]');
        if (targetDiv) {
            let navContainer = document.querySelector('.navigation-buttons');
            if (!navContainer) {
                navContainer = document.createElement('div');
                navContainer.className = 'navigation-buttons';
                navContainer.innerHTML = `
                    <button id="prevBtn">Prev.</button>
                    <button id="nextBtn">Next</button>
                `;
                targetDiv.insertBefore(navContainer, targetDiv.firstChild);

                // Add event listeners to the buttons
                document.getElementById("nextBtn").addEventListener("click", function() {
                    logTestIdElements(); // Fetch the latest responses
                    const closestIndex = findClosestResponseIndex();
                    if (closestIndex < responses.length - 1) {
                        const nextIndex = closestIndex + 1;
                        console.log('Navigating to:', responses[nextIndex].getAttribute('data-testid'));
                        responses[nextIndex].scrollIntoView({ behavior: "smooth" });
                    } else {
                        console.log('No more responses to navigate to.');
                    }
                });

                document.getElementById("prevBtn").addEventListener("click", function() {
                    logTestIdElements(); // Fetch the latest responses
                    const closestIndex = findClosestResponseIndex();
                    if (closestIndex > 0) {
                        const prevIndex = closestIndex - 1;
                        console.log('Navigating to:', responses[prevIndex].getAttribute('data-testid'));
                        responses[prevIndex].scrollIntoView({ behavior: "smooth" });
                    } else {
                        console.log('No previous responses to navigate to.');
                    }
                });
            }
        } else {
            console.error('Target div not found');
        }
    }

    // Function to find the closest response based on the current scroll position
    function findClosestResponseIndex() {
        let closestIndex = 0;
        let minDistance = Infinity;
        const viewportTop = window.scrollY;
        const viewportBottom = viewportTop + window.innerHeight;

        responses.forEach((response, index) => {
            const rect = response.getBoundingClientRect();
            const responseTop = rect.top + window.scrollY;
            const responseBottom = rect.bottom + window.scrollY;

            const distance = Math.min(
                Math.abs(viewportTop - responseTop),
                Math.abs(viewportBottom - responseBottom)
            );

            if (distance < minDistance) {
                minDistance = distance;
                closestIndex = index;
            }
        });

        return closestIndex;
    }

    // Function to initialize the script
    function initialize() {
        logTestIdElements();
        addNavigationButtons();
    }

    // Wait for a short delay before running the script
    setTimeout(() => {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initialize);
        } else {
            initialize();
        }
    }, 2000); // Adjust the delay time as needed

    // Ensure buttons are reloaded when navigating to a new conversation
    setInterval(addNavigationButtons, 2000);

})();


This script and embedded styling aren’t perfect, so feel free to modify it to your liking. Keep in mind that adding custom Javascript to Tampermonkey poses a potential risk if the source code contains malware. The script I’m sharing is safe and does nothing other than what’s listed above, but always check the source code if you copy something off the internet to make sure there’s no hidden features.

1 Like

Edited original post and updated script as the previous/next buttons didn’t show when switching between conversations. That’s now been fixed.

Hey this is amazing but can we get a non-dark mode option for the old people? Thanks!

I just realized the dark mode is in the original, so this is a legit annoying feature request!

This is fantastic, thank you for sharing! Also I just realized we have Tampermonkey in Opera, which is nice.

Hey, just open the browser’s developer tools window (usually F12), then use select element to find the element’s class, and then add or modify the CSS on the right hand side to see what works. Once you’ve got it the way you like, copy those CSS rules and put them in a browser extension to override CSS (like I did with tampermonkey):

Find the Class that contains the background color:

Change it to a color you like:

Then find the element that controls the text color and change it:

Looks like you need to modify these 2 classes:
Code window background:
.bg-gray-950 {
–tw-bg-opacity: 1;
background-color: rgb(255 255 255); #(change to whatever you like)
}

Code syntax:
code.hljs, code[class*=language-], pre[class*=language-] {
color: #fc0000; # change to whatever color you like
}

These changes are not persistent until you add them to a browser extension that overrides the default CSS. If it doesn’t work, you may need to add !important to force the modification.

I mean, I did all this before, but with your help I’m rocking immediately :wink: I’ll spare you the story of things I tried.

THANK YOU!

1 Like

Here’s a kinda final result…

.bg-gray-950 {
    background-color: #F5F5F5;
}

/* Sets the default text color for code blocks */
code.hljs, code[class*=language-], pre[class*=language-] {
    color: black !important; 
}

span.hljs-selector-tag, code[class*=language-] span.hljs-tag, pre[class*=language-] span.hljs-tag {
    color: orange;
}

span.hljs-selector-attr, code[class*=language-] span.hljs-attribute, pre[class*=language-] span.hljs-attribute {
    color: green;
}

span.hljs-selector-class, code[class*=language-] span.hljs-class, pre[class*=language-] span.hljs-class {
    color: darkgray;
}

span.hljs-keyword, code[class*=language-] span.hljs-keyword, pre[class*=language-] span.hljs-keyword {
    color: #a71d5d; /* A shade of darker pink to make keywords distinct */
}

span.hljs-string, code[class*=language-] span.hljs-string, pre[class*=language-] span.hljs-string {
    color: #183691; /* Dark blue for string literals */
}

span.hljs-number, code[class*=language-] span.hljs-number, pre[class*=language-] span.hljs-number {
    color: #0086b3; /* Teal for numbers to differentiate from strings */
}

span.hljs-comment, code[class*=language-] span.hljs-comment, pre[class*=language-] span.hljs-comment {
    color: #969896; /* Gray for comments to reduce visual prominence */
}

/* Additional types and identifiers */
span.hljs-type, code[class*=language-] span.hljs-type, pre[class*=language-] span.hljs-type,
span.hljs-identifier, code[class*=language-] span.hljs-identifier, pre[class*=language-] span.hljs-identifier {
    color: #b52a1d; /* Rust red, good for types and unique identifiers */
}

Thanks again for the help! I have no idea how my targeting was wrong, but… anyway, moving on.

1 Like