Running your agents from the couch, on an iPad
You kick off a long agent run and want to leave the desk — but still keep an eye on it. We already had a mobile view for that. What we wanted in v1.5.1 was different: the whole desktop UI, in Safari on an iPad, over Tailscale, with almost every function intact. This is the friction we had to remove to make the couch feel like the desk.
The desk is the bottleneck
The whole point of running coding agents is that they work while you don't have to. But "supervising" still pins you to the desk: an agent stops at a Y/n prompt, hits a rate limit, or finishes a task, and someone has to notice and nudge it. Do that for a few hours and the chair, not the agent, becomes the bottleneck.
We already built a mobile experience for exactly this — a phone-shaped, DOM-rendered view you open over Tailscale to glance at sessions and tap a reply. It's great on a phone. But on an iPad it felt like wearing someone else's coat: a tablet has a real screen and a real keyboard, and the cut-down mobile view leaves most of the app behind. You can't reorder repos, drive the command palette, open the git panel, or use the smart-prompt buttons. You're glancing, not working.
So the goal for v1.5.1 was blunt: open the actual desktop UI in a browser on the iPad and have it just work — sidebar, tabs, panels, terminals, the lot — well enough to run a full session from the sofa.
Ship the real UI, don't rebuild it
The key decision was to not build an "iPad app." TUICommander already serves its frontend over HTTP for remote access, and the desktop bundle is a web app to begin with. So the iPad just loads the same UI the desktop window renders — same SolidJS app, same components — over a Tailscale link to your machine. Add it to the Home Screen and it runs full-screen as a PWA.
That decision is also why "make it work on iPad" turned into a list of small, specific bugs rather than a rewrite. Once the same UI runs in mobile Safari instead of a desktop WebView, every assumption it made about having a mouse, a hardware keyboard, and a native menu becomes a paper cut. v1.5.1 is mostly the story of removing those paper cuts.
The things that stood between us and the couch
Each of these was the difference between "almost usable" and "actually usable" on the iPad.
- Settings wouldn't load. Over Tailscale, every config request came back
403 — only accessible from localhost. The guard checked the socket address and nothing else, so a request that had already authenticated with a valid session token was still rejected just for arriving from a non-loopback IP. The app booted without its saved layout, and a duplicate tab appeared because session state never hydrated. We made a token-authenticated request first-class: the auth middleware marks every request that passes a gate, and the route guard now accepts loopback or an authenticated caller. On a Tailscale link (encrypted, device-pinned) that's exactly the trust model you want. - Scrolling went the wrong way. Touch-dragging the terminal scrolled it backwards — because the touch path reused the wheel's sign convention, and direct manipulation is the opposite of a wheel. Drag up, content goes up now.
- The keyboard covered what you were typing, and typing barely worked. The on-screen keyboard slid up over the prompt; dictation and soft-keyboard input wrote nothing because the desktop relies on
keydown+preventDefault, which mobile IME doesn't deliver; and holding Backspace deleted a single character. We lift only the focused terminal above the keyboard with a pure transform (no PTY resize, no SIGWINCH churn), forward IME input events straight to the PTY, and keep a tiny buffer so key-repeat actually repeats. - Scrolling the sidebar grabbed a repo. The repo list is drag-to-reorder, and on touch a 5-pixel vertical swipe — i.e. the start of a scroll — was enough to pick up a repo and start dragging it. Touch drags now arm on a long-press, the standard mobile gesture, so a swipe scrolls and a hold reorders. The mouse path is untouched.
- The "needs you" signal was unreliable. An agent parked at an interactive menu sometimes showed a calm green tab instead of the amber question state — the one cue you're relying on from across the room. The backend always knew; the frontend was re-deriving it and second-guessing itself. We made the backend the single source of truth and had the tab, the repo star, and notifications all mirror it.
- New tabs doubled. Opening a terminal in the browser sometimes spawned a second, phantom "PTY:" tab — a race where the server's "session created" broadcast beat the create call's own reply, so the client didn't recognize its own session. The browser now reserves the session id before it asks, and the echo is recognized instead of duplicated.
- No keyboard meant no shortcuts. On a tablet you don't have
Cmd+K, and the browser eats many shortcuts anyway — so half the app, which lives behind the command palette, was unreachable. The toolbar's external-editor button can't do anything in a browser (it launches local apps), so in that slot we now show a Command Palette button instead. One tap opens the palette, and everything reachable through it — commands, smart prompts, navigation — comes back.
The tradeoffs we took on purpose
Two of these are worth naming, because we chose them deliberately rather than stumbling into them.
Trusting an authenticated remote. Unlocking config (and agent/prompt actions) for any token-authenticated caller means the session token is the boundary. Over Tailscale that's fine — the link is encrypted and the device is pinned to your tailnet. Over plain HTTP on a local network the token travels in cleartext and could be sniffed, so that's a setup we flag rather than recommend. The default posture is unchanged: loopback always works, unauthenticated remotes are still refused, and the one true remote-code-execution surface (the debug JS-eval endpoint) stays loopback-only regardless.
Long-press to drag on touch. Making touch drags wait for a hold adds a beat before a reorder starts. But it's the only way scroll and reorder can share the same vertical gesture, it matches what every mobile OS already trains your thumb to expect, and it costs the desktop nothing — mouse and trackpad keep their instant 5-pixel threshold.
What it feels like now
You start a run at the desk, walk to the sofa, and open the Home Screen icon. The same sidebar is there with the same repos in the same order. You scroll a terminal with your thumb and it goes the right way. An agent hits a menu and the tab turns amber from across the room; you tap the terminal, dictate a reply, and the keyboard slides the prompt into view instead of burying it. Need to switch branches or fire a smart prompt? Tap the palette button. It is, deliberately, boring — which is the whole point. The couch should be a place you can actually work from, not a place where you squint at a read-only mirror and get up anyway.
Almost every desktop function is now within reach on the iPad. The gaps that remain are the genuinely physical ones — drag-and-drop to Finder, launching a local editor — and those belong to the desk by definition. Everything else came to the couch.