您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhances the ChatGPT interface by visually highlighting the active chat link, styling message boxes, and adding top-centered navigation buttons to traverse messages.
// ==UserScript== // @name ChatGPT Visual Enhancements with message Navigation // @namespace http://tampermonkey.net/ // @version 1.8 // @description Enhances the ChatGPT interface by visually highlighting the active chat link, styling message boxes, and adding top-centered navigation buttons to traverse messages. // @author noushadBug // @match https://chatgpt.com/* // @icon https://chatgpt.com/favicon.ico // @license MIT // @grant none // ==/UserScript== (function () { 'use strict'; // Initialize variables for navigation let messageElements = []; let currentIndex = -1; // Create and style navigation buttons function createNavigationButtons() { const navContainer = document.createElement('div'); navContainer.id = 'custom-nav-container'; navContainer.style.position = 'absolute'; navContainer.style.top = '10px'; navContainer.style.left = '50%'; navContainer.style.transform = 'translateX(-50%)'; navContainer.style.display = 'flex'; navContainer.style.gap = '10px'; navContainer.style.zIndex = '1000'; const prevButton = document.createElement('button'); prevButton.id = 'custom-prev-button'; prevButton.innerHTML = '‹'; styleNavButton(prevButton, 'Previous Message'); const nextButton = document.createElement('button'); nextButton.id = 'custom-next-button'; nextButton.innerHTML = '›'; styleNavButton(nextButton, 'Next Message'); navContainer.appendChild(prevButton); navContainer.appendChild(nextButton); document.body.appendChild(navContainer); // Add event listeners prevButton.addEventListener('click', () => navigateMessages(-1)); nextButton.addEventListener('click', () => navigateMessages(1)); } // Style individual navigation buttons function styleNavButton(button, ariaLabel) { // Apply the new styles as per your specifications button.style.padding = '4px 19px'; button.style.fontSize = '20px'; button.style.border = 'none'; button.style.borderRadius = '50px'; button.style.cursor = 'pointer'; button.style.backgroundColor = 'rgb(65, 76, 59)'; button.style.color = 'rgb(255, 255, 255)'; button.style.boxShadow = 'rgba(0, 0, 0, 0.3) 0px 2px 5px'; button.style.transition = 'background-color 0.3s, transform 0.2s'; button.style.transform = 'scale(1)'; // Accessibility button.setAttribute('aria-label', ariaLabel); // Hover effects button.addEventListener('mouseover', () => { button.style.backgroundColor = '#5a6e57'; button.style.transform = 'scale(1.1)'; }); button.addEventListener('mouseout', () => { button.style.backgroundColor = 'rgb(65, 76, 59)'; button.style.transform = 'scale(1)'; }); } // Function to update the list of message elements function updateMessageElements() { // Select all message containers; adjust the selector if necessary messageElements = Array.from(document.querySelectorAll('[data-message-author-role]')); } // Function to navigate through messages function navigateMessages(direction) { if (messageElements.length === 0) return; // Remove highlight from the current message if (currentIndex >= 0 && currentIndex < messageElements.length) { const currentMessage = messageElements[currentIndex]; currentMessage.style.border = ''; currentMessage.style.boxShadow = ''; } // Update the current index currentIndex += direction; if (currentIndex < 0) { currentIndex = messageElements.length - 1; // Wrap to last } else if (currentIndex >= messageElements.length) { currentIndex = 0; // Wrap to first } const targetMessage = messageElements[currentIndex]; // Ensure the targetMessage exists if (targetMessage) { // Scroll to the message container, aligning it to the top targetMessage.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Highlight the target message targetMessage.style.border = '2px solid #2f2f2f'; targetMessage.style.boxShadow = 'rgb(123 166 175) 0px 0px 10px'; // Focus on the message for better accessibility targetMessage.setAttribute('tabindex', '-1'); // Make it focusable targetMessage.focus({ preventScroll: true }); } } // Function to update the active <li> state function updateActiveState() { const listItems = document.querySelectorAll('li.relative'); const currentPath = window.location.pathname; listItems.forEach(li => { const anchor = li.querySelector('a'); // Find the <a> inside <li> if (anchor && anchor.getAttribute('href') === currentPath) { // If href matches the current path, style it as active li.classList.add('active'); li.style.background = 'black'; li.style.borderRadius = '20px'; } else { // Remove active styles from other <li> elements li.classList.remove('active'); li.style.background = ''; li.style.borderRadius = ''; } }); } // Function to style assistant messages function styleAssistantMessages() { const messages = document.querySelectorAll('[data-message-author-role="assistant"]'); messages.forEach(message => { if (!message.classList.contains('custom-assistant-message-container')) { // Add custom class and apply styles message.classList.add('custom-assistant-message-container'); message.style.background = '#414c3b'; message.style.padding = '1em'; message.style.borderRadius = '10px'; message.style.color = '#ffffff'; // Ensure text is readable message.setAttribute('tabindex', '-1'); // Make it focusable } }); } // Function to highlight user messages (optional enhancement) function styleUserMessages() { const userMessages = document.querySelectorAll('[data-message-author-role="user"]'); userMessages.forEach(message => { if (!message.classList.contains('custom-user-message-container')) { // Add custom class and apply styles message.classList.add('custom-user-message-container'); message.style.background = '#2a2a2a'; message.style.padding = '1em'; message.style.borderRadius = '10px'; message.style.color = '#ffffff'; // Ensure text is readable message.setAttribute('tabindex', '-1'); // Make it focusable } }); } // Debounce function to limit the rate of function execution function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Observe URL and DOM changes with debounced callback const observer = new MutationObserver(debounce(() => { updateActiveState(); styleAssistantMessages(); styleUserMessages(); updateMessageElements(); }, 300)); // Start observing changes to the document body observer.observe(document.body, { childList: true, subtree: true }); // Initial setup updateActiveState(); styleAssistantMessages(); styleUserMessages(); updateMessageElements(); createNavigationButtons(); // Clean up when the script is removed window.addEventListener('unload', () => observer.disconnect()); // Additional CSS Styling const otherCSS = `.h-header-height{background-color: var(--sidebar-surface-primary);}`; const styleTag = document.createElement('style'); styleTag.textContent = otherCSS; document.head.appendChild(styleTag); })();