Resizable ChatGPT Sidebar

It adds a draggable handle to resize the ChatGPT sidebar, so you could see more characters of the chat titles / chat history etc. It uses MutationObserver for reliability.

// ==UserScript==
// @name         Resizable ChatGPT Sidebar
// @namespace    Violentmonkey userscripts by ReporterX
// @version      1.501
// @description  It adds a draggable handle to resize the ChatGPT sidebar, so you could see more characters of the chat titles / chat history etc. It uses MutationObserver for reliability.
// @author       ReporterX
// @match        *://chat.openai.com/*
// @match        *://chatgpt.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @icon         https://www.google.com/s2/favicons?sz=64&domain=openai.com
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const SIDEBAR_SELECTOR = '#stage-slideover-sidebar';
    const MIN_WIDTH = 200;
    const MAX_WIDTH = 800;
    const DEFAULT_WIDTH = 260;

    /**
     * The main function to set up the resizer.
     * This is called only when the sidebar is found.
     */
    function setupResizer(sidebar) {
        // Prevent setup from running more than once
        if (sidebar.dataset.resizable) return;
        sidebar.dataset.resizable = 'true';

        console.log("Resizable Sidebar: Sidebar found, setting up handle.");

        // Apply saved width or default
        const savedWidth = GM_getValue('sidebarWidth', DEFAULT_WIDTH);
        sidebar.style.width = `${savedWidth}px`;
        sidebar.style.setProperty('--sidebar-width', `${savedWidth}px`);

        // Create the resize handle
        const handle = document.createElement('div');
        handle.id = 'resize-handle-chatgpt';

        // Add CSS for the handle and sidebar
        GM_addStyle(`
            /* Ensure the sidebar is a positioning context for the handle */
            ${SIDEBAR_SELECTOR} {
                position: relative !important;
                transition: none !important; /* For smooth, real-time resizing */
            }

            #resize-handle-chatgpt {
                position: absolute;
                top: 0;
                right: 0; /* Positioned INSIDE the edge to avoid being hidden by overflow */
                width: 8px; /* A bit wider for easier grabbing */
                height: 100%;
                cursor: col-resize;
                z-index: 1000;
            }

            /* Visual flair for the handle */
            #resize-handle-chatgpt:hover::after,
            #resize-handle-chatgpt.resizing::after {
                content: '';
                position: absolute;
                top: 0;
                left: 3px;
                width: 2px;
                height: 100%;
                background-color: #56a2f8; /* A blue line appears on hover/drag */
                border-radius: 2px;
            }
        `);

        sidebar.appendChild(handle);

        // --- Dragging Logic ---
        const onMouseMove = (e) => {
            let newWidth = e.clientX;
            if (newWidth < MIN_WIDTH) newWidth = MIN_WIDTH;
            if (newWidth > MAX_WIDTH) newWidth = MAX_WIDTH;
            sidebar.style.width = `${newWidth}px`;
            sidebar.style.setProperty('--sidebar-width', `${newWidth}px`);
        };

        const onMouseUp = () => {
            handle.classList.remove('resizing');
            document.body.style.cursor = 'default';
            document.body.style.userSelect = 'auto';
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);

            GM_setValue('sidebarWidth', parseInt(sidebar.style.width, 10));
        };

        handle.addEventListener('mousedown', (e) => {
            e.preventDefault();
            handle.classList.add('resizing');
            document.body.style.cursor = 'col-resize';
            document.body.style.userSelect = 'none';
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });
    }

    /**
     * Use a MutationObserver to wait for the sidebar to be added to the page.
     * This is the most reliable way to handle dynamically loaded content.
     */
    const observer = new MutationObserver((mutationsList, obs) => {
        const sidebar = document.querySelector(SIDEBAR_SELECTOR);
        if (sidebar) {
            setupResizer(sidebar);
            // We found it, so we can stop observing to save resources.
            // obs.disconnect(); // Uncomment this line if you find it's firing too often, but it's generally safe to leave it running for SPA navigation.
        }
    });

    // Start observing the entire document for changes
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

})();