feat: implement mobile terminal UX
- Add mobile viewport detection hook - Add virtual keyboard detection with fallback - Add auto-hide hook for header/keys strip - Add special keys mapping hook - Create MobileTerminalHeader, SpecialKeysStrip, SpecialKeysPanel components - Create MobileTerminalWrapper component - Update TerminalComponent with mobile support, font scaling, copy/paste, reconnection - Update AppShell to hide chrome on mobile terminal pages - Update TerminalPage to use MobileTerminalWrapper - Add comprehensive mobile terminal styles - TypeScript check passes - Build succeeds
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
|
||||
interface VirtualKeyboardState {
|
||||
isOpen: boolean;
|
||||
height: number;
|
||||
viewportHeight: number;
|
||||
}
|
||||
|
||||
export function useVirtualKeyboard() {
|
||||
const [state, setState] = useState<VirtualKeyboardState>({
|
||||
isOpen: false,
|
||||
height: 0,
|
||||
viewportHeight: typeof window !== "undefined" ? window.innerHeight : 0,
|
||||
});
|
||||
|
||||
const updateKeyboardState = useCallback(() => {
|
||||
const visualViewport = window.visualViewport;
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
if (visualViewport) {
|
||||
const viewportHeight = visualViewport.height;
|
||||
const keyboardHeight = windowHeight - viewportHeight;
|
||||
const isOpen = keyboardHeight > 100; // Threshold to avoid false positives
|
||||
|
||||
setState({
|
||||
isOpen,
|
||||
height: keyboardHeight,
|
||||
viewportHeight,
|
||||
});
|
||||
} else {
|
||||
// Fallback: compare window height to a stored reference
|
||||
// This is less reliable but works on older browsers
|
||||
const currentHeight = windowHeight;
|
||||
const isOpen = currentHeight < state.viewportHeight - 100;
|
||||
|
||||
setState((prev) => ({
|
||||
isOpen,
|
||||
height: isOpen ? prev.viewportHeight - currentHeight : 0,
|
||||
viewportHeight: isOpen ? prev.viewportHeight : currentHeight,
|
||||
}));
|
||||
}
|
||||
}, [state.viewportHeight]);
|
||||
|
||||
useEffect(() => {
|
||||
const visualViewport = window.visualViewport;
|
||||
|
||||
if (visualViewport) {
|
||||
visualViewport.addEventListener("resize", updateKeyboardState);
|
||||
visualViewport.addEventListener("scroll", updateKeyboardState);
|
||||
} else {
|
||||
window.addEventListener("resize", updateKeyboardState);
|
||||
}
|
||||
|
||||
// Initial check
|
||||
updateKeyboardState();
|
||||
|
||||
return () => {
|
||||
if (visualViewport) {
|
||||
visualViewport.removeEventListener("resize", updateKeyboardState);
|
||||
visualViewport.removeEventListener("scroll", updateKeyboardState);
|
||||
} else {
|
||||
window.removeEventListener("resize", updateKeyboardState);
|
||||
}
|
||||
};
|
||||
}, [updateKeyboardState]);
|
||||
|
||||
return state;
|
||||
}
|
||||
Reference in New Issue
Block a user