(function () { console.log("Keyboard shortcuts script loaded"); // --- Autoscroll control --- let lastSendTs = 0; let userPinnedScroll = false; let chatContainer = null; let chatObserver = null; const now = () => Date.now(); const SEND_AUTOSCROLL_WINDOW_MS = 2500; const NEAR_BOTTOM_PX = 120; const getChatContainer = () => { if (chatContainer && document.body.contains(chatContainer)) return chatContainer; // Prefer explicit elem_id container const root = document.getElementById('chatbot') || document.querySelector('#chatbot'); // Fallbacks: any visible gr-chatbot within left pane const candidates = []; if (root) candidates.push(root); candidates.push( document.querySelector('#left-pane .gr-chatbot'), document.querySelector('.gr-chatbot'), document.querySelector('#left-pane [data-testid="chatbot"]') ); for (const el of candidates) { if (!el) continue; // Try common inner scroll area let container = el.querySelector('[data-testid="bot"]') || el.querySelector('[data-testid="chatbot"]') || el; // Walk down to the element that actually scrolls const stack = [container]; while (stack.length) { const cur = stack.shift(); if (!cur) continue; const style = cur instanceof Element ? getComputedStyle(cur) : null; const canScroll = style && (style.overflowY === 'auto' || style.overflowY === 'scroll'); if (canScroll && cur.scrollHeight > cur.clientHeight + 10) { chatContainer = cur; break; } if (cur.children && cur.children.length) stack.push(...cur.children); } if (!chatContainer) chatContainer = container; if (chatContainer) break; } return chatContainer; }; const isNearBottom = (el) => { if (!el) return true; const distance = el.scrollHeight - el.scrollTop - el.clientHeight; return distance <= NEAR_BOTTOM_PX; }; const scrollToBottom = (el) => { if (!el) return; el.scrollTo({ top: el.scrollHeight, behavior: 'auto' }); }; const attachScrollListener = () => { const el = getChatContainer(); if (!el) return; el.addEventListener('scroll', () => { // If the user scrolls up away from bottom, pin the position userPinnedScroll = !isNearBottom(el); }, { passive: true }); }; const observeChat = () => { const el = getChatContainer(); if (!el) return; if (chatObserver) chatObserver.disconnect(); chatObserver = new MutationObserver(() => { const withinSendWindow = now() - lastSendTs < SEND_AUTOSCROLL_WINDOW_MS; const shouldScroll = withinSendWindow || (!userPinnedScroll && isNearBottom(el)); if (shouldScroll) scrollToBottom(el); }); chatObserver.observe(el, { childList: true, subtree: true, characterData: true }); }; const send = () => { // Try multiple selectors to find the send button const selectors = [ '#send-btn', 'button[id="send-btn"]', '.gr-button:contains("Send")', 'button:contains("Send")', '#send-btn button', '[data-testid*="send"]', 'button[variant="primary"]' ]; let btn = null; for (let selector of selectors) { try { if (selector.includes(':contains')) { // Handle :contains selector manually const buttons = document.querySelectorAll('button'); for (let button of buttons) { if (button.textContent.trim() === 'Send') { btn = button; break; } } } else { btn = document.querySelector(selector); } if (btn) { console.log("Found send button with selector:", selector); break; } } catch (e) { // Skip invalid selectors } } if (btn) { console.log("Clicking send button"); lastSendTs = now(); // When sending, allow autoscroll for initial response userPinnedScroll = false; // Ensure observers are up setTimeout(() => { attachScrollListener(); observeChat(); }, 0); btn.click(); return true; } else { console.log("Send button not found"); // Debug: log all buttons const allButtons = document.querySelectorAll('button'); console.log("All buttons found:", allButtons); return false; } }; const setupKeyboardShortcuts = () => { console.log("Setting up keyboard shortcuts"); // Initialize observers once UI is present setTimeout(() => { attachScrollListener(); observeChat(); }, 50); setTimeout(() => { attachScrollListener(); observeChat(); }, 400); document.addEventListener('keydown', (e) => { const isCmdEnter = (e.metaKey || e.ctrlKey) && e.key === 'Enter'; const isEnter = e.key === 'Enter' && !e.shiftKey && !e.metaKey && !e.ctrlKey; const isInputFocused = document.activeElement && (document.activeElement.matches('#chat-input textarea') || document.activeElement.matches('.chat-input-box textarea')); if ((isCmdEnter || (isEnter && isInputFocused)) && isInputFocused) { console.log("Send triggered"); e.preventDefault(); e.stopPropagation(); const success = send(); if (!success) { console.log("Failed to send, trying again in 100ms"); setTimeout(send, 100); } } if (e.key === 'Escape') { const input = document.querySelector('#chat-input textarea') || document.querySelector('.chat-input-box textarea'); if (input) { input.focus(); console.log("Focused input field"); } } }, true); console.log("Keyboard shortcuts set up"); }; // Multiple attempts to set up shortcuts const attempts = [100, 500, 1000, 2000]; attempts.forEach(delay => { setTimeout(() => { console.log(`Attempting setup after ${delay}ms`); setupKeyboardShortcuts(); }, delay); }); // Force light theme - disable dark mode switching const forceTheme = () => { // Remove any dark theme classes that might be added document.documentElement.classList.remove('dark'); document.body.classList.remove('dark'); // Set light color scheme document.documentElement.style.colorScheme = 'light'; // Override any theme preference that might be set if (window.matchMedia) { const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); // Override the matches property to always return false Object.defineProperty(darkModeQuery, 'matches', { value: false, writable: false }); } }; // Apply theme override immediately and on any DOM changes forceTheme(); // Watch for any changes and reapply theme override const observer = new MutationObserver(() => { forceTheme(); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class', 'data-theme', 'theme'] }); // Also set up immediately if DOM is ready if (document.readyState !== 'loading') { setupKeyboardShortcuts(); } else { document.addEventListener('DOMContentLoaded', setupKeyboardShortcuts); } })();