Skip to content

Mobile fullscreen input: API-backed Paste/Submit buttons - #935

Merged
selfcontained merged 4 commits into
mainfrom
agt_6f78aaff3ef5/build-mobile-fullscreen-submit-paste-buttons
Aug 11, 2026
Merged

Mobile fullscreen input: API-backed Paste/Submit buttons#935
selfcontained merged 4 commits into
mainfrom
agt_6f78aaff3ef5/build-mobile-fullscreen-submit-paste-buttons

Conversation

@selfcontained

Copy link
Copy Markdown
Owner

What

The mobile fullscreen keyboard input had three buttons (header Send, "Send + Enter", "Send Raw") that all wrote raw bytes straight to the pty over the terminal WebSocket — none of them reliably submitted.

Replaces them with exactly two buttons in the header, top-right (Cancel stays top-left):

  • Paste — injects the typed text without a trailing return
  • Submit — injects the typed text with a trailing return

Both go through a new server endpoint, POST /api/v1/agents/:id/terminal/inject-text, mirroring the existing inject-phrase/inject-pin routes: delivery via the injection coordinator + tmux paste-buffer (TmuxTerminal.pasteText/sendCommand) instead of a raw pty write, so multi-line text lands as a single paste.

Style

Bigger tap targets (44px) matching the mobile keyboard control bar, with an exaggerated outside-top-corner radius on Submit so it's easy to hit and doesn't clip on curved-screen devices.

Testing

  • pnpm run check — clean
  • pnpm run finalize:web — clean build
  • pnpm --filter @dispatch/server test — 155 suites / 2644 tests pass (added apps/server/test/inject-text-route.test.ts covering the new route's validation/auth/409 boundaries)
  • pnpm run test:e2e — 179 passed / 12 skipped (pre-existing live-terminal skips)
  • Manual Playwright validation on a 390×844 mobile viewport against a live dev instance: opened the fullscreen input, confirmed Submit executes a command and Paste leaves unsubmitted text on the prompt line.

🤖 Generated with Claude Code

selfcontainedand others added 4 commits August 11, 2026 07:25
The fullscreen keyboard input had three buttons (header Send, "Send +
Enter", "Send Raw") that all wrote raw bytes straight to the pty over
the terminal WebSocket — none of them reliably submitted.
Replace with exactly two buttons under the textarea:
- Paste (left): injects the text without a trailing return
- Submit (right): injects the text with a trailing return
Both now go through a new server endpoint,
POST /api/v1/agents/:id/terminal/inject-text, which mirrors the
existing inject-phrase/inject-pin routes — tmux paste-buffer delivery
via the injection coordinator instead of a raw pty write, so
multi-line text lands as a single paste.
Style: bigger tap targets (h-16) matching the keyboard control bar,
with an exaggerated outside-bottom-corner radius (rounded-bl/br-[28px])
so the buttons are easy to hit and don't clip on curved screens.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Match the intended layout: Cancel stays top-left, Paste and Submit
sit top-right in the header, and the separate button row under the
textarea is gone — the textarea now fills the remaining space.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Now that the buttons sit in the header instead of the bottom of the
screen, the curved-screen corner treatment doesn't apply — plain
button rounding matches the rest of the header controls.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Preserve the draft: sendFullscreenInput no longer clears the
textarea/closes the overlay until the inject-text request succeeds.
On failure the overlay stays open, the text is untouched, and focus
returns to the textarea so the user can retry.
- Give Cancel a real touch target: swap the bare text <button> for the
shared Button component at h-11, matching Paste/Submit sizing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@selfcontained
selfcontained merged commit b5bdb3c into mainAug 11, 2026
1 check passed
@selfcontained
selfcontained deleted the agt_6f78aaff3ef5/build-mobile-fullscreen-submit-paste-buttons branch August 11, 2026 13:42
selfcontained added a commit that referenced this pull request Aug 12, 2026
Worktrees deep-dive against workspace-prep.ts, shared/git/worktree.ts,
tmux/setup-script.ts, and archive.ts:
- "When creation fails" claimed the card shows an Attention badge. That
badge renders only for status `error`; a worktree failure goes through
markSetupFailed, which sets `stopped` — and the reconciler never
revisits it, since its query only selects running/stopping/creating/
archiving. What users actually see is the status line flipping to
Blocked with the git error, so say that.
- Both launch paths fetch the starting branch from origin and fork the
new branch from `origin/<branch>`; checking out the starting branch
directly uses the local copy. Neither was documented.
- Removing a worktree also deletes the branch Dispatch created for it,
and "Archive and remove worktree" force-deletes it (`branch -D`)
including unpushed commits. The section only described the directory.
- The tmux setup script skips the dependency install for terminal-type
agents.
Also from the diff since the last audit: the shortcut-pin `disabled`
state (#937) in the Pins paragraph, the two terminal injection routes
inject-text (#935) and inject-pin (#930) in the api-spec Terminal table,
and a new ambient tip for shortcut pins deep-linked to a new
media#media-sidebar anchor.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
selfcontained added a commit that referenced this pull request Aug 13, 2026
* Cover the mobile terminal toolbar's input contracts
The toolbar is the only input surface on mobile and had zero unit tests
after #935 reworked it with API-backed Paste/Submit. Almost everything it
does is a side effect on something outside its own markup — the escape
sequence handed to the terminal socket, the ref the terminal's onData
handler reads to fold in the ctrl modifier, and the inject-text POST that
deliberately does not go over the terminal WS — so none of it was
observable from the existing E2E check.
Mounts the real tree (including the copy-mode banner) with only the HTTP
seam, the toaster and the audio cue mocked, and pins: the exact escape
sequence for all seven shortcut keys; the ctrl modifier arming/disarming
ctrlPendingRef and clearing on the terminal's ctrl-consumed announcement
without writing the ref back; the 420ms press flash and its
next-frame re-arm; the copy-mode banner appearing for copy/exiting but
not for the "unknown" state the terminal reports before its first poll;
the sound-cue short-circuit; and the composer's inject-text payload,
empty-draft short-circuit, no-agent guard, in-flight gating, and
failure path keeping the draft, the overlay and focus.
33 of 35 mutants killed. The two survivors are diagnosed, not gaps: the
!isConnected guard inside sendKey is redundant with the button's disabled
attribute (mutating the pair together dies), and the onSuccess draft
clear is unobservable because the overlay unmounts on close.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Sharpen the ctrl-flash assertion to target the ctrl button
Review catch: the test named "does not flash the ctrl toggle" only
asserted on the other controls, so it stayed green if the ctrl button
started rendering the flash class or the ctrl handler started arming a
flash. Now it asserts the ctrl button carries neither the flash token nor
the animation class, and — since the flash slot is shared and
triggerFlash blanks it first — that arming ctrl mid-flash leaves the key
the user just pressed still lit.
That last assertion is what makes a flashing ctrl handler observable at
all: adding flashButtonClass("ctrl") to the button alone is inert,
because nothing ever sets a flash keyed "ctrl".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@selfcontained