Greasy Fork is available in English.
Floating navigator for your prompts in ChatGPT, Gemini, Aistudio, Kimi, Grok, GLM conversations
当前为
// ==UserScript==
// @name AI Conversation Navigator
// @namespace http://greasyfork.icu
// @version 2.4
// @description Floating navigator for your prompts in ChatGPT, Gemini, Aistudio, Kimi, Grok, GLM conversations
// @author Bui Quoc Dung
// @match https://chatgpt.com/*
// @match https://gemini.google.com/*
// @match https://aistudio.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 COMMON_TITLE = 'Your Prompts';
const BASE_CONTAINER_CSS = `
right: 20px; width: 250px; max-height: 90vh; overflow-y: auto;
z-index: 9999; border-radius: 2px; box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.3);
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: '35px',
width: 270,
bgClass: "text-token-text-primary bg-token-main-surface-primary rounded-lg shadow-lg"
},
gemini: {
match: /gemini\.google\.com/,
msgSelector: 'p.query-text-line',
top: '55px',
width: 270
},
aistudio: {
match: /aistudio\.google\.com/,
msgSelector: '.user-prompt-container',
top: '55px',
width: 250,
shiftTarget: 'ms-autoscroll-container'
},
grok: {
match: /grok\.com/,
msgSelector: '.relative.group.flex.flex-col.justify-center.items-end',
top: '55px',
width: 280,
},
deepseek: {
match: /chat\.deepseek\.com/,
msgSelector: '.ds-message:nth-child(odd)',
top: '55px',
width: 270,
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',
width: 270
},
glm: {
match: /chat\.z\.ai/,
msgSelector: '.chat-user',
top: '55px',
width: 280
}
};
const site = Object.values(SITE_CONFIG).find(s => s.match.test(location.hostname));
if (!site) return;
GM_addStyle(getShiftStyle(site.width, site.shiftTarget || ''));
let userMsgCounter = 0;
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: 'space-between',
padding: '5px', cursor: 'pointer', fontWeight: 'bold'
});
header.textContent = COMMON_TITLE;
const toggleBtn = document.createElement('button');
Object.assign(toggleBtn.style, {
background: 'none', border: 'none', cursor: 'pointer', fontSize: '16px'
});
toggleBtn.textContent = '⯈';
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 = '130px';
toggleBtn.textContent = '⯆';
}
const toggleHandler = (e) => {
e.stopPropagation();
isCollapsed = !isCollapsed;
if (!isCollapsed) {
content.style.display = 'block';
container.style.width = '250px';
toggleBtn.textContent = '⯈';
} else {
content.style.display = 'none';
container.style.width = '130px';
toggleBtn.textContent = '⯆';
}
updateBodyClassForLayout();
};
toggleBtn.addEventListener('click', toggleHandler);
updateBodyClassForLayout();
}
return container;
}
function assignIdToMessage(msgElem) {
if (!msgElem.id) {
userMsgCounter++;
msgElem.id = 'user-msg-' + userMsgCounter;
msgElem.dataset.index = userMsgCounter;
}
}
function createListItem(msgElem) {
const index = msgElem.dataset.index || '?';
const text = msgElem.innerText.trim();
const preview = text.length > 80 ? text.slice(0, 80) + '...' : text;
const listItem = document.createElement('li');
listItem.textContent = `${index}. ${preview}`;
Object.assign(listItem.style, { cursor: 'pointer', padding: '5px 5px' });
listItem.addEventListener('click', () => {
listItem.parentElement.querySelectorAll('li').forEach(li => li.style.fontWeight = 'normal');
listItem.style.fontWeight = 'bold';
msgElem.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 userMessages = document.querySelectorAll(site.msgSelector);
const existingListItems = list.querySelectorAll('li');
let shouldReset = false;
if (userMessages.length < existingListItems.length) {
shouldReset = true;
}
if (!shouldReset && userMessages.length > 0 && existingListItems.length > 0) {
const firstMsgDOM = userMessages[0];
const firstListItem = existingListItems[0];
if (!firstMsgDOM.id || (firstListItem.dataset.msgId && firstMsgDOM.id !== firstListItem.dataset.msgId)) {
shouldReset = true;
}
}
if (shouldReset) {
list.innerHTML = '';
userMsgCounter = 0;
}
const currentItems = list.querySelectorAll('li');
if (userMessages.length > currentItems.length) {
for (let i = currentItems.length; i < userMessages.length; i++) {
const msgElem = userMessages[i];
assignIdToMessage(msgElem);
const listItem = createListItem(msgElem);
listItem.dataset.msgId = msgElem.id;
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, attributes: false });
}
setTimeout(() => updateMessageList(), 500);
observeConversation();
})();