您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Grok 和 AI Studio 等多个ai页面上添加可折叠的悬浮导航控件
// ==UserScript== // @name AI对话页面导航增强 // @namespace http://tampermonkey.net/ // @version 1.7.2 // @description 在 Grok 和 AI Studio 等多个ai页面上添加可折叠的悬浮导航控件 // @author YourName // @match https://grok.com/* // @match https://*.x.ai/* // @match https://aistudio.google.com/* // @match https://chat.deepseek.com/* // @match https://chatgpt.com/* // @license MIT // ==/UserScript== (function() { 'use strict'; // 配置对象,定义选择器 const config = { selectors: { 'grok.com': 'div.relative.items-end', 'x.ai': 'div.relative.items-end', 'google.com': 'div.user-prompt-container', 'deepseek.com': 'div._9663006', 'chatgpt.com': '[data-turn="user"]', default: 'div' } }; // 添加导航控件样式,使用CSS变量 GM_addStyle(` :root { --background: rgba(255, 255, 255, 0.95); --shadow: rgba(0, 0, 0, 0.2); --border: #e0e0e0; --primary-color: #4285f4; --active-color: #34a853; --refresh-color: #fbbc05; --text-color: #333; --secondary-text: #666; --disabled-color: #cccccc; --disabled-text: #888888; } [data-theme="dark"] { --background: rgba(30, 30, 30, 0.95); --shadow: rgba(255, 255, 255, 0.1); --border: #444; --text-color: #ddd; --secondary-text: #aaa; } #grok-nav-container { position: fixed; top: 100px; right: 20px; z-index: 10000; background: var(--background); padding: 15px; border-radius: 10px; box-shadow: 0 4px 15px var(--shadow); border: 1px solid var(--border); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; max-width: 300px; backdrop-filter: blur(5px); display: flex; flex-direction: column; gap: 10px; transition: all 0.3s ease; } .collapsed { width: 90px !important; // height: 150px !important; overflow: hidden; padding: 10px !important; } .collapsed .nav-header { justify-content: center; margin-bottom: 5px; } .collapsed .nav-title { display: none; } .collapsed .refresh-btn, .collapsed .close-btn { display: none; } .collapsed .nav-display { display: none; } .collapsed .nav-numbers { display: none; } .collapsed .debug-info { display: none; } .collapsed .nav-arrows { flex-direction: column; gap: 5px; justify-content: center; align-items: center; } .nav-header { display: flex; justify-content: space-between; align-items: center; } .nav-title { font-weight: bold; font-size: 16px; color: var(--text-color); margin: 0; } .nav-btn-group { display: flex; gap: 3px; } .nav-btn { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--primary-color); color: white; font-weight: bold; cursor: pointer; user-select: none; border: none; font-size: 12px; transition: all 0.3s ease; } .toggle-btn { background: var(--active-color); font-size: 14px; width: 26px; height: 26px; } .refresh-btn { background: var(--refresh-color); font-size: 14px; width: 26px; height: 26px; } .close-btn { background: #ea4335; font-size: 14px; width: 26px; height: 26px; } .nav-btn:hover { background: #3367d6; transform: scale(1.1); } .nav-btn:disabled { background: var(--disabled-color); color: var(--disabled-text); cursor: not-allowed; transform: scale(1); } .nav-display { text-align: center; font-size: 14px; color: var(--secondary-text); padding: 5px 0; margin: 0; } .nav-arrows { display: flex; justify-content: center; gap: 15px; } .arrow-btn { width: 40px; height: 40px; font-size: 18px; } .nav-numbers { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 10px 0 0; /* 移除 max-height 限制,允许动态扩展 */ overflow-y: auto; /* 仅在内容溢出时显示滚动条 */ padding: 5px; } .no-results { color: #d93025; padding: 10px; text-align: center; font-size: 14px; } .debug-info { background-color: #f1f3f4; padding: 10px; border-radius: 5px; font-family: monospace; font-size: 12px; color: #d3d3d3; margin-top: 10px; max-height: 100px; overflow: auto; } `); // 创建导航控件容器 const container = document.createElement('div'); container.id = 'grok-nav-container'; document.body.appendChild(container); // 添加标题 const header = document.createElement('div'); header.className = 'nav-header'; container.appendChild(header); const title = document.createElement('div'); title.className = 'nav-title'; title.textContent = '定位会话' header.appendChild(title); // 添加按钮组 const btnGroup = document.createElement('div'); btnGroup.className = 'nav-btn-group'; header.appendChild(btnGroup); // 添加折叠按钮 const toggleBtn = document.createElement('button'); toggleBtn.className = 'nav-btn toggle-btn'; toggleBtn.title = '折叠/展开导航'; toggleBtn.textContent = '−'; btnGroup.appendChild(toggleBtn); // 添加刷新按钮 const refreshBtn = document.createElement('button'); refreshBtn.className = 'nav-btn refresh-btn'; refreshBtn.title = '重新扫描页面'; refreshBtn.textContent = '↻'; btnGroup.appendChild(refreshBtn); // 添加关闭按钮 const closeBtn = document.createElement('button'); closeBtn.className = 'nav-btn close-btn'; closeBtn.title = '关闭导航'; closeBtn.textContent = '×'; btnGroup.appendChild(closeBtn); // 添加位置显示 const positionDisplay = document.createElement('p'); positionDisplay.className = 'nav-display'; positionDisplay.id = 'positionDisplay'; positionDisplay.textContent = '0/0'; container.appendChild(positionDisplay); // 添加箭头按钮容器 const arrowsContainer = document.createElement('div'); arrowsContainer.className = 'nav-arrows'; container.appendChild(arrowsContainer); // 添加上按钮 const upBtn = document.createElement('button'); upBtn.className = 'nav-btn arrow-btn'; upBtn.id = 'upBtn'; upBtn.textContent = '▲'; arrowsContainer.appendChild(upBtn); // 添加下按钮 const downBtn = document.createElement('button'); downBtn.className = 'nav-btn arrow-btn'; downBtn.id = 'downBtn'; downBtn.textContent = '▼'; arrowsContainer.appendChild(downBtn); // 添加数字按钮容器 const numbersContainer = document.createElement('div'); numbersContainer.className = 'nav-numbers'; numbersContainer.id = 'numbersContainer'; container.appendChild(numbersContainer); // 添加调试信息 const debugInfo = document.createElement('div'); debugInfo.className = 'debug-info'; debugInfo.id = 'debugInfo'; debugInfo.textContent = '调试信息将显示在这里...'; container.appendChild(debugInfo); // 查找目标div元素 let divs = []; let currentIndex = 0; // 添加日志到调试面板 function logDebugInfo(message) { try { const logEntry = document.createElement('div'); logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; debugInfo.appendChild(logEntry); debugInfo.scrollTop = debugInfo.scrollHeight; } catch (error) { console.error(`调试信息记录错误: ${error.message}`); } } // 查找目标元素 function findTargetDivs() { try { const hostname = location.hostname || ''; logDebugInfo(`当前主机名: ${hostname}`); const selectorKey = Object.keys(config.selectors).find(key => hostname.includes(key)) || 'default'; const selector = config.selectors[selectorKey]; logDebugInfo(`使用选择器: ${selector} (匹配的键: ${selectorKey})`); const result = document.querySelectorAll(selector); divs = Array.from(result); if (divs.length === 0) { logDebugInfo(`警告: 选择器 ${selector} 未找到任何内容块`); } else { logDebugInfo(`找到 ${divs.length} 个内容块`); } return divs; } catch (error) { logDebugInfo(`错误: ${error.message}`); console.error(`findTargetDivs 错误: ${error.message}`); return []; } } // 更新导航状态 function updateNavStatus() { try { // 清空 numbersContainer 的所有子节点 while (numbersContainer.firstChild) { numbersContainer.removeChild(numbersContainer.firstChild); } if (divs.length === 0) { const noResultsDiv = document.createElement('div'); noResultsDiv.className = 'no-results'; const textNode1 = document.createTextNode('未找到内容块'); noResultsDiv.appendChild(textNode1); noResultsDiv.appendChild(document.createElement('br')); // 添加换行 const textNode2 = document.createTextNode('(尝试点击刷新按钮)'); noResultsDiv.appendChild(textNode2); numbersContainer.appendChild(noResultsDiv); positionDisplay.textContent = "0/0"; return; } divs.forEach((div, index) => { const numBtn = document.createElement('button'); numBtn.className = 'nav-btn'; numBtn.textContent = index + 1; // numBtn.title = `跳转到第 ${index + 1} 个内容块`; numBtn.title = div.textContent; numBtn.style.background = index === currentIndex ? 'var(--active-color)' : 'var(--primary-color)'; numBtn.addEventListener('click', () => { try { scrollToIndex(index); } catch (error) { logDebugInfo(`数字按钮点击错误: ${error.message}`); console.error(`数字按钮点击错误: ${error.message}`); } }); numbersContainer.appendChild(numBtn); logDebugInfo(`创建按钮 ${index + 1}`); }); upBtn.disabled = currentIndex === 0; downBtn.disabled = currentIndex === divs.length - 1; positionDisplay.textContent = `${currentIndex + 1}/${divs.length}`; positionDisplay.title = `当前内容块: ${currentIndex + 1},共 ${divs.length} 个`; } catch (error) { logDebugInfo(`更新导航状态错误: ${error.message}`); console.error(`更新导航状态错误: ${error.message}`); } } // 滚动到指定索引 function scrollToIndex(index) { try { if (index < 0 || index >= divs.length || !divs[index]) { logDebugInfo(`无效索引: ${index}`); return; } currentIndex = index; divs[index].scrollIntoView({ behavior: 'smooth', block: 'start' }); logDebugInfo(`滚动到块 ${index + 1}`); updateNavStatus(); } catch (error) { logDebugInfo(`滚动错误: ${error.message}`); console.error(`滚动错误: ${error.message}`); } } // 初始化函数 function initNavigation() { try { findTargetDivs(); currentIndex = divs.length > 0 ? 0 : -1; updateNavStatus(); if (divs.length > 0) { scrollToIndex(0); } } catch (error) { logDebugInfo(`初始化导航错误: ${error.message}`); console.error(`初始化导航错误: ${error.message}`); } } // 按钮事件监听 upBtn.addEventListener('click', () => { try { if (currentIndex > 0) { scrollToIndex(currentIndex - 1); } } catch (error) { logDebugInfo(`上按钮错误: ${error.message}`); console.error(`上按钮错误: ${error.message}`); } }); downBtn.addEventListener('click', () => { try { if (currentIndex < divs.length - 1) { scrollToIndex(currentIndex + 1); } } catch (error) { logDebugInfo(`下按钮错误: ${error.message}`); console.error(`下按钮错误: ${error.message}`); } }); refreshBtn.addEventListener('click', () => { try { initNavigation(); } catch (error) { logDebugInfo(`刷新按钮错误: ${error.message}`); console.error(`刷新按钮错误: ${error.message}`); } }); toggleBtn.addEventListener('click', () => { try { container.classList.toggle('collapsed'); toggleBtn.textContent = container.classList.contains('collapsed') ? '+' : '−'; toggleBtn.title = container.classList.contains('collapsed') ? '展开导航' : '折叠导航'; } catch (error) { logDebugInfo(`折叠按钮错误: ${error.message}`); console.error(`折叠按钮错误: ${error.message}`); } }); closeBtn.addEventListener('click', () => { try { container.remove(); logDebugInfo('导航控件已关闭'); } catch (error) { logDebugInfo(`关闭按钮错误: ${error.message}`); console.error(`关闭按钮错误: ${error.message}`); } }); // 全局错误处理 window.addEventListener('error', (event) => { logDebugInfo(`未捕获的错误: ${event.message}`); console.error(`未捕获的错误: ${event.message}`); }); // 初始化导航并自动刷新 document.addEventListener('DOMContentLoaded', () => { try { logDebugInfo("导航工具启动..."); const observer = new MutationObserver((mutations, obs) => { const hostname = location.hostname || ''; const selector = config.selectors[Object.keys(config.selectors).find(key => hostname.includes(key)) || 'default']; if (document.querySelector(selector)) { logDebugInfo("检测到目标元素,执行自动刷新..."); initNavigation(); obs.disconnect(); // 仅刷新一次后停止观察 } }); observer.observe(document.body, { childList: true, subtree: true }); } catch (error) { logDebugInfo(`初始化错误: ${error.message}`); console.error(`初始化错误: ${error.message}`); } }); })();