mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-22 14:06:02 +00:00
Fix Android keyboard covering Claude Code text input (#510)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: J Wylie <jfuginay@Js-MacBook-Pro-2.local> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
b41e808494
commit
ab2e57ab05
5 changed files with 139 additions and 4 deletions
4
.github/workflows/claude-code-review.yml
vendored
4
.github/workflows/claude-code-review.yml
vendored
|
|
@ -87,8 +87,8 @@ jobs:
|
|||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
|
||||
# Use Claude Opus 4 for more thorough reviews
|
||||
model: "claude-opus-4-20250514"
|
||||
# Use Claude Sonnet 4 (default model)
|
||||
# model: "claude-opus-4-20250514"
|
||||
|
||||
# Direct prompt for automated review with detailed instructions
|
||||
direct_prompt: |
|
||||
|
|
|
|||
54
ANDROID_KEYBOARD_FIX.md
Normal file
54
ANDROID_KEYBOARD_FIX.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Android Keyboard Fix for Claude Code
|
||||
|
||||
## Issue #504: Android keyboard covers Claude Code text input
|
||||
|
||||
### Problem
|
||||
When Claude Code runs inside a VibeTunnel terminal session on Android Chrome, the on-screen keyboard covers the text input area, preventing users from seeing what they're typing.
|
||||
|
||||
### Root Cause
|
||||
While VibeTunnel handles keyboard appearance for its own UI elements, embedded applications like Claude Code running inside the terminal don't benefit from these adjustments. The terminal content remains fixed in position when the keyboard appears.
|
||||
|
||||
### Solution Implemented
|
||||
|
||||
#### 1. Modern Viewport Units
|
||||
- Added `100dvh` (dynamic viewport height) units alongside existing `100vh`
|
||||
- Dynamic viewport units automatically adjust when the keyboard appears/disappears
|
||||
- Updated in `index.html` for better mobile browser support
|
||||
|
||||
#### 2. Interactive Widget Meta Tag
|
||||
- Added `interactive-widget=resizes-content` to viewport meta tag
|
||||
- This tells Android Chrome to resize the viewport when keyboard appears
|
||||
- Provides better native handling of keyboard appearance
|
||||
|
||||
#### 3. CSS Improvements
|
||||
- Made terminal viewport scrollable when keyboard is visible
|
||||
- Added specific styles for `data-keyboard-visible="true"` state
|
||||
- Ensured xterm viewport can scroll to show content behind keyboard
|
||||
- Used `env(keyboard-inset-height)` for future-proof keyboard handling
|
||||
|
||||
#### 4. Enhanced Keyboard Detection
|
||||
- Set CSS custom property `--keyboard-height` with actual keyboard height
|
||||
- Added `data-keyboard-visible` attribute to body element
|
||||
- Dispatch custom events `vibetunnel:keyboard-shown` and `vibetunnel:keyboard-hidden`
|
||||
- These allow embedded apps to react to keyboard state changes
|
||||
|
||||
### Benefits
|
||||
- Claude Code (and other embedded apps) can now be scrolled when keyboard appears
|
||||
- No more hidden input fields on Android devices
|
||||
- Better visual feedback and smoother transitions
|
||||
- Future-proof solution using modern CSS and viewport APIs
|
||||
|
||||
### Testing
|
||||
1. Open VibeTunnel on Android Chrome
|
||||
2. Start a Claude Code session
|
||||
3. Tap on Claude's input field
|
||||
4. Verify that:
|
||||
- The viewport adjusts when keyboard appears
|
||||
- You can scroll to see the input field
|
||||
- Text input remains visible while typing
|
||||
- Keyboard dismissal restores normal view
|
||||
|
||||
### Compatibility
|
||||
- Android Chrome: Full support
|
||||
- iOS Safari: Improved support (already had better handling)
|
||||
- Desktop browsers: No impact (mobile-only styles)
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no"
|
||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no, interactive-widget=resizes-content"
|
||||
/>
|
||||
<title>VibeTunnel - Terminal Multiplexer</title>
|
||||
<meta
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
width: 100%;
|
||||
height: 100vh;
|
||||
height: calc(var(--vh, 1vh) * 100); /* Dynamic viewport height for mobile */
|
||||
height: 100dvh; /* Use dynamic viewport height for better keyboard handling */
|
||||
overscroll-behavior-y: none; /* Prevent pull-to-refresh */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ const logger = createLogger('lifecycle-event-manager');
|
|||
// Re-export the interface for backward compatibility
|
||||
export type { LifecycleEventManagerCallbacks } from './interfaces.js';
|
||||
|
||||
// Threshold for determining when keyboard is considered visible (in pixels)
|
||||
const KEYBOARD_VISIBLE_THRESHOLD = 50;
|
||||
|
||||
export class LifecycleEventManager extends ManagerEventEmitter {
|
||||
private callbacks: LifecycleEventManagerCallbacks | null = null;
|
||||
private session: Session | null = null;
|
||||
|
|
@ -391,6 +394,16 @@ export class LifecycleEventManager extends ManagerEventEmitter {
|
|||
// Store keyboard height in state
|
||||
this.callbacks.setKeyboardHeight(keyboardHeight);
|
||||
|
||||
// Set CSS custom property for keyboard height
|
||||
document.documentElement.style.setProperty('--keyboard-height', `${keyboardHeight}px`);
|
||||
|
||||
// Add data attribute to body for CSS targeting
|
||||
if (keyboardHeight > KEYBOARD_VISIBLE_THRESHOLD) {
|
||||
document.body.setAttribute('data-keyboard-visible', 'true');
|
||||
} else {
|
||||
document.body.setAttribute('data-keyboard-visible', 'false');
|
||||
}
|
||||
|
||||
// Update quick keys component if it exists
|
||||
const quickKeys = this.callbacks.querySelector('terminal-quick-keys') as HTMLElement & {
|
||||
keyboardHeight: number;
|
||||
|
|
@ -401,8 +414,32 @@ export class LifecycleEventManager extends ManagerEventEmitter {
|
|||
|
||||
logger.log(`Visual Viewport keyboard height: ${keyboardHeight}px`);
|
||||
|
||||
// Dispatch custom events that embedded apps can listen to
|
||||
if (
|
||||
keyboardHeight > KEYBOARD_VISIBLE_THRESHOLD &&
|
||||
previousKeyboardHeight <= KEYBOARD_VISIBLE_THRESHOLD
|
||||
) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('vibetunnel:keyboard-shown', {
|
||||
detail: { height: keyboardHeight },
|
||||
})
|
||||
);
|
||||
} else if (
|
||||
keyboardHeight <= KEYBOARD_VISIBLE_THRESHOLD &&
|
||||
previousKeyboardHeight > KEYBOARD_VISIBLE_THRESHOLD
|
||||
) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('vibetunnel:keyboard-hidden', {
|
||||
detail: { height: 0 },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Detect keyboard dismissal (height drops to 0 or near 0)
|
||||
if (previousKeyboardHeight > 50 && keyboardHeight < 50) {
|
||||
if (
|
||||
previousKeyboardHeight > KEYBOARD_VISIBLE_THRESHOLD &&
|
||||
keyboardHeight < KEYBOARD_VISIBLE_THRESHOLD
|
||||
) {
|
||||
logger.log('Keyboard dismissed detected via viewport change');
|
||||
|
||||
// Check if we're using direct keyboard mode
|
||||
|
|
|
|||
|
|
@ -1982,6 +1982,49 @@ body.initial-session-load .session-flex-responsive > session-card:nth-child(n +
|
|||
}
|
||||
}
|
||||
|
||||
/* Android keyboard fix for embedded applications like Claude Code */
|
||||
@media (max-width: 768px) {
|
||||
/* When visualViewport changes due to keyboard, ensure terminal is scrollable */
|
||||
.terminal-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Make xterm viewport scrollable when keyboard appears */
|
||||
.xterm-viewport {
|
||||
overflow-y: auto !important;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
/* Ensure the terminal screen can be scrolled to see content behind keyboard */
|
||||
.xterm-screen {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* When keyboard is visible, adjust the entire view to be scrollable */
|
||||
.session-view-grid[data-keyboard-visible="true"] {
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Ensure app content adjusts for keyboard using env() */
|
||||
.app-container,
|
||||
vibe-app,
|
||||
session-view {
|
||||
height: 100vh;
|
||||
height: 100dvh; /* Dynamic viewport height accounts for keyboard */
|
||||
max-height: 100dvh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Additional helper for when keyboard height is detected */
|
||||
.session-view-grid[data-keyboard-visible="true"] .terminal-container {
|
||||
padding-bottom: env(keyboard-inset-height, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Phosphor Terminal Decay effect for exited sessions */
|
||||
.session-exited {
|
||||
filter: sepia(0.3) hue-rotate(45deg) brightness(0.8) contrast(1.2);
|
||||
|
|
|
|||
Loading…
Reference in a new issue