diff --git a/index.js b/index.js index b3ebd15..74effa9 100644 --- a/index.js +++ b/index.js @@ -519,6 +519,8 @@ async function initUI() { setupSettingsPopup(); addDiceQuickReply(); setupPlotButtons(); + setupMobileKeyboardHandling(); + setupContentEditableScrolling(); } /** @@ -1883,6 +1885,70 @@ function removeMobileTabs() { $('.rpg-divider').show(); } +/** + * Sets up mobile keyboard handling using Visual Viewport API. + * Prevents layout squashing when keyboard appears by detecting + * viewport changes and adding CSS classes for adjustment. + */ +function setupMobileKeyboardHandling() { + if (!window.visualViewport) { + // console.log('[RPG Mobile] Visual Viewport API not supported'); + return; + } + + const $panel = $('#rpg-companion-panel'); + let keyboardVisible = false; + + // Listen for viewport resize (keyboard show/hide) + window.visualViewport.addEventListener('resize', () => { + // Only handle if panel is open on mobile + if (!$panel.hasClass('rpg-mobile-open')) return; + + const viewportHeight = window.visualViewport.height; + const windowHeight = window.innerHeight; + + // Keyboard visible if viewport significantly smaller than window + // Using 75% threshold to account for browser UI variations + const isKeyboardShowing = viewportHeight < windowHeight * 0.75; + + if (isKeyboardShowing && !keyboardVisible) { + // Keyboard just appeared + keyboardVisible = true; + $panel.addClass('rpg-keyboard-visible'); + // console.log('[RPG Mobile] Keyboard opened'); + } else if (!isKeyboardShowing && keyboardVisible) { + // Keyboard just disappeared + keyboardVisible = false; + $panel.removeClass('rpg-keyboard-visible'); + // console.log('[RPG Mobile] Keyboard closed'); + } + }); +} + +/** + * Handles focus on contenteditable fields to ensure they're visible when keyboard appears. + * Uses smooth scrolling to bring focused field into view with proper padding. + */ +function setupContentEditableScrolling() { + const $panel = $('#rpg-companion-panel'); + + // Use event delegation for all contenteditable fields + $panel.on('focusin', '[contenteditable="true"]', function(e) { + const $field = $(this); + + // Small delay to let keyboard animate in + setTimeout(() => { + // Scroll field into view with padding + // Using 'center' to ensure field is in middle of viewport + $field[0].scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'nearest' + }); + }, 300); + }); +} + /** * Sets up the collapse/expand toggle button for side panels. */ diff --git a/style.css b/style.css index 3992bb8..05ee71a 100644 --- a/style.css +++ b/style.css @@ -3229,6 +3229,28 @@ body:has(.rpg-panel.rpg-position-left) #sheld { } } + /* ======================================== + MOBILE KEYBOARD HANDLING + ======================================== */ + + /* When mobile keyboard is visible, adjust panel layout to prevent squashing */ + .rpg-panel.rpg-keyboard-visible { + /* Prevent content from being pushed too far up */ + padding-bottom: 20px; + } + + /* Make sections more compact when keyboard visible */ + .rpg-panel.rpg-keyboard-visible .rpg-stats-section, + .rpg-panel.rpg-keyboard-visible .rpg-info-section, + .rpg-panel.rpg-keyboard-visible .rpg-thoughts-section { + padding: 8px 12px; + } + + /* Reduce spacing in stat bars when keyboard visible */ + .rpg-panel.rpg-keyboard-visible .rpg-stats-grid { + gap: 4px; + } + /* Disable collapsed state on mobile */ .rpg-panel.rpg-collapsed { max-width: 100dvw !important;