diff --git a/CHANGELOG.md b/CHANGELOG.md index 6753c898..f31ec42e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # aicodeman +## 0.4.5 + +### Patch Changes + +- Fix mobile keyboard toolbar positioning on iOS Safari: toolbar (Run/Stop/Run Shell) was hidden behind the accessory bar when virtual keyboard was active due to overlapping CSS positions. Remove the aggressive safety check in `updateLayoutForKeyboard()` that incorrectly dismissed keyboard state when iOS scrolled the visual viewport during typing. Add Safari-bar CSS offset to accessory bar so it properly stacks above the toolbar. Remove the double-counted Safari-bar offset when keyboard is visible since the JS transform already covers the full distance. + ## 0.4.4 ### Patch Changes diff --git a/CLAUDE.md b/CLAUDE.md index 81f610a6..7b5121cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,7 @@ When user says "COM": 4. **Sync CLAUDE.md version**: Update the `**Version**` line below to match the new version from `package.json` 5. **Commit and deploy**: `git add -A && git commit -m "chore: version packages" && git push && npm run build && systemctl --user restart codeman-web` -**Version**: 0.4.4 (must match `package.json`) +**Version**: 0.4.5 (must match `package.json`) ## Project Overview diff --git a/package.json b/package.json index 466838db..063655a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aicodeman", - "version": "0.4.4", + "version": "0.4.5", "description": "The missing control plane for AI coding agents - run 20 autonomous agents with real-time monitoring and session persistence", "type": "module", "main": "dist/index.js", diff --git a/src/web/public/mobile-handlers.js b/src/web/public/mobile-handlers.js index 8b67b35b..4fbb9dcd 100644 --- a/src/web/public/mobile-handlers.js +++ b/src/web/public/mobile-handlers.js @@ -272,35 +272,31 @@ const KeyboardHandler = { const main = document.querySelector('.main'); if (this.keyboardVisible) { - // Calculate keyboard offset + // Calculate how far the toolbar (position:fixed, bottom:0) needs to + // translate up so it sits at the bottom of the visual viewport. + // This formula accounts for iOS scrolling the visual viewport (offsetTop) + // when the user types in xterm's hidden textarea. const layoutHeight = window.innerHeight; const visualBottom = window.visualViewport.offsetTop + window.visualViewport.height; - const keyboardOffset = layoutHeight - visualBottom; + const keyboardOffset = Math.max(0, layoutHeight - visualBottom); - // Safety: if keyboard is supposedly visible but offset is 0 or negative, - // the keyboard is actually gone — force dismiss. This catches cases where - // visualViewport.resize fires late or with intermediate values on iOS. - if (keyboardOffset <= 0) { - this.keyboardVisible = false; - document.body.classList.remove('keyboard-visible'); - this.onKeyboardHide(); - return; - } - - // Move toolbar up above keyboard + // Move toolbar and accessory bar above keyboard. + // When keyboardOffset is 0 (viewport scrolled to layout bottom), + // the bars are naturally positioned via their CSS bottom values — + // just clear the transforms. Never dismiss keyboard state here; + // that's handleViewportResize's job. if (toolbar) { - toolbar.style.transform = `translateY(${-keyboardOffset}px)`; + toolbar.style.transform = keyboardOffset > 0 ? `translateY(${-keyboardOffset}px)` : ''; } - - // Move accessory bar up (it sits above toolbar) if (accessoryBar) { - accessoryBar.style.transform = `translateY(${-keyboardOffset}px)`; + accessoryBar.style.transform = keyboardOffset > 0 ? `translateY(${-keyboardOffset}px)` : ''; } - // Shrink main content area so terminal doesn't extend behind keyboard - // Account for keyboard height + toolbar height (40px) + accessory bar (44px) - if (main) { - main.style.paddingBottom = `${keyboardOffset + 94}px`; + // Shrink main content area so terminal doesn't extend behind keyboard. + // Use stable keyboard height (not scroll-dependent) for padding. + const keyboardHeight = this.initialViewportHeight - (window.visualViewport.height || window.innerHeight); + if (main && keyboardHeight > 0) { + main.style.paddingBottom = `${keyboardHeight + 94}px`; } } else { this.resetLayout(); diff --git a/src/web/public/mobile.css b/src/web/public/mobile.css index 2d50325b..4450d2b7 100644 --- a/src/web/public/mobile.css +++ b/src/web/public/mobile.css @@ -534,6 +534,17 @@ html.mobile-init .file-browser-panel { bottom: calc(var(--safe-area-bottom) + (100vh - var(--app-height, 100vh))); } + /* When keyboard is visible the JS translateY already accounts for the full + distance from the visual-viewport bottom to the layout-viewport bottom + (keyboard + Safari bar). Remove the CSS Safari-bar offset to avoid + double-counting, which otherwise creates a visible gap above the keyboard. */ + .keyboard-visible.ios-device.safari-browser .toolbar { + bottom: var(--safe-area-bottom); + } + .keyboard-visible.ios-device.safari-browser .keyboard-accessory-bar { + bottom: calc(var(--safe-area-bottom) + 40px); + } + /* Show case selector in center */ .toolbar-center { display: flex !important; diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 00000000..5fca3f84 --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file