Mobile experience
The same static web application serves phones, tablets and desktops. Below 768 px it switches to a
purpose-built phone layout. Source: apps/web/src/components/shell/mobile-shell.tsx,
components/terminal/{mobile-terminal,mobile-toolbar,keys,touch,controller}.ts(x),
hooks/use-viewport.ts, public/{sw.js,manifest.webmanifest}.
Verification status: WebKit iPhone 15 emulation in Playwright only. Nothing in this document has been verified on a physical iPhone, in Safari or as an installed PWA.
Layout
| Breakpoint | Width | Shell |
|---|---|---|
| mobile | < 768 px | phone shell: top bar, full-bleed content, bottom navigation |
| tablet | 768–1023 px | desktop shell components at a narrower width |
| desktop | ≥ 1024 px | sidebar, tabbed terminal workspace, right panel |
Phone shell:
- The root element is
position: fixedand sized fromwindow.visualViewport(CSS variables--vvh,--vvt), so its bottom edge is the top of the software keyboard. iOS scrolls the layout viewport to reveal a focused field; the shell undoes that scroll. - Bottom navigation: Terminal, Projects, Files, Tasks, Cluster, plus a "More" entry (Sessions, Skills, MCP, System, Settings). It hides while the keyboard is open.
viewport-fit=cover; touch targets are at least 44 px high on coarse pointers.- Files use a drill-down list and a full-screen editor instead of the desktop split view.
Terminal on a phone
MobileTerminal: a top bar (session title, state, connection indicator), the terminal edge to edge, and
the key toolbar directly above the keyboard.
- A session sheet lists the sessions for switching; a swipe from the left edge opens it.
- The terminal font size comes from the
mobileFontSizesetting (default 12, range 8–28) and can be changed by pinching. - On iOS the terminal uses xterm.js's DOM renderer (the WebGL renderer is used elsewhere when available).
- One WebSocket per session, same protocol as on desktop; the terminal is re-fitted on rotation and when the keyboard opens or closes, and the new size is sent to the server.
Key toolbar
Default keys (mobileToolbar in the user settings — the list is configurable):
esc tab ctrl alt shift ↑ ↓ ← → enter / | ~ -, followed by Paste (reads the
clipboard) and Hide keyboard.
- Buttons act on
pointerdownwithpreventDefaultand are not focusable, so the terminal keeps focus and the keyboard stays open. - Sticky modifiers (
ctrl,alt,shift): one tap = applies to the next key only; double tap = locked; tap while locked = off. They apply both to toolbar keys and to characters typed on the software keyboard (for examplectrlthencsends0x03). - Arrow keys honour the application cursor mode reported by xterm.js (DECCKM), so they work in programs that switch it.
- Keys that make sense repeated auto-repeat when held (after 420 ms, every 70 ms).
- The toolbar scrolls horizontally if it does not fit.
Touch gestures
| Gesture | Effect |
|---|---|
| Tap | focuses the terminal (opens the keyboard), clears a previous selection |
| One-finger drag | scrolls the scrollback with momentum; when the application inside tracks the mouse, wheel reports are sent instead |
| Long press (420 ms), then drag | starts and extends a text selection |
| Two-finger pinch | changes the font size |
Connection behaviour on mobile networks
Identical to desktop, and designed for it (see session-persistence.md):
- The session keeps running on the server whatever the phone does.
- Reconnect with backoff (0.5 s → 10 s) and immediately when the page becomes visible, the network comes back, or the page is restored from the back/forward cache.
- A connection that looks open after a suspension is probed with a ping and replaced if it does not answer within 2.5 s.
- Every re-attach replays the history and repaints the screen.
- While offline the terminal shows "Offline — the session keeps running on the server"; keystrokes are not queued.
Installable shell (PWA)
manifest.webmanifest: name "Claude Remote",display: standalone,start_url: /terminal/, 192/512 px icons (regular and maskable); Apple touch icon andapple-mobile-web-app-capablemetadata are set inapp/layout.tsx.sw.js(registered in production builds only) caches the static application shell: the HTML shells of the routes,/_next/static/*, fonts, icons and the manifest. Navigations are network-first with the cached shell as fallback; content-hashed assets are cache-first./api/*is never intercepted and never cached; WebSockets cannot be. No terminal data, API response or credential is stored by the service worker.- Offline, the cached shell loads and shows an explicit offline state. Nothing can be done offline.
Notifications on mobile
In-app only: a notification list and toasts while the application is open, fed by the events WebSocket
(session.waiting, session.exited, task.succeeded, task.failed). There is no push notification
when the application is closed or suspended.
Tests
apps/web/e2e/mobile.spec.ts, Playwright project mobile (devices['iPhone 15'], WebKit), against the
dev mock API — 4 tests, all passing on 2026-11-10:
- login, purpose-built layout, toolbar above the terminal, no horizontal overflow;
- toolbar keys send real sequences; sticky modifiers (one-shot, lock); focus is kept;
- session sheet, switching, font size, files drill-down with full-screen editor;
- larger phone (430 × 932) and landscape refit.
Key sequences and sticky-modifier logic are also unit-tested (apps/web/src/__tests__/keys.test.ts).
apps/web/e2e/setup-offline.spec.ts covers the offline shell.
Limitations / not verified
- No real-device test. Not verified: Safari on a real iPhone, the installed PWA in standalone mode,
the real iOS software keyboard and
visualViewportbehaviour, iOS suspension and resume, clipboard permission prompts for Paste, safe-area insets on real hardware, Android browsers. - Emulation runs WebKit on macOS with a touch-enabled viewport; it does not reproduce the iOS keyboard, Safari's toolbar, or background suspension.
- The mobile tests run against the mock API, not the real backend.
- Web Push is not implemented. The
webPushsetting is stored, but the client does not subscribe and the server does not send. A phone will not be alerted that a session is waiting unless the application is open. - iOS may evict a suspended PWA; the application then reloads and re-attaches (the session is unaffected), but this path was not observed on a device.
- Hardware keyboards on tablets, split view and stage manager were not tested.
- The laptop-independence test (laptop off, phone on cellular) has not been run.