diff --git a/content.js b/content.js index 23f0ace..d9b6e76 100644 --- a/content.js +++ b/content.js @@ -3,6 +3,37 @@ let messageElements = new Map(); // Хранит пары текст:элемент let currentUrl = window.location.href; let navPanel; + let toggleButton; + let isNavPanelVisible = localStorage.getItem('grok-nav-panel-visible') !== 'false'; + + // Проверяет, есть ли сообщения в панели + function updatePanelVisibility() { + if (processedMessages.size > 0 && isNavPanelVisible) { + navPanel.style.display = 'flex'; + + // Проверяем, нужен ли скролл + setTimeout(() => { + if (navPanel.scrollHeight > navPanel.clientHeight) { + navPanel.style.overflowY = 'scroll'; + } else { + navPanel.style.overflowY = 'auto'; + } + }, 100); + } else { + navPanel.style.display = 'none'; + } + } + + // Функция для переключения панели + function toggleNavPanel() { + isNavPanelVisible = !isNavPanelVisible; + localStorage.setItem('grok-nav-panel-visible', isNavPanelVisible); + updatePanelVisibility(); + + // Обновляем иконку кнопки + toggleButton.innerHTML = isNavPanelVisible ? '📋' : '📋'; + toggleButton.style.opacity = isNavPanelVisible ? '1' : '0.6'; + } // Функция для очистки панели навигации const resetNavigation = () => { @@ -11,41 +42,87 @@ navPanel.remove(); } + // Удаляем старую кнопку если она существует + if (toggleButton) { + toggleButton.remove(); + } + // Сбрасываем данные processedMessages = new Set(); messageElements = new Map(); - // Создаем новую панель + // Создаем новую панель и кнопку + toggleButton = createToggleButton(); navPanel = createNavPanel(); // Обрабатываем уже существующие сообщения document.querySelectorAll('.message-bubble').forEach(processMessage); }; + // Создаем кнопку переключения панели + const createToggleButton = () => { + const button = document.createElement('button'); + button.id = 'nav-panel-toggle'; + button.innerHTML = '📋'; + button.title = 'Navigation Panel ein/ausblenden'; + button.style.cssText = ` + position: fixed; + top: 70px; + right: 20px; + z-index: 10001; + background-color: light-dark(hsl(0, 0%, 97%), hsl(0, 0%, 13%)); + border: 1px solid light-dark(hsl(0, 0%, 85%), hsl(0, 0%, 25%)); + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + font-size: 18px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; + `; + + button.addEventListener('click', toggleNavPanel); + button.addEventListener('mouseenter', () => { + button.style.transform = 'scale(1.05)'; + }); + button.addEventListener('mouseleave', () => { + button.style.transform = 'scale(1)'; + }); + + document.body.appendChild(button); + return button; + }; + // Создаем навигационную панель const createNavPanel = () => { const panel = document.createElement('div'); panel.id = 'message-nav-panel'; panel.style.cssText = ` position: fixed; - top: 70px; - right: 50px; - max-height: 80vh; + top: 120px; + right: 20px; + max-height: calc(80vh - 60px); overflow-y: auto; - z-index: 1; - background-color: hsl(0, 0.00%, 97.30%); + z-index: 10000; + background-color: light-dark(hsl(0, 0%, 97%), hsl(0, 0%, 13%)); + border: 1px solid light-dark(hsl(0, 0%, 85%), hsl(0, 0%, 25%)); border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); padding: 10px; display: flex; flex-direction: column; gap: 5px; - width: 350px; - display: none; /* Изначально скрываем панель */ + width: min(350px, calc(100vw - 60px)); + max-width: 350px; + display: ${isNavPanelVisible ? 'none' : 'none'}; /* Изначально скрываем панель */ + transition: opacity 0.3s ease, transform 0.3s ease; /* Стили для скроллбара */ scrollbar-width: thin; - scrollbar-color: rgba(0, 0, 0, 0.3) transparent; + scrollbar-color: light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)) transparent; `; // Стили для скроллбара в WebKit браузерах (Chrome, Safari) @@ -58,7 +135,7 @@ background: transparent; } #message-nav-panel::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.3); + background-color: light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.3)); border-radius: 10px; } @@ -83,7 +160,8 @@ // Периодически проверяем изменение URL (для случаев, когда popstate не срабатывает) setInterval(checkUrlChange, 1000); - // Инициализируем панель + // Инициализируем кнопку и панель + toggleButton = createToggleButton(); navPanel = createNavPanel(); // Создает кнопку навигации для сообщения @@ -99,9 +177,10 @@ button.style.cssText = ` text-align: left; padding: 5px 8px; - border: 1px solid #ddd; + border: 1px solid light-dark(#ddd, #444); border-radius: 4px; background-color: ${backgroundColor}; + color: light-dark(#333, #fff); cursor: pointer; font-size: 12px; white-space: nowrap; @@ -117,41 +196,24 @@ return button; }; - // Проверяет, есть ли сообщения в панели - const updatePanelVisibility = () => { - if (processedMessages.size > 0) { - navPanel.style.display = 'flex'; - - // Проверяем, нужен ли скролл - setTimeout(() => { - if (navPanel.scrollHeight > navPanel.clientHeight) { - navPanel.style.overflowY = 'scroll'; - } else { - navPanel.style.overflowY = 'auto'; - } - }, 100); - } else { - navPanel.style.display = 'none'; - } - }; // Функция для обработки сообщения function processMessage(message) { // Получаем уникальный идентификатор сообщения (может быть любым атрибутом) const messageId = message.id || message.dataset.messageId || Date.now() + Math.random().toString(36).substring(2, 9); - // Получаем текст сообщения, исключая содержимое div элементов + // Ищем текст в span элементах с классом whitespace-pre-wrap или берем весь текст сообщения 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') { - text += node.textContent; - } - }); + const textSpan = message.querySelector('.whitespace-pre-wrap'); + if (textSpan) { + text = textSpan.textContent; + } else { + // Fallback: берем текст из всего сообщения, исключая кнопки + const clonedMessage = message.cloneNode(true); + const buttonsToRemove = clonedMessage.querySelectorAll('button, .action-buttons'); + buttonsToRemove.forEach(btn => btn.remove()); + text = clonedMessage.textContent; + } text = text.trim(); @@ -218,7 +280,9 @@ // Если добавлен контейнер сообщения или сообщение if (node.classList && ( node.classList.contains('message-container') || - node.querySelector('.message-bubble') + node.classList.contains('group') || + node.querySelector('.message-bubble') || + node.querySelector('.whitespace-pre-wrap') )) { shouldRescan = true; }