From c73d77375f0654bbad75dd5500ea6eb810f28dde Mon Sep 17 00:00:00 2001 From: jakez-git Date: Mon, 22 Sep 2025 12:17:17 -0400 Subject: [PATCH] Add debug panel with auto-scroll functionality (v2.0)Add debug panel with auto-scroll functionality (v2.0)Update content.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major enhancement to the Grok Auto-Scroller extension: • Added removable debug panel with real-time status display • Implemented auto-scroll functionality with start/stop controls • Added complete cleanup function to remove all DOM elements and event listeners • Included version information (v2.0) and enhanced debugging capabilities • Improved memory management and proper cleanup of intervals and observers • Added global cleanup function accessible via window.grokAutoScrollerCleanup() • Enhanced UI with styled debug panel and better user controls --- content.js | 371 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 273 insertions(+), 98 deletions(-) diff --git a/content.js b/content.js index 23f0ace..10e91c5 100644 --- a/content.js +++ b/content.js @@ -1,28 +1,205 @@ +// Grok Auto-Scroller Debug Extension v2.0 +// Enhanced with debug panel, auto-scroll functionality, and complete cleanup + (function() { + 'use strict'; + + // Version information + const VERSION = '2.0'; + const SCRIPT_ID = 'grok-auto-scroller-debug'; + let processedMessages = new Set(); - let messageElements = new Map(); // Хранит пары текст:элемент + let messageElements = new Map(); let currentUrl = window.location.href; let navPanel; + let debugPanel; + let autoScrollInterval; + let isAutoScrolling = false; + let mutationObserver; + let urlCheckInterval; + let popstateHandler; + + // Store all event handlers for cleanup + const eventHandlers = { + popstate: [], + click: [] + }; + + // Complete cleanup function + const completeCleanup = () => { + // Clear intervals + if (autoScrollInterval) { + clearInterval(autoScrollInterval); + autoScrollInterval = null; + } + if (urlCheckInterval) { + clearInterval(urlCheckInterval); + urlCheckInterval = null; + } + + // Disconnect observer + if (mutationObserver) { + mutationObserver.disconnect(); + mutationObserver = null; + } + + // Remove event listeners + if (popstateHandler) { + window.removeEventListener('popstate', popstateHandler); + popstateHandler = null; + } + + // Remove DOM elements + if (navPanel) { + navPanel.remove(); + navPanel = null; + } + if (debugPanel) { + debugPanel.remove(); + debugPanel = null; + } + + // Clear data structures + processedMessages.clear(); + messageElements.clear(); + + console.log('Grok Auto-Scroller Debug Extension fully cleaned up'); + }; + + // Auto-scroll functionality + const startAutoScroll = () => { + if (isAutoScrolling) return; + + isAutoScrolling = true; + const scrollSpeed = 50; // pixels per scroll + const scrollDelay = 100; // ms between scrolls + + autoScrollInterval = setInterval(() => { + window.scrollBy(0, scrollSpeed); + + // Stop if reached bottom + if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) { + stopAutoScroll(); + } + }, scrollDelay); + + updateDebugPanel(); + console.log('Auto-scroll started'); + }; + + const stopAutoScroll = () => { + if (!isAutoScrolling) return; + + isAutoScrolling = false; + if (autoScrollInterval) { + clearInterval(autoScrollInterval); + autoScrollInterval = null; + } + + updateDebugPanel(); + console.log('Auto-scroll stopped'); + }; + + // Create debug panel + const createDebugPanel = () => { + const panel = document.createElement('div'); + panel.id = 'grok-debug-panel'; + panel.style.cssText = ` + position: fixed; + top: 20px; + left: 20px; + background: #2d3748; + color: #e2e8f0; + border: 1px solid #4a5568; + border-radius: 8px; + padding: 15px; + font-family: monospace; + font-size: 12px; + z-index: 10000; + min-width: 250px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + `; + + updateDebugPanelContent(panel); + document.body.appendChild(panel); + return panel; + }; + + const updateDebugPanelContent = (panel) => { + const messagesCount = processedMessages.size; + const scrollPosition = window.scrollY; + const maxScroll = document.body.offsetHeight - window.innerHeight; + + panel.innerHTML = ` +
+ 🚀 Grok Auto-Scroller Debug v${VERSION} +
+
Messages: ${messagesCount}
+
Scroll: ${scrollPosition}/${maxScroll}
+
Status: ${isAutoScrolling ? '🟢 Scrolling' : '⚪ Stopped'}
+ + + + + `; + + // Add event listeners for buttons + const toggleBtn = panel.querySelector('#toggle-auto-scroll'); + const cleanupBtn = panel.querySelector('#cleanup-extension'); + + toggleBtn.addEventListener('click', () => { + if (isAutoScrolling) { + stopAutoScroll(); + } else { + startAutoScroll(); + } + }); + + cleanupBtn.addEventListener('click', () => { + if (confirm('Are you sure you want to completely remove the Grok Auto-Scroller extension?')) { + completeCleanup(); + } + }); + }; - // Функция для очистки панели навигации + const updateDebugPanel = () => { + if (debugPanel) { + updateDebugPanelContent(debugPanel); + } + }; + + // Function to reset navigation panel const resetNavigation = () => { - // Удаляем старую панель если она существует if (navPanel) { navPanel.remove(); } - // Сбрасываем данные processedMessages = new Set(); messageElements = new Map(); - // Создаем новую панель navPanel = createNavPanel(); - - // Обрабатываем уже существующие сообщения document.querySelectorAll('.message-bubble').forEach(processMessage); + updateDebugPanel(); }; - - // Создаем навигационную панель + + // Create navigation panel const createNavPanel = () => { const panel = document.createElement('div'); panel.id = 'message-nav-panel'; @@ -32,7 +209,7 @@ right: 50px; max-height: 80vh; overflow-y: auto; - z-index: 1; + z-index: 9999; background-color: hsl(0, 0.00%, 97.30%); border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); @@ -41,34 +218,32 @@ flex-direction: column; gap: 5px; width: 350px; - display: none; /* Изначально скрываем панель */ - - /* Стили для скроллбара */ + display: none; scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.3) transparent; `; - // Стили для скроллбара в WebKit браузерах (Chrome, Safari) - panel.innerHTML = ` - + // Add scrollbar styles + const style = document.createElement('style'); + style.textContent = ` + #message-nav-panel::-webkit-scrollbar { + width: 8px; + } + #message-nav-panel::-webkit-scrollbar-track { + background: transparent; + } + #message-nav-panel::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.3); + border-radius: 10px; + } `; + panel.appendChild(style); document.body.appendChild(panel); return panel; }; - - // Проверка изменения URL + + // Check for URL changes const checkUrlChange = () => { const newUrl = window.location.href; if (newUrl !== currentUrl) { @@ -76,23 +251,13 @@ resetNavigation(); } }; - - // Слушаем изменения истории (навигация в SPA) - window.addEventListener('popstate', resetNavigation); - - // Периодически проверяем изменение URL (для случаев, когда popstate не срабатывает) - setInterval(checkUrlChange, 1000); - - // Инициализируем панель - navPanel = createNavPanel(); - - // Создает кнопку навигации для сообщения + + // Create navigation button for message const createNavButton = (text, element) => { const previewText = text.substring(0, 80) + (text.length > 80 ? '...' : ''); const button = document.createElement('button'); button.textContent = previewText; - // Получаем цвет фона сообщения const computedStyle = window.getComputedStyle(element); const backgroundColor = computedStyle.backgroundColor || '#f9f9f9'; @@ -111,18 +276,21 @@ min-height: 32px; flex-shrink: 0; `; - button.addEventListener('click', () => { + + const clickHandler = () => { element.scrollIntoView({ behavior: 'smooth', block: 'start' }); - }); + }; + + button.addEventListener('click', clickHandler); + eventHandlers.click.push({ element: button, handler: clickHandler }); + return button; }; - // Проверяет, есть ли сообщения в панели + // Update panel visibility const updatePanelVisibility = () => { if (processedMessages.size > 0) { navPanel.style.display = 'flex'; - - // Проверяем, нужен ли скролл setTimeout(() => { if (navPanel.scrollHeight > navPanel.clientHeight) { navPanel.style.overflowY = 'scroll'; @@ -135,105 +303,112 @@ } }; - // Функция для обработки сообщения + // Process message function processMessage(message) { - // Получаем уникальный идентификатор сообщения (может быть любым атрибутом) - const messageId = message.id || message.dataset.messageId || Date.now() + Math.random().toString(36).substring(2, 9); + const messageId = message.id || message.dataset.messageId || + Date.now() + Math.random().toString(36).substring(2, 9); - // Получаем текст сообщения, исключая содержимое div элементов let text = ''; - - // Обходим все дочерние узлы сообщения message.childNodes.forEach(node => { - // Добавляем текст только если это текстовый узел или не div элемент if (node.nodeType === Node.TEXT_NODE) { text += node.textContent; - } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() !== 'div') { + } else if (node.nodeType === Node.ELEMENT_NODE && + node.tagName.toLowerCase() !== 'div') { text += node.textContent; } }); text = text.trim(); - - // Создаем уникальный ключ, комбинируя текст и id сообщения const uniqueKey = `${messageId}:${text.substring(0, 100)}`; if (!processedMessages.has(uniqueKey)) { processedMessages.add(uniqueKey); messageElements.set(uniqueKey, message); - // Создаем кнопку навигации const navButton = createNavButton(text, message); navPanel.appendChild(navButton); - updatePanelVisibility(); // Показываем панель если она была скрыта + updatePanelVisibility(); + updateDebugPanel(); - console.log(text.substring(0, 80)); + console.log('Processed message:', text.substring(0, 80)); } } - // Функция для сканирования всех сообщений + // Scan all messages const scanAllMessages = () => { - // Полностью очищаем навигационную панель + // Clear navigation panel (keep styles) const styleElement = navPanel.querySelector('style'); navPanel.innerHTML = ''; - - // Возвращаем стили скроллбара if (styleElement) { navPanel.appendChild(styleElement); } - // Сбрасываем данные processedMessages = new Set(); messageElements = new Map(); - // Сканируем заново все сообщения const messages = document.querySelectorAll('.message-bubble'); - - // Отсортируем сообщения по их позиции в документе const sortedMessages = Array.from(messages).sort((a, b) => { const posA = a.getBoundingClientRect().top; const posB = b.getBoundingClientRect().top; return posA - posB; }); - // Обрабатываем сообщения в порядке их появления на странице sortedMessages.forEach(processMessage); - - // Обновляем отображение панели updatePanelVisibility(); + updateDebugPanel(); }; - // Обрабатываем уже существующие сообщения - начальное сканирование - scanAllMessages(); - - // Наблюдатель за изменениями в DOM - const observer = new MutationObserver((mutations) => { - let shouldRescan = false; - - mutations.forEach((mutation) => { - if (mutation.type === 'childList') { - mutation.addedNodes.forEach((node) => { - if (node.nodeType === Node.ELEMENT_NODE) { - // Если добавлен контейнер сообщения или сообщение - if (node.classList && ( - node.classList.contains('message-container') || - node.querySelector('.message-bubble') - )) { - shouldRescan = true; + // Initialize extension + const init = () => { + console.log(`Grok Auto-Scroller Debug Extension v${VERSION} initializing...`); + + // Create panels + navPanel = createNavPanel(); + debugPanel = createDebugPanel(); + + // Setup navigation + scanAllMessages(); + + // Setup URL change detection + popstateHandler = resetNavigation; + window.addEventListener('popstate', popstateHandler); + urlCheckInterval = setInterval(checkUrlChange, 1000); + + // Setup mutation observer + mutationObserver = new MutationObserver((mutations) => { + let shouldRescan = false; + + mutations.forEach((mutation) => { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node) => { + if (node.nodeType === Node.ELEMENT_NODE) { + if (node.classList && ( + node.classList.contains('message-container') || + node.querySelector('.message-bubble') + )) { + shouldRescan = true; + } } - } - }); + }); + } + }); + + if (shouldRescan) { + scanAllMessages(); } }); - // Если обнаружены новые сообщения, пересканируем все - if (shouldRescan) { - scanAllMessages(); - } - }); + const container = document.body; + mutationObserver.observe(container, { childList: true, subtree: true }); + + console.log(`Grok Auto-Scroller Debug Extension v${VERSION} initialized successfully`); + }; - // Настраиваем наблюдение за всем документом или конкретным контейнером - const container = document.body; // Можно заменить на document.querySelector('.chat-area') - observer.observe(container, { childList: true, subtree: true }); -})(); \ No newline at end of file + // Start the extension + init(); + + // Make cleanup function globally available for debugging + window.grokAutoScrollerCleanup = completeCleanup; + +})();