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.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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
    });

})();