b6bda3d692
- 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
109 lines
5.7 KiB
Markdown
109 lines
5.7 KiB
Markdown
## Context
|
|
|
|
The current terminal implementation (`apps/web/src/components/terminal.tsx`) uses xterm.js with a fixed header and minimal mobile considerations. The terminal page (`apps/web/src/pages/terminal.tsx`) renders inside the standard AppShell layout (`apps/web/src/components/app-shell.tsx`), which consumes significant viewport space on mobile devices.
|
|
|
|
On mobile devices (viewport < 768px):
|
|
- The virtual keyboard covers 40-50% of the screen
|
|
- xterm.js touch events conflict with browser touch behavior
|
|
- Special keys (Ctrl, Esc, Tab, Arrows) are not available on mobile keyboards
|
|
- The AppShell header and sidebar waste precious screen real estate
|
|
- No mechanism exists to handle virtual keyboard appearance/disappearance
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
- Make terminal sessions practical on mobile devices for occasional use
|
|
- Provide access to special terminal keys without external keyboard
|
|
- Maximize terminal screen real estate on mobile
|
|
- Handle virtual keyboard gracefully
|
|
- Support full terminal functionality (vim, tmux, etc.)
|
|
|
|
**Non-Goals:**
|
|
- Native mobile app (stays web-based)
|
|
- Command palette / quick commands (future enhancement)
|
|
- Offline terminal access
|
|
- Mobile-first redesign of the entire application (terminal pages only)
|
|
- Gesture-based text selection (use xterm.js native)
|
|
|
|
## Decisions
|
|
|
|
### Decision: Collapsible AppShell, Not Hidden
|
|
|
|
**Choice**: Collapse AppShell to a minimal auto-hiding header instead of completely hiding it.
|
|
|
|
**Rationale**: Users need a way to navigate back and access the menu. Complete removal would trap users in the terminal page.
|
|
|
|
**Alternative considered**: Fullscreen mode with swipe-from-edge to reveal nav. Rejected because it's not discoverable and conflicts with browser gestures.
|
|
|
|
### Decision: Auto-Hide Header and Keys Strip
|
|
|
|
**Choice**: Both header and special keys strip auto-hide after 3 seconds of inactivity.
|
|
|
|
**Rationale**: Maximizes terminal space while keeping controls accessible. Tap to toggle visibility is intuitive.
|
|
|
|
**Alternative considered**: Always-visible fixed bars. Rejected because they permanently reduce terminal height by ~20%.
|
|
|
|
### Decision: Hidden Input for Keyboard Management
|
|
|
|
**Choice**: Use a hidden/transparent input element to maintain virtual keyboard focus.
|
|
|
|
**Rationale**: xterm.js handles keyboard input directly, but mobile browsers need a focused input to show the virtual keyboard. A hidden input bridges this gap without interfering with xterm.js rendering.
|
|
|
|
**Alternative considered**: Custom on-screen keyboard. Rejected because native virtual keyboards provide better UX (autocorrect, swipe typing, user's preferred keyboard layout).
|
|
|
|
### Decision: Special Keys as Bottom Strip, Not Floating
|
|
|
|
**Choice**: Fixed bottom strip that slides up, not floating action buttons.
|
|
|
|
**Rationale**: Bottom placement is thumb-friendly and doesn't obscure terminal content. Fixed position makes it always accessible.
|
|
|
|
**Alternative considered**: Floating action button that expands to a menu. Rejected because it requires two taps for every special key.
|
|
|
|
### Decision: Debounced Resize (250ms)
|
|
|
|
**Choice**: 250ms debounce for resize events.
|
|
|
|
**Rationale**: Mobile keyboard animation is slow and produces multiple resize events. 250ms catches the final state without being sluggish.
|
|
|
|
**Alternative considered**: No debounce (immediate resize). Rejected because it causes excessive xterm.js refits and WebSocket resize messages.
|
|
|
|
### Decision: No New Dependencies
|
|
|
|
**Choice**: Implement using existing React, xterm.js, and browser APIs.
|
|
|
|
**Rationale**: All required functionality (touch events, viewport API, clipboard) is available natively. Adding libraries increases bundle size for a feature used occasionally.
|
|
|
|
**Alternative considered**: `react-use` hooks, `xterm-addon-webgl`. Rejected to keep bundle size down.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
**[Risk] Visual Viewport API unreliability** → **Mitigation**: Implement fallback using `window.innerHeight` comparison and focus-based detection. Accept imperfect behavior on older browsers.
|
|
|
|
**[Risk] xterm.js touch conflicts** → **Mitigation**: Use `touch-action: none` on terminal container. Let xterm.js handle its own touch events. Disable browser zoom to prevent pinch conflicts.
|
|
|
|
**[Risk] WebSocket drops on network change/backgrounding** → **Mitigation**: Implement reconnect logic with exponential backoff. Show clear status to user. Document that mobile networks may cause disconnections.
|
|
|
|
**[Risk] Clipboard API restrictions on mobile Safari** → **Mitigation**: Use both modern Clipboard API and `document.execCommand('copy')` fallback. Show user feedback on failure.
|
|
|
|
**[Risk] Screen rotation causes layout flicker** → **Mitigation**: Debounced resize. CSS transitions on layout changes. Consider `orientation` lock prompt for landscape preference.
|
|
|
|
**[Trade-off] Touch targets vs terminal density** → Larger touch targets mean fewer terminal cells visible. Compromise: 16px minimum font size provides readable text while keeping reasonable cell count.
|
|
|
|
## Migration Plan
|
|
|
|
No migration needed. This is a purely additive frontend change that doesn't affect data models, APIs, or existing desktop behavior. Desktop terminal experience remains unchanged.
|
|
|
|
**Deployment:**
|
|
1. Merge changes to dev branch
|
|
2. Verify on actual mobile devices (iOS Safari, Android Chrome)
|
|
3. Monitor for any desktop regressions
|
|
|
|
**Rollback:** Revert frontend commit. No database or API changes involved.
|
|
|
|
## Open Questions
|
|
|
|
1. Should we implement a landscape orientation prompt? ("Rotate for better experience")
|
|
2. Should font size preference sync across devices (via backend user config) or stay local?
|
|
3. What's the maximum number of special keys to show in the primary strip before requiring "More"?
|
|
4. Should the header show connection status, or is the terminal's own status dot sufficient?
|