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;
+
+})();