您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Displays a floating container on the right with every message you sent in the current conversation. Clicking a message scrolls smoothly to it.
当前为
// ==UserScript==
// @name ChatGPT Conversation Navigator
// @namespace https://greasyfork.org/en/users/1444872-tlbstation
// @author TLBSTATION
// @icon https://i.ibb.co/jZ3HpwPk/pngwing-com.png
// @version 1.4.2
// @description Displays a floating container on the right with every message you sent in the current conversation. Clicking a message scrolls smoothly to it.
// @match https://chatgpt.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
let chatID = ''; // To track conversation changes
let userMsgCounter = 0;
// Create (or retrieve) the floating container on the right
function createContainer() {
let container = document.getElementById('chatgpt-message-nav');
if (!container) {
container = document.createElement('div');
container.id = 'chatgpt-message-nav';
container.style.position = 'fixed';
container.style.top = '60px';
container.style.right = '10px';
container.style.width = '250px';
container.style.maxHeight = '80vh';
container.style.overflowY = 'auto';
container.className = "text-token-text-primary bg-token-main-surface-primary rounded-lg shadow-lg";
container.style.zIndex = '1';
container.style.borderRadius = '8px';
container.style.boxShadow = '0px 4px 10px rgba(0, 0, 0, 0.3)';
container.style.fontSize = '14px';
container.style.transition = 'width 0.3s, padding 0.3s';
// Create header with title and toggle button
const header = document.createElement('div');
header.id = 'chatgpt-message-nav-header';
header.style.display = 'flex';
header.style.alignItems = 'center';
header.style.justifyContent = 'space-between';
header.style.padding = '10px';
header.style.paddingTop = '15px';
header.style.cursor = 'pointer';
header.style.position = 'sticky';
header.style.top = '-7px';
header.style.background = 'inherit';
header.style.zIndex = '1';
const title = document.createElement('div');
title.id = 'chatgpt-message-nav-title';
title.innerText = 'Your Messages';
title.style.fontWeight = 'bold';
const toggleBtn = document.createElement('button');
toggleBtn.id = 'chatgpt-message-nav-toggle';
toggleBtn.style.background = 'none';
toggleBtn.style.border = 'none';
toggleBtn.style.color = 'white';
toggleBtn.style.fontSize = '16px';
toggleBtn.style.cursor = 'pointer';
// Default state: expanded, so show "<"
toggleBtn.innerHTML = ``;
toggleBtn.style.rotate = '-90deg';
toggleBtn.style.transition = 'rotate 0.3s';
header.appendChild(title);
header.appendChild(toggleBtn);
// Create content container for the list
const content = document.createElement('div');
content.id = 'chatgpt-message-nav-content';
content.style.padding = '10px';
content.style.paddingTop = '0px';
container.appendChild(header);
container.appendChild(content);
document.body.appendChild(container);
// Check if collapsed state is stored
const collapsed = localStorage.getItem('chatgptMessageNavCollapsed') === 'true';
if (collapsed) {
content.style.display = 'none';
container.style.width = 'min-content';
container.style.padding = '5px';
title.style.display = 'none';
toggleBtn.style.rotate = '90deg';
header.style.paddingTop = '10px';
}
// Toggle functionality
toggleBtn.addEventListener('click', () => {
if (content.style.display === 'none') {
// Expand
content.style.display = 'block';
container.style.width = '250px';
container.style.padding = '7px';
title.style.display = 'block';
toggleBtn.style.rotate = '-90deg';
header.style.paddingTop = '15px';
localStorage.setItem('chatgptMessageNavCollapsed', 'false');
} else {
// Collapse
content.style.display = 'none';
container.style.width = 'min-content';
container.style.padding = '5px';
title.style.display = 'none';
toggleBtn.style.rotate = '90deg';
header.style.paddingTop = '10px';
localStorage.setItem('chatgptMessageNavCollapsed', 'true');
}
});
}
return container;
}
// Ensure each user message gets a unique ID
function assignIdToMessage(msgElem) {
if (!msgElem.id) {
userMsgCounter++;
msgElem.id = 'user-msg-' + userMsgCounter;
}
}
// Update the list in the floating container with all user messages
function updateMessageList() {
const container = createContainer();
const content = document.getElementById('chatgpt-message-nav-content');
if (!content) return;
// Preserve current scroll position
const prevScrollTop = content.scrollTop;
const list = content.querySelector('ul');
if (!list) {
// Create the list only once
const newList = document.createElement('ul');
newList.style.padding = '0';
newList.style.margin = '0';
newList.style.listStyle = 'none';
content.appendChild(newList);
}
// Get the existing message IDs to avoid duplicate entries
const existingIds = new Set([...content.querySelectorAll('li')].map(li => li.dataset.msgId));
// Select user messages
const userMessages = document.querySelectorAll('div[data-message-author-role="user"]');
userMessages.forEach(msgElem => {
assignIdToMessage(msgElem);
const text = msgElem.innerText.trim();
// Skip if already in the list
if (existingIds.has(msgElem.id)) return;
const listItem = document.createElement('li');
listItem.dataset.msgId = msgElem.id; // Store msg ID to prevent duplication
listItem.style.cursor = 'pointer';
listItem.style.padding = '5px 10px';
listItem.style.marginTop = '5px';
listItem.style.borderRadius = '10px';
listItem.style.borderBottom = '1px solid var(--main-surface-primary-inverse)';
listItem.style.transition = 'background 0.2s';
listItem.style.whiteSpace = 'nowrap';
listItem.style.overflow = 'hidden';
listItem.style.textOverflow = 'ellipsis';
listItem.addEventListener('mouseenter', () => {
listItem.style.background = '#c5c5c54d' ;
});
listItem.addEventListener('mouseleave', () => {
listItem.style.background = 'transparent';
});
listItem.textContent = text;
// Smooth scroll to message when clicked
listItem.addEventListener('click', () => {
const target = document.getElementById(msgElem.id);
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
// Append new list item
content.querySelector('ul').appendChild(listItem);
});
// Restore scroll position after updating the list
content.scrollTop = prevScrollTop;
}
// Get the current conversation ID based on the URL
function getChatID() {
const chatURL = window.location.pathname;
return chatURL.includes('/c/') ? chatURL.split('/c/')[1] : 'global';
}
// Observe the conversation container for changes
function observeConversation() {
const mainElem = document.querySelector('main');
if (!mainElem) return;
const observer = new MutationObserver(() => {
updateMessageList();
});
observer.observe(mainElem, { childList: true, subtree: true });
}
function toggleContainerVisibility() {
const container = document.getElementById('chatgpt-message-nav');
const isChatPage = window.location.pathname.startsWith('/c/');
if (container) {
container.style.display = isChatPage ? 'block' : 'none';
}
}
// Wait for the conversation area to load
function waitForChat() {
const interval = setInterval(() => {
if (document.querySelector('main')) {
clearInterval(interval);
toggleContainerVisibility();
chatID = getChatID();
updateMessageList();
observeConversation();
}
}, 500);
}
waitForChat();
// Update when switching between conversations (for SPA navigation)
let lastUrl = location.href;
const urlObserver = new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
chatID = getChatID();
userMsgCounter = 0;
updateMessageList();
toggleContainerVisibility();
}
});
urlObserver.observe(document.body, { childList: true, subtree: true });
})();