// Mobile Keyboard Overlay Handler // This script dynamically adjusts the layout when the virtual keyboard appears (function() { 'use strict'; // Only run on mobile devices if (!/Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { return; } let initialViewportHeight = window.innerHeight; let isKeyboardOpen = false; let activeInputContainer = null; // Track which input container is currently active let focusedInput = null; // Track the currently focused input element // Function to handle viewport changes (keyboard show/hide) function handleViewportChange() { const currentHeight = window.innerHeight; const heightDifference = initialViewportHeight - currentHeight; const threshold = 150; // Minimum height change to consider keyboard open const conversationContainer = document.querySelector('.conversation-container'); const allChatInputContainers = document.querySelectorAll('.chat-input-container'); const body = document.body; // Update debug counter const visibleInputs = Array.from(allChatInputContainers).filter( container => container.style.display !== 'none' ).length; body.setAttribute('data-visible-inputs', visibleInputs); if (heightDifference > threshold && !isKeyboardOpen) { // Keyboard opened isKeyboardOpen = true; console.log('Keyboard opened, adjusting layout. Found', allChatInputContainers.length, 'input containers'); if (conversationContainer) { conversationContainer.classList.add('keyboard-open'); conversationContainer.style.height = `${currentHeight * 0.4}px`; conversationContainer.style.maxHeight = `${currentHeight * 0.4}px`; } // Apply keyboard positioning to ALL chat input containers allChatInputContainers.forEach((chatInputContainer, index) => { if (chatInputContainer) { console.log(`Processing input container ${index + 1}/${allChatInputContainers.length}`); chatInputContainer.style.position = 'fixed'; chatInputContainer.style.bottom = '0'; chatInputContainer.style.left = '0'; chatInputContainer.style.right = '0'; chatInputContainer.style.zIndex = '1000'; chatInputContainer.style.background = 'white'; chatInputContainer.style.borderTop = '1px solid #e2e8f0'; chatInputContainer.style.padding = '15px'; chatInputContainer.style.paddingBottom = '20px'; chatInputContainer.style.boxShadow = '0 -2px 10px rgba(0, 0, 0, 0.1)'; chatInputContainer.style.margin = '0'; chatInputContainer.style.borderRadius = '0'; chatInputContainer.classList.add('keyboard-fixed'); // Ensure the input area is properly styled const inputArea = chatInputContainer.querySelector('.input-area'); if (inputArea) { inputArea.style.margin = '0'; inputArea.style.borderRadius = '20px'; inputArea.style.background = '#f8f9fa'; } } }); // Hide non-active input containers if (activeInputContainer) { allChatInputContainers.forEach(container => { if (container !== activeInputContainer) { container.style.display = 'none'; console.log('Hiding non-active input container'); } else { console.log('Keeping active input container visible'); } }); } body.classList.add('keyboard-open'); // Update debug counter after hiding const visibleAfter = Array.from(allChatInputContainers).filter( container => container.style.display !== 'none' ).length; body.setAttribute('data-visible-inputs', visibleAfter); } else if (heightDifference <= threshold && isKeyboardOpen) { // Keyboard closed isKeyboardOpen = false; console.log('Keyboard closed, restoring layout for', allChatInputContainers.length, 'input containers'); if (conversationContainer) { conversationContainer.classList.remove('keyboard-open'); conversationContainer.style.removeProperty('height'); conversationContainer.style.removeProperty('max-height'); } // Reset ALL chat input containers allChatInputContainers.forEach((chatInputContainer, index) => { if (chatInputContainer) { console.log(`Restoring input container ${index + 1}/${allChatInputContainers.length}`); chatInputContainer.style.removeProperty('position'); chatInputContainer.style.removeProperty('bottom'); chatInputContainer.style.removeProperty('left'); chatInputContainer.style.removeProperty('right'); chatInputContainer.style.removeProperty('z-index'); chatInputContainer.style.removeProperty('background'); chatInputContainer.style.removeProperty('border-top'); chatInputContainer.style.removeProperty('padding'); chatInputContainer.style.removeProperty('padding-bottom'); chatInputContainer.style.removeProperty('box-shadow'); chatInputContainer.style.removeProperty('margin'); chatInputContainer.style.removeProperty('border-radius'); chatInputContainer.style.removeProperty('display'); chatInputContainer.classList.remove('keyboard-fixed'); // Reset input area styles const inputArea = chatInputContainer.querySelector('.input-area'); if (inputArea) { inputArea.style.removeProperty('margin'); inputArea.style.removeProperty('border-radius'); inputArea.style.removeProperty('background'); } } }); activeInputContainer = null; body.classList.remove('keyboard-open'); body.removeAttribute('data-visible-inputs'); } } // Function to scroll input into view when focused function scrollInputIntoView(inputElement) { setTimeout(() => { // Always ensure input is visible when focused const elementRect = inputElement.getBoundingClientRect(); const viewportHeight = window.innerHeight; const keyboardHeight = initialViewportHeight - viewportHeight; // If there's likely a keyboard (height reduced by significant amount) if (keyboardHeight > 150) { const availableHeight = viewportHeight; const inputBottom = elementRect.bottom; // If input is in bottom 40% of available viewport, scroll it up if (inputBottom > availableHeight * 0.6) { // Scroll the input container's parent into view const container = inputElement.closest('.conversation-container') || inputElement.closest('.thinking-section'); if (container) { container.scrollTop = container.scrollHeight - availableHeight * 0.5; } // Also try scrolling the window window.scrollTo({ top: window.scrollY + (inputBottom - availableHeight * 0.4), behavior: 'smooth' }); } } }, 100); // Shorter delay for more responsive feel } // Enhanced focus handler for chat textarea function handleChatInputFocus(event) { const chatTextarea = event.target; focusedInput = chatTextarea; // Add focused class for styling chatTextarea.classList.add('focused'); // Immediately apply keyboard-friendly positioning const chatInputContainer = chatTextarea.closest('.chat-input-container'); if (chatInputContainer && window.innerWidth <= 768) { // Set this as the active input container activeInputContainer = chatInputContainer; // Hide all other input containers immediately const allInputContainers = document.querySelectorAll('.chat-input-container'); allInputContainers.forEach(container => { if (container !== chatInputContainer) { container.style.display = 'none'; } }); // Apply keyboard positioning immediately on focus setTimeout(() => { chatInputContainer.style.position = 'fixed'; chatInputContainer.style.bottom = '0'; chatInputContainer.style.left = '0'; chatInputContainer.style.right = '0'; chatInputContainer.style.zIndex = '1001'; chatInputContainer.style.background = 'white'; chatInputContainer.style.borderTop = '1px solid #e2e8f0'; chatInputContainer.style.padding = '15px'; chatInputContainer.style.paddingBottom = '25px'; chatInputContainer.style.boxShadow = '0 -2px 10px rgba(0, 0, 0, 0.1)'; chatInputContainer.style.margin = '0'; chatInputContainer.style.display = 'block'; // Ensure this one is visible chatInputContainer.classList.add('input-focused'); // Add class to body for CSS targeting (fallback for :has() selector) document.body.classList.add('input-focused-active'); // Adjust conversation container const conversationContainer = document.querySelector('.conversation-container'); if (conversationContainer) { conversationContainer.style.paddingBottom = '80px'; conversationContainer.style.marginBottom = '0'; } }, 50); } // Scroll into view if keyboard is likely to appear scrollInputIntoView(chatTextarea); // For iOS, prevent zoom by ensuring font-size is 16px if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { chatTextarea.style.fontSize = '16px'; } } function handleChatInputBlur(event) { event.target.classList.remove('focused'); // Clear focused input reference if (focusedInput === event.target) { focusedInput = null; } // Clean up keyboard positioning when input loses focus const chatInputContainer = event.target.closest('.chat-input-container'); if (chatInputContainer && chatInputContainer.classList.contains('input-focused')) { setTimeout(() => { // Only remove if keyboard is not detected as open if (!isKeyboardOpen) { // Show all input containers again const allInputContainers = document.querySelectorAll('.chat-input-container'); allInputContainers.forEach(container => { container.style.removeProperty('display'); }); chatInputContainer.style.removeProperty('position'); chatInputContainer.style.removeProperty('bottom'); chatInputContainer.style.removeProperty('left'); chatInputContainer.style.removeProperty('right'); chatInputContainer.style.removeProperty('z-index'); chatInputContainer.style.removeProperty('background'); chatInputContainer.style.removeProperty('border-top'); chatInputContainer.style.removeProperty('padding'); chatInputContainer.style.removeProperty('padding-bottom'); chatInputContainer.style.removeProperty('box-shadow'); chatInputContainer.style.removeProperty('margin'); chatInputContainer.classList.remove('input-focused'); // Remove body class document.body.classList.remove('input-focused-active'); // Reset conversation container const conversationContainer = document.querySelector('.conversation-container'); if (conversationContainer) { conversationContainer.style.removeProperty('padding-bottom'); conversationContainer.style.removeProperty('margin-bottom'); } // Clear active input container if (activeInputContainer === chatInputContainer) { activeInputContainer = null; } } }, 300); // Delay to avoid flicker } } // Debounced resize handler let resizeTimeout; function debouncedViewportChange() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(handleViewportChange, 150); } // Initialize when DOM is ready function initialize() { console.log('Initializing mobile keyboard handler'); // Store initial viewport height initialViewportHeight = window.innerHeight; // Listen for viewport changes window.addEventListener('resize', debouncedViewportChange); // Listen for orientation changes window.addEventListener('orientationchange', () => { setTimeout(() => { initialViewportHeight = window.innerHeight; handleViewportChange(); }, 500); // Wait for orientation change to complete }); // Add focus/blur handlers for chat inputs (using event delegation for dynamic content) document.addEventListener('focus', (event) => { if (event.target.matches('.chat-textarea, #question-input, .step-thinking-space textarea')) { handleChatInputFocus(event); } }, true); document.addEventListener('blur', (event) => { if (event.target.matches('.chat-textarea, #question-input, .step-thinking-space textarea')) { handleChatInputBlur(event); } }, true); // Listen for new chat input containers being added to the DOM if (window.MutationObserver) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // Element node // Check if the added node contains chat input containers const newInputContainers = node.querySelectorAll ? node.querySelectorAll('.chat-input-container') : []; if (newInputContainers.length > 0) { console.log('New chat input containers detected:', newInputContainers.length); // If keyboard is currently open and we have an active input, hide the new ones if (isKeyboardOpen && activeInputContainer) { newInputContainers.forEach(container => { if (container !== activeInputContainer) { container.style.display = 'none'; } }); } } } }); }); }); // Observe the conversation container for new additions const conversationContainer = document.querySelector('.conversation-container'); if (conversationContainer) { observer.observe(conversationContainer, { childList: true, subtree: true }); } // Also observe the main content area const mainContent = document.querySelector('.main-content, .thinking-steps'); if (mainContent) { observer.observe(mainContent, { childList: true, subtree: true }); } } // Visual Viewport API support (newer browsers) if (window.visualViewport) { window.visualViewport.addEventListener('resize', () => { const heightDifference = window.innerHeight - window.visualViewport.height; if (heightDifference > 150 && !isKeyboardOpen) { handleViewportChange(); } else if (heightDifference <= 150 && isKeyboardOpen) { handleViewportChange(); } }); } } // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } // Add utility class for JavaScript document.documentElement.classList.add('js-mobile-keyboard-handler'); })();