---
id: frontend-terminal
title: Frontend terminal UI
altitude: 1
topics:
- frontend
relations:
- type: refines
target: architecture
summary: The xterm.js-based mobile-friendly terminal UI with vendored assets, reconnection, and resize support.
---
# Frontend terminal UI
`frontend/index.html` is a single-file web app that renders a terminal using xterm.js. It's embedded into the Rust binary at compile time via `rust-embed`.
## Vendored dependencies
xterm.js and the fit addon are vendored in `frontend/vendor/` and served via `/assets/vendor/...`. No CDN dependency — the binary works fully offline.
## Connection flow
1. Checks `localStorage` for a refresh token. If present and no `?token=` in the URL, connects with the refresh token. Otherwise uses the initial token from the URL.
2. Opens a WebSocket to `/ws?token=...` or `/ws?refresh=...`, using `wss:` if the page was loaded over HTTPS.
3. On successful connection, sends a resize message with the current terminal dimensions.
## Message handling
- **Binary frames** (server → client): raw PTY output, written to the terminal via `term.write(new Uint8Array(data))`.
- **JSON text frames** (server → client): `refresh_token` (stored in localStorage) and `exit` (shows exit message, sets `sessionEnded` flag).
- **JSON text frames** (client → server): `input` (keystrokes from `term.onData`) and `resize` (from `fitAddon.fit()` on window/container resize).
## Mobile support
- **Touch detection**: `('ontouchstart' in window) || navigator.maxTouchPoints > 0` gates mobile-only features.
- **Toolbar**: fixed bar at the bottom with Esc, Tab, arrow keys, and Enter. Buttons use `flex: 1 1 0` to fill the full width for easy tapping. Sends escape sequences via the WebSocket (e.g. `\x1b[A` for up arrow, `\r` for Enter).
- **Virtual keyboard handling**: listens to `visualViewport` resize and scroll events. When the keyboard appears, the toolbar repositions above it and the terminal shrinks to fit the remaining space.
- **Touch scrolling**: overrides xterm.js's 1:1 pixel touch scroll with a 3x multiplier on the `.xterm-viewport` element. xterm's `scrollSensitivity` option only affects mouse wheel, not touch.
- **Scrollback**: 5000 lines (up from xterm default of 1000).
## Reconnection
On WebSocket close, the client reconnects after 2 seconds using the refresh token — unless `sessionEnded` is true (process exited), in which case it shows "Session closed" and stops.
## Status indicator
A fixed-position bar at the top shows: "Connecting...", "Connected" (green, auto-hides), "Reconnecting..." (yellow), or "Session closed" (red, persistent).