ChatGPT bring back date grouping

Brings back the date grouping on chatgpt.com

// ==UserScript==
// @name        ChatGPT bring back date grouping
// @version     1
// @author      tiramifue
// @description Brings back the date grouping on chatgpt.com
// @match       https://chatgpt.com/*
// @run-at      document-end
// @namespace   https://gf.qytechs.cn/users/570213
// @license     Apache-2.0
// @noframes
// ==/UserScript==

// updated 2025-06-08


(function () {
    'use strict';

    function getDateGroupLabel(isoString) {
        const createdDate = new Date(isoString);
        const now = new Date();

        const msInDay = 24 * 60 * 60 * 1000;
        const daysAgo = Math.floor((now - createdDate) / msInDay);

        const createdMonth = createdDate.getMonth();
        const createdYear = createdDate.getFullYear();
        const nowMonth = now.getMonth();
        const nowYear = now.getFullYear();

        if (daysAgo === 0) return 'Today';
        if (daysAgo === 1) return 'Yesterday';
        if (daysAgo <= 6) return `${daysAgo} days ago`;
        if (daysAgo <= 13) return 'Last week';
        if (daysAgo <= 20) return '2 weeks ago';
        if (daysAgo <= 31) return 'Last month';

        const monthsAgo = (nowYear - createdYear) * 12 + (nowMonth - createdMonth);

        if (monthsAgo <= 11) return `${monthsAgo} months ago`;
        return 'Last year';
    }

    function getReactFiber(dom) {
        for (const key in dom) {
            if (key.startsWith('__reactFiber$')) return dom[key];
        }
        return null;
    }

    function extractChatInfo(fiber) {
        const c = fiber.memoizedProps?.conversation;
        return c
            ? {
            id: c.id,
            title: c.title,
            created: c.create_time,
            updated: c.update_time,
            node: fiber.stateNode
        }
        : null;
    }

    const seenIds = new Set();
    const chatList = [];

    function processNewChatNode(node) {
        const fiber = getReactFiber(node);
        if (!fiber) return;

        let current = fiber;
        while (current && !current.memoizedProps?.conversation) {
            current = current.return;
        }

        if (!current || !current.memoizedProps?.conversation) return;

        const chat = extractChatInfo(current);
        if (chat && !seenIds.has(chat.id)) {
            seenIds.add(chat.id);
            chat.group = getDateGroupLabel(chat.created);
            chat.node = node;
            chatList.push(chat);

            // console.log("New chat loaded:", {
            //     id: chat.id,
            //     title: chat.title,
            //     created: chat.created,
            //     updated: chat.updated,
            //     group: chat.group
            // });

            queueRender();
        }
    }

    function groupChatsByGroupName() {
        const groups = new Map();

        for (const chat of chatList) {
            if (!groups.has(chat.group)) groups.set(chat.group, []);
            groups.get(chat.group).push(chat);
        }

        return [...groups.entries()].sort((a, b) => {
            const aTime = new Date(a[1][0].created).getTime();
            const bTime = new Date(b[1][0].created).getTime();
            return bTime - aTime;
        });
    }

    function clearGroupedChats(aside) {
        aside.querySelectorAll('a[href^="/c/"], .__chat-group-header')
            .forEach(el => el.remove());
    }

    function renderGroupedChats(aside) {
        clearGroupedChats(aside);
        const groups = groupChatsByGroupName();

        for (const [label, chats] of groups) {
            const header = document.createElement('div');
            header.className = '__chat-group-header';
            header.textContent = label;
            header.style = `
    font-weight: normal;
    padding: 6px 10px;
    font-size: 0.85rem;
    color: #999;
    margin-top: ${aside.querySelector('.__chat-group-header') ? '12px' : '0'};
`;
            aside.appendChild(header);

            chats
                .sort((a, b) => new Date(b.created) - new Date(a.created))
                .forEach(chat => aside.appendChild(chat.node));
        }
    }

    let renderTimer = null;

    function queueRender() {
        if (renderTimer) clearTimeout(renderTimer);
        renderTimer = setTimeout(() => {
            const aside = document.querySelector('#history aside');
            if (aside) renderGroupedChats(aside);
        }, 200);
    }

    function observeChatList(aside) {
        const observer = new MutationObserver(mutations => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1 && node.matches('a[href^="/c/"]')) {
                        processNewChatNode(node);
                    }
                }
            }
        });

        observer.observe(aside, { childList: true, subtree: true });

        aside.querySelectorAll('a[href^="/c/"]').forEach(processNewChatNode);
    }

    function waitForElement(selector, timeout = 10000) {
        return new Promise((resolve, reject) => {
            const el = document.querySelector(selector);
            if (el) return resolve(el);

            const observer = new MutationObserver(() => {
                const el = document.querySelector(selector);
                if (el) {
                    observer.disconnect();
                    resolve(el);
                }
            });

            observer.observe(document.body, { childList: true, subtree: true });

            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Timeout waiting for ${selector}`));
            }, timeout);
        });
    }

    Promise.all([
        waitForElement('#history'),
        waitForElement('#history aside')
    ])
        .then(([_, aside]) => {
        observeChatList(aside);
        // console.log("Chat list observer active. Scroll to load all chats.");
    })
        .catch(err => console.warn("ChatGPT grouping script error:", err));
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址