您需要先安装一个扩展,例如 篡改猴、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或关注我们的公众号极客氢云获取最新地址