Greasy Fork is available in English.
Floating navigator for your prompts in ChatGPT, Gemini, Notebooklm, Grok, Deepseek, Kimi, GLM conversations
当前为
// ==UserScript==
// @name AI Conversation Navigator
// @namespace http://greasyfork.icu
// @version 2.8
// @description Floating navigator for your prompts in ChatGPT, Gemini, Notebooklm, Grok, Deepseek, Kimi, 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
// ==/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();
})();