* fix(mobile): a swipe scrolls a full-screen program
A swipe over the terminal scrolled xterm's scrollback, and the alternate
screen keeps none, so over Claude Code's full-screen view, less or vim
it did nothing. On the alternate screen a swipe now turns the mouse
wheel instead, a notch a row, as on the desktop: a wheel report at the
finger for a program that asked for the mouse (SGR or the legacy
encoding, whichever it chose), arrow keys for one that did not.
* fix(mobile): pinyin keeps going when typing straight into the pane
Typing on the terminal itself goes through a hidden field, which was
emptied after every committed piece of text. iOS keeps its own copy of
the field's text, and a field cleared under it left the input method
stuck after the first character a pinyin composition committed. The
field now keeps its text while it has the keys and the pane is sent
what changed since the last send: characters taken off as DEL, then
what is new. It is emptied on blur.
While the field has the keys the pane's cursor is drawn solid, so a tap
on the terminal shows that it is ready to type into.
* feat(mobile): close a pane from the phone
The app could watch and drive a pane but never close one. Hold a pane's
row in the list, or pick Close pane from the terminal's menu: a sheet
says what will be stopped and on which machine, and only its danger
button closes it. From the terminal the app goes back to the list, which
drops the pane when the next tree arrives.
On the wire this is a new one-shot Open::ClosePane { pane_id, machine },
answered Ok or Denied. The gateway does what `tty7 pane close` does:
finds the workspace holding the pane, sends PaneClose, and hangs up the
panes it reports removed, on the gateway's machine or through the
desktop's existing link to a remote one. A gateway that predates the
variant drops the stream, which the app reports as "too old to close
panes from the phone".
3.7 KiB
Product
Platform
web
One design language on both phones: the app is a Tauri 2 WebView shipped to iOS and Android, deliberately not styled per OS. It must still behave like an installed app — safe areas, touch targets, no web-page chrome — rather than a website in a frame.
Users
People who run tty7 on their desktop (often with coding agents such as Claude Code or Codex in its panes) and are away from that desk. The primary job is operating a terminal remotely: open a pane on the desktop and type into it from the phone — run commands, answer an agent, interrupt something.
Product Purpose
tty7 mobile lets a paired phone watch and drive the desktop's tty7 panes. Success is reaching a pane and typing into it within seconds, from anywhere, without a VPN or port forwarding.
Positioning
The phone is a second window onto the same live panes, not an SSH client: it sees the desktop's workspaces, tabs, panes and agent status exactly as tty7 organises them, and connects peer-to-peer over iroh by public key, end-to-end encrypted.
Operating Context
- Pairing:
tty7-gateway pairon the desktop prints a QR code and atty7pair:code; the app currently accepts the pasted code (QR scanning is planned). - A paired machine shows its tree live: workspaces → tabs → panes, with each pane's title, cwd, and agent status, under the workspace it belongs to.
- A pane is observed, not attached: it keeps the desktop's size (cols × rows), and the phone fits that width by shrinking the font. Input goes in beside the desktop.
- Take over runs the pane at the phone's size until the phone lets go or leaves. The desktop keeps its window, says the phone has the pane, and can Take Back at any time; only that explicit button takes it back, never typing on the desktop.
- Connection state matters and is shown: direct vs relay path, round-trip time, connecting, offline.
Capabilities and Constraints
- Screens today: machines (paired list + pair form), one machine's tree, one pane's terminal (xterm.js) with an extra-keys bar (esc, tab, ⇧tab, ctrl latch, arrows, ^C, paste, | / ~ -) and a compose box that sends a whole message, then Enter. A copy view shows the pane's text for native selection.
- Forget a machine (requires re-pairing).
- Close a pane on the machine — hold its row, or Close pane in its ⋯ menu — after a confirmation, since it ends whatever runs there; the desktop loses it too.
- No framework: vanilla TypeScript with a tiny DOM helper; xterm.js for the terminal.
- Not yet: QR scanning, push notifications, keychain storage.
Brand Commitments
- Carries the desktop tty7 look: its Light and Dark presets (following the system
appearance), their ANSI palettes for the terminal, and the agent status dots —
Working blue
#3B82F6, Waiting amber#F59E0Bdrawn hollow so it differs in shape from Done, Done green#22C55E, Idle no dot. - The "Duo" logo mark: green
#3FDD8Ctile behind a near-black#17171Atile with a light›chevron (assets/logo.svg). - Agent brand icons exist in
assets/icons/agents/(claude, codex, gemini, …).
Evidence on Hand
Real data only comes from a paired desktop; there are no screenshots, testimonials or metrics to show and none should be invented.
Product Principles
- The terminal is the product: every other screen is a way to reach a pane fast.
- Same truth as the desktop: names, grouping, and status mean exactly what they mean on the desktop app.
- Honest connection state: the user always knows whether keystrokes will land.
- Never take the pane away from the desktop: a take-over only borrows its size, and the desktop can always take it back.