// ==UserScript== // @name AI Conversation Navigator // @namespace https://greasyfork.org // @version 2.8 // @description Floating navigator for your prompts in ChatGPT, Gemini, Notebooklm, Kimi, Grok, GLM conversations // @author Bui Quoc Dung // @match https://chatgpt.com/* // @match https://gemini.google.com/* // @match https://notebooklm.google.com/* // @match https://grok.com/* // @match https://www.kimi.com/* // @match https://chat.deepseek.com/* // @match https://chat.z.ai/* // @grant GM_addStyle // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; const NAV_WIDTH = 250; const NAV_COLLAPSED_WIDTH = 80; const BASE_CONTAINER_CSS = ` right: 0px; width: ${NAV_WIDTH}px; max-height: 90vh; overflow-y: auto; z-index: 9999; transition: width 0.3s, padding 0.3s, opacity 0.3s, transform 0.3s; font-family: Calibri, sans-serif; font-size: 15px; color: CanvasText; position: fixed; `; const getShiftStyle = (width, selector = '') => ` body.navigator-expanded ${selector} { margin-left: 0 !important; margin-right: ${width}px !important; max-width: calc(100% - ${width}px) !important; transition: margin-right 0.3s ease; } `; const SITE_CONFIG = { chatgpt: { match: /chatgpt\.com/, msgSelector: 'div[data-message-author-role="user"]', top: '55px', bgClass: "text-token-text-primary bg-token-main-surface-primary rounded-lg shadow-lg" }, gemini: { match: /gemini\.google\.com/, msgSelector: '.query-text', top: '55px', fallbackSelectors: [ '[data-message-author-role="user"]', '.user-message', '[role="user"]', '.message.user', 'div[data-test-id*="user"]', 'div[data-test-id*="prompt"]', '.prompt-content', '.user-input', '[class*="user"][class*="message"]' ] }, notebooklm: { match: /notebooklm\.google\.com/, msgSelector: 'chat-message .from-user-container', top: '55px', }, grok: { match: /grok\.com/, msgSelector: '.relative.group.flex.flex-col.justify-center.items-end', top: '55px' }, deepseek: { match: /chat\.deepseek\.com/, msgSelector: '.ds-message:nth-child(odd)', top: '55px', shiftTarget: '#root > div > div > div:nth-child(2) > div:nth-child(3) > div > div:nth-child(2) > div' }, kimi: { match: /www\.kimi\.com/, msgSelector: '.user-content', top: '55px' }, glm: { match: /chat\.z\.ai/, msgSelector: '.chat-user', top: '55px' } }; const site = Object.values(SITE_CONFIG).find(s => s.match.test(location.hostname)); if (!site) return; const currentWidth = site.width || NAV_WIDTH; GM_addStyle(getShiftStyle(currentWidth, site.shiftTarget || '')); let conversationObserver = null; let isCollapsed = false; window.navigatorUpdateTimeout = null; function updateBodyClassForLayout() { const container = document.getElementById('message-nav'); const content = document.getElementById('message-nav-content'); if (container && content && content.style.display !== 'none') { document.body.classList.add('navigator-expanded'); } else { document.body.classList.remove('navigator-expanded'); } } function createContainer() { let container = document.getElementById('message-nav'); if (!container) { container = document.createElement('div'); container.id = 'message-nav'; container.className = site.bgClass || ''; container.style.cssText = `top: ${site.top}; ${BASE_CONTAINER_CSS}`; const header = document.createElement('div'); Object.assign(header.style, { display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', fontWeight: 'bold' }); const toggleBtn = document.createElement('button'); Object.assign(toggleBtn.style, { background: 'none', border: 'none', cursor: 'pointer', fontSize: '18px', color: 'inherit' }); toggleBtn.textContent = 'Close'; header.appendChild(toggleBtn); const content = document.createElement('div'); content.id = 'message-nav-content'; content.style.padding = '5px'; container.appendChild(header); container.appendChild(content); document.body.appendChild(container); if (isCollapsed) { content.style.display = 'none'; container.style.width = `${NAV_COLLAPSED_WIDTH}px`; toggleBtn.textContent = 'Open'; } const toggleHandler = (e) => { e.stopPropagation(); isCollapsed = !isCollapsed; if (!isCollapsed) { content.style.display = 'block'; container.style.width = `${currentWidth}px`; toggleBtn.textContent = 'Close'; } else { content.style.display = 'none'; container.style.width = `${NAV_COLLAPSED_WIDTH}px`; toggleBtn.textContent = 'Open'; } updateBodyClassForLayout(); }; toggleBtn.addEventListener('click', toggleHandler); updateBodyClassForLayout(); } return container; } function findUserPrompts() { const prompts = []; let elements = document.querySelectorAll(site.msgSelector); if (elements.length > 0) { elements.forEach((element) => { const text = element.textContent.trim(); if (text) prompts.push({ element, text }); }); return prompts; } if (site.fallbackSelectors) { for (const selector of site.fallbackSelectors) { elements = document.querySelectorAll(selector); if (elements.length > 0) { elements.forEach((element) => { const text = element.textContent.trim(); if (text) prompts.push({ element, text }); }); if (prompts.length > 0) break; } } } return prompts; } function createListItem(prompt, index) { const listItem = document.createElement('li'); const preview = prompt.text.length > 80 ? prompt.text.slice(0, 80) + '...' : prompt.text; listItem.textContent = `${index}. ${preview}`; Object.assign(listItem.style, { cursor: 'pointer', padding: '5px 0px 5px 5px' }); listItem.addEventListener('click', () => { listItem.parentElement.querySelectorAll('li').forEach(li => li.style.fontWeight = 'normal'); listItem.style.fontWeight = 'bold'; prompt.element.scrollIntoView({ behavior: 'smooth', block: 'start' }); }); return listItem; } function updateMessageList() { const container = createContainer(); const content = document.getElementById('message-nav-content'); if (!content) return; let list = content.querySelector('ul'); if (!list) { list = document.createElement('ul'); list.style.cssText = 'padding: 0; margin: 0; list-style: none;'; content.appendChild(list); } const prompts = findUserPrompts(); while (list.firstChild) { list.removeChild(list.firstChild); } if (prompts.length === 0) { const noContent = document.createElement('div'); noContent.textContent = 'Waiting'; noContent.style.cssText = 'color: #999; font-style: italic; text-align: center; padding: 15px 0;'; list.appendChild(noContent); return; } prompts.forEach((prompt, index) => { const listItem = createListItem(prompt, index + 1); list.appendChild(listItem); }); } function observeConversation() { if (conversationObserver) conversationObserver.disconnect(); conversationObserver = new MutationObserver(() => { clearTimeout(window.navigatorUpdateTimeout); window.navigatorUpdateTimeout = setTimeout(() => { updateMessageList(); }, 500); }); conversationObserver.observe(document.body, { childList: true, subtree: true }); } setTimeout(() => updateMessageList(), 1000); observeConversation(); })();