您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Brings back the date grouping on chatgpt.com
- // ==UserScript==
- // @name ChatGPT bring back date grouping
- // @version 1.3
- // @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
- // @grant GM_addStyle
- // @grant GM_getValue
- // @grant GM_setValue
- // @noframes
- // ==/UserScript==
- // updated 2025-06-14
- (function () {
- 'use strict';
- let groupBy = GM_getValue('groupBy', 'updated');
- GM_addStyle(`
- .__chat-group-header {
- font-weight: normal;
- padding: 6px 10px;
- font-size: 0.85rem;
- color: #999;
- margin-top: 0;
- }
- .__chat-group-header:not(:first-of-type) {
- margin-top: 12px;
- }
- `)
- function getDateGroupLabel(isoString) {
- const date = new Date(isoString);
- const now = new Date();
- const msInDay = 24 * 60 * 60 * 1000;
- const daysAgo = Math.floor((now - date) / msInDay);
- const monthsAgo = (now.getFullYear() - date.getFullYear()) * 12 + (now.getMonth() - date.getMonth());
- 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';
- 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);
- const dateKey = chat[groupBy];
- chat.node = node;
- chatList.push(chat);
- queueRender();
- }
- }
- function groupChatsByGroupName() {
- const groups = new Map();
- for (const chat of chatList) {
- chat.group = getDateGroupLabel(chat[groupBy]);
- 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][groupBy]).getTime();
- const bTime = new Date(b[1][0][groupBy]).getTime();
- return bTime - aTime;
- });
- }
- function clearGroupedChats(aside) {
- aside.querySelectorAll('a[href^="/c/"], .__chat-group-header').forEach(el => el.remove());
- }
- function renderGroupedChats(aside) {
- const observer = aside.__chatObserver;
- if (observer) observer.disconnect();
- clearGroupedChats(aside);
- const groups = groupChatsByGroupName();
- for (const [label, chats] of groups) {
- const header = document.createElement('div');
- header.className = '__chat-group-header';
- header.textContent = label;
- aside.appendChild(header);
- chats
- .sort(sortChats)
- .forEach(chat => aside.appendChild(chat.node));
- }
- if (observer) observer.observe(aside, { childList: true, subtree: true });
- }
- function sortChats(a, b) {
- return new Date(b[groupBy]) - new Date(a[groupBy]);
- }
- 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);
- }
- }
- for (const node of mutation.removedNodes) {
- if (node.nodeType === 1 && node.matches('a[href^="/c/"]')) {
- const index = chatList.findIndex(c => c.node === node);
- if (index !== -1) {
- const removed = chatList.splice(index, 1)[0];
- seenIds.delete(removed.id);
- queueRender();
- }
- }
- }
- }
- });
- observer.observe(aside, { childList: true, subtree: true });
- aside.__chatObserver = observer;
- aside.querySelectorAll('a[href^="/c/"]').forEach(processNewChatNode);
- }
- function insertToggleButton(aside) {
- const header = aside.querySelector('h2');
- if (!header || header.querySelector('.__group-toggle')) return;
- // Wrap h2 content to align flexibly
- const wrapper = document.createElement('div');
- wrapper.style.cssText = `
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-right: 10px;
- `;
- // Move existing h2 text/content into the wrapper
- while (header.firstChild) {
- wrapper.appendChild(header.firstChild);
- }
- header.appendChild(wrapper);
- const btn = document.createElement('button');
- btn.className = '__group-toggle';
- const icon = '⇅';
- btn.textContent = `${icon} By ${groupBy}`;
- btn.title = 'Click to toggle sorting mode';
- btn.style.cssText = `
- font-size: 0.75rem;
- background-color: #2a2b32;
- border: 1px solid #444;
- border-radius: 999px;
- padding: 3px 10px;
- color: #ccc;
- cursor: pointer;
- transition: background-color 0.2s;
- `;
- btn.addEventListener('mouseenter', () => {
- btn.style.backgroundColor = '#3a3b42';
- });
- btn.addEventListener('mouseleave', () => {
- btn.style.backgroundColor = '#2a2b32';
- });
- btn.addEventListener('click', () => {
- groupBy = groupBy === 'updated' ? 'created' : 'updated';
- GM_setValue('groupBy', groupBy);
- btn.textContent = `${icon} By ${groupBy}`;
- queueRender();
- });
- wrapper.appendChild(btn);
- }
- (function watchSidebar() {
- let lastAside = null;
- function setup(aside) {
- if (!aside || aside === lastAside) return;
- lastAside = aside;
- insertToggleButton(aside);
- observeChatList(aside);
- renderGroupedChats(aside);
- console.log("ChatGPT grouping: sidebar attached.");
- }
- const rootObserver = new MutationObserver(() => {
- const history = document.querySelector('#history');
- if (!history) return;
- const aside = history.querySelector('aside');
- if (aside && aside !== lastAside) {
- setup(aside);
- }
- });
- rootObserver.observe(document.body, { childList: true, subtree: true });
- const asideNow = document.querySelector('#history aside');
- if (asideNow) setup(asideNow);
- })();
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址