Skip to content

fix(web): keep send reachable while a turn is running on mobile - #4781

Open
AMohamedAakhil wants to merge 5 commits into
pingdotgg:mainfrom
AMohamedAakhil:fix/mobile-send-while-running
Open

fix(web): keep send reachable while a turn is running on mobile#4781
AMohamedAakhil wants to merge 5 commits into
pingdotgg:mainfrom
AMohamedAakhil:fix/mobile-send-while-running

Conversation

@AMohamedAakhil

@AMohamedAakhil AMohamedAakhil commented Jul 28, 2026

Copy link
Copy Markdown

Fixes #4775

What Changed

On viewports below sm, there is no way to send a follow-up while a turn is running.

Two guards overlap and between them leave no send path:

  • ComposerPrimaryActions.tsx returns only the stop button whenever isRunning is set, replacing the submit button.
  • shouldSubmitComposerOnEnter in composer-logic.ts returns !isMobileViewport && !shiftKey, so Enter never submits below sm.

This adds a showSendWhileRunning prop, wired to isMobileViewport, that renders the send button next to stop while a turn is running and the composer has sendable content. Desktop rendering is unchanged.

To render both buttons without duplicating markup, the running branch moves below the send button, which becomes a sendButton const. That reorders it past the plan follow-up branch, which is inert: showPlanFollowUpPrompt requires latestTurnSettled, so it cannot be set while a turn is running.

Non-test diff is +29 / -17 across two files.

Why

Sending mid-run is already supported. onSend in ChatView.tsx has no isRunning guard, and isSendBusy is activeLocalDispatch !== null rather than the turn state. Desktop users reach that path by pressing Enter. Mobile has no path to it at all.

So this is not a new follow-up mode, and it takes no position on queue vs steer semantics (#231). It only makes an action the app already supports reachable by touch.

The collapsed mobile composer hides how sharp this is. It passes isRunning={false} (ChatComposer.tsx:2748, :3032), so it shows a send arrow while a turn runs. But tapping it to type expands the composer, and the expanded footer is where send is swapped for stop. The affordance is only visible while you cannot type into it.

UI Changes

Captured in a real browser at 390x844 against a dev server, with a turn running and text typed in the composer.

Before, stop is the only action:

before

After, send sits next to stop:

after

Enumerating the composer's visible buttons in the same states gives ["Stop generation"] before and ["Stop generation", "Send message"] after.

No visual change on desktop: showSendWhileRunning defaults to false, and the only caller passes isMobileViewport.

Tests

ComposerPrimaryActions.test.ts became .test.tsx so the component can be rendered with renderToStaticMarkup, matching ComposerBannerStack.test.tsx. Three cases added:

  • running with content, Enter-to-send available: stop only
  • running with content, Enter-to-send unavailable: stop and send
  • running with an empty composer: stop only

vp test run apps/web/src/components/chat/ComposerPrimaryActions.test.tsx passes 11/11. vp lint and vp fmt --check are clean, and vp run --filter @t3tools/web typecheck exits 0.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes (no motion or interaction changes)

Note

Low Risk
Localized composer UI and prop wiring; send while running was already supported server-side—this only exposes it on mobile.

Overview
Mobile users can send follow-up messages while a turn is still running by showing Send next to Stop when the composer has sendable content. Desktop behavior is unchanged (showSendWhileRunning defaults to false).

Previously, ComposerPrimaryActions replaced the submit control with only Stop whenever isRunning was true, and Enter-to-send is disabled below sm, so mobile had no touch path to an already-supported send flow. ChatComposer now passes showSendWhileRunning={isMobileViewport}.

The send control is built once as a shared sendButton; when not running it is returned alone; when running, stop is shown and send is added only if showSendWhileRunning && hasSendableContent. Tests cover stop-only (desktop / empty composer) vs stop+send on mobile.

Reviewed by Cursor Bugbot for commit 9affbce. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Show Send button alongside Stop button during a running turn on mobile viewports

On mobile, Enter-to-send is unavailable, so users had no way to send a new message while a turn was running. This adds a showSendWhileRunning prop to ComposerPrimaryActions that, when true and content is sendable, renders the Send button alongside the Stop button. ChatComposer passes isMobileViewport as this prop value.

Macroscope summarized 9affbce.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f0368ddf-3858-467d-8818-8a99629dc341

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 28, 2026
macroscopeapp[bot]
macroscopeapp Bot previously approved these changes Jul 28, 2026
@macroscopeapp

macroscopeapp Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

Straightforward mobile UX bug fix that adds a send button alongside stop when a turn is running (since Enter-to-send is unavailable on mobile). Self-contained to chat composer with good test coverage and no impact on desktop behavior.

You can customize Macroscope's approvability policy. Learn more.

On viewports below `sm` there is no way to send a follow-up while a turn
is running. `ComposerPrimaryActions` replaces the submit button with the
stop button whenever `isRunning` is set, and `shouldSubmitComposerOnEnter`
returns false on mobile viewports, so the keyboard path that desktop users
rely on does not exist there.

Sending mid-run is already supported: `onSend` has no `isRunning` guard and
`isSendBusy` tracks the local dispatch rather than the turn state. Desktop
reaches it with Enter. This only makes the same action reachable by touch.

Adds `showSendWhileRunning`, wired to `isMobileViewport`, which renders the
send button next to stop while a turn runs and the composer has content.
Desktop rendering is unchanged.

The running branch moves below the send button so both can be rendered
without duplicating markup. That reorders it past the plan follow-up
branch, which is inert: `showPlanFollowUpPrompt` requires
`latestTurnSettled`, so it cannot be set while a turn is running.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AMohamedAakhil
AMohamedAakhil force-pushed the fix/mobile-send-while-running branch from 484c7e6 to 627d963 Compare July 28, 2026 20:30
@macroscopeapp
macroscopeapp Bot dismissed their stale review July 28, 2026 20:30

Dismissing prior approval to re-evaluate 627d963

@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Jul 28, 2026
@AMohamedAakhil

Copy link
Copy Markdown
Author

@juliusmarminge can you please take a look at this 👀

@AMohamedAakhil

Copy link
Copy Markdown
Author

Some extra context that may help triage this.

This is a recent regression, not long-standing behavior. shouldSubmitComposerOnEnter gained its isMobileViewport guard in 6a17629 (#3930, merged 18 Jul). That change is right on its own, since Enter should insert a newline on a software keyboard. But ComposerPrimaryActions already replaced send with stop whenever isRunning, so removing the Enter path removed the last remaining way to send mid-turn below sm. Before #3930, a mobile user could still interject by pressing Enter.

It lands hardest on T3 Connect. #4775 has a report reproducing it over a managed relay tunnel from an iPhone on 5G. With Connect now GA you can reach an agent from your phone on any network, but you cannot interject once a turn starts, which removes most of the value of remote control for anything longer than a quick question. Their workaround is a useful confirmation of the cause: rotating to landscape crosses the sm breakpoint, 430px to 932px on a 15 Pro Max, and Enter-to-send comes back. So the trigger is the breakpoint, not anything touch specific.

Status: checks are green, non-test diff is +29/-17 across two files, and desktop rendering is unchanged since showSendWhileRunning defaults to false and the only caller passes isMobileViewport.

No rush if this collides with the Steer and Queue work in #231. Happy to close it if that already covers the mobile affordance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: No way to send a follow-up mid-turn on mobile viewports — send button is replaced by stop and Enter-to-send is disabled below sm

1 participant