Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 105 additions & 41 deletions content.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand All @@ -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)
Expand All @@ -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;
}
</style>
Expand All @@ -83,7 +160,8 @@
// Периодически проверяем изменение URL (для случаев, когда popstate не срабатывает)
setInterval(checkUrlChange, 1000);

// Инициализируем панель
// Инициализируем кнопку и панель
toggleButton = createToggleButton();
navPanel = createNavPanel();

// Создает кнопку навигации для сообщения
Expand All @@ -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;
Expand All @@ -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();

Expand Down Expand Up @@ -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;
}
Expand Down